package pt.ist.vaadinframework.ui; import java.io.Serializable; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import com.vaadin.ui.AbstractOrderedLayout; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Component; import com.vaadin.ui.CustomComponent; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; public class Wizard extends CustomComponent { private static final String CSS_STEP = "v-wizard-step"; private static final String CSS_STEP_SELECTED = "v-wizard-step-selected"; private static final String CSS_WIZARD = "v-wizard"; private static final String CSS_WIZARD_CONTENT = "v-wizard-content"; private static final String CSS_WIZARD_STEP_BAR = "v-wizard-step-bar"; private static final String CSS_WIZARD_BUTTON_PANEL = "v-wizard-button-panel"; private static final String CSS_WIZARD_BUTTON_PREV = "v-wizard-button-prev"; private static final String CSS_WIZARD_BUTTON_NEXT = "v-wizard-button-next"; private final AbstractOrderedLayout layout; private final StepBar tabsheet; private final List steps; private int currentStepIndex; private WizardStep currentStep; private final HorizontalLayout content; private final ButtonPanel buttonPanel; private static Method ON_NEXT_STEP_EVENT_METHOD; private static Method ON_PREV_STEP_EVENT_METHOD; private static Method ON_WIZARD_COMPLETED_METHOD; static { try { ON_NEXT_STEP_EVENT_METHOD = NextStepEventListener.class.getDeclaredMethod("nextStep", new Class[] { OnNextStepEvent.class }); ON_PREV_STEP_EVENT_METHOD = PrevStepEventListener.class.getDeclaredMethod("prevStep", new Class[] { OnPrevStepEvent.class }); ON_WIZARD_COMPLETED_METHOD = WizardCompletedListener.class.getDeclaredMethod("complete", new Class[] { OnWizardCompleted.class }); } catch (final java.lang.NoSuchMethodException e) { // This should never happen throw new java.lang.RuntimeException("Internal error finding methods in Wizard"); } } public interface NextStepEventListener extends Serializable { public void nextStep(OnNextStepEvent event); } public interface PrevStepEventListener extends Serializable { public void prevStep(OnPrevStepEvent event); } public interface WizardCompletedListener extends Serializable { public void complete(OnWizardCompleted event); } public class OnStepEvent extends Component.Event { private static final long serialVersionUID = 1L; public OnStepEvent(Component source) { super(source); // TODO Auto-generated constructor stub } public WizardStep getWizardStep() { return (WizardStep) getSource(); } } public class OnNextStepEvent extends OnStepEvent { public OnNextStepEvent(Component source) { super(source); // TODO Auto-generated constructor stub } } public class OnPrevStepEvent extends OnStepEvent { public OnPrevStepEvent(Component source) { super(source); // TODO Auto-generated constructor stub } } public class OnWizardCompleted extends Component.Event { public OnWizardCompleted(Component source) { super(source); // TODO Auto-generated constructor stub } } private boolean fireWizardComplete() { if (currentStepIndex == steps.size() - 1) { fireEvent(new OnWizardCompleted(currentStep)); return true; } return false; } private void fireNextStep() { if (!fireWizardComplete()) { final int next = currentStepIndex + 1; if (changeCurrentStep(next)) { fireEvent(new OnNextStepEvent(currentStep)); } } } private void firePrevStep() { final int prev = currentStepIndex - 1; if (changeCurrentStep(prev)) { fireEvent(new OnPrevStepEvent(currentStep)); } } private boolean changeCurrentStep(int index) { if (currentStepIndex == index) { return false; } if (index >= 0 && index <= steps.size() - 1) { currentStepIndex = index; buttonPanel.prev.setVisible(currentStepIndex != 0); currentStep = steps.get(currentStepIndex); tabsheet.selectStep(currentStep); return true; } return false; } public void addListener(NextStepEventListener listener) { super.addListener(OnNextStepEvent.class, listener, ON_NEXT_STEP_EVENT_METHOD); } public void addListener(PrevStepEventListener listener) { super.addListener(OnPrevStepEvent.class, listener, ON_PREV_STEP_EVENT_METHOD); } public void addListener(WizardCompletedListener listener) { super.addListener(OnWizardCompleted.class, listener, ON_WIZARD_COMPLETED_METHOD); } class ButtonPanel extends HorizontalLayout implements ClickListener { public Button prev; public Button next; public ButtonPanel(String prevCaption, String nextCaption) { addStyleName(CSS_WIZARD_BUTTON_PANEL); prev = new Button(); prev.setCaption(prevCaption); prev.addListener(this); prev.addStyleName(CSS_WIZARD_BUTTON_PREV); addComponent(prev); next = new Button(); next.setCaption(nextCaption); next.addStyleName(CSS_WIZARD_BUTTON_NEXT); next.addListener(this); addComponent(next); } @Override public void buttonClick(ClickEvent event) { if (event.getButton().equals(next)) { fireNextStep(); } else if (event.getButton().equals(prev)) { firePrevStep(); } } } class StepBar extends CustomComponent { private final HorizontalLayout stepBar; private final Map headers; public StepBar() { stepBar = new HorizontalLayout(); stepBar.setWidth("100%"); stepBar.addStyleName(CSS_WIZARD_STEP_BAR); headers = new HashMap(); setCompositionRoot(stepBar); } public void addStep(Component c, String label) { final Label lblHeader = new Label(label); lblHeader.addStyleName(CSS_STEP); lblHeader.setImmediate(true); headers.put(steps.indexOf(c), lblHeader); stepBar.addComponent(lblHeader); } public void selectStep(Component c) { final int curr = steps.indexOf(c); for (Integer i : headers.keySet()) { final Label label = headers.get(i); if (i != curr) { label.removeStyleName(CSS_STEP_SELECTED); label.addStyleName(CSS_STEP); } else { label.addStyleName(CSS_STEP_SELECTED); } } content.removeAllComponents(); content.addComponent(c); } } public Wizard(final List steps, String prevCaption, String nextCaption) { addStyleName(CSS_WIZARD); currentStepIndex = -1; currentStep = null; this.steps = steps; layout = new VerticalLayout(); layout.setSizeFull(); tabsheet = new StepBar(); tabsheet.setWidth("100%"); content = new HorizontalLayout(); content.setSizeFull(); content.addStyleName(CSS_WIZARD_CONTENT); buttonPanel = new ButtonPanel(prevCaption, nextCaption); createVerticalWizard(prevCaption, nextCaption); changeCurrentStep(0); } private void createVerticalWizard(String prevCaption, String nextCaption) { for (WizardStep ws : steps) { tabsheet.addStep(ws, ws.getStepTitle()); } layout.addComponent(tabsheet); layout.addComponent(content); layout.setExpandRatio(content, 1f); layout.addComponent(buttonPanel); } @Override public void attach() { super.attach(); setSizeFull(); setCompositionRoot(layout); } }