All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pietschy.wizard.Wizard Maven / Gradle / Ivy

/**
 * Wizard Framework
 * Copyright 2004 - 2005 Andrew Pietsch
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: Wizard.java,v 1.20 2005/05/22 07:13:29 pietschy Exp $
 */

package org.pietschy.wizard;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.AbstractBorder;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Iterator;

/**
 * The wizard class is the main entry point for creating wizards.  Typically you will create
 * the wizard with your own {@link WizardModel} implementation and then call {@link Wizard#showInFrame}
 * or one of the releated methods.
 * 

* In the simplest case, you would subclass {@link org.pietschy.wizard.models.StaticModel} and add a number * of {@link WizardStep} instances. *

 *    StaticModel model = new StaticModel();
 *    model.add(new MyFirestStep());
 *    model.add(...);
 *    ...
 *
 *    Wizard wizard = new Wizard(model);
 *    wizard.showInFrame("My Wizard");
 * 
* * @see org.pietschy.wizard.models.StaticModel * @see org.pietschy.wizard.models.DynamicModel * @see org.pietschy.wizard.models.MultiPathModel */ public class Wizard extends JPanel { protected static final int BORDER_WIDTH = 8; /** * When specified as the {@link #setDefaultExitMode(int) defaultExitMode}, this * causes the wizard to continue displaying the final step after finished has been pressed. The * wizard is closed when the user presses the close button. This allows the wizard to display a * final confirmation screen. * * @see #EXIT_ON_FINISH */ public static final int EXIT_ON_CLOSE = 1; /** * When specified as the {@link #setDefaultExitMode(int) defaultExitMode}, this * causes the wizard to exit immediately once finish is pressed and * {@link WizardStep#applyState()} has been invoked. * * @see #EXIT_ON_CLOSE */ public static final int EXIT_ON_FINISH = 2; private NextAction nextAction; private PreviousAction previousAction; private LastAction lastAction; private FinishAction finishAction; private CancelAction cancelAction; private CloseAction closeAction; private HelpAction helpAction; private HelpBroker helpBroker; private WizardStep activeStep; private WizardModel model; private int defaultExitMode = EXIT_ON_CLOSE; private JComponent titleComponent; private ButtonBar buttonBar; private JPanel viewPanel; private JPanel mainContainer; private JPanel overviewContainer; private boolean overviewVisible = true; private boolean canceled = false; private PropertyChangeListener viewListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { handleViewChange(); } }; /** * Creates a new Wizard that uses the specified {@link WizardModel}. * * @param model the model that the wizard is to use. */ public Wizard(WizardModel model) { if (model == null) throw new NullPointerException("models is null"); this.model = model; this.model.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("activeStep")) { handleStepChange(); } } }); nextAction = new NextAction(this); previousAction = new PreviousAction(this); lastAction = new LastAction(this); finishAction = new FinishAction(this); cancelAction = new CancelAction(this); closeAction = new CloseAction(this); helpAction = new HelpAction(this); // initialize all the wizard steps. for (Iterator iter = model.stepIterator(); iter.hasNext();) { ((WizardStep) iter.next()).init(this.model); } setLayout(new BorderLayout()); titleComponent = createTitleComponent(); buttonBar = createButtonBar(); mainContainer = new JPanel(new BorderLayout()); overviewContainer = new JPanel(new BorderLayout()); viewPanel = new JPanel(new BorderLayout()); viewPanel.setPreferredSize(calculatePreferredStepSize()); mainContainer.add(titleComponent, BorderLayout.NORTH); JPanel p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createEmptyBorder(BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH)); p.add(viewPanel, BorderLayout.CENTER); mainContainer.add(p, BorderLayout.CENTER); // mainContainer.add(buttonBar, BorderLayout.SOUTH); JPanel commandPanel = new JPanel(new BorderLayout()); commandPanel.add(new JSeparator(JSeparator.HORIZONTAL), BorderLayout.NORTH); commandPanel.add(buttonBar, BorderLayout.CENTER); mainContainer.add(commandPanel, BorderLayout.SOUTH); if (model instanceof OverviewProvider) { p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2), BorderFactory.createEtchedBorder())); // p.setBorder(new OverviewBorder()); p.add(((OverviewProvider) model).getOverviewComponent(), BorderLayout.CENTER); overviewContainer.add(p, BorderLayout.WEST); //overviewContainer.add(Box.createVerticalStrut(buttonBar.getPreferredSize().height + BORDER_WIDTH), BorderLayout.SOUTH); } add(mainContainer, BorderLayout.CENTER); add(overviewContainer, BorderLayout.WEST); configureOverviewContainer(); if (model instanceof HelpBroker) setHelpBroker((HelpBroker) model); this.model.reset(); } /** * Set the time when the wizard exits. The two allowable values are * {@link #EXIT_ON_FINISH} and {@link #EXIT_ON_CLOSE}. *

* The default value is {@link #EXIT_ON_CLOSE} * * @param defaultExitMode the default exit mode. */ public void setDefaultExitMode(int defaultExitMode) { if (defaultExitMode != EXIT_ON_FINISH && defaultExitMode != EXIT_ON_CLOSE) throw new IllegalArgumentException(); this.defaultExitMode = defaultExitMode; } /** * Gets the exit mode of the wizard, being either {@link #EXIT_ON_FINISH} or * {@link #EXIT_ON_CLOSE} * * @return the default close opertation. */ public int getDefaultExitMode() { return defaultExitMode; } /** * Called by the constructor to create the wizards title component. The default component will be an instance of * {@link DefaultTitleComponent} but subclasses may override to provide a custom implementation. Typically the * title component listens to the wizard model and updates its appearance accordingly. * * @return the wizards title component. * @see #getTitleComponent() */ protected JComponent createTitleComponent() { return new DefaultTitleComponent(this); } /** * Gets the component being used to render the wizards title. By default this will be an instance of * {@link DefaultTitleComponent}. Subclasses can change the default by overriding {@link #createTitleComponent()}. *

* For example, to activate the gradient background on the default title component you can call. *

    * ((DefaultTitleComponent) wizard.getTitleComponent()).setGradientBackground(true);
    * 
* * @return the wizards title component. */ public JComponent getTitleComponent() { return titleComponent; } /** * Called by the constructor to create the button bar. Subclasses may override to provide a custom {@link ButtonBar} * implementation. * * @return the wizards {@link ButtonBar}. */ protected ButtonBar createButtonBar() { return new ButtonBar(this); } /** * Resets the wizard. This method delegates directly to {@link WizardModel#reset}. */ public void reset() { canceled = false; getModel().reset(); }; /** * Gets the models this wizard is using. * * @return the wizard models. */ public WizardModel getModel() { return model; } /** * Checks the visibily of the overview panel that is displayed on the wizards left * panel. The overview panel will only be displayed if this property is true and * the current {@link WizardModel} implements {@link OverviewProvider}. */ public boolean isOverviewVisible() { return overviewVisible; } /** * Configures the visibily of the overview panel that is displayed on the wizards left * panel. This method will only have an effect if the current {@link WizardModel} implements * {@link OverviewProvider}. * * @param overviewVisible true to display the overview, false otherwise. */ public void setOverviewVisible(boolean overviewVisible) { if (this.overviewVisible != overviewVisible) { boolean old = this.overviewVisible; this.overviewVisible = overviewVisible; firePropertyChange("overviewVisible", old, overviewVisible); configureOverviewContainer(); } } private void configureOverviewContainer() { overviewContainer.setVisible(overviewVisible && model instanceof OverviewProvider); } public void setHelpBroker(HelpBroker broker) { HelpBroker old = this.helpBroker; this.helpBroker = broker; firePropertyChange("helpBroker", old, broker); } public HelpBroker getHelpBroker() { return helpBroker; } /** * Returns the action that is bound to the next button. */ public Action getNextAction() { return nextAction; } /** * Returns the action that is bound to the previous button. */ public Action getPreviousAction() { return previousAction; } /** * Returns the action that is bound to the last button. */ public Action getLastAction() { return lastAction; } /** * Returns the action that is bound to the showCloseButton button. */ public Action getFinishAction() { return finishAction; } /** * Returns the action that is bound to the cancel button. */ public Action getCancelAction() { return cancelAction; } /** * Returns the action that is bound to the close button. */ public Action getHelpAction() { return helpAction; } /** * Returns the action that is bound to the close button. */ public Action getCloseAction() { return closeAction; } /** * Marks this wizard as finished. This will cause the button bar to only display * the close button. */ protected void showCloseButton() { buttonBar.showCloseButton(true); } /** * Cancels this wizard. This method simply fires the {@link WizardListener#wizardCancelled} * event. */ public void cancel() { WizardStep activeStep = getModel().getActiveStep(); if (activeStep != null && activeStep.isBusy()) { if (!confirmAbort()) return; activeStep.abortBusy(); } canceled = true; fireWizardCancelled(); } /** * Checks if the wizard was canceled. This method will return false unless the user * canceled the wizard. * * @return true if the user canceled the wizard, false otherwise. */ public boolean wasCanceled() { return canceled; } /** * This method is called when the user cancels the wizard while the {@link #activeStep} is * {@link WizardStep#isBusy busy}. This method displays a {@link JOptionPane} asking if the * user wants to abort the wizard. * * @return true if the user confirms the abort, false otherwise. */ protected boolean confirmAbort() { int response = JOptionPane.showConfirmDialog(this, "Cancel the currently active task?", "Abort", JOptionPane.YES_NO_CANCEL_OPTION); return response == JOptionPane.YES_OPTION; } /** * Closes this wizard. This method simply fires the {@link WizardListener#wizardClosed} * event. */ public void close() { fireWizardClosed(); } /** * Adds a {@link WizardListener} to this wizard. * * @param l the listener to add. */ public void addWizardListener(WizardListener l) { listenerList.add(WizardListener.class, l); } /** * Removes a {@link WizardListener} from this wizard. * * @param l the listener to remove. */ public void removeWizardListener(WizardListener l) { listenerList.remove(WizardListener.class, l); } /** * Handles a change in the {@link WizardModel} active step. */ private void handleStepChange() { if (activeStep != null) { activeStep.removePropertyChangeListener("view", viewListener); } activeStep = model.getActiveStep(); activeStep.addPropertyChangeListener("view", viewListener); activeStep.prepare(); handleViewChange(); } /** * Handles changes in the current {@link WizardStep}s view. */ private void handleViewChange() { viewPanel.removeAll(); viewPanel.add(activeStep.getView(), BorderLayout.CENTER); viewPanel.revalidate(); viewPanel.repaint(); } /** * Caculates the preferred size of the main wizard area based on the {@link WizardStep#getPreferredSize} * values of all the wizard steps. */ private Dimension calculatePreferredStepSize() { int w = 0; int h = 0; for (Iterator iter = getModel().stepIterator(); iter.hasNext();) { WizardStep step = (WizardStep) iter.next(); Dimension d = step.getPreferredSize(); w = Math.max(d.width, w); h = Math.max(d.height, h); } return new Dimension(w, h); } private void fireWizardClosed() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); WizardEvent event = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == WizardListener.class) { // Lazily create the event: if (event == null) event = new WizardEvent(this); ((WizardListener) listeners[i + 1]).wizardClosed(event); } } } private void fireWizardCancelled() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); WizardEvent event = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == WizardListener.class) { // Lazily create the event: if (event == null) event = new WizardEvent(this); ((WizardListener) listeners[i + 1]).wizardCancelled(event); } } } /** * Displays the wizard in a new {@link JFrame} with the specified title. The frame will * be automatically closed when the wizard is completed or canceled. This method will * not block. * * @param title the title of the frame. * @see #addWizardListener * @see #removeWizardListener */ public void showInFrame(String title) { showInFrame(title, null, null); } /** * Displays the wizard in a new {@link JFrame} with the specified title. The frame will * be automatically closed when the wizard is completed or canceled. This method will * not block. * * @param title the title of the frame. * @param windowIcon the icon to use for the frame. This is used to configure {@link javax.swing.JFrame#setIconImage(java.awt.Image)}. * @see #addWizardListener * @see #removeWizardListener */ public void showInFrame(String title, Image windowIcon) { showInFrame(title, windowIcon, null); } /** * Displays the wizard in a new {@link JFrame} with the specified title. The frame will * be automatically closed when the wizard is completed or canceled. This method will * not block. * * @param title the title of the frame. * @param relativeTo the new {@link JFrame} will be displayed relative to this component. If * the component is null, the window will be centered on the * desktop as per {@link JWindow#setLocationRelativeTo(java.awt.Component)}. * @see #addWizardListener * @see #removeWizardListener */ public void showInFrame(String title, Component relativeTo) { showInFrame(title, null, relativeTo); } /** * Displays the wizard in a new {@link JFrame} with the specified title. The frame will * be automatically closed when the wizard is completed or canceled. This method will * not block. * * @param title the title of the frame. * @param windowIcon the icon to use for the frame. This is used to configure {@link javax.swing.JFrame#setIconImage(java.awt.Image)}. * @param relativeTo the new {@link JFrame} will be displayed relative to this component. If * the component is null, the window will be centered on the * desktop as per {@link JWindow#setLocationRelativeTo(java.awt.Component)}. * @see #addWizardListener * @see #removeWizardListener */ public void showInFrame(String title, Image windowIcon, Component relativeTo) { JFrame window = new JFrame(title); window.setIconImage(windowIcon); showInWindow(window, relativeTo); } /** * Displays the wizard in a new {@link JDialog} with the specified title. The dialog will * be automatically closed when the wizard is completed or canceled. This method will * block if the dialog is modal. * * @param title the dialog title. * @param parent the component that will own the dialog. * @param modal true to make the dialog modal, false otherwise. * @see #addWizardListener * @see #removeWizardListener */ public void showInDialog(String title, Component parent, boolean modal) { JDialog dialog = null; if (parent != null) { Window w = (parent instanceof Window) ? (Window) parent : SwingUtilities.getWindowAncestor(parent); if (w instanceof Frame) { dialog = new JDialog((Frame) w, title, modal); } else if (w instanceof Dialog) { dialog = new JDialog((Dialog) w, title, modal); } else { throw new IllegalArgumentException("Parent component must be within a Frame or Dialog"); } if (parent instanceof Window) { dialog = createDialogFor((Window) parent, title, modal); } else { dialog = createDialogFor(SwingUtilities.getWindowAncestor(parent), title, modal); } } else { dialog = new JDialog(); dialog.setModal(modal); dialog.setTitle(title); } dialog.setLocationRelativeTo(parent); showInWindow(dialog, parent); } private JDialog createDialogFor(Window window, String title, boolean modal) { if (window == null) throw new NullPointerException("window is null"); JDialog dialog = null; if (window instanceof Frame) { dialog = new JDialog((Frame) window, title, modal); } else if (window instanceof Dialog) { dialog = new JDialog((Dialog) window, title, modal); } return dialog; } /** * Displays this wizard in the specified {@link Window} that is positioned relative to the * specified component. * * @param window the window that will contain the wizard. * @param relativeTo the component used to position the window. If the component is null, * the window will be centered on the desktop as per * {@link JWindow#setLocationRelativeTo(java.awt.Component)}. */ private void showInWindow(Window window, Component relativeTo) { ((RootPaneContainer) window).getContentPane().add(this); window.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { cancel(); } }); WizardFrameCloser.bind(this, window); window.pack(); window.setLocationRelativeTo(relativeTo); window.setVisible(true); window.toFront(); } private class OverviewBorder extends AbstractBorder { private int width = 5; private Insets insets = new Insets(0, 0, 0, 2); public boolean isBorderOpaque() { return true; } public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { Color old = g.getColor(); g.setColor(Color.BLACK); g.drawLine(x + width - 1, y, x + width - 1, y + height); g.setColor(old); } public Insets getBorderInsets(Component c) { return insets; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy