/* * Copyright (C) 2012 Jan Pokorsky * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package cz.cas.lib.proarc.webapp.client.widget; import com.smartgwt.client.types.Alignment; import com.smartgwt.client.types.VerticalAlignment; import com.smartgwt.client.widgets.Button; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.IButton; import com.smartgwt.client.widgets.Label; import com.smartgwt.client.widgets.events.ClickEvent; import com.smartgwt.client.widgets.events.ClickHandler; import com.smartgwt.client.widgets.layout.HLayout; import com.smartgwt.client.widgets.layout.Layout; import com.smartgwt.client.widgets.layout.VLayout; import cz.cas.lib.proarc.webapp.client.ClientMessages; import cz.cas.lib.proarc.webapp.client.ClientUtils; import cz.cas.lib.proarc.webapp.client.Editor; /** * The wizard allows to connect multiple layout widgets as steps * and define a workflow. * * @author Jan Pokorsky */ public class Wizard extends VLayout { private final String WIZARD_LABEL_PREFIX = "<b>%s</b> - %s"; private final HLayout bottomLayout; public enum StepKind {BACK, FORWARD, CANCEL} private final ClientMessages i18n; private LazyWizardStep[] steps; private int stepIdx = -1; private Button btnBack; private Button btnForward; private IButton btnCancel; private Layout stepContainer; private Label lblHeader; private boolean canStepForward = true; private boolean canStepBack = true; public Wizard(ClientMessages i18n, WizardStep... steps) { assert steps != null && steps.length > 0 && steps[0] != null; this.i18n = i18n; this.steps = LazyWizardStep.asLazySteps(steps); setHeight100(); setWidth100(); lblHeader = new Label(); lblHeader.setAutoHeight(); lblHeader.setPadding(4); lblHeader.setStyleName(Editor.CSS_PANEL_DESCRIPTION_TITLE); addMember(lblHeader); stepContainer = new Layout(); stepContainer.setHeight100(); addMember(stepContainer); bottomLayout = new HLayout(); bottomLayout.setAlign(Alignment.RIGHT); bottomLayout.setDefaultLayoutAlign(VerticalAlignment.CENTER); bottomLayout.setBorder("1px solid gray"); bottomLayout.setLayoutTopMargin(4); bottomLayout.setPadding(2); bottomLayout.setMembersMargin(2); btnBack = new Button(i18n.Wizard_ButtonBack_Title()); btnBack.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { stepBack(); } }); bottomLayout.addMember(btnBack); btnForward = new Button(i18n.Wizard_ButtonForward_Title()); btnForward.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { stepForward(); } }); bottomLayout.addMember(btnForward); btnCancel = new IButton("Cancel"); // bottomLayout.addMember(btnCancel); addMember(bottomLayout); } /** * Helper to use the wizard just as a container. * @param showSteps */ public void setShowButtons(boolean showSteps) { bottomLayout.setVisible(showSteps); } public void setWizardLabel(String title, String msg) { lblHeader.setContents(ClientUtils.format(WIZARD_LABEL_PREFIX, title, msg)); } public void stepInit() { stepIdx = 0; updateOnStepAction(stepIdx, stepIdx); } private void stepBack() { if (stepIdx > 0) { if (!steps[stepIdx].onStepAction(this, StepKind.BACK)) { // cancel step change return ; } updateOnStepAction(stepIdx, --stepIdx); } } private void stepForward() { if (stepIdx + 1 < steps.length) { if (!steps[stepIdx].onStepAction(this, StepKind.FORWARD)) { // cancel step change return ; } updateOnStepAction(stepIdx, ++stepIdx); } } public void moveAt(WizardStep step) { int oldStepIdx = stepIdx; stepIdx = getStepIndex(step); updateOnStepAction(oldStepIdx, stepIdx); } private int getStepIndex(WizardStep step) { for (int i = 0; i < steps.length; i++) { if (steps[i].getDelegate() == step) { return i; } } throw new IllegalStateException("Unknown step: " + step); } private void updateOnStepAction(int oldStep, int newStep) { if (oldStep >= 0 && oldStep != newStep) { steps[oldStep].onHide(this); } stepContainer.setMembers(steps[newStep].asWidget()); // reset buttons setBackButton(true, null); setForwardButton(true, null); this.canStepBack = true; this.canStepForward = true; steps[newStep].onShow(this); updateButtonStates(); } private void updateButtonStates() { boolean btnBackDisabled = !canStepBack || stepIdx < 1; boolean btnForwardDisabled = (!canStepForward) || (stepIdx + 1 >= steps.length); boolean btnCancelDisabled = stepIdx % 2 > 0; btnBack.setDisabled(btnBackDisabled); btnForward.setDisabled(btnForwardDisabled); btnCancel.setDisabled(btnCancelDisabled); } public WizardStep getActiveStep() { return steps[stepIdx]; } public void setCanStepForward(boolean allowed) { this.canStepForward = allowed; updateButtonStates(); } public void setCanStepBack(boolean allowed) { this.canStepBack = allowed; updateButtonStates(); } public void setBackButton(boolean show, String title) { setButton(btnBack, show, title == null ? i18n.Wizard_ButtonBack_Title() : title); } public void setForwardButton(boolean show, String title) { setButton(btnForward, show, title == null ? i18n.Wizard_ButtonForward_Title() : title); } private void setButton(Button btn, boolean show, String title) { if (show) { btn.show(); btn.setTitle(title); } else { btn.hide(); } } public static WizardStep emptyStep() { return new EmptyStep(); } /** * One step of the wizard. */ public interface WizardStep { public void onShow(Wizard wizard); public void onHide(Wizard wizard); /** * Notifies about click on a wizard button. * * @return {@code false} stands for cancel the action */ public boolean onStepAction(Wizard wizard, StepKind step); public Canvas asWidget(); } /** * Creates and holds widget on demand. */ private static final class LazyWizardStep implements WizardStep { private final WizardStep delegate; private Canvas widget; public LazyWizardStep(WizardStep step) { this.delegate = step; } @Override public void onShow(Wizard wizard) { delegate.onShow(wizard); } @Override public void onHide(Wizard wizard) { delegate.onHide(wizard); } @Override public boolean onStepAction(Wizard wizard, StepKind step) { return delegate.onStepAction(wizard, step); } @Override public Canvas asWidget() { if (widget == null) { widget = delegate.asWidget(); } return widget; } public WizardStep getDelegate() { return delegate; } public static LazyWizardStep[] asLazySteps(WizardStep[] steps) { if (steps == null) { return null; } LazyWizardStep[] handles = new LazyWizardStep[steps.length]; for (int i = 0; i < steps.length; i++) { handles[i] = new LazyWizardStep(steps[i]); } return handles; } } private static final class EmptyStep implements WizardStep { @Override public void onShow(Wizard wizard) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void onHide(Wizard wizard) { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean onStepAction(Wizard wizard, StepKind step) { throw new UnsupportedOperationException("Not supported yet."); } @Override public Canvas asWidget() { return new Canvas(); } } }