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: PanelWizardStep.java,v 1.6 2005/08/13 01:18:39 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 a base class for JPanel based wizard steps. Subclasses override the methods
029: * {@link #init}, {@link #prepare}, {@link #applyState}.
030: * <p>
031: * The {@link Wizard} listens to property change events from the step and will update
032: * accordingly when ever {@link #setComplete} or {@link #setBusy} is called.
033: * <p>
034: * An example is shown below.
035: * <pre>
036: * public class MyWizardStep
037: * extends PanelWizardStep
038: * {
039: * private MyModel model;
040: * private JCheckBox agreeCheckbox;
041: * private JTextArea license;
042: *
043: * public MyWizardStep()
044: * {
045: * super("My First Step", "A summary of the first step");
046: *
047: * // build and layout the components..
048: * agreeCheckbox = new JCheckBox("Agree");
049: * license = new JTextArea();
050: * setLayout(...);
051: * add(agreeCheckbox);
052: * ...
053: *
054: * // listen to changes in the state..
055: * agreeCheckbox.addItemListener(new ItemListener()
056: * {
057: * public void itemSelected(ItemEvent e)
058: * {
059: * // only continue if they agree
060: * MyWizardStep.this.<b>setComplete(agreeCheckbox.isSelected());</b>
061: * }
062: * });
063: * }
064: *
065: * public void init(WizardModel model)
066: * {
067: * this.model = (MyModel) model;
068: * }
069: *
070: * public void prepare()
071: * {
072: * // read the model and configure the panel
073: * }
074: *
075: * public void applyState()
076: * throws InvalidStateException
077: * {
078: * // load a progress bar of some kind..
079: * ...
080: *
081: * <b>setBusy(true);</b>
082: * try
083: * {
084: * // do some work on another thread.. see <a href="http://foxtrot.sourceforge.net/">Foxtrot</a>
085: * ...
086: * }
087: * finally
088: * {
089: * <b>setBusy(false);</b>
090: * }
091: *
092: * // if error then throw an exception
093: * if (!ok)
094: * {
095: * // restore our original view..
096: * ......
097: * throw new InvalidStateException("That didn't work!");
098: * }
099: *
100: * // this isn't really meaningful as we refuse to continue
101: * // while the checkbox is un-checked.
102: * model.setAcceptsLicense(agreeCheckbox.isSelected());
103: * }
104: *
105: * public void getPreferredSize()
106: * {
107: * // use the size of our main view...
108: * return mainView.getPreferredSize();
109: * }
110: * }
111: * </pre>
112: */
113: public class PanelWizardStep extends JPanel implements WizardStep {
114: /**
115: * A summary of this step, or some usage advice.
116: */
117: private String summary;
118:
119: /**
120: * An Icon that represents this step.
121: */
122: private Icon icon;
123:
124: /**
125: * Marks this step as being fully configured. Only when this is <tt>true</tt> can the wizard
126: * progress. This is a bound property.
127: */
128: private boolean complete;
129:
130: /**
131: * Marks the task as being busy. While in this state the wizard will prevent cancel opertations.
132: */
133: private boolean busy = false;
134:
135: /**
136: * A default constructor make this class JavaBean compatible.
137: */
138: public PanelWizardStep() {
139: }
140:
141: /**
142: * Creates a new step with the specified name and summary. The name and summary are displayed in
143: * the wizard title block while this step is active.
144: *
145: * @param name the name of this step.
146: * @param summary a brief summary of this step or some usage guidelines.
147: */
148: public PanelWizardStep(String name, String summary) {
149: this (name, summary, null);
150: }
151:
152: /**
153: * Creates a new step with the specified name and summary. The name and summary are displayed in
154: * the wizard title block while this step is active.
155: *
156: * @param name the name of this step.
157: * @param summary a brief summary of this step or some usage guidelines.
158: */
159: public PanelWizardStep(String name, String summary, Icon icon) {
160: setName(name);
161: this .summary = summary;
162: this .icon = icon;
163: }
164:
165: /**
166: * Gets the summary of this step. This will be displayed in the title of the wizard while this
167: * step is active. The summary is typically an overview of the step or some usage guidelines
168: * for the user.
169: *
170: * @return the summary of this step.
171: */
172: public String getSummary() {
173: return summary;
174: }
175:
176: /**
177: * Sets this steps summary. This will be displayed in the title of the wizard while this
178: * step is active. The summary is typically an overview of the step or some usage guidelines
179: * for the user.
180: *
181: * @param summary the summary of this step.
182: */
183: public void setSummary(String summary) {
184: if ((this .summary != null && !this .summary.equals(summary))
185: || this .summary == null && summary != null) {
186: String old = this .summary;
187: this .summary = summary;
188: firePropertyChange("summary", old, summary);
189: }
190: }
191:
192: /**
193: * Gets the {@link javax.swing.Icon} that represents this step.
194: *
195: * @return the {@link javax.swing.Icon} that represents this step, or <tt>null</tt> if the step
196: * doesn't have an icon.
197: */
198: public Icon getIcon() {
199: return icon;
200: }
201:
202: /**
203: * Sets the {@link javax.swing.Icon} that represents this step.
204: *
205: * @param icon the {@link javax.swing.Icon} that represents this step, or <tt>null</tt> if the step
206: * doesn't have an icon.
207: */
208: public void setIcon(Icon icon) {
209: if ((this .icon != null && !this .icon.equals(icon))
210: || this .icon == null && icon != null) {
211: Icon old = this .icon;
212: this .icon = icon;
213: firePropertyChange("icon", old, icon);
214: }
215: }
216:
217: /**
218: * Returns 'this'.
219: *
220: * @return this panel.
221: */
222: public Component getView() {
223: return this ;
224: }
225:
226: // /**
227: // * Sets the current view this step is displaying. This component will be displayed in the main
228: // * section of the wizard with this step is active. This method may changed at any time and the
229: // * wizard will update accordingly.
230: // *
231: // * @param component the current view of the step.
232: // */
233: // protected void
234: // setView(Component component)
235: // {
236: // if (!component.equals(view))
237: // {
238: // Component old = view;
239: // view = component;
240: // pcs.firePropertyChange("view", old, view);
241: // }
242: // }
243:
244: /**
245: * Checks if this step is compete. This method should return true if the wizard can proceed
246: * to the next step. This property is bound and changes can be made at anytime by calling
247: * {@link #setComplete(boolean)} .
248: *
249: * @return <tt>true</tt> if the wizard can proceed from this step, <tt>false</tt> otherwise.
250: * @see #setComplete
251: */
252: public boolean isComplete() {
253: return complete;
254: }
255:
256: /**
257: * Marks this step as compete. The wizard will not be able to proceed from this step until
258: * this property is configured to <tt>true</tt>.
259: *
260: * @param complete <tt>true</tt> to allow the wizard to proceed, <tt>false</tt> otherwise.
261: * @see #isComplete
262: */
263: public void setComplete(boolean complete) {
264: if (this .complete != complete) {
265: this .complete = complete;
266: firePropertyChange("complete", !complete, complete);
267: }
268: }
269:
270: /**
271: * Checks if the current task is busy. This usually indicates that the step is performing
272: * a time consuming task on a background thread.
273: *
274: * @return <tt>true</tt> if step is busy performing a background operation, <tt>false</tt>
275: * otherwise.
276: */
277: public boolean isBusy() {
278: return busy;
279: }
280:
281: /**
282: * Sets the busy state of this wizard step. This should usually be set when a time consuming
283: * task is being performed on a background thread. The Wizard responds by disabling the various
284: * buttons appropriately.<p>
285: * Wizard steps that go into a busy state must also implement {@link #abortBusy} to cancel any
286: * inprogress operation.
287: *
288: * @param busy <tt>true</tt> to mark the step as busy and disable further user action, <tt>false</tt>
289: * to return the wizard to its normal state.
290: */
291: public void setBusy(boolean busy) {
292: if (this .busy != busy) {
293: boolean old = this .busy;
294: this .busy = busy;
295: firePropertyChange("busy", old, busy);
296: }
297: }
298:
299: /////////////////////////////////////////////////////////////////////
300: // WizardStep Abstract Methods
301: //
302:
303: /**
304: * Called to initialize the step. This method will be called when the wizard is
305: * first initialising.
306: *
307: * @param model the model to which the step belongs.
308: */
309: public void init(WizardModel model) {
310: }
311:
312: /**
313: * Called to prepare this step to display. Subclasses should query the model and configure
314: * their view appropriately.
315: * <p>
316: * This method will be called whenever the step is to be displayed, regardless of whether the
317: * user pressed next or previous.
318: */
319: public void prepare() {
320: }
321:
322: /**
323: * This method is called whenever the user presses next while this step is active.
324: * <p>
325: * If this method will take a long time to complete, subclasses should consider executing the
326: * work and a separate thread and displaying some kind of progress indicator.
327: * <p>
328: * This method will only be called if {@link WizardModel#isNextAvailable} and {@link #isComplete}
329: * return true.
330: *
331: * @throws InvalidStateException if an error occurs and the wizard can't progress to the next
332: * step. By default the message of this exception will be displayed to the user. If you wish to
333: * prevent this behaviour please ensure {@link InvalidStateException#setShowUser} is called with
334: * a value of <tt>false</tt>.
335: */
336: public void applyState() throws InvalidStateException {
337: }
338:
339: /**
340: * Called by the wizard if the user presses cancel while the step is in a {@link #isBusy busy}
341: * state. Steps that are never busy need not override this method.
342: */
343: public void abortBusy() {
344: }
345: }
|