001: /**
002: * Wizard Framework
003: * Copyright 2004 - 2005 Andrew Pietsch
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * $Id: AbstractWizardStep.java,v 1.3 2005/08/13 01:18:03 pietschy Exp $
020: */package org.pietschy.wizard;
021:
022: import javax.swing.*;
023: import java.awt.*;
024: import java.beans.PropertyChangeListener;
025: import java.beans.PropertyChangeSupport;
026:
027: /**
028: * This is the base class for all non panel related wizard steps. Subclasses must implement the
029: * abstract methods {@link #init}, {@link #prepare}, {@link #applyState} and
030: * {@link #getPreferredSize}. In addition an appropriate UI must be installed by
031: * calling {@link #setView}.
032: * <p>
033: * The {@link Wizard} listens to property change events from the step and will update
034: * accordingly when ever {@link #setView}, {@link #setComplete} or {@link #setBusy} is called.
035: * <p>
036: * An example is shown below.
037: * <pre>
038: * public class MyWizardStep
039: * extends WizardStep
040: * {
041: * private MyModel model;
042: * private JPanel mainView;
043: * private JCheckBox agreeCheckbox;
044: * private JTextArea license;
045: *
046: * public MyWizardStep()
047: * {
048: * super("My First Step", "A summary of the first step");
049: *
050: * // build and layout the components..
051: * mainView = new JPanel();
052: * agreeCheckbox = new JCheckBox("Agree");
053: * license = new JTextArea();
054: * mainView.setLayout(...);
055: * mainView.add(agreeCheckbox);
056: * ...
057: *
058: * // listen to changes in the state..
059: * agreeCheckbox.addItemListener(new ItemListener()
060: * {
061: * public void itemSelected(ItemEvent e)
062: * {
063: * // only continue if they agree
064: * MyWizardStep.this.<b>setComplete(agreeCheckbox.isSelected());</b>
065: * }
066: * });
067: * }
068: *
069: * public void init(WizardModel model)
070: * {
071: * this.model = (MyModel) model;
072: * }
073: *
074: * public void prepare()
075: * {
076: * // load our view...
077: * <b>setView(mainView);</b>
078: * }
079: *
080: * public void applyState()
081: * throws InvalidStateException
082: * {
083: * // display a progress bar of some kind..
084: * <b>setView(myProgressView);</b>
085: *
086: * <b>setBusy(true);</b>
087: * try
088: * {
089: * // do some work on another thread.. see <a href="http://foxtrot.sourceforge.net/">Foxtrot</a>
090: * ...
091: * }
092: * finally
093: * {
094: * <b>setBusy(false);</b>
095: * }
096: *
097: * // if error then throw an exception
098: * if (!ok)
099: * {
100: * // restore our original view..
101: * <b>setView(mainView)</b>
102: * throw new InvalidStateException("That didn't work!");
103: * }
104: *
105: * // this isn't really meaningful as we refuse to continue
106: * // while the checkbox is un-checked.
107: * model.setAcceptsLicense(agreeCheckbox.isSelected());
108: * }
109: *
110: * public void getPreferredSize()
111: * {
112: * // use the size of our main view...
113: * return mainView.getPreferredSize();
114: * }
115: * }
116: * </pre>
117: */
118: public abstract class AbstractWizardStep implements WizardStep {
119: private PropertyChangeSupport pcs;
120:
121: /**
122: * The name of this step.
123: */
124: private String name;
125:
126: /**
127: * A summary of this step, or some usage advice.
128: */
129: private String summary;
130:
131: /**
132: * An Icon that represents this step.
133: */
134: private Icon icon;
135:
136: /**
137: * The current view of the step. This will be displayed in the main area of the wizard. This
138: * property is bound.
139: */
140: private Component view;
141:
142: /**
143: * Marks this step as being fully configured. Only when this is <tt>true</tt> can the wizard
144: * progress. This is a bound property.
145: */
146: private boolean complete;
147:
148: /**
149: * Marks the task as being busy. While in this state the wizard will prevent cancel opertations.
150: */
151: private boolean busy = false;
152:
153: /**
154: * Creates a new step with the specified name and summary. The name and summary are displayed in
155: * the wizard title block while this step is active.
156: *
157: * @param name the name of this step.
158: * @param summary a brief summary of this step or some usage guidelines.
159: */
160: public AbstractWizardStep(String name, String summary) {
161: this (name, summary, null);
162: }
163:
164: /**
165: * Creates a new step with the specified name and summary. The name and summary are displayed in
166: * the wizard title block while this step is active.
167: *
168: * @param name the name of this step.
169: * @param summary a brief summary of this step or some usage guidelines.
170: */
171: public AbstractWizardStep(String name, String summary, Icon icon) {
172: this .pcs = new PropertyChangeSupport(this );
173: this .name = name;
174: this .summary = summary;
175: this .icon = icon;
176: }
177:
178: /**
179: * Gets the name of this step. This will be displayed in the title of the wizard while this
180: * step is active.
181: *
182: * @return the name of this step.
183: */
184: public String getName() {
185: return name;
186: }
187:
188: /**
189: * Sets the name of this step. This will be displayed in the title of the wizard while this
190: * step is active.
191: *
192: * @param name the name of this step.
193: */
194: public void setName(String name) {
195: if ((this .name != null && !this .name.equals(name))
196: || this .name == null && name != null) {
197: String old = this .name;
198: this .name = name;
199: pcs.firePropertyChange("name", old, name);
200: }
201: }
202:
203: /**
204: * Gets the summary of this step. This will be displayed in the title of the wizard while this
205: * step is active. The summary is typically an overview of the step or some usage guidelines
206: * for the user.
207: *
208: * @return the summary of this step.
209: */
210: public String getSummary() {
211: return summary;
212: }
213:
214: /**
215: * Sets the summary of this step. This will be displayed in the title of the wizard while this
216: * step is active. The summary is typically an overview of the step or some usage guidelines
217: * for the user.
218: *
219: * @param summary the summary of this step.
220: */
221: public void setSummary(String summary) {
222: if ((this .summary != null && !this .summary.equals(summary))
223: || this .summary == null && summary != null) {
224: String old = this .summary;
225: this .summary = summary;
226: pcs.firePropertyChange("summary", old, summary);
227: }
228: }
229:
230: /**
231: * Gets the {@link javax.swing.Icon} that represents this step.
232: *
233: * @return the {@link javax.swing.Icon} that represents this step, or <tt>null</tt> if the step
234: * doesn't have an icon.
235: */
236: public Icon getIcon() {
237: return icon;
238: }
239:
240: /**
241: * Sets the {@link javax.swing.Icon} that represents this step.
242: *
243: * @param icon the {@link javax.swing.Icon} that represents this step, or <tt>null</tt> if the step
244: * doesn't have an icon.
245: */
246: public void setIcon(Icon icon) {
247: if ((this .icon != null && !this .icon.equals(icon))
248: || this .icon == null && icon != null) {
249: Icon old = this .icon;
250: this .icon = icon;
251: pcs.firePropertyChange("icon", old, icon);
252: }
253: }
254:
255: /**
256: * Returns the current view this step is displaying. This component will be displayed in the main
257: * section of the wizard with this step is active. This may changed at any time by calling
258: * {@link #setView} and the wizard will update accordingly.
259: *
260: * @return the current view of the step.
261: * @see #setView
262: */
263: public Component getView() {
264: return view;
265: }
266:
267: /**
268: * Sets the current view this step is displaying. This component will be displayed in the main
269: * section of the wizard with this step is active. This method may changed at any time and the
270: * wizard will update accordingly.
271: *
272: * @param component the current view of the step.
273: */
274: protected void setView(Component component) {
275: if (!component.equals(view)) {
276: Component old = view;
277: view = component;
278: pcs.firePropertyChange("view", old, view);
279: }
280: }
281:
282: /**
283: * Checks if this step is compete. This method should return true if the wizard can proceed
284: * to the next step. This property is bound and changes can be made at anytime by calling
285: * {@link #setComplete(boolean)} .
286: *
287: * @return <tt>true</tt> if the wizard can proceed from this step, <tt>false</tt> otherwise.
288: * @see #setComplete
289: */
290: public boolean isComplete() {
291: return complete;
292: }
293:
294: /**
295: * Marks this step as compete. The wizard will not be able to proceed from this step until
296: * this property is configured to <tt>true</tt>.
297: *
298: * @param complete <tt>true</tt> to allow the wizard to proceed, <tt>false</tt> otherwise.
299: * @see #isComplete
300: */
301: public void setComplete(boolean complete) {
302: if (this .complete != complete) {
303: this .complete = complete;
304: pcs.firePropertyChange("complete", !complete, complete);
305: }
306: }
307:
308: /**
309: * Checks if the current task is busy. This usually indicates that the step is performing
310: * a time consuming task on a background thread.
311: *
312: * @return <tt>true</tt> if step is busy performing a background operation, <tt>false</tt>
313: * otherwise.
314: */
315: public boolean isBusy() {
316: return busy;
317: }
318:
319: /**
320: * Sets the busy state of this wizard step. This should usually be set when a time consuming
321: * task is being performed on a background thread. The Wizard responds by disabling the various
322: * buttons appropriately.<p>
323: * Wizard steps that go into a busy state must also implement {@link #abortBusy} to cancel any
324: * inprogress operation.
325: *
326: * @param busy <tt>true</tt> to mark the step as busy and disable further user action, <tt>false</tt>
327: * to return the wizard to its normal state.
328: */
329: public void setBusy(boolean busy) {
330: if (this .busy != busy) {
331: boolean old = this .busy;
332: this .busy = busy;
333: pcs.firePropertyChange("busy", old, busy);
334: }
335: }
336:
337: /////////////////////////////////////////////////////////////////////
338: // Abstract Methods
339: //
340:
341: /**
342: * Called to initialize the step. This method will be called when the wizard is
343: * first initialising.
344: *
345: * @param model the model to which the step belongs.
346: */
347: public abstract void init(WizardModel model);
348:
349: /**
350: * Called by the wizard if the user presses cancel while the step is in a {@link #isBusy busy}
351: * state. Steps that are never busy need not override this method.
352: */
353: public void abortBusy() {
354: }
355:
356: /////////////////////////////////////////////////////////////////////
357: // Property change support
358: //
359: public void addPropertyChangeListener(
360: PropertyChangeListener listener) {
361: pcs.addPropertyChangeListener(listener);
362: }
363:
364: public void removePropertyChangeListener(
365: PropertyChangeListener listener) {
366: pcs.removePropertyChangeListener(listener);
367: }
368:
369: public void addPropertyChangeListener(String propertyName,
370: PropertyChangeListener listener) {
371: pcs.addPropertyChangeListener(propertyName, listener);
372: }
373:
374: public void removePropertyChangeListener(String propertyName,
375: PropertyChangeListener listener) {
376: pcs.removePropertyChangeListener(propertyName, listener);
377: }
378: }
|