001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   License, or (at your option) any later version.
009
010   This library is distributed in the hope that it will be useful,
011   but WITHOUT ANY WARRANTY; without even the implied warranty of
012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: WizardView.java,v $
023   Revision 1.16  2004/06/29 07:56:44  markl
024   Keyboard focus fix.
025
026   Revision 1.15  2004/05/12 18:52:03  markl
027   comment block updates
028
029   Revision 1.14  2004/03/16 06:43:39  markl
030   LocaleManager method change
031
032   Revision 1.13  2004/03/15 05:45:31  markl
033   allow subclasses to provide a sequence
034
035   Revision 1.12  2003/01/19 09:50:54  markl
036   Javadoc & comment header updates.
037
038   Revision 1.11  2001/03/20 00:54:53  markl
039   Fixed deprecated calls.
040
041   Revision 1.10  2001/03/12 09:56:54  markl
042   KLabel/KLabelArea changes.
043
044   Revision 1.9  2001/03/12 09:28:02  markl
045   Source code and Javadoc cleanup.
046
047   Revision 1.8  2000/10/11 10:46:17  markl
048   Fixes to better support enabling/disabling navigation.
049
050   Revision 1.7  2000/08/26 09:04:41  markl
051   Changed Wizard*.java APIs to facilitate easier control over forward/
052   backward navigation.
053
054   Revision 1.6  1999/11/19 06:23:53  markl
055   Added addButton() and removeButton() methods.
056
057   Revision 1.5  1999/04/19 05:59:47  markl
058   I18N changes.
059
060   Revision 1.4  1999/02/28 11:44:08  markl
061   Minor fixes.
062
063   Revision 1.3  1999/02/28 00:27:35  markl
064   Added a updateData() method. This gives subclasses a chance to store their
065   values in the config object.
066
067   Revision 1.2  1999/01/10 03:05:32  markl
068   added GPL header & RCS tag
069   ----------------------------------------------------------------------------
070*/
071
072package kiwi.ui;
073
074import java.awt.*;
075import java.awt.event.*;
076import javax.swing.*;
077import javax.swing.border.*;
078import javax.swing.event.*;
079
080import kiwi.event.*;
081import kiwi.util.*;
082
083/** A wizard-style component. <code>WizardView</code> essentially displays a
084  * sequence of panels (or "cards") to the user;  each panel typically
085  * contains messages and/or input elements. A <code>WizardPanelSequence</code>
086  * object functions as the source of these panels, and determines the order
087  * in which the panels are presented to the user, and the conditions under
088  * which forward and backward movement is allowed between consecutive panels.
089  * <p>
090  * This component is arranged as follows. The leftmost portion of the
091  * component is used to display an image (which for best results should be
092  * transparent). Animated GIFs are acceptable. The bottom portion of the
093  * component displays the <i>Back</i>, <i>Next</i>, and <i>Cancel</i>
094  * buttons. The remaining space is occupied by the current
095  * <code>WizardPanel</code> provided by the <code>WizardPanelSequence</code>
096  * object.
097  * <p>
098  * The <code>WizardPanelSequence</code> determines when the user may move to
099  * the next or previous panel. Whenever these conditions change, the
100  * <code>WizardPanelSequence</code> fires a <code>ChangeEvent</code> to
101  * notify the <code>WizardView</code>, which responds by dimming or
102  * undimming the <i>Next</i> and <i>Back</i> buttons, as appropriate. When
103  * the final panel in the sequence is reached, the <i>Next</i> button changes
104  * to a <i>Finish</i> button.
105  * <p>
106  * If the <i>Cancel</i> button is pressed, an <code>ActionEvent</code> is
107  * fired with an action command of "cancel". If the <i>Finish</i> button is
108  * pressed, an <code>ActionEvent</code> is fired with an action command of
109  * "finish".
110  *
111  * <p><center>
112  * <img src="snapshot/WizardView.gif"><br>
113  * <i>An example WizardView.</i>
114  * </center>
115  *
116  * @see javax.swing.event.ChangeEvent
117  * @see kiwi.ui.WizardPanelSequence
118  *
119  * @author Mark Lindner
120  */
121
122public class WizardView extends KPanel
123  {
124  private KButton b_prev, b_next, b_cancel;
125  private KLabel iconLabel;
126  private WizardPanelSequence sequence;
127  private KPanel content;
128  private WizardPanel curPanel = null;
129  private int pos = 0, count = 0;
130  private Icon i_next;
131  private boolean finish = false;
132  private ActionSupport support;
133  private String s_next, s_back, s_finish, s_cancel;
134  private _ActionListener actionListener;
135  private ButtonPanel p_buttons;
136
137  /** Construct a new <code>WizardView</code>. The <code>buildSequence()</code>
138   * method will be called to construct the <code>WizardPanel</code> sequence.
139   *
140   * @since Kiwi 2.0
141   *
142   */
143
144  public WizardView()
145    {
146    this(null);
147    }
148  
149  /** Construct a new <code>WizardView</code>.
150   *
151   * @param sequence The <code>WizardPanelSequence</code> that will provide
152   * <code>WizardPanel</code>s for the wizard.
153   */
154  
155  public WizardView(WizardPanelSequence sequence)
156    {
157    if(sequence == null)
158      sequence = buildSequence();
159    
160    this.sequence = sequence;
161
162    sequence.addChangeListener(new ChangeListener()
163                               {
164                               public void stateChanged(ChangeEvent evt)
165                                 {
166                                 refresh();
167                                 }
168                               });
169
170    LocaleData loc = LocaleManager.getDefault().getLocaleData("KiwiDialogs");
171
172    s_next = loc.getMessage("kiwi.button.next");
173    s_back = loc.getMessage("kiwi.button.back");
174    s_finish = loc.getMessage("kiwi.button.finish");
175    s_cancel = loc.getMessage("kiwi.button.cancel");
176    
177    support = new ActionSupport(this);
178    
179    setLayout(new BorderLayout(5, 5));
180
181    content = new KPanel();
182    content.setLayout(new GridLayout(1, 0));
183
184    iconLabel = new KLabel(KiwiUtils.getResourceManager()
185                           .getIcon("wizard.gif"));
186    add("West", iconLabel);
187
188    add("Center", content);
189
190    Insets margin = new Insets(1, 5, 1, 5);
191    
192    p_buttons = new ButtonPanel();
193
194    actionListener = new _ActionListener();
195
196    b_prev = new KButton(s_back, KiwiUtils.getResourceManager()
197                         .getIcon("left.gif"));
198    b_prev.setFocusPainted(false);
199    b_prev.addActionListener(actionListener);
200    b_prev.setEnabled(false);
201    b_prev.setMargin(margin);
202    p_buttons.addButton(b_prev);
203
204    i_next = KiwiUtils.getResourceManager().getIcon("right.gif");
205    
206    b_next = new KButton("");
207    b_next.setHorizontalTextPosition(SwingConstants.LEFT);
208    b_next.setFocusPainted(false);
209    b_next.addActionListener(actionListener);
210    b_next.setMargin(margin);
211    p_buttons.addButton(b_next);
212
213    b_cancel = new KButton(s_cancel);
214    b_cancel.setFocusPainted(false);
215    b_cancel.addActionListener(actionListener);
216    b_cancel.setMargin(margin);
217    p_buttons.addButton(b_cancel);
218
219    KPanel p_bottom = new KPanel();
220    p_bottom.setLayout(new BorderLayout(5, 5));
221
222    p_bottom.add("Center", new JSeparator());
223    p_bottom.add("South", p_buttons);
224    
225    add("South", p_bottom);
226
227    reset();
228    }
229
230  /** A method for constructing the wizard panel sequence. This method may be
231   * overridden by subclasses to provide the sequence for the view instead of
232   * having it supplied via the constructor. The default implementation returns
233   * <b>null</b>.
234   *
235   * @since Kiwi 2.0
236   */
237
238  protected WizardPanelSequence buildSequence()
239    {
240    return(null);
241    }
242
243  /** Get a reference to the <i>Cancel</i> button.
244    *
245    * @return The <i>Cancel</i> button.
246    */
247  
248  public JButton getCancelButton()
249    {
250    return(b_cancel);
251    }
252
253  /** Get a reference to the <i>Finish</i> button.
254    *
255    * @return The <i>Finish</i> button.
256    */
257  
258  public JButton getFinishButton()
259    {
260    return(b_next);
261    }
262
263  /** Add a button to the <code>WizardView</code> at the specified position.
264   *
265   * @param button The button to add.
266   * @param pos The position at which to add the button. The value 0 denotes
267   * the first position, and -1 denotes the last position.
268   * @exception java.lang.IllegalArgumentException If the value of
269   * <code>pos</code> is invalid.
270   */
271
272  public void addButton(JButton button, int pos)
273    throws IllegalArgumentException
274    {
275    p_buttons.addButton(button, pos);
276    }
277
278  /** Remove a button from the specified position in the
279    * <code>ButtonPanel</code>.
280    *
281    * @param pos The position of the button to remove, where 0 denotes the
282    * first position.
283    * @exception java.lang.IllegalArgumentException If an attempt is made
284    * to remove one of the predefined wizard buttons.
285    */
286  
287  public void removeButton(int pos) throws IllegalArgumentException
288    {
289    JButton b = (JButton)p_buttons.getButton(pos);
290    if((b == b_cancel) || (b == b_prev) || (b == b_next))
291      throw(new IllegalArgumentException("Can't remove predefined buttons."));
292    else
293      p_buttons.removeButton(pos);
294    }
295
296  /** Set the component's icon. Animated and/or transparent GIF images add a
297    * professional touch when used with <code>WizardView</code>s.
298    *
299    * @param icon The new icon to use, or <code>null</code> if no icon is
300    * needed.
301    */
302
303  public void setIcon(Icon icon)
304    {
305    iconLabel.setIcon(icon);
306    }
307  
308  /* show a panel */
309  
310  private void showPanel(WizardPanel panel)
311    {
312    if(curPanel != null)
313      {
314      content.remove(curPanel);
315      curPanel.syncData();
316      }
317    
318    content.add(curPanel = panel);
319    content.validate();
320    content.repaint();
321    curPanel.syncUI();
322    refresh();
323    curPanel.beginFocus();
324    }
325
326  /** Reset the <code>WizardView</code>. Resets the component so that the first
327    * panel is displayed. This method also calls the
328    * <code>WizardPanelSequence</code>'s <code>reset()</code> method.
329    *
330    * @see kiwi.ui.WizardPanelSequence#reset
331    */
332  
333  public void reset()
334    {
335    sequence.reset();
336    b_next.setText(s_next);
337    b_next.setIcon(i_next);
338    showPanel(sequence.getNextPanel());
339    }
340
341  /* refresh the buttons based on what the sequence tells us */
342  
343  private void refresh()
344    {
345    finish = sequence.isLastPanel();
346    b_next.setEnabled(sequence.canMoveForward());
347    b_prev.setEnabled(sequence.canMoveBackward());
348
349    if(!finish)
350      {
351      b_next.setText(s_next);
352      b_next.setIcon(i_next);
353      }
354    else
355      {
356      b_next.setText(s_finish);
357      b_next.setIcon(null);
358      }
359    }
360
361  /** Add an <code>ActionListener</code> to this component's list of listeners.
362    *
363    * @param listener The listener to add.
364    */
365
366  public void addActionListener(ActionListener listener)
367    {
368    support.addActionListener(listener);
369    }
370
371  /** Add an <code>ActionListener</code> to this component's list of listeners.
372    *
373    * @param listener The listener to add.
374    */
375
376  public void removeActionListener(ActionListener listener)
377    {
378    support.removeActionListener(listener);
379    }
380
381  /* Handle events. */
382
383  private class _ActionListener implements ActionListener
384    {
385    public void actionPerformed(ActionEvent evt)
386      {
387      Object o = evt.getSource();
388
389      if(o == b_next)
390        {
391        if(finish)
392          {
393          curPanel.syncData();
394          support.fireActionEvent("finish");
395          }
396        else
397          showPanel(sequence.getNextPanel());
398        }
399
400      else if(o == b_prev)
401        showPanel(sequence.getPreviousPanel());
402
403      else if(o == b_cancel)
404        support.fireActionEvent("cancel");
405      }
406    }
407
408  }
409
410/* end of source file */