Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: echopoint/WizardPane.java


1   package echopoint;
2   
3   /* 
4    * This file is part of the Echo Point Project.  This project is a collection
5    * of Components that have extended the Echo Web Application Framework.
6    *
7    * EchoPoint is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as published by
9    * the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   *
12   * EchoPoint is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public License
18   * along with Echo Point; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   */
21  
22  import java.io.Serializable;
23  
24  import nextapp.echo.Button;
25  import nextapp.echo.Color;
26  import nextapp.echo.Component;
27  import nextapp.echo.Container;
28  import nextapp.echo.EchoConstants;
29  import nextapp.echo.Insets;
30  import nextapp.echo.Style;
31  import nextapp.echo.event.ActionEvent;
32  import nextapp.echo.event.ActionListener;
33  import nextapp.echo.event.ChangeListener;
34  import echopoint.model.DefaultSingleSelectionModel;
35  import echopoint.model.SingleSelectionModel;
36  import echopoint.positionable.Scrollable;
37  import echopoint.stylesheet.StyleAttrDescriptor;
38  import echopoint.stylesheet.StyleInfo;
39  import echopoint.wizard.WizardNavigationEvent;
40  import echopoint.wizard.WizardNavigationListener;
41  import echopoint.wizard.WizardNavigationVetoException;
42  
43  /**
44   * The <code>WizardPane</code> class provides a component to the Echo Web Framework that
45   * allows the user to navigate through a series of logical steps.
46   * <p>
47   * Any Component object can be used as the interior of a given pane, however in 
48   * general you would use a Panel object as the contents, and then fill that
49   * Panel with other components.
50   * <p>
51   * The <code>WizardPane</code> uses a <code>SingleSelectionModel</code> to represent the set of tab 
52   * indices and the currently selected index. 
53   * <p>
54   * If the count is greater than 0, then there will always be a selected index, 
55   * which by default will be initialized to the first component. If the count is 0,
56   * then the selected index will be -1.
57   * <p>
58   * <code>WizardPane</code> has an optimisation in place that means that only the
59   * currently selected component is given a rendering peer.  This ensures that the
60   * smallest amount of memory is sxued to render the contents.  
61   * <p>
62   * The <code>WizardPane</code> will call any <code>WizardNavigationListener's</code> 
63   * that are registered when the user wants to navigate to another component.
64   * <p>
65   * If any listeners are provided, then they can throw a <code>WizardNavigationVetoException</code> 
66   * to stop the user from navigating way from the current pane.  You might do this to implement
67   * validation of the pane.
68   * <p>
69   * The WizardPane stores the selected index before invoking the WizardNavigationListener
70   * interface.  If, after completion of the call, the model's selected index is different
71   * to the saved index, the selected index will not be decremented or incremented.  This allows
72   * you to "jump" the WizardPane to a specific index via the WizardNavigationListener processing.
73   * <p>
74   * The WizardPane can contain a <code>TitleBar</code> object as its title and footer.  
75   * Every time the pane changes, the text of the title and footer will change.
76   * <p>
77   * The width and height of the <code>WizardPane</code> can be set.  This sets the dimensions
78   * of the interior of the contents area.  If the width is not set, ie it is set to Integer.MAX_VALUE or
79   * Integer.MIN_VALUE, then the <code>WizardPane</code> will fill its containing component.  
80   * <p>
81   * Scrollbars can also be used within the <code>WizardPane</code>.
82   * <p>
83   * @see echopoint.positionable.Scrollable for more information.
84   *
85   * @author Brad Baker 
86   */
87  
88  public class WizardPane extends EchoPointComponent implements ActionListener, Scrollable {
89  
90    /** 
91     * Nested public static StyleInfo class.  For more info:
92     * @see echopoint.stylesheet.StyleInfo 
93     */
94    public static class NestedStyleInfo implements StyleInfo {
95      private static StyleAttrDescriptor[] styleDescriptors = { 
96        new StyleAttrDescriptor(STYLE_BORDER_COLOR,Color.class,"borderColor"),
97        new StyleAttrDescriptor(STYLE_BORDER_SIZE,Integer.class,"borderSize"),
98        new StyleAttrDescriptor(STYLE_INSETS,Insets.class,"insets"),
99        new StyleAttrDescriptor(STYLE_FINISH_ONLY_ON_LAST,Boolean.class,"finishButtonOnLastOnly"),
100     };
101     /**
102      * @see echopoint.stylesheet.StyleInfo#getStyleDescriptors()
103      */
104     public StyleAttrDescriptor[] getStyleDescriptors() {
105       return styleDescriptors;
106     }
107   }
108 
109   private class WizardEntry implements Serializable {
110     Component component;
111     String footer;
112     String title;
113   }
114   public static final String BORDER_COLOR_CHANGED_PROPERTY = "borderColor";
115   public static final String BORDER_SIZE_CHANGED_PROPERTY = "borderSize";
116   public static final String BUTTONS_CHANGED_PROPERTY = "buttons";
117   public static final String COMPONENT_CHANGED_PROPERTY = "component";
118 
119   //
120   // defaults colors and insets
121   //
122   public static final Insets DEFAULT_INSETS = new Insets(0);
123 
124   public static final String ENTRY_CHANGED_PROPERTY = "entry";
125   public static final String FINISH_LAST_ONLY_CHANGED_PROPERTY = "finishLastOnly";
126   public static final String FOOTER_CHANGED_PROPERTY = "footer";
127   public static final String FOOTERBAR_CHANGED_PROPERTY = "footerBar";
128   public static final String HEIGHT_CHANGED_PROPERTY = "height";
129 
130   public static final String INSETS_CHANGED_PROPERTY = "insets";
131   public static final String MODEL_CHANGED_PROPERTY = "model";
132 
133   public static final String PLACEMENT_CHANGED_PROPERTY = "placement";
134 
135   public static final String RENDERING_CHANGED_PROPERTY = "rendering";
136   /**
137     * This property name is fired when the scolling bar X value changes
138     */
139   public static final String SCROLL_BAR_X_CHANGED_PROPERTY = "scrollbarX";
140   /**
141     * This property name is fired when the scolling bar Y value changes
142     */
143   public static final String SCROLL_BAR_Y_CHANGED_PROPERTY = "scrollbarX";
144 
145   public static final String SCROLLBAR_POLICY_CHANGED_PROPERTY = "scrollbarsPolicy";
146 
147   /* Property Change Names */
148   public static final String SELECTED_INDEX_CHANGED_PROPERTY = "selindex";
149 
150   /** 
151    * A style constant for the Border Color property. 
152    * Values of this key must be of type <code>nextapp.echo.Color.</code>
153    */
154   public static final String STYLE_BORDER_COLOR = "borderColor";
155   /** 
156    * A style constant for the Border Size property. 
157    * Values of this key must be of type <code>Integer.</code>
158    */
159   public static final String STYLE_BORDER_SIZE = "borderSize";
160 
161   /** 
162    * A style constant for the FinishOnlyOnLast property. 
163    * Values of this key must be of type <code>boolean </code>
164    */
165   public static final String STYLE_FINISH_ONLY_ON_LAST = "finishButtonOnLastOnly";
166   /** 
167    * A style constant for the Insets property. 
168    * Values of this key must be of type <code>nextapp.echo.Insets.</code>
169    */
170   public static final String STYLE_INSETS = "insets";
171 
172   public static final String TITLE_CHANGED_PROPERTY = "title";
173   public static final String TITLEBAR_CHANGED_PROPERTY = "titleBar";
174   public static final String WIDTH_CHANGED_PROPERTY = "width";
175 
176   //
177   // we always have a border for tabs
178   private  Color borderColor = Color.BLACK;
179   private  int borderSize = 1;
180 
181   private  boolean finishButtonOnLastOnly = false;
182   private  TitleBar footerBar;
183   private  Insets insets = DEFAULT_INSETS;
184   //
185   // our width and height
186   private  int width = Integer.MAX_VALUE;
187   private  int scrollBarX = 0;
188   private  int scrollBarY = 0;
189   private  int height = Integer.MAX_VALUE;
190   //
191   // the invisible component container
192   private DevNull invisibleContainer;
193   // 
194   // the visible component container
195   private Container visibleContainer;
196   
197   //
198   // our selection model
199   private  SingleSelectionModel model = null;
200   
201   //
202   // the buttons
203   private  Button nextButton;
204   private  Button prevButton;
205   private  Button cancelButton;
206   private  Button finishButton;
207 
208   //
209   // contain where the buttons are placed
210   private  int placement = EchoConstants.BOTTOM;
211   private  int scrollBarPolicy = SCROLLBARS_NEVER;
212   //
213   // our title and footer
214   private  TitleBar titleBar;
215   //
216   // contains our list of components
217   private  java.util.ArrayList wizardList = new java.util.ArrayList();
218 
219   /**
220    * Constructs a WizardPane with the button placement as <code>EchoConstants.BOTTOM</code>
221    */
222   public WizardPane() {
223     this(EchoConstants.BOTTOM);
224   }
225   /**
226    * Constructs a new WizardPane
227    * <br>
228    * @param buttonPlacement  the button placement of the WizardPane,
229    *        can be one of the following values.
230    *        <ul>
231    *        <li>EchoConstants.BOTTOM (the default)</li>
232    *        <li>EchoConstants.TOP</li>
233    *        </ul>
234    */
235   public WizardPane(int buttonPlacement) {
236     super();
237     setScrollBarPolicy(SCROLLBARS_NEVER);
238     
239     invisibleContainer = new DevNull();
240     super.add(invisibleContainer,-1);
241     
242     visibleContainer = new Container();
243     super.add(visibleContainer,-1);
244     
245     //
246     // by default we dont have a cancel of finish button.  They must be added
247     setNextButton(new Button("Next >"));
248     setPreviousButton(new Button("< Previous"));
249 
250     //
251     // we start with some default title bars
252     setTitle(new TitleBar(null, TitleBar.TB_CLOSE));
253     setFooter(new TitleBar(null));
254 
255     setModel(new DefaultSingleSelectionModel());
256   }
257   /**
258    * Called when one of the buttons has been pressed and navigation 
259    * should be done.  Each of the WizardNavigationListners will be called
260    * to determine if navigation should take place.
261    */
262   public void actionPerformed(ActionEvent actionEvent) {
263     int index = getSelectedIndex();
264     if (index == -1)
265       return; //?? how did they press anything
266 
267     int fromIndex = index;
268     int toIndex = -1;
269     try {
270       if (actionEvent.getSource() == getNextButton()) {
271         toIndex = Math.min(fromIndex + 1, wizardList.size());
272         fireOnNavigation(WizardNavigationEvent.NEXT, getComponentAt(fromIndex), getComponentAt(toIndex), fromIndex, toIndex);
273         checkAndSetSelectedIndex(fromIndex, toIndex);
274         return;
275       }
276 
277       if (actionEvent.getSource() == getPreviousButton()) {
278         toIndex = Math.max(fromIndex - 1, 0);
279         fireOnNavigation(WizardNavigationEvent.PREVIOUS, getComponentAt(fromIndex), getComponentAt(toIndex), fromIndex, toIndex);
280         checkAndSetSelectedIndex(fromIndex, toIndex);
281         return;
282       }
283 
284       if (actionEvent.getSource() == getCancelButton() || (actionEvent.getSource() instanceof TitleBar && TitleBar.ACTION_CLOSE.equals(actionEvent.getActionCommand()))) {
285 
286         fireOnNavigation(WizardNavigationEvent.CANCEL, getComponentAt(fromIndex), getComponentAt(fromIndex), fromIndex, fromIndex);
287         return;
288       }
289 
290       if (actionEvent.getSource() == getFinishButton()) {
291         fireOnNavigation(WizardNavigationEvent.FINISH, getComponentAt(fromIndex), getComponentAt(fromIndex), fromIndex, fromIndex);
292         return;
293       }
294 
295     } catch (WizardNavigationVetoException we) {
296       //
297       // they have veto'ed the navigation.  We should go back to where we started
298       // however we need to be careful about this since they may have changed the
299       // model underneath us.
300       index = Math.min(fromIndex, wizardList.size());
301       if (wizardList.size() == 0)
302         setSelectedIndex(-1);
303       else
304         setSelectedIndex(index);
305       return;
306     }
307   }
308   /**
309    * Adds a component to the <code>WizardPane</code>
310    * <p>
311    * Cover method for insert()
312    *
313    * @param component
314    */
315   public void add(Component component) {
316     insert(null, null, component, wizardList.size());
317   }
318   /**
319    * Adds a component to the <code>WizardPane</code> at the specified index
320    * <p>
321    * Cover method for insert()
322    * <p>
323    * @param component
324    */
325   public void add(Component component, int index) {
326     insert(null, null, component, index);
327   }
328   /**
329    * Adds a component to the <code>WizardPane</code>
330    *
331    */
332   public void add(String title, Component component) {
333     insert(title, null, component, wizardList.size());
334   }
335   /**
336    * Adds a component to the <code>WizardPane</code>
337    *
338    */
339   public void add(String title, String footer, Component component) {
340     insert(title, footer, component, wizardList.size());
341   }
342   /**
343    * Adds a <code>ChangeListener</code> to the WizardPane.
344    *
345    * @param l The <code>ChangeListener</code> to be added.
346    */
347   public void addChangeListener(ChangeListener l) {
348     model.addChangeListener(l);
349   }
350   /**
351    * Adds a <code>WizardNavigationListener</code> to the WizardPane.
352    *
353    * @param l The <code>ChangeListener</code> to be added.
354    */
355   public void addWizardNavigationListener(WizardNavigationListener l) {
356     listenerList.addListener(WizardNavigationListener.class, l);
357   }
358   /**
359    * Applies the provided style to the component.  The base <code>nextapp.echo.Component</code>
360    * style names can be used as well.
361    */
362   public void applyStyle(Style style) {
363     super.applyStyle(style);
364 
365     if (style.hasAttribute(STYLE_BORDER_COLOR)) {
366       setBorderColor((Color) style.getAttribute(STYLE_BORDER_COLOR));
367     }
368     if (style.hasAttribute(STYLE_BORDER_SIZE)) {
369       setBorderSize(style.getIntegerAttribute(STYLE_BORDER_SIZE));
370     }
371     if (style.hasAttribute(STYLE_INSETS)) {
372       setInsets((Insets) style.getAttribute(STYLE_INSETS));
373     }
374     if (style.hasAttribute(STYLE_FINISH_ONLY_ON_LAST)) {
375       setFinishButtonOnLastOnly(style.getBooleanAttribute(STYLE_FINISH_ONLY_ON_LAST));
376     }
377 
378     if (style.hasAttribute(STYLE_SCROLL_BAR_POLICY))
379       setScrollBarPolicy(style.getIntegerAttribute(STYLE_SCROLL_BAR_POLICY));
380 
381     if (style.hasAttribute(STYLE_HEIGHT))
382       setHeight(style.getIntegerAttribute(STYLE_HEIGHT));
383 
384     if (style.hasAttribute(STYLE_WIDTH))
385       setWidth(style.getIntegerAttribute(STYLE_WIDTH));
386 
387   }
388   /**
389    * This method will check that the currently selected index of the
390    * the WizardPane is still the same as a saved value.  If not then it 
391    * will NOT set the selected index.
392    *
393    * This is used during the WizardNavigationListener events to allow the listener
394    * a chance to set the selected index during the event processing, reagrdless of what 
395    * is logically next.  we * dont want to blat that away by blindly 
396    * settting it to index+1 or index -1
397    */
398   private void checkAndSetSelectedIndex(int savedSelectedIndex, int toIndex) {
399     int currentIndex = getSelectedIndex();
400     if (currentIndex == savedSelectedIndex) {
401       setSelectedIndex(toIndex);
402     }
403   }
404   /**
405    * Called to notify all WizardNavigationListener's of a navigation event.
406    */
407   private void fireOnNavigation(int type, Component fromComponent, Component toComponent, int fromIndex, int toIndex) throws WizardNavigationVetoException {
408     Object[] listeners = listenerList.getListeners(WizardNavigationListener.class);
409     WizardNavigationEvent e = null;
410 
411     for (int index = 0; index < listeners.length; ++index) {
412       if (e == null)
413         e = new WizardNavigationEvent(this, type, fromComponent, toComponent, fromIndex, toIndex);
414 
415       ((WizardNavigationListener) listeners[index]).onNavigation(e);
416     }
417   }
418   /**
419    * @return The <code>Color</code> of the <code>WizardPane's</code> border.
420    */
421   public Color getBorderColor() {
422     return borderColor;
423   }
424   /**
425    * @return The size of the <code>WizardPane's</code> border.
426    */
427   public int getBorderSize() {
428     return borderSize;
429   }
430   /**
431    * Returns the button that acts as the "Cancel" button in the <code>WizardPane</code>.
432    */
433   public Button getCancelButton() {
434     return cancelButton;
435   }
436   /**
437    * @return The <code>Component</code> at the specified index.
438    */
439   public Component getComponentAt(int index) {
440     WizardEntry te = getWizardEntryAt(index);
441     return (te != null) ? te.component : null;
442   }
443   /**
444    * Returns the number of entries within the <code>WizardPane</code>.
445    */
446   public int getCount() {
447     return wizardList.size();
448   }
449   /**
450    * Returns the button that acts as the "Finish" button in the <code>WizardPane</code>.
451    */
452   public Button getFinishButton() {
453     return finishButton;
454   }
455   /**
456    * @return The footer TitleBar of the <code>WizardPane</code>
457    */
458   public TitleBar getFooter() {
459     return footerBar;
460   }
461   /**
462    * @return The footer of the Component at the specified index.
463    */
464   public String getFooterAt(int index) {
465     WizardEntry te = getWizardEntryAt(index);
466     return (te != null) ? te.footer : null;
467   }
468   /**
469    * Returns the height of the <code>WizardPane</code>.  If this value
470    * is -1, then no height will be used.
471    */
472   public int getHeight() {
473     return height;
474   }
475   /** 
476   * Returns the insets (margins) of the tabbed pane contents.
477   *
478   * @return The insets of the tabbed pane.
479   */
480   public Insets getInsets() {
481     return insets;
482   }
483   /**
484    * Returns the model associated with this <code>WizardPane</code>.
485    *
486    */
487   public SingleSelectionModel getModel() {
488     return model;
489   }
490   /**
491    * Returns the button that acts as the "Next" button in the <code>WizardPane</code>.
492    */
493   public Button getNextButton() {
494     return nextButton;
495   }
496   /**
497    * @return The placement of buttons wihtin the <code>WizardPane</code>
498    *
499    */
500   public int getPlacement() {
501     return placement;
502   }
503   /**
504    * Returns the button that acts as the "Previous" button in the <code>WizardPane</code>.
505    */
506   public Button getPreviousButton() {
507     return prevButton;
508   }
509   /**
510    * Returns the ScrollBarPolicy in place
511    * 
512    * This can be one of :
513    * <ul>
514    * <li>SCOLLBARS_NONE</li>
515    * <li>SCOLLBARS_ALWAYS</li>
516    * <li>SCOLLBARS_AUTO</li>
517    * </ul>
518    */
519   public int getScrollBarPolicy() {
520     return scrollBarPolicy;
521   }
522   /**
523    * Returns the position of the horizontal scroll bar, in pixels offset from 
524    * the left of the component.
525    *
526    * @return The position of the horizontal scroll bar.
527    */
528   public int getScrollBarX() {
529     return scrollBarX;
530   }
531   /**
532    * Returns the position of the vertical scroll bar, in pixels offset from 
533    * the top the component.
534    *
535    * @return The position of the vertical scroll bar.
536    */
537   public int getScrollBarY() {
538     return scrollBarY;
539   }
540   /**
541    * @return the <code>Component</code> at the currently selected index or null if
542    * none is currently selected.
543    */
544   public Component getSelectedComponent() {
545     int index = model.getSelectedIndex();
546     if (index < 0)
547       return null;
548     return getComponentAt(index);
549   }
550   /**
551    * Returns the currently selected index or -1 if none is selected
552    */
553   public int getSelectedIndex() {
554     return model.getSelectedIndex();
555   }
556   /**
557    * @return The title TitleBar of the <code>WizardPane</code>
558    */
559   public TitleBar getTitle() {
560     return titleBar;
561   }
562   /**
563    * @return The title of the Component at the specified index.
564    */
565   public String getTitleAt(int index) {
566     WizardEntry te = getWizardEntryAt(index);
567     return (te != null) ? te.title : null;
568   }
569   /**
570    * Returns the width of the <code>WizardPane</code>.  If this value
571    * is -1, then no width will be used.
572    */
573   public int getWidth() {
574     return width;
575   }
576   /**
577    * Returns the tab entry at a specified index.  Returns null if the
578    * index is out of range.
579    *
580    */
581   private WizardEntry getWizardEntryAt(int index) {
582     if (index < 0 || index >= wizardList.size())
583       return null;
584 
585     WizardEntry te = (WizardEntry) wizardList.get(index);
586 
587     return te;
588   }
589   /**
590    * Returns the index of the given <code>Component</code> or -1 if it is not found
591    */
592   public int indexOfComponent(Component c) {
593     for (int i = 0; i < wizardList.size(); i++) {
594       WizardEntry te = getWizardEntryAt(i);
595       if (te.component == c)
596         return i;
597     }
598     return -1;
599   }
600   /**
601    * Returns the index with the given title or -1 if it is not found
602    */
603   public int indexOfTitle(String title) {
604     for (int i = 0; i < wizardList.size(); i++) {
605       WizardEntry te = getWizardEntryAt(i);
606       if (te != null && te.title.equals(title))
607         return i;
608     }
609     return -1;
610   }
611   /**
612    * Insert a Component at the specified index.  
613    */
614   public void insert(String title, Component component, int index) {
615     insert(title, null, component, index);
616   }
617   /**
618    * Insert a Component at the specified index.  
619    */
620   public void insert(String title, String footer, Component component, int index) {
621 
622     if (index < 0)
623       index = 0;
624 
625     WizardEntry tc = new WizardEntry();
626     tc.title = title;
627     tc.footer = footer;
628     tc.component = component;
629 
630     int newIndex = -1;
631     if (index >= wizardList.size()) {
632       wizardList.add(tc);
633       newIndex = wizardList.size() - 1;
634     } else {
635       wizardList.add(index, tc);
636       newIndex = index;
637     }
638 
639     //
640     // we always have a selected Index if we have some entries
641     if (getSelectedIndex() == -1)
642       setSelectedIndex(newIndex);
643       
644     reArrangeVisibleComponents(null);
645     
646     firePropertyChange(ENTRY_CHANGED_PROPERTY, null, component);
647   }
648   /**
649    * This returns TRUE if the Finish button will be shown on the last wizard page only.  
650    * If this is FALSE, then the Finsih button will show on all wizard pages.  
651    * 
652    * @return boolean
653    */
654   public boolean isFinishButtonOnLastOnly() {
655     return finishButtonOnLastOnly;
656   }
657   
658   /**
659    * Called to rearrange the components inside the WizardPane
660    * It moves the currently selected component into the visible
661    * container and the rest into the invisible bucket.  This 
662    * ensures that they get no rendering peers and hence
663    * this is more efficient. 
664    * 
665    * @param removedComponent - an optinal component that has been removed
666    */
667   private void reArrangeVisibleComponents(Component removedComponent) {
668     
669     Component selectedC = getSelectedComponent();
670     for (int i = 0; i < getCount(); i++) {
671       Component c = getComponentAt(i);
672       if (c == selectedC) {
673         if (! visibleContainer.isAncestorOf(c))
674           visibleContainer.add(c);
675       } else {
676         if (! invisibleContainer.isAncestorOf(c))
677           invisibleContainer.add(c);
678       }
679     }
680     if (visibleContainer.isAncestorOf(removedComponent))
681       visibleContainer.remove(removedComponent);
682     else if (invisibleContainer.isAncestorOf(removedComponent))
683       invisibleContainer.remove(removedComponent);
684   }
685   /**
686    * Removes a entry from the <code>WizardPane</code> at represented by the Component c
687    */
688   public void remove(Component c) {
689     for (int i = getCount() - 1; i >= 0; i--) {
690       WizardEntry te = getWizardEntryAt(i);
691       if (te == null)
692         continue;
693 
694       if (te.component == c) {
695         removeComponentAt(i);
696       }
697 
698     }
699   }
700   
701   /**
702    * Removes a Component entry from the <code>WizardPane</code> at the specified index.
703    */
704   public void remove(int index) {
705     removeComponentAt(index);
706   }
707   /**
708    * Removes all entries from the <code>WizardPane</code>.
709    */
710   public void removeAll() {
711     for (int i = getCount() - 1; i >= 0; i--) {
712       removeComponentAt(i);
713     }
714   }
715   /**
716    * Removes a ChangeListener from this component.
717    *
718    * @param l the ChangeListener to remove
719    *
720    */
721   public void removeChangeListener(ChangeListener l) {
722     model.removeChangeListener(l);
723   }
724   
725   /**
726    * Removes a Component entry from the <code>WizardPane</code> at the specified index.
727    */
728   public void removeComponentAt(int index) {
729     WizardEntry te = getWizardEntryAt(index);
730     if (te == null)
731       return;
732 
733     wizardList.remove(index);
734 
735     int newIndex = index - 1;
736     if (newIndex < 0)
737       newIndex = 0;
738 
739     if (wizardList.size() == 0)
740       newIndex = -1;
741 
742     setSelectedIndex(newIndex);
743 
744     reArrangeVisibleComponents(te.component);
745     
746     firePropertyChange(ENTRY_CHANGED_PROPERTY, null, te.component);
747   }
748   /**
749    * Removes a <code>WizardNavigationListener</code> to the WizardPane.
750    *
751    * @param l The <code>ChangeListener</code> to be added.
752    */
753   public void removeWizardNavigationListener(WizardNavigationListener l) {
754     listenerList.removeListener(WizardNavigationListener.class, l);
755   }
756   
757   /**
758    * Adds and removes a Component as a child of the WizardPane. 
759    * 
760    * @param oldValue - the old component to remove - can be null
761    * @param newValue - the new component to add - can be null
762    */
763   protected void reRegisterComponents(Component oldValue, Component newValue) {
764     if (oldValue != null && this.isAncestorOf(oldValue)) {
765       super.remove(oldValue);
766       if (oldValue instanceof TitleBar)
767          ((TitleBar) oldValue).removeActionListener(this);
768     }
769 
770     if (newValue != null && !this.isAncestorOf(newValue)) {
771       super.add(newValue, -1);
772       if (newValue instanceof TitleBar)
773          ((TitleBar) newValue).addActionListener(this);
774     }
775   }  
776   
777   /**
778    * Sets the border color of the <code>WizardPane</code>
779    * <br>
780    * This methods fires a <code>PropertyChangeEvent</code> with a
781    * <code>getPropertyName()</code> value of 
782    * <code>WizardPane.BORDER_COLOR_CHANGED_PROPERTY</code>
783    */
784   public void setBorderColor(Color newValue) {
785     Color oldValue = borderColor;
786     borderColor = newValue;
787     firePropertyChange(BORDER_COLOR_CHANGED_PROPERTY, oldValue, newValue);
788   }
789   /**
790    * Sets the border size of the <code>WizardPane</code>
791    * <br>
792    * This methods fires a <code>PropertyChangeEvent</code> with a
793    * <code>getPropertyName()</code> value of 
794    * <code>WizardPane.BORDER_SIZE_CHANGED_PROPERTY</code>
795    *
796    */
797   public void setBorderSize(int newBorderSize) {
798     int oldValue = borderSize;
799     borderSize = newBorderSize;
800     firePropertyChange(BORDER_SIZE_CHANGED_PROPERTY, oldValue, newBorderSize);
801   }
802   /**
803    * Sets the button that acts as the "Cancel" button in the <code>WizardPane</code>
804    * <br>
805    * This methods fires a <code>PropertyChangeEvent</code> with a
806    * <code>getPropertyName()</code> value of 
807    * <code>WizardPane.BUTTONS_CHANGED_PROPERTY</code>
808    *
809    */
810   public void setCancelButton(Button newButton) {
811     Button oldValue = cancelButton;
812     cancelButton = newButton;
813     reRegisterComponents(oldValue, newButton);
814     if (cancelButton != null) {
815       cancelButton.setActionCommand("finish");
816       cancelButton.addActionListener(this);
817     }
818     firePropertyChange(BUTTONS_CHANGED_PROPERTY, oldValue, newButton);
819   }
820   /**
821    * Sets the <code>Component</code> at the specified index
822    * <br>
823    * This methods fires a <code>PropertyChangeEvent</code> with a
824    * <code>getPropertyName()</code> value of 
825    * <code>WizardPane.COMPONENT_CHANGED_PROPERTY</code>
826    *
827    */
828   public void setComponentAt(int index, Component component) {
829 
830     WizardEntry te = getWizardEntryAt(index);
831     if (te == null)
832       return;
833     Component oldValue = te.component;
834     te.component = component;
835     
836     reArrangeVisibleComponents(null);
837     
838     firePropertyChange(COMPONENT_CHANGED_PROPERTY, oldValue, component);
839   }
840   
841 
842   /**
843    * @see nextapp.echo.Component#setEnabled(boolean)
844    */
845   public void setEnabled(boolean newValue) {
846     Component c = getCancelButton();
847     if (c != null) c.setEnabled(newValue);
848     c = getFinishButton();
849     if (c != null) c.setEnabled(newValue);
850     c = getNextButton();
851     if (c != null) c.setEnabled(newValue);
852     c = getPreviousButton();
853     if (c != null) c.setEnabled(newValue);
854     c = getTitle();
855     if (c != null) c.setEnabled(newValue);
856     c = getFooter();
857     if (c != null) c.setEnabled(newValue);
858   }
859   /**
860    * Sets the button that acts as the "Finish" button in the <code>WizardPane</code>
861    * <br>
862    * This methods fires a <code>PropertyChangeEvent</code> with a
863    * <code>getPropertyName()</code> value of 
864    * <code>WizardPane.BUTTONS_CHANGED_PROPERTY</code>
865    * 
866    */
867   public void setFinishButton(Button newButton) {
868     Button oldValue = finishButton;
869     finishButton = newButton;
870     reRegisterComponents(oldValue, newButton);
871     if (finishButton != null) {
872       finishButton.setActionCommand("finish");
873       finishButton.addActionListener(this);
874     }
875     firePropertyChange(BUTTONS_CHANGED_PROPERTY, oldValue, newButton);
876   }
877   /**
878    * if this is set to TRUE, the Finish button will be shown on the last wizard page only.  
879    * If this is FALSE, then the Finsih button will show on all wizard pages.  
880    * 
881    * @param newFinishButtonOnLastOnly boolean
882    */
883   public void setFinishButtonOnLastOnly(boolean newFinishButtonOnLastOnly) {
884     boolean oldValue = finishButtonOnLastOnly;
885     finishButtonOnLastOnly = newFinishButtonOnLastOnly;
886     firePropertyChange(FINISH_LAST_ONLY_CHANGED_PROPERTY, oldValue, newFinishButtonOnLastOnly);
887   }
888   /**
889    * Sets the footer TitleBar of the WizardPane.
890    * <br>
891    * This methods fires a <code>PropertyChangeEvent</code> with a
892    * <code>getPropertyName()</code> value of 
893    * <code>WizardPane.FOOTERBAR_CHANGED_PROPERTY</code>
894    *
895    */
896   public void setFooter(TitleBar newFooterBar) {
897     TitleBar oldValue = footerBar;
898     footerBar = newFooterBar;
899     reRegisterComponents(oldValue, newFooterBar);
900     firePropertyChange(FOOTERBAR_CHANGED_PROPERTY, oldValue, newFooterBar);
901   }
902   /**
903    * Sets the footer at the specified index
904    * <br>
905    * This methods fires a <code>PropertyChangeEvent</code> with a
906    * <code>getPropertyName()</code> value of 
907    * <code>WizardPane.FOOTER_CHANGED_PROPERTY</code>
908    */
909   public void setFooterAt(int index, String footerText) {
910 
911     WizardEntry te = getWizardEntryAt(index);
912     if (te == null)
913       return;
914 
915     String oldValue = te.footer;
916     te.footer = footerText;
917     firePropertyChange(FOOTER_CHANGED_PROPERTY, oldValue, footerText);
918   }
919   /**
920    * Sets the height of the <code>WizardPane</code>.  If this
921    * value is -1, then no height will be used.
922    * <p>
923    * Note that this is the height of the contents of the WizardPane
924    * not the outside of the Wizard Pane.  This allows scrolling within
925    * the contents of the wizard pane.
926    * <p>
927    * This methods fires a <code>PropertyChangeEvent</code> with a
928    * <code>getPropertyName()</code> value of 
929    * <code>WizardPane.HEIGHT_CHANGED_PROPERTY</code>
930    *
931    */
932   public void setHeight(int newValue) {
933     int oldValue = height;
934 
935     height = newValue;
936 
937     firePropertyChange(HEIGHT_CHANGED_PROPERTY, oldValue, newValue);
938   }
939   /**
940   * Sets the insets (margins) of the <code>WizardPane</code>
941   *
942   * @param newValue An insets object that specifies the size of each margin 
943   *        of this <code>WizardPane</code>
944   */
945   public void setInsets(Insets newValue) {
946     Insets oldValue = insets;
947     insets = newValue;
948     firePropertyChange(INSETS_CHANGED_PROPERTY, oldValue, newValue);
949   }
950   /**
951    * Sets the model to be used with this <code>WizardPane</code>
952    *.
953    */
954   public void setModel(SingleSelectionModel model) {
955     SingleSelectionModel oldModel = getModel();
956     this.model = model;
957     firePropertyChange(MODEL_CHANGED_PROPERTY, oldModel, model);
958   }
959   /**
960    * Sets the button that acts as the "Next" button in the <code>WizardPane</code>
961    * <br>
962    * This methods fires a <code>PropertyChangeEvent</code> with a
963    * <code>getPropertyName()</code> value of 
964    * <code>WizardPane.BUTTONS_CHANGED_PROPERTY</code>
965    * 
966    */
967   public void setNextButton(Button newButton) {
968     Button oldValue = nextButton;
969     nextButton = newButton;
970     reRegisterComponents(oldValue, newButton);
971     if (nextButton != null) {
972       nextButton.setActionCommand("finish");
973       nextButton.addActionListener(this);
974     }
975     firePropertyChange(BUTTONS_CHANGED_PROPERTY, oldValue, newButton);
976   }
977   /** 
978    * Sets the placement of buttons within the <code>WizardPane</code>
979    * <br>
980    * @param newPlacement the placement of the buttons 
981    *        can be one of the following values.
982    *        <ul>
983    *        <li>EchoConstants.BOTTOM (the default)</li>
984    *        <li>EchoConstants.TOP</li>
985    *        </ul>
986    */
987   public void setPlacement(int newPlacement) {
988     int oldValue = placement;
989 
990     placement = newPlacement;
991 
992     firePropertyChange(PLACEMENT_CHANGED_PROPERTY, oldValue, newPlacement);
993   }
994   /**
995    * Sets the button that acts as the "Previous" button in the <code>WizardPane</code>
996    * <br>
997    * This methods fires a <code>PropertyChangeEvent</code> with a
998    * <code>getPropertyName()</code> value of 
999    * <code>WizardPane.BUTTONS_CHANGED_PROPERTY</code>
1000   * 
1001   */
1002  public void setPreviousButton(Button newButton) {
1003    Button oldValue = prevButton;
1004    prevButton = newButton;
1005    reRegisterComponents(oldValue, newButton);
1006    if (prevButton != null) {
1007      prevButton.setActionCommand("finish");
1008      prevButton.addActionListener(this);
1009    }
1010    firePropertyChange(BUTTONS_CHANGED_PROPERTY, oldValue, newButton);
1011  }
1012  /**
1013   * This sets the scroll bar policy to use.
1014   * 
1015   * This can be one of :
1016   * <ul>
1017   * <li>SCOLLBARS_NONE</li>
1018   * <li>SCOLLBARS_ALWAYS</li>
1019   * <li>SCOLLBARS_AUTO</li>
1020   * </ul>
1021   *
1022   */
1023  public void setScrollBarPolicy(int newScrollBarPolicy) {
1024    int oldValue = scrollBarPolicy;
1025    scrollBarPolicy = newScrollBarPolicy;
1026    firePropertyChange(SCROLLBAR_POLICY_CHANGED_PROPERTY, oldValue, newScrollBarPolicy);
1027  }
1028  /**
1029   * Sets the position of the horizontal scroll bar, in pixels offset from 
1030   * the left of the component.
1031   *
1032   * @param newValue The new position of the horizontal scroll bar, in pixels.
1033   */
1034  public void setScrollBarX(int newValue) {
1035    int oldValue = scrollBarX;
1036    scrollBarX = newValue;
1037    firePropertyChange(SCROLL_BAR_X_CHANGED_PROPERTY, oldValue, newValue);
1038  }
1039  /**
1040   * Sets the position of the vertical scroll bar, in pixels offset from 
1041   * the top of the component.
1042   *
1043   * @param newValue The new position of the vertical scroll bar, in pixels.
1044   */
1045  public void setScrollBarY(int newValue) {
1046    int oldValue = scrollBarY;
1047    scrollBarY = newValue;
1048    firePropertyChange(SCROLL_BAR_Y_CHANGED_PROPERTY, oldValue, newValue);
1049  }
1050  /**
1051   * Sets the selected tab of the <code>WizardPane</code>.
1052   */
1053  public void setSelectedIndex(int index) {
1054    model.setSelectedIndex(index);
1055    //
1056    // now we have to change the title and footer of for that pane
1057    if (getTitle() != null) {
1058      getTitle().setText(getTitleAt(index));
1059    }
1060    if (getFooter() != null) {
1061      getFooter().setText(getFooterAt(index));
1062    }
1063  }
1064  /**
1065   * Sets the title TitleBar of the WizardPane.
1066   * <br>
1067   * This methods fires a <code>PropertyChangeEvent</code> with a
1068   * <code>getPropertyName()</code> value of 
1069   * <code>WizardPane.TITLEBAR_CHANGED_PROPERTY</code>
1070   * 
1071   */
1072  public void setTitle(TitleBar newTitleBar) {
1073    TitleBar oldValue = titleBar;
1074    titleBar = newTitleBar;
1075    reRegisterComponents(oldValue, newTitleBar);
1076    firePropertyChange(TITLEBAR_CHANGED_PROPERTY, oldValue, newTitleBar);
1077  }
1078  /**
1079   * Sets the title at the specified index
1080   * <br>
1081   * This methods fires a <code>PropertyChangeEvent</code> with a
1082   * <code>getPropertyName()</code> value of 
1083   * <code>WizardPane.TITLE_CHANGED_PROPERTY</code>
1084   * 
1085   */
1086  public void setTitleAt(int index, String title) {
1087
1088    WizardEntry te = getWizardEntryAt(index);
1089    if (te == null)
1090      return;
1091
1092    String oldValue = te.title;
1093    te.title = title;
1094    firePropertyChange(TITLE_CHANGED_PROPERTY, oldValue, te.title);
1095  }
1096  /**
1097   * Sets the width of the <code>WizardPane</code>.  If this
1098   * value is -1, then no width will be used.
1099   * <p>
1100   * Note that this is the height of the contents of the WizardPane
1101   * not the outside of the Wizard Pane.  This allows scrolling within
1102   * the contents of the wizard pane.
1103   * <p>
1104   * This methods fires a <code>PropertyChangeEvent</code> with a
1105   * <code>getPropertyName()</code> value of 
1106   * <code>WizardPane.WIDTH_CHANGED_PROPERTY</code>
1107   *
1108   */
1109  public void setWidth(int newValue) {
1110    int oldValue = width;
1111    width = newValue;
1112    firePropertyChange(WIDTH_CHANGED_PROPERTY, oldValue, newValue);
1113  }
1114  
1115  /**
1116   * @see nextapp.echo.Component#validate()
1117   */
1118  public void validate() {
1119    super.validate();
1120    //
1121    // make sure that the current selected pane component
1122    // is in our visible container and hence gets a rendering
1123    // peer.  Then put all the rest in the invisible
1124    // container so it gets no peer and hence its all
1125    // the more efficient.
1126    //
1127    reArrangeVisibleComponents(null);
1128  }
1129
1130  
1131  /** @see echopoint.util.ReflectionSetter#set(Field, Object) */  
1132  public Object set(java.lang.reflect.Field field, Object newValue) throws Exception {
1133    Object oldValue = field.get(this); field.set(this,newValue); return oldValue;
1134  }
1135  
1136
1137}