001: package com.nexes.wizard;
002:
003: import infrastructure.internationalization.InternationalizationManager;
004: import infrastructure.logging.Log;
005: import infrastructure.propertymanager.PropertyManager;
006:
007: import java.awt.*;
008: import java.awt.event.*;
009: import java.beans.*;
010: import java.util.*;
011: import java.net.*;
012:
013: import javax.swing.*;
014: import javax.swing.border.*;
015:
016: /**
017: * This class implements a basic wizard dialog, where the programmer can
018: * insert one or more Components to act as panels. These panels can be navigated
019: * through arbitrarily using the 'Next' or 'Back' buttons, or the dialog itself
020: * can be closed using the 'Cancel' button. Note that even though the dialog
021: * uses a CardLayout manager, the order of the panels is not linear. Each panel
022: * determines at runtime what its next and previous panel will be.
023: */
024: public class Wizard extends WindowAdapter implements
025: PropertyChangeListener {
026:
027: /**
028: * Indicates that the 'Finish' button was pressed to close the dialog.
029: */
030: public static final int FINISH_RETURN_CODE = 0;
031: /**
032: * Indicates that the 'Cancel' button was pressed to close the dialog, or
033: * the user pressed the close box in the corner of the window.
034: */
035: public static final int CANCEL_RETURN_CODE = 1;
036: /**
037: * Indicates that the dialog closed due to an internal error.
038: */
039: public static final int ERROR_RETURN_CODE = 2;
040:
041: /**
042: * The String-based action command for the 'Next' button.
043: */
044: public static final String NEXT_BUTTON_ACTION_COMMAND = "NextButtonActionCommand";
045: /**
046: * The String-based action command for the 'Back' button.
047: */
048: public static final String BACK_BUTTON_ACTION_COMMAND = "BackButtonActionCommand";
049: /**
050: * The String-based action command for the 'Cancel' button.
051: */
052: public static final String CANCEL_BUTTON_ACTION_COMMAND = "CancelButtonActionCommand";
053:
054: // The i18n text used for the buttons. Loaded from a property resource file.
055:
056: static String BACK_TEXT;
057: static String NEXT_TEXT;
058: static String FINISH_TEXT;
059: static String CANCEL_TEXT;
060:
061: // The image icons used for the buttons. Filenames are loaded from a property resource file.
062:
063: static Icon BACK_ICON;
064: static Icon NEXT_ICON;
065: static Icon FINISH_ICON;
066: static Icon CANCEL_ICON;
067:
068: private WizardModel wizardModel;
069: private WizardController wizardController;
070: private JDialog wizardDialog;
071:
072: private JPanel cardPanel;
073: private CardLayout cardLayout;
074: private JButton backButton;
075: private JButton nextButton;
076: private JButton cancelButton;
077:
078: private int returnCode;
079:
080: /**
081: * Default constructor. This method creates a new WizardModel object and passes it
082: * into the overloaded constructor.
083: */
084: public Wizard() {
085: this ((Frame) null);
086: }
087:
088: /**
089: * This method accepts a java.awt.Dialog object as the javax.swing.JDialog's
090: * parent.
091: * @param owner The java.awt.Dialog object that is the owner of this dialog.
092: */
093: public Wizard(Dialog owner) {
094: wizardModel = new WizardModel();
095: wizardDialog = new JDialog(owner);
096: initComponents();
097: }
098:
099: /**
100: * This method accepts a java.awt.Frame object as the javax.swing.JDialog's
101: * parent.
102: * @param owner The java.awt.Frame object that is the owner of the javax.swing.JDialog.
103: */
104: public Wizard(Frame owner) {
105: wizardModel = new WizardModel();
106: wizardDialog = new JDialog(owner);
107: initComponents();
108: }
109:
110: /**
111: * Returns an instance of the JDialog that this class created. This is useful in
112: * the event that you want to change any of the JDialog parameters manually.
113: * @return The JDialog instance that this class created.
114: */
115: public JDialog getDialog() {
116: return wizardDialog;
117: }
118:
119: /**
120: * Returns the owner of the generated javax.swing.JDialog.
121: * @return The owner (java.awt.Frame or java.awt.Dialog) of the javax.swing.JDialog generated
122: * by this class.
123: */
124: public Component getOwner() {
125: return wizardDialog.getOwner();
126: }
127:
128: /**
129: * Sets the title of the generated javax.swing.JDialog.
130: * @param s The title of the dialog.
131: */
132: public void setTitle(String s) {
133: wizardDialog.setTitle(s);
134: }
135:
136: /**
137: * Returns the current title of the generated dialog.
138: * @return The String-based title of the generated dialog.
139: */
140: public String getTitle() {
141: return wizardDialog.getTitle();
142: }
143:
144: /**
145: * Sets the modality of the generated javax.swing.JDialog.
146: * @param b the modality of the dialog
147: */
148: public void setModal(boolean b) {
149: wizardDialog.setModal(b);
150: }
151:
152: /**
153: * Returns the modality of the dialog.
154: * @return A boolean indicating whether or not the generated javax.swing.JDialog is modal.
155: */
156: public boolean isModal() {
157: return wizardDialog.isModal();
158: }
159:
160: /**
161: * Convienence method that displays a modal wizard dialog and blocks until the dialog
162: * has completed.
163: * @return Indicates how the dialog was closed. Compare this value against the RETURN_CODE
164: * constants at the beginning of the class.
165: */
166: public int showModalDialog() {
167:
168: wizardDialog.setModal(true);
169: //wizardDialog.pack();
170: wizardDialog.show();
171:
172: return returnCode;
173: }
174:
175: /**
176: * Returns the current model of the wizard dialog.
177: * @return A WizardModel instance, which serves as the model for the wizard dialog.
178: */
179: public WizardModel getModel() {
180: return wizardModel;
181: }
182:
183: /**
184: * Add a Component as a panel for the wizard dialog by registering its
185: * WizardPanelDescriptor object. Each panel is identified by a unique Object-based
186: * identifier (often a String), which can be used by the setCurrentPanel()
187: * method to display the panel at runtime.
188: * @param id An Object-based identifier used to identify the WizardPanelDescriptor object.
189: * @param panel The WizardPanelDescriptor object which contains helpful information about the panel.
190: */
191: public void registerWizardPanel(Object id,
192: WizardPanelDescriptor panel) {
193:
194: // Add the incoming panel to our JPanel display that is managed by
195: // the CardLayout layout manager.
196:
197: cardPanel.add(panel.getPanelComponent(), id);
198:
199: // Set a callback to the current wizard.
200:
201: panel.setWizard(this );
202:
203: // Place a reference to it in the model.
204:
205: wizardModel.registerPanel(id, panel);
206:
207: }
208:
209: /**
210: * Displays the panel identified by the object passed in. This is the same Object-based
211: * identified used when registering the panel.
212: * @param id The Object-based identifier of the panel to be displayed.
213: */
214: public void setCurrentPanel(Object id) {
215:
216: // Get the hashtable reference to the panel that should
217: // be displayed. If the identifier passed in is null, then close
218: // the dialog.
219:
220: if (id == null)
221: close(ERROR_RETURN_CODE);
222:
223: WizardPanelDescriptor oldPanelDescriptor = wizardModel
224: .getCurrentPanelDescriptor();
225: if (oldPanelDescriptor != null)
226: oldPanelDescriptor.aboutToHidePanel();
227:
228: wizardModel.setCurrentPanel(id);
229: wizardModel.getCurrentPanelDescriptor().aboutToDisplayPanel();
230:
231: // Show the panel in the dialog.
232:
233: cardLayout.show(cardPanel, id.toString());
234: wizardModel.getCurrentPanelDescriptor().displayingPanel();
235:
236: }
237:
238: /**
239: * Method used to listen for property change events from the model and update the
240: * dialog's graphical components as necessary.
241: * @param evt PropertyChangeEvent passed from the model to signal that one of its properties has changed value.
242: */
243: public void propertyChange(PropertyChangeEvent evt) {
244:
245: if (evt.getPropertyName().equals(
246: WizardModel.CURRENT_PANEL_DESCRIPTOR_PROPERTY)) {
247: wizardController.resetButtonsToPanelRules();
248: } else if (evt.getPropertyName().equals(
249: WizardModel.NEXT_FINISH_BUTTON_TEXT_PROPERTY)) {
250: nextButton.setText(evt.getNewValue().toString());
251: } else if (evt.getPropertyName().equals(
252: WizardModel.BACK_BUTTON_TEXT_PROPERTY)) {
253: backButton.setText(evt.getNewValue().toString());
254: } else if (evt.getPropertyName().equals(
255: WizardModel.CANCEL_BUTTON_TEXT_PROPERTY)) {
256: cancelButton.setText(evt.getNewValue().toString());
257: } else if (evt.getPropertyName().equals(
258: WizardModel.NEXT_FINISH_BUTTON_ENABLED_PROPERTY)) {
259: nextButton.setEnabled(((Boolean) evt.getNewValue())
260: .booleanValue());
261: } else if (evt.getPropertyName().equals(
262: WizardModel.BACK_BUTTON_ENABLED_PROPERTY)) {
263: backButton.setEnabled(((Boolean) evt.getNewValue())
264: .booleanValue());
265: } else if (evt.getPropertyName().equals(
266: WizardModel.CANCEL_BUTTON_ENABLED_PROPERTY)) {
267: cancelButton.setEnabled(((Boolean) evt.getNewValue())
268: .booleanValue());
269: } else if (evt.getPropertyName().equals(
270: WizardModel.NEXT_FINISH_BUTTON_ICON_PROPERTY)) {
271: nextButton.setIcon((Icon) evt.getNewValue());
272: } else if (evt.getPropertyName().equals(
273: WizardModel.BACK_BUTTON_ICON_PROPERTY)) {
274: backButton.setIcon((Icon) evt.getNewValue());
275: } else if (evt.getPropertyName().equals(
276: WizardModel.CANCEL_BUTTON_ICON_PROPERTY)) {
277: cancelButton.setIcon((Icon) evt.getNewValue());
278: }
279:
280: }
281:
282: /**
283: * Retrieves the last return code set by the dialog.
284: * @return An integer that identifies how the dialog was closed. See the *_RETURN_CODE
285: * constants of this class for possible values.
286: */
287: public int getReturnCode() {
288: return returnCode;
289: }
290:
291: /**
292: * Mirrors the WizardModel method of the same name.
293: * @return A boolean indicating if the button is enabled.
294: */
295: public boolean getBackButtonEnabled() {
296: return wizardModel.getBackButtonEnabled().booleanValue();
297: }
298:
299: /**
300: * Mirrors the WizardModel method of the same name.
301: * @param boolean newValue The new enabled status of the button.
302: */
303: public void setBackButtonEnabled(boolean newValue) {
304: wizardModel.setBackButtonEnabled(new Boolean(newValue));
305: }
306:
307: /**
308: * Mirrors the WizardModel method of the same name.
309: * @return A boolean indicating if the button is enabled.
310: */
311: public boolean getNextFinishButtonEnabled() {
312: return wizardModel.getNextFinishButtonEnabled().booleanValue();
313: }
314:
315: /**
316: * Mirrors the WizardModel method of the same name.
317: * @param boolean newValue The new enabled status of the button.
318: */
319: public void setNextFinishButtonEnabled(boolean newValue) {
320: wizardModel.setNextFinishButtonEnabled(new Boolean(newValue));
321: }
322:
323: /**
324: * Mirrors the WizardModel method of the same name.
325: * @return A boolean indicating if the button is enabled.
326: */
327: public boolean getCancelButtonEnabled() {
328: return wizardModel.getCancelButtonEnabled().booleanValue();
329: }
330:
331: /**
332: * Mirrors the WizardModel method of the same name.
333: * @param boolean newValue The new enabled status of the button.
334: */
335: public void setCancelButtonEnabled(boolean newValue) {
336: wizardModel.setCancelButtonEnabled(new Boolean(newValue));
337: }
338:
339: /**
340: * Closes the dialog and sets the return code to the integer parameter.
341: * @param code The return code.
342: */
343: void close(int code) {
344: returnCode = code;
345: wizardDialog.dispose();
346: }
347:
348: /**
349: * This method initializes the components for the wizard dialog: it creates a JDialog
350: * as a CardLayout panel surrounded by a small amount of space on each side, as well
351: * as three buttons at the bottom.
352: */
353:
354: private void initComponents() {
355:
356: wizardModel.addPropertyChangeListener(this );
357: wizardController = new WizardController(this );
358:
359: wizardDialog.getContentPane().setLayout(new BorderLayout());
360: wizardDialog.addWindowListener(this );
361:
362: // Create the outer wizard panel, which is responsible for three buttons:
363: // Next, Back, and Cancel. It is also responsible a JPanel above them that
364: // uses a CardLayout layout manager to display multiple panels in the
365: // same spot.
366:
367: JPanel buttonPanel = new JPanel();
368: JSeparator separator = new JSeparator();
369: Box buttonBox = new Box(BoxLayout.X_AXIS);
370:
371: cardPanel = new JPanel();
372: cardPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10)));
373:
374: cardLayout = new CardLayout();
375: cardPanel.setLayout(cardLayout);
376:
377: backButton = new JButton(new ImageIcon(
378: "com/nexes/wizard/backIcon.gif"));
379: nextButton = new JButton();
380: cancelButton = new JButton();
381:
382: backButton.setActionCommand(BACK_BUTTON_ACTION_COMMAND);
383: nextButton.setActionCommand(NEXT_BUTTON_ACTION_COMMAND);
384: cancelButton.setActionCommand(CANCEL_BUTTON_ACTION_COMMAND);
385:
386: backButton.addActionListener(wizardController);
387: nextButton.addActionListener(wizardController);
388: cancelButton.addActionListener(wizardController);
389:
390: // Create the buttons with a separator above them, then place them
391: // on the east side of the panel with a small amount of space between
392: // the back and the next button, and a larger amount of space between
393: // the next button and the cancel button.
394:
395: buttonPanel.setLayout(new BorderLayout());
396: buttonPanel.add(separator, BorderLayout.NORTH);
397:
398: buttonBox.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10)));
399: buttonBox.add(backButton);
400: buttonBox.add(Box.createHorizontalStrut(10));
401: buttonBox.add(nextButton);
402: buttonBox.add(Box.createHorizontalStrut(30));
403: buttonBox.add(cancelButton);
404:
405: buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST);
406:
407: wizardDialog.getContentPane().add(buttonPanel,
408: java.awt.BorderLayout.SOUTH);
409: wizardDialog.getContentPane().add(cardPanel,
410: java.awt.BorderLayout.CENTER);
411:
412: }
413:
414: private static Object getImage(String name) {
415:
416: URL url = null;
417:
418: try {
419: Class c = Class.forName("com.nexes.wizard.Wizard");
420: url = c.getResource(name);
421: } catch (ClassNotFoundException cnfe) {
422: System.err.println("Unable to find Wizard class");
423: }
424: return url;
425:
426: }
427:
428: /**
429: * If the user presses the close box on the dialog's window, treat it
430: * as a cancel.
431: * @param WindowEvent The event passed in from AWT.
432: */
433:
434: public void windowClosing(WindowEvent e) {
435: returnCode = CANCEL_RETURN_CODE;
436: }
437:
438: // static {
439: //
440: // try {
441: //
442: // PropertyResourceBundle resources = (PropertyResourceBundle)
443: // ResourceBundle.getBundle("com.nexes.wizard.wizard");
444: //
445: // BACK_TEXT = (String)(resources.getObject("backButtonText"));
446: // NEXT_TEXT = (String)(resources.getObject("nextButtonText"));
447: // CANCEL_TEXT = (String)(resources.getObject("cancelButtonText"));
448: // FINISH_TEXT = (String)(resources.getObject("finishButtonText"));
449: //
450: // BACK_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("backButtonIcon"))));
451: // NEXT_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("nextButtonIcon"))));
452: // CANCEL_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("cancelButtonIcon"))));
453: // FINISH_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("finishButtonIcon"))));
454: //
455: // } catch (MissingResourceException mre) {
456: // System.out.println(mre);
457: // System.exit(1);
458: // }
459: // }
460:
461: static {
462:
463: try {
464:
465: BACK_TEXT = InternationalizationManager
466: .getInstance()
467: .getMessage(
468: "dbbrowser-export-wizard",
469: "dbbrowser-ui-export-wizard-previous-button-label",
470: null);
471: NEXT_TEXT = InternationalizationManager
472: .getInstance()
473: .getMessage(
474: "dbbrowser-export-wizard",
475: "dbbrowser-ui-export-wizard-next-button-label",
476: null);
477: CANCEL_TEXT = InternationalizationManager
478: .getInstance()
479: .getMessage(
480: "dbbrowser-export-wizard",
481: "dbbrowser-ui-export-wizard-cancel-button-label",
482: null);
483: FINISH_TEXT = InternationalizationManager
484: .getInstance()
485: .getMessage(
486: "dbbrowser-export-wizard",
487: "dbbrowser-ui-export-wizard-finish-button-label",
488: null);
489:
490: BACK_ICON = new ImageIcon(
491: PropertyManager
492: .getInstance()
493: .getProperty(
494: "dbbrowser-ui-view-record-window-previous-record-icon"));
495: NEXT_ICON = new ImageIcon(
496: PropertyManager
497: .getInstance()
498: .getProperty(
499: "dbbrowser-ui-view-record-window-next-record-icon"));
500: CANCEL_ICON = new ImageIcon(PropertyManager.getInstance()
501: .getProperty("dbbrowser-ui-cross-icon"));
502: FINISH_ICON = new ImageIcon(
503: PropertyManager
504: .getInstance()
505: .getProperty(
506: "dbbrowser-ui-view-record-window-update-record-icon"));
507:
508: } catch (MissingResourceException mre) {
509: Log.getInstance().warnMessage("Icons not setup in Wizard",
510: Wizard.class.getName());
511: }
512: }
513:
514: }
|