/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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 Lesser General Public License for more details. * * Copyright (c) 2002-2017 Pentaho Corporation.. All rights reserved. */ package org.pentaho.gwt.widgets.client.wizards; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.DeckPanel; import com.google.gwt.user.client.ui.DockPanel; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import org.pentaho.gwt.widgets.client.dialogs.DialogBox; import org.pentaho.gwt.widgets.client.messages.Messages; /** * @author wseyler * * Framework for creating Wizards */ @SuppressWarnings( "deprecation" ) public abstract class AbstractWizardDialog extends DialogBox implements IWizardPanelListener { private static final int STEPS_COUNT = 15; // Defines the height of the steps ListBox private static final String WIZARD_DECK_PANEL = "pentaho-wizard-deck-panel"; //$NON-NLS-1$ private static final String WIZARD_BUTTON_PANEL = "pentaho-wizard-button-panel"; //$NON-NLS-1$ public static final String PENTAHO_BUTTON = "pentaho-button"; public enum ScheduleDialogType { SCHEDULER, BLOCKOUT } protected ScheduleDialogType dialogType; // gui elements protected Button backButton = new Button( Messages.getString( "dialog.button.back" ) ); protected Button nextButton = new Button( Messages.getString( "dialog.button.next" ) ); protected Button cancelButton = new Button( Messages.getString( "dialog.button.cancel" ) ); protected Button finishButton = new Button( Messages.getString( "dialog.button.finish" ) ); ListBox steps = new ListBox(); protected DeckPanel wizardDeckPanel = new DeckPanel(); VerticalPanel stepsList = new VerticalPanel(); private IWizardPanel[] wizardPanels; private boolean canceled = false; public AbstractWizardDialog( ScheduleDialogType type, String title, IWizardPanel[] panels, boolean autohide, boolean modal ) { super( autohide, modal ); dialogType = type; setText( title ); init(); layout(); setWizardPanels( panels ); show(); } public ScheduleDialogType getDialogType() { return dialogType; } /** * Init() * * Initialize the GUI Elements by setting up the required listeners and state NOTE: This method can be overridden to * provided new/additional functionality but should NEVER be called more than once during the lifecycle of the object */ protected void init() { backButton.getElement().setId( "wizard-back-button" ); nextButton.getElement().setId( "wizard-next-button" ); cancelButton.getElement().setId( "wizard-cancel-button" ); finishButton.getElement().setId( "wizard-finish-button" ); backButton.setStyleName( PENTAHO_BUTTON ); nextButton.setStyleName( PENTAHO_BUTTON ); cancelButton.setStyleName( PENTAHO_BUTTON ); finishButton.setStyleName( PENTAHO_BUTTON ); nextButton.addClickListener( new ClickListener() { @Override public void onClick( Widget sender ) { nextClicked(); } } ); backButton.addClickListener( new ClickListener() { @Override public void onClick( Widget sender ) { backClicked(); } } ); cancelButton.addClickListener( new ClickListener() { @Override public void onClick( Widget sender ) { cancelClicked(); } } ); finishButton.addClickListener( new ClickListener() { @Override public void onClick( Widget sender ) { finishClicked(); } } ); steps.setEnabled( false ); } protected boolean enableNext( int index ) { return ( (IWizardPanel) wizardDeckPanel.getWidget( index ) ).canContinue() && index < wizardDeckPanel.getWidgetCount() - 1; } protected boolean enableFinish( int index ) { return ( (IWizardPanel) wizardDeckPanel.getWidget( index ) ).canFinish(); } protected boolean enableBack( int index ) { return index > 0; } protected boolean showNext( int index ) { return wizardDeckPanel.getWidgetCount() > 1; } protected boolean showBack( int index ) { return wizardDeckPanel.getWidgetCount() > 1; } protected boolean showFinish( int index ) { return true; } protected int getIndex() { return wizardDeckPanel.getVisibleWidget(); } protected void nextClicked() { int oldIndex = steps.getSelectedIndex(); // The panel currently being displayed int newIndex = oldIndex + 1; // The panel that is going to be displayed // Get the actors (next and previous panels) IWizardPanel nextPanel = (IWizardPanel) wizardDeckPanel.getWidget( newIndex ); IWizardPanel previousPanel = (IWizardPanel) wizardDeckPanel.getWidget( oldIndex ); if ( !onNext( nextPanel, previousPanel ) ) { return; } // Update the Listeners previousPanel.removeWizardPanelListener( AbstractWizardDialog.this ); nextPanel.addWizardPanelListener( AbstractWizardDialog.this ); // Update the GUI with the current widget index; updateGUI( newIndex ); } protected void backClicked() { int oldIndex = getIndex(); int newIndex = oldIndex - 1; // The panel that is going to be displayed // Get the actors (next and previous panels) IWizardPanel previousPanel = (IWizardPanel) wizardDeckPanel.getWidget( newIndex ); IWizardPanel currentPanel = (IWizardPanel) wizardDeckPanel.getWidget( oldIndex ); if ( !onPrevious( previousPanel, currentPanel ) ) { return; } // Update the Listeners currentPanel.removeWizardPanelListener( AbstractWizardDialog.this ); previousPanel.addWizardPanelListener( AbstractWizardDialog.this ); // Update the GUI with the current widget index; updateGUI( newIndex ); } protected void finishClicked() { if ( onFinish() ) { AbstractWizardDialog.this.hide(); } } protected void cancelClicked() { canceled = true; AbstractWizardDialog.this.hide(); } /** * @param index * of the widget that will be shown. * * updateGUI(int index) sets up the panels and buttons based on the state of the widget (IWizardPanel) that * will be shown (index). */ protected void updateGUI( int index ) { stepsList.setVisible( wizardDeckPanel.getWidgetCount() > 1 ); finishButton.setVisible( showFinish( index ) ); backButton.setVisible( showBack( index ) ); nextButton.setVisible( showNext( index ) ); // Updates the selected step steps.setSelectedIndex( index ); // Shows the current IWizardPanel wizardDeckPanel.showWidget( index ); // Enables the next button if the current IWizardPanel can continue and we're not at the last IWizardPanel nextButton.setEnabled( enableNext( index ) ); // Back button always enabled unless we're on the first IWizardPanel backButton.setEnabled( enableBack( index ) ); // Current IWizardPanel can finish at any step. finishButton.setEnabled( enableFinish( index ) ); } /** * layout() * * Lays out the GUI elements. Should only be called ONCE during the objects lifecycle */ protected void layout() { // Create the overall container to be displayed in the dialog SimplePanel deckWrapper = new SimplePanel(); deckWrapper.setHeight( "100%" ); deckWrapper.setWidth( "100%" ); deckWrapper.setStyleName( "dialog-content" ); DockPanel content = new DockPanel(); // Create the Steps and add it to the content stepsList = new VerticalPanel(); stepsList.add( new Label( Messages.getString( "dialog.steps" ) ) ); steps.setVisibleItemCount( STEPS_COUNT ); stepsList.add( steps ); // steps.setSize("30%", "100%"); content.add( stepsList, DockPanel.WEST ); // Add the wizardPanels to the Deck and add the deck to the content // wizardDeckPanel.setSize("70%", "100%"); deckWrapper.setWidget( wizardDeckPanel ); content.add( deckWrapper, DockPanel.CENTER ); wizardDeckPanel.addStyleName( WIZARD_DECK_PANEL ); // Add the control buttons HorizontalPanel wizardButtonPanel = new HorizontalPanel(); wizardButtonPanel.setSpacing( 2 ); // If we have only one button then we dont need to show the back and next button. wizardButtonPanel.add( backButton ); wizardButtonPanel.add( nextButton ); wizardButtonPanel.add( finishButton ); wizardButtonPanel.add( cancelButton ); wizardButtonPanel.addStyleName( WIZARD_BUTTON_PANEL ); HorizontalPanel wizardButtonPanelWrapper = new HorizontalPanel(); wizardButtonPanelWrapper.setWidth( "100%" ); //$NON-NLS-1$ wizardButtonPanelWrapper.setHorizontalAlignment( HasHorizontalAlignment.ALIGN_RIGHT ); wizardButtonPanelWrapper.setVerticalAlignment( HasVerticalAlignment.ALIGN_BOTTOM ); wizardButtonPanelWrapper.add( wizardButtonPanel ); content.add( wizardButtonPanelWrapper, DockPanel.SOUTH ); content.setCellVerticalAlignment( wizardButtonPanelWrapper, HasVerticalAlignment.ALIGN_BOTTOM ); // Add the content to the dialog add( content ); content.setWidth( "100%" ); //$NON-NLS-1$ content.setHeight( "100%" ); //$NON-NLS-1$ content.setCellHeight( deckWrapper, "98%" ); } /** * @param wizardPanels * - IWizardPanel[] * * Creates a wizardDeckPanel with the contents of wizardPanels respecting the order. Creates a step panel * populated with the step names from the wizardPanels and then sets the current wizard panel to the first * panel in the list and updates the GUI. */ public void setWizardPanels( IWizardPanel[] wizardPanels ) { this.wizardPanels = wizardPanels; steps.clear(); wizardDeckPanel.clear(); if ( wizardPanels != null && wizardPanels.length > 0 ) { // Add new wizardPanels for ( IWizardPanel panel : wizardPanels ) { steps.addItem( panel.getName() ); wizardDeckPanel.add( (Widget) panel ); } ( (IWizardPanel) wizardDeckPanel.getWidget( 0 ) ).addWizardPanelListener( this ); if ( wizardPanels.length == 1 ) { // We only have one item so change the Finish button to ok. finishButton.setText( Messages.getString( "dialog.button.finish" ) ); } updateGUI( 0 ); } } /** * @return the current list if IWizardPanel in an array. */ public IWizardPanel[] getWizardPanels() { return wizardPanels; } /* * (non-Javadoc) * * @see * org.pentaho.gwt.widgets.client.wizards.IWizardPanelListener#panelChanged(org.pentaho.gwt.widgets.client.wizards * .IWizardPanel) */ @Override public void panelUpdated( IWizardPanel wizardPanel ) { int index = getIndex(); nextButton.setEnabled( enableNext( index ) ); finishButton.setEnabled( enableFinish( index ) ); } public boolean wasCancelled() { return canceled; } /** * abstract onFinish() * * Override for action to take when user presses the finish button. Return true if the wizard dialog should close * after the finish() method completes. */ protected abstract boolean onFinish(); /** * @param nextPanel * @param previousPanel * @return boolean if the "Next" operation should complete. * * Users should return true if the Wizard should proceed to the next panel. This would be a good spot to * retrieve/update state information between the two panels. This method is call before the next method * executes (ie. next panel is displayed). If nothing needs to be done simply return true */ protected abstract boolean onNext( IWizardPanel nextPanel, IWizardPanel previousPanel ); /** * @param previousPanel * @param currentPanel * @return boolean if the "Back" operation should complete * * Users should return true if the Wizard should proceed to the next panel. This would be a good spot to * retrieve/update state information between the two panels. This method is call before the back method * executes (ie. previous panel is displayed). If nothing needs to be done simply return true; */ protected abstract boolean onPrevious( IWizardPanel previousPanel, IWizardPanel currentPanel ); }