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}