View Javadoc

1   package com.nexes.wizard;
2   
3   import java.awt.*;
4   import java.awt.event.*;
5   import java.beans.*;
6   import java.util.*;
7   import java.net.*;
8   
9   import javax.swing.*;
10  import javax.swing.border.*;
11  
12  /***
13   * This class implements a basic wizard dialog, where the programmer can
14   * insert one or more Components to act as panels. These panels can be navigated
15   * through arbitrarily using the 'Next' or 'Back' buttons, or the dialog itself
16   * can be closed using the 'Cancel' button. Note that even though the dialog
17   * uses a CardLayout manager, the order of the panels is not linear. Each panel
18   * determines at runtime what its next and previous panel will be.
19   */
20  public class Wizard extends WindowAdapter implements PropertyChangeListener {
21  
22      /***
23       * Indicates that the 'Finish' button was pressed to close the dialog.
24       */    
25      public static final int FINISH_RETURN_CODE = 0;
26      /***
27       * Indicates that the 'Cancel' button was pressed to close the dialog, or
28       * the user pressed the close box in the corner of the window.
29       */    
30      public static final int CANCEL_RETURN_CODE = 1;
31      /***
32       * Indicates that the dialog closed due to an internal error.
33       */    
34      public static final int ERROR_RETURN_CODE = 2;
35          
36      /***
37       * The String-based action command for the 'Next' button.
38       */    
39      public static final String NEXT_BUTTON_ACTION_COMMAND = "NextButtonActionCommand";
40      /***
41       * The String-based action command for the 'Back' button.
42       */    
43      public static final String BACK_BUTTON_ACTION_COMMAND = "BackButtonActionCommand";
44      /***
45       * The String-based action command for the 'Cancel' button.
46       */    
47      public static final String CANCEL_BUTTON_ACTION_COMMAND = "CancelButtonActionCommand";
48          
49  
50      // The i18n text used for the buttons. Loaded from a property resource file.    
51      
52      static String BACK_TEXT;
53      static String NEXT_TEXT;
54      static String FINISH_TEXT;
55      static String CANCEL_TEXT;
56  
57      // The image icons used for the buttons. Filenames are loaded from a property resource file.    
58  
59      static Icon BACK_ICON;
60      static Icon NEXT_ICON;
61      static Icon FINISH_ICON;
62      static Icon CANCEL_ICON;
63      
64      
65      private WizardModel wizardModel;
66      private WizardController wizardController;
67      private JDialog wizardDialog;
68          
69      private JPanel cardPanel;
70      private CardLayout cardLayout;            
71      private JButton backButton;
72      private JButton nextButton;
73      private JButton cancelButton;
74      
75      private int returnCode;
76  
77      
78      
79      /***
80       * Default constructor. This method creates a new WizardModel object and passes it
81       * into the overloaded constructor.
82       */    
83      public Wizard() {
84          this((Frame)null);
85      }
86      
87      /***
88       * This method accepts a java.awt.Dialog object as the javax.swing.JDialog's
89       * parent.
90       * @param owner The java.awt.Dialog object that is the owner of this dialog.
91       */    
92      public Wizard(Dialog owner) {
93          wizardModel = new WizardModel();
94          wizardDialog = new JDialog(owner);         
95          initComponents();
96      }
97   
98      /***
99       * This method accepts a java.awt.Frame object as the javax.swing.JDialog's
100      * parent.
101      * @param owner The java.awt.Frame object that is the owner of the javax.swing.JDialog.
102      */    
103     public Wizard(Frame owner) {
104         wizardModel = new WizardModel();
105         wizardDialog = new JDialog(owner);         
106         initComponents();
107     }
108     
109     /***
110      * Returns an instance of the JDialog that this class created. This is useful in
111      * the event that you want to change any of the JDialog parameters manually.
112      * @return The JDialog instance that this class created.
113      */    
114     public JDialog getDialog() {
115         return wizardDialog;
116     }
117     
118     /***
119      * Returns the owner of the generated javax.swing.JDialog.
120      * @return The owner (java.awt.Frame or java.awt.Dialog) of the javax.swing.JDialog generated
121      * by this class.
122      */    
123     public Component getOwner() {
124         return wizardDialog.getOwner();
125     }
126     
127     /***
128      * Sets the title of the generated javax.swing.JDialog.
129      * @param s The title of the dialog.
130      */    
131     public void setTitle(String s) {
132         wizardDialog.setTitle(s);
133     }
134     
135     /***
136      * Returns the current title of the generated dialog.
137      * @return The String-based title of the generated dialog.
138      */    
139     public String getTitle() {
140         return wizardDialog.getTitle();
141     }
142     
143     /***
144      * Sets the modality of the generated javax.swing.JDialog.
145      * @param b the modality of the dialog
146      */    
147     public void setModal(boolean b) {
148         wizardDialog.setModal(b);
149     }
150     
151     /***
152      * Returns the modality of the dialog.
153      * @return A boolean indicating whether or not the generated javax.swing.JDialog is modal.
154      */    
155     public boolean isModal() {
156         return wizardDialog.isModal();
157     }
158     
159     /***
160      * Convienence method that displays a modal wizard dialog and blocks until the dialog
161      * has completed.
162      * @return Indicates how the dialog was closed. Compare this value against the RETURN_CODE
163      * constants at the beginning of the class.
164      */    
165     public int showModalDialog() {
166         
167         wizardDialog.setModal(true);
168         wizardDialog.pack();
169         wizardDialog.setVisible(true);
170         
171         return returnCode;
172     }
173     
174     /***
175      * Returns the current model of the wizard dialog.
176      * @return A WizardModel instance, which serves as the model for the wizard dialog.
177      */    
178     public WizardModel getModel() {
179         return wizardModel;
180     }
181     
182     /***
183      * Add a Component as a panel for the wizard dialog by registering its
184      * WizardPanelDescriptor object. Each panel is identified by a unique Object-based
185      * identifier (often a String), which can be used by the setCurrentPanel()
186      * method to display the panel at runtime.
187      * @param id An Object-based identifier used to identify the WizardPanelDescriptor object.
188      * @param panel The WizardPanelDescriptor object which contains helpful information about the panel.
189      */    
190     public void registerWizardPanel(Object id, WizardPanelDescriptor panel) {
191         
192         //  Add the incoming panel to our JPanel display that is managed by
193         //  the CardLayout layout manager.
194         
195         cardPanel.add(panel.getPanelComponent(), id);
196         
197         //  Set a callback to the current wizard.
198         
199         panel.setWizard(this);
200         
201         //  Place a reference to it in the model. 
202         
203         wizardModel.registerPanel(id, panel);
204         
205     }  
206     
207     /***
208      * Displays the panel identified by the object passed in. This is the same Object-based
209      * identified used when registering the panel.
210      * @param id The Object-based identifier of the panel to be displayed.
211      */    
212     public void setCurrentPanel(Object id) {
213 
214         //  Get the hashtable reference to the panel that should
215         //  be displayed. If the identifier passed in is null, then close
216         //  the dialog.
217         
218         if (id == null)
219             close(ERROR_RETURN_CODE);
220         
221         WizardPanelDescriptor oldPanelDescriptor = wizardModel.getCurrentPanelDescriptor();
222         if (oldPanelDescriptor != null)
223             oldPanelDescriptor.aboutToHidePanel();
224         
225         wizardModel.setCurrentPanel(id);
226         wizardModel.getCurrentPanelDescriptor().aboutToDisplayPanel();
227         
228         //  Show the panel in the dialog.
229         
230         cardLayout.show(cardPanel, id.toString());
231         wizardModel.getCurrentPanelDescriptor().displayingPanel();        
232         
233         
234     }
235     
236     /***
237      * Method used to listen for property change events from the model and update the
238      * dialog's graphical components as necessary.
239      * @param evt PropertyChangeEvent passed from the model to signal that one of its properties has changed value.
240      */    
241     public void propertyChange(PropertyChangeEvent evt) {
242         
243         if (evt.getPropertyName().equals(WizardModel.CURRENT_PANEL_DESCRIPTOR_PROPERTY)) {
244             wizardController.resetButtonsToPanelRules(); 
245         } else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_TEXT_PROPERTY)) {            
246             nextButton.setText(evt.getNewValue().toString());
247         } else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_TEXT_PROPERTY)) {            
248             backButton.setText(evt.getNewValue().toString());
249         } else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_TEXT_PROPERTY)) {            
250             cancelButton.setText(evt.getNewValue().toString());
251         } else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_ENABLED_PROPERTY)) {            
252             nextButton.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
253         } else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_ENABLED_PROPERTY)) {            
254             backButton.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
255         } else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_ENABLED_PROPERTY)) {            
256             cancelButton.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
257         } else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_ICON_PROPERTY)) {            
258             nextButton.setIcon((Icon)evt.getNewValue());
259         } else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_ICON_PROPERTY)) {            
260             backButton.setIcon((Icon)evt.getNewValue());
261         } else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_ICON_PROPERTY)) {            
262             cancelButton.setIcon((Icon)evt.getNewValue());
263         }
264         
265     }
266     
267     /***
268      * Retrieves the last return code set by the dialog.
269      * @return An integer that identifies how the dialog was closed. See the *_RETURN_CODE
270      * constants of this class for possible values.
271      */    
272     public int getReturnCode() {
273         return returnCode;
274     }
275     
276    /***
277      * Mirrors the WizardModel method of the same name.
278      * @return A boolean indicating if the button is enabled.
279      */  
280     public boolean getBackButtonEnabled() {
281         return wizardModel.getBackButtonEnabled().booleanValue();
282     }
283 
284    /***
285      * Mirrors the WizardModel method of the same name.
286      * @param boolean newValue The new enabled status of the button.
287      */ 
288     public void setBackButtonEnabled(boolean newValue) {
289         wizardModel.setBackButtonEnabled(new Boolean(newValue));
290     }
291 
292    /***
293      * Mirrors the WizardModel method of the same name.
294      * @return A boolean indicating if the button is enabled.
295      */ 
296     public boolean getNextFinishButtonEnabled() {
297         return wizardModel.getNextFinishButtonEnabled().booleanValue();
298     }
299 
300    /***
301      * Mirrors the WizardModel method of the same name.
302      * @param boolean newValue The new enabled status of the button.
303      */ 
304     public void setNextFinishButtonEnabled(boolean newValue) {
305         wizardModel.setNextFinishButtonEnabled(new Boolean(newValue));
306     }
307  
308    /***
309      * Mirrors the WizardModel method of the same name.
310      * @return A boolean indicating if the button is enabled.
311      */ 
312     public boolean getCancelButtonEnabled() {
313         return wizardModel.getCancelButtonEnabled().booleanValue();
314     }
315 
316     /***
317      * Mirrors the WizardModel method of the same name.
318      * @param boolean newValue The new enabled status of the button.
319      */ 
320     public void setCancelButtonEnabled(boolean newValue) {
321         wizardModel.setCancelButtonEnabled(new Boolean(newValue));
322     }
323     
324     /***
325      * Closes the dialog and sets the return code to the integer parameter.
326      * @param code The return code.
327      */    
328     void close(int code) {
329         returnCode = code;
330         wizardDialog.dispose();
331     }
332     
333     /***
334      * This method initializes the components for the wizard dialog: it creates a JDialog
335      * as a CardLayout panel surrounded by a small amount of space on each side, as well
336      * as three buttons at the bottom.
337      */
338     
339     private void initComponents() {
340 
341         wizardModel.addPropertyChangeListener(this);       
342         wizardController = new WizardController(this);       
343 
344         wizardDialog.getContentPane().setLayout(new BorderLayout());
345         wizardDialog.addWindowListener(this);
346                 
347         //  Create the outer wizard panel, which is responsible for three buttons:
348         //  Next, Back, and Cancel. It is also responsible a JPanel above them that
349         //  uses a CardLayout layout manager to display multiple panels in the 
350         //  same spot.
351         
352         JPanel buttonPanel = new JPanel();
353         JSeparator separator = new JSeparator();
354         Box buttonBox = new Box(BoxLayout.X_AXIS);
355 
356         cardPanel = new JPanel();
357         cardPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10)));       
358 
359         cardLayout = new CardLayout(); 
360         cardPanel.setLayout(cardLayout);
361         
362         backButton = new JButton(new ImageIcon("com/nexes/wizard/backIcon.gif"));
363         nextButton = new JButton();
364         cancelButton = new JButton();
365         
366         backButton.setActionCommand(BACK_BUTTON_ACTION_COMMAND);
367         nextButton.setActionCommand(NEXT_BUTTON_ACTION_COMMAND);
368         cancelButton.setActionCommand(CANCEL_BUTTON_ACTION_COMMAND);
369 
370         backButton.addActionListener(wizardController);
371         nextButton.addActionListener(wizardController);
372         cancelButton.addActionListener(wizardController);
373         
374         //  Create the buttons with a separator above them, then place them
375         //  on the east side of the panel with a small amount of space between
376         //  the back and the next button, and a larger amount of space between
377         //  the next button and the cancel button.
378         
379         buttonPanel.setLayout(new BorderLayout());
380         buttonPanel.add(separator, BorderLayout.NORTH);
381 
382         buttonBox.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10)));       
383         buttonBox.add(backButton);
384         buttonBox.add(Box.createHorizontalStrut(10));
385         buttonBox.add(nextButton);
386         buttonBox.add(Box.createHorizontalStrut(30));
387         buttonBox.add(cancelButton);
388         
389         buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST);
390         
391         wizardDialog.getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH);
392         wizardDialog.getContentPane().add(cardPanel, java.awt.BorderLayout.CENTER);
393 
394     }
395     
396     private static Object getImage(String name) {
397 
398         URL url = null;
399 
400         try {
401             Class c = Class.forName("com.nexes.wizard.Wizard");
402             url = c.getResource(name);
403         } catch (ClassNotFoundException cnfe) {
404             System.err.println("Unable to find Wizard class");
405         }
406         return url;
407 
408     }
409 
410    /***
411      * If the user presses the close box on the dialog's window, treat it
412      * as a cancel.
413      * @param WindowEvent The event passed in from AWT.
414      */ 
415     
416     public void windowClosing(WindowEvent e) {
417         returnCode = CANCEL_RETURN_CODE;
418     }
419     
420     
421     
422     static {
423 
424         try { 
425             
426             PropertyResourceBundle resources = (PropertyResourceBundle)
427                 ResourceBundle.getBundle("com.nexes.wizard.wizard");
428             
429             BACK_TEXT = (String)(resources.getObject("backButtonText"));
430             NEXT_TEXT = (String)(resources.getObject("nextButtonText"));
431             CANCEL_TEXT = (String)(resources.getObject("cancelButtonText"));
432             FINISH_TEXT = (String)(resources.getObject("finishButtonText"));
433             
434             BACK_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("backButtonIcon"))));
435             NEXT_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("nextButtonIcon"))));
436             CANCEL_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("cancelButtonIcon"))));
437             FINISH_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("finishButtonIcon"))));
438         
439         } catch (MissingResourceException mre) {
440             System.out.println(mre);
441             System.exit(1);
442         }
443     }
444 
445 }