org.rstudio.core.client.widget.Wizard.java Source code

Java tutorial

Introduction

Here is the source code for org.rstudio.core.client.widget.Wizard.java

Source

/*
 * Wizard.java
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
package org.rstudio.core.client.widget;

import java.util.ArrayList;

import org.rstudio.core.client.CommandWithArg;
import org.rstudio.core.client.theme.res.ThemeResources;

import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.layout.client.Layout.AnimationCallback;
import com.google.gwt.layout.client.Layout.Layer;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class Wizard<I, T> extends ModalDialog<T> {
    public Wizard(String caption, String okCaption, I initialData, WizardPage<I, T> firstPage,
            final ProgressOperationWithInput<T> operation) {
        super(caption, operation);
        initialData_ = initialData;
        okCaption_ = okCaption;
        firstPage_ = firstPage;
        activePage_ = firstPage;

        resetOkButtonCaption();
        setOkButtonVisible(false);

        addCloseHandler(new CloseHandler<PopupPanel>() {
            @Override
            public void onClose(CloseEvent<PopupPanel> arg0) {
                cleanupPage(firstPage_);
            }
        });

        // add next button
        nextButton_ = new ThemedButton("Next", new ClickHandler() {
            @Override
            public void onClick(ClickEvent arg0) {
                if (activePage_ instanceof WizardIntermediatePage<?, ?>) {
                    final WizardIntermediatePage<I, T> page = (WizardIntermediatePage<I, T>) activePage_;

                    // collect input from this page asynchronously and advance when
                    // we have input
                    page.collectIntermediateInput(getProgressIndicator(), new OperationWithInput<T>() {
                        @Override
                        public void execute(T input) {
                            if (page.validate(input)) {
                                intermediateResult_ = input;
                                page.advance();
                            }
                        }
                    });
                }
            }
        });
        nextButton_.setVisible(false);
        addActionButton(nextButton_);
    }

    @Override
    protected Widget createMainWidget() {
        WizardResources res = WizardResources.INSTANCE;
        WizardResources.Styles styles = res.styles();

        VerticalPanel mainWidget = new VerticalPanel();
        mainWidget.addStyleName(styles.mainWidget());

        headerPanel_ = new LayoutPanel();
        headerPanel_.addStyleName(styles.headerPanel());

        // layout constants
        final int kTopMargin = 5;
        final int kLeftMargin = 8;
        final int kCaptionWidth = 400;
        final int kCaptionHeight = 30;
        final int kPageUILeftMargin = 123;

        // first page caption
        subCaptionLabel_ = new Label(firstPage_.getPageCaption());
        subCaptionLabel_.addStyleName(styles.headerLabel());
        headerPanel_.add(subCaptionLabel_);
        headerPanel_.setWidgetLeftWidth(subCaptionLabel_, kTopMargin, Unit.PX, kCaptionWidth, Unit.PX);
        headerPanel_.setWidgetTopHeight(subCaptionLabel_, kLeftMargin, Unit.PX, kCaptionHeight, Unit.PX);

        // second page back button
        ImageResource bkImg = res.wizardBackButton();
        backButton_ = new Label("Back");
        backButton_.addStyleName(styles.wizardBackButton());
        backButton_.addStyleName(ThemeResources.INSTANCE.themeStyles().handCursor());
        headerPanel_.add(backButton_);
        headerPanel_.setWidgetLeftWidth(backButton_, kTopMargin - 2, Unit.PX, bkImg.getWidth(), Unit.PX);
        headerPanel_.setWidgetTopHeight(backButton_, kTopMargin - 2, Unit.PX, bkImg.getHeight(), Unit.PX);
        backButton_.setVisible(false);
        backButton_.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                goBack();
            }
        });

        // second page caption label
        pageCaptionLabel_ = new Label();
        pageCaptionLabel_.addStyleName(styles.headerLabel());
        headerPanel_.add(pageCaptionLabel_);
        headerPanel_.setWidgetLeftWidth(pageCaptionLabel_, kPageUILeftMargin, Unit.PX, kCaptionWidth, Unit.PX);
        headerPanel_.setWidgetTopHeight(pageCaptionLabel_, kLeftMargin, Unit.PX, kCaptionHeight, Unit.PX);
        pageCaptionLabel_.setVisible(false);

        mainWidget.add(headerPanel_);

        // main body panel for transitions
        bodyPanel_ = new LayoutPanel();
        bodyPanel_.addStyleName(styles.wizardBodyPanel());
        bodyPanel_.getElement().getStyle().setProperty("overflowX", "hidden");
        mainWidget.add(bodyPanel_);

        // add first page (and all sub-pages recursively)
        addAndInitializePage(firstPage_, true);

        setNextButtonState(firstPage_);

        return mainWidget;
    }

    private void addAndInitializePage(WizardPage<I, T> page, boolean visible) {
        page.setSize("100%", "100%");

        bodyPanel_.add(page);
        bodyPanel_.setWidgetTopBottom(page, 0, Unit.PX, 0, Unit.PX);
        bodyPanel_.setWidgetLeftRight(page, 0, Unit.PX, 0, Unit.PX);
        bodyPanel_.setWidgetVisible(page, visible);

        page.initialize(initialData_);

        CommandWithArg<WizardPage<I, T>> showPageCmd = new CommandWithArg<WizardPage<I, T>>() {
            @Override
            public void execute(WizardPage<I, T> page) {
                showPage(page);
            };
        };

        if (page instanceof WizardNavigationPage<?, ?>) {
            ((WizardNavigationPage<I, T>) page).setSelectionHandler(showPageCmd);
        } else if (page instanceof WizardIntermediatePage<?, ?>) {
            ((WizardIntermediatePage<I, T>) page).setNextHandler(showPageCmd);
        }

        // recursively initialize child pages
        ArrayList<WizardPage<I, T>> subPages = page.getSubPages();
        if (subPages != null) {
            for (int i = 0; i < subPages.size(); i++) {
                addAndInitializePage(subPages.get(i), false);
            }
        }
    }

    @Override
    protected T collectInput() {
        WizardPage<I, T> inputPage = activeInputPage();
        if (inputPage != null) {
            T input = ammendInput(inputPage.collectInput());
            return input;
        } else
            return null;
    }

    @Override
    protected boolean validate(T input) {
        WizardPage<I, T> inputPage = activeInputPage();
        if (inputPage != null)
            return inputPage.validate(input);
        else
            return false;
    }

    private WizardPage<I, T> activeInputPage() {
        if (activePage_ != null && !(activePage_ instanceof WizardNavigationPage<?, ?>)) {
            return activePage_;
        } else {
            return null;
        }
    }

    private void animate(final Widget from, final Widget to, boolean rightToLeft, final Command onCompleted) {
        // protect against multiple calls
        if (isAnimating_)
            return;

        bodyPanel_.setWidgetVisible(to, true);

        int width = getOffsetWidth();

        bodyPanel_.setWidgetLeftWidth(from, 0, Unit.PX, width, Unit.PX);
        bodyPanel_.setWidgetLeftWidth(to, rightToLeft ? width : -width, Unit.PX, width, Unit.PX);
        bodyPanel_.forceLayout();

        bodyPanel_.setWidgetLeftWidth(from, rightToLeft ? -width : width, Unit.PX, width, Unit.PX);
        bodyPanel_.setWidgetLeftWidth(to, 0, Unit.PX, width, Unit.PX);

        isAnimating_ = true;

        bodyPanel_.animate(300, new AnimationCallback() {
            @Override
            public void onAnimationComplete() {
                bodyPanel_.setWidgetVisible(from, false);

                bodyPanel_.setWidgetLeftRight(to, 0, Unit.PX, 0, Unit.PX);
                bodyPanel_.forceLayout();

                isAnimating_ = false;

                onCompleted.execute();
            }

            @Override
            public void onLayout(Layer layer, double progress) {
            }
        });
    }

    private void showPage(final WizardPage<I, T> page) {
        // ask whether the page will accept the navigation
        if (!page.acceptNavigation())
            return;

        // give the page the currently accumulated result, if any
        page.setIntermediateResult(intermediateResult_);

        // determine behavior based on whether this is standard page or 
        // a navigation page
        final boolean okButtonVisible = pageIsFinal(page);
        activeParentNavigationPage_ = activePage_;

        animate(activePage_, page, true, new Command() {
            @Override
            public void execute() {
                // set active page
                activePage_ = page;

                // update header
                subCaptionLabel_.setVisible(false);
                backButton_.setVisible(true);
                pageCaptionLabel_.setText(page.getPageCaption());
                pageCaptionLabel_.setVisible(true);

                // make ok button visible
                setOkButtonVisible(okButtonVisible);

                // if this is an intermediate page, make Next visible
                setNextButtonState(page);

                // let wizard and page know that the new page is active
                onPageActivated(page, okButtonVisible);
                page.onActivate();

                // set focus
                FocusHelper.setFocusDeferred(page);
            }
        });
    }

    private void goBack() {
        final boolean isNavigationPage = activeParentNavigationPage_ != null;

        // determine behavior based on whether we are going back to a
        // navigation page or a selector page
        final Widget toWidget = isNavigationPage ? activeParentNavigationPage_ : firstPage_;

        final String pageCaptionLabel = isNavigationPage ? activeParentNavigationPage_.getPageCaption() : "";

        final WizardPage<I, T> newActivePage = isNavigationPage ? activeParentNavigationPage_ : firstPage_;

        final CanFocus focusWidget = (CanFocus) toWidget;

        activeParentNavigationPage_ = null;

        animate(activePage_, toWidget, false, new Command() {
            @Override
            public void execute() {
                // update active page
                activePage_ = newActivePage;

                // update header
                subCaptionLabel_.setVisible(newActivePage == firstPage_);
                pageCaptionLabel_.setVisible(newActivePage != firstPage_ && isNavigationPage);
                pageCaptionLabel_.setText(pageCaptionLabel);

                setNextButtonState(newActivePage);
                backButton_.setVisible(newActivePage != firstPage_);

                // make ok button invisible
                setOkButtonVisible(false);

                // call hook
                onSelectorActivated();

                // set focus
                focusWidget.focus();
            }
        });
    }

    protected void onPageActivated(WizardPage<I, T> page, boolean okButtonVisible) {
    }

    protected void onSelectorActivated() {
    }

    protected T ammendInput(T input) {
        return input;
    }

    private void resetOkButtonCaption() {
        setOkButtonCaption(okCaption_);
    }

    private boolean pageIsFinal(WizardPage<I, T> page) {
        return page.getSubPages() == null || page.getSubPages().size() == 0;
    }

    private void setNextButtonState(WizardPage<I, T> page) {
        boolean isIntermediate = page instanceof WizardIntermediatePage<?, ?>;
        nextButton_.setVisible(isIntermediate);
        setDefaultOverrideButton(isIntermediate ? nextButton_ : null);
    }

    private void cleanupPage(WizardPage<I, T> page) {
        if (page == null)
            return;

        // notify child pages first (do cleanup in reverse order of construction)
        ArrayList<WizardPage<I, T>> subPages = page.getSubPages();
        if (subPages != null) {
            for (int i = 0; i < subPages.size(); i++) {
                cleanupPage(subPages.get(i));
            }
        }

        // clean this page
        page.onWizardClosing();
    }

    private final I initialData_;
    private T intermediateResult_;

    private final String okCaption_;

    private LayoutPanel headerPanel_;
    private Label subCaptionLabel_;
    private Label backButton_;
    private Label pageCaptionLabel_;
    private ThemedButton nextButton_;

    private LayoutPanel bodyPanel_;
    private WizardPage<I, T> firstPage_ = null;
    private WizardPage<I, T> activePage_ = null;
    private WizardPage<I, T> activeParentNavigationPage_ = null;
    private boolean isAnimating_ = false;
}