001/* DefaultButtonModel.java --
002   Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing;
040
041import java.awt.ItemSelectable;
042import java.awt.event.ActionEvent;
043import java.awt.event.ActionListener;
044import java.awt.event.ItemEvent;
045import java.awt.event.ItemListener;
046import java.awt.event.KeyEvent;
047import java.io.Serializable;
048import java.util.EventListener;
049
050import javax.swing.event.ChangeEvent;
051import javax.swing.event.ChangeListener;
052import javax.swing.event.EventListenerList;
053
054/**
055 * The default implementation of {@link ButtonModel}.
056 * The purpose of this class is to model the dynamic state of an abstract
057 * button. The concrete button type holding this state may be a a "toggle"
058 * button (checkbox, radio button) or a "push" button (menu button, button).
059 * If the model is disabled, only the "selected" property can be changed. An
060 * attempt to change the "armed", "rollover" or "pressed" properties  while
061 * the model is disabled will be blocked. Any successful (non-blocked) change
062 * to the model's properties will trigger the firing of a ChangeEvent. Any
063 * change to the "selected" property will trigger the firing of an ItemEvent
064 * in addition to ChangeEvent. This is true whether the model is enabled or
065 * not. One other state change is special: the transition from "enabled,
066 * armed and pressed" to "enabled, armed and not-pressed". This is considered
067 * the "trailing edge" of a successful mouse click, and therefore fires an
068 * ActionEvent in addition to a ChangeEvent. In all other respects this class
069 * is just a container of boolean flags.
070 *
071 * @author Graydon Hoare (graydon_at_redhat.com)
072 */
073public class DefaultButtonModel implements ButtonModel, Serializable
074{
075  /** DOCUMENT ME! */
076  private static final long serialVersionUID = -5342609566534980231L;
077
078  /**
079   * Indicates that the button is <em>partially</em> committed to being
080   * pressed, but not entirely. This usually happens when a user has pressed
081   * but not yet released the mouse button.
082   */
083  public static final int ARMED = 1;
084
085  /**
086   * State constant indicating that the button is enabled. Buttons cannot be
087   * pressed or selected unless they are enabled.
088   */
089  public static final int ENABLED = 8;
090
091  /**
092   * State constant indicating that the user is holding down the button. When
093   * this transitions from true to false, an ActionEvent may be fired,
094   * depending on the value of the "armed" property.
095   */
096  public static final int PRESSED = 4;
097
098  /**
099   * State constant indicating that the mouse is currently positioned over the
100   * button.
101   */
102  public static final int ROLLOVER = 16;
103
104  /**
105   * State constant indicating that the button is selected. This constant is
106   * only meaningful for toggle-type buttons (radio buttons, checkboxes).
107   */
108  public static final int SELECTED = 2;
109
110  /**
111   * Represents the "state properties" (armed, enabled, pressed, rollover and
112   * selected) by a bitwise combination of integer constants.
113   */
114  protected int stateMask = ENABLED;
115
116  /**
117   * List of ItemListeners, ChangeListeners, and ActionListeners registered on
118   * this model.
119   */
120  protected EventListenerList listenerList = new EventListenerList();
121
122  /** The single ChangeEvent this model (re)uses to call its ChangeListeners. */
123  protected ChangeEvent changeEvent = new ChangeEvent(this);
124
125  /**
126   * The group this model belongs to. Only one button in a group may be
127   * selected at any given time.
128   */
129  protected ButtonGroup group;
130
131  /**
132   * The key code (one of {@link java.awt.event.KeyEvent} VK_) used to press
133   * this button via a keyboard interface.
134   */
135  protected int mnemonic = KeyEvent.VK_UNDEFINED;
136
137  /**
138   * The string used as the "command" property of any ActionEvent this model
139   * sends.
140   */
141  protected String actionCommand;
142
143  /**
144   * Creates a new DefaultButtonModel object.
145   */
146  public DefaultButtonModel()
147  {
148    // Nothing to do here.
149  }
150
151  /**
152   * Return <code>null</code>. Use {@link AbstractButton} if you wish to
153   * interface with a button via an {@link ItemSelectable} interface.
154   *
155   * @return <code>null</code>
156   */
157  public Object[] getSelectedObjects()
158  {
159    return null;
160  }
161
162  /**
163   * Returns a specified class of listeners.
164   *
165   * @param listenerType the type of listener to return
166   *
167   * @return array of listeners
168   */
169  public <T extends EventListener> T[] getListeners(Class<T> listenerType)
170  {
171    return listenerList.getListeners(listenerType);
172  }
173
174  /**
175   * Add an ActionListener to the model. Usually only called to subscribe an
176   * AbstractButton's listener to the model.
177   *
178   * @param l The listener to add
179   */
180  public void addActionListener(ActionListener l)
181  {
182    listenerList.add(ActionListener.class, l);
183  }
184
185  /**
186   * Remove an ActionListener to the model. Usually only called to unsubscribe
187   * an AbstractButton's listener to the model.
188   *
189   * @param l The listener to remove
190   */
191  public void removeActionListener(ActionListener l)
192  {
193    listenerList.remove(ActionListener.class, l);
194  }
195
196  /**
197   * Returns all registered <code>ActionListener</code> objects.
198   *
199   * @return array of <code>ActionListener</code> objects
200   */
201  public ActionListener[] getActionListeners()
202  {
203    return (ActionListener[]) listenerList.getListeners(ActionListener.class);
204  }
205
206  /**
207   * Add an ItemListener to the model. Usually only called to subscribe an
208   * AbstractButton's listener to the model.
209   *
210   * @param l The listener to add
211   */
212  public void addItemListener(ItemListener l)
213  {
214    listenerList.add(ItemListener.class, l);
215  }
216
217  /**
218   * Remove an ItemListener to the model. Usually only called to unsubscribe
219   * an AbstractButton's listener to the model.
220   *
221   * @param l The listener to remove
222   */
223  public void removeItemListener(ItemListener l)
224  {
225    listenerList.remove(ItemListener.class, l);
226  }
227
228  /**
229   * Returns all registered <code>ItemListener</code> objects.
230   *
231   * @return array of <code>ItemListener</code> objects
232   */
233  public ItemListener[] getItemListeners()
234  {
235    return (ItemListener[]) listenerList.getListeners(ItemListener.class);
236  }
237
238  /**
239   * Add a ChangeListener to the model. Usually only called to subscribe an
240   * AbstractButton's listener to the model.
241   *
242   * @param l The listener to add
243   */
244  public void addChangeListener(ChangeListener l)
245  {
246    listenerList.add(ChangeListener.class, l);
247  }
248
249  /**
250   * Remove a ChangeListener to the model. Usually only called to unsubscribe
251   * an AbstractButton's listener to the model.
252   *
253   * @param l The listener to remove
254   */
255  public void removeChangeListener(ChangeListener l)
256  {
257    listenerList.remove(ChangeListener.class, l);
258  }
259
260  /**
261   * Returns all registered <code>ChangeListener</code> objects.
262   *
263   * @return array of <code>ChangeListener</code> objects
264   */
265  public ChangeListener[] getChangeListeners()
266  {
267    return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
268  }
269
270  /**
271   * Inform each ItemListener in the {@link #listenerList} that an ItemEvent
272   * has occurred. This happens in response to any change to the {@link
273   * #stateMask} field.
274   *
275   * @param e The ItemEvent to fire
276   */
277  protected void fireItemStateChanged(ItemEvent e)
278  {
279    ItemListener[] ll = getItemListeners();
280
281    for (int i = 0; i < ll.length; i++)
282      ll[i].itemStateChanged(e);
283  }
284
285  /**
286   * Inform each ActionListener in the {@link #listenerList} that an
287   * ActionEvent has occurred. This happens in response to the any change to
288   * the {@link #stateMask} field which makes the enabled, armed and pressed
289   * properties all simultaneously <code>true</code>.
290   *
291   * @param e The ActionEvent to fire
292   */
293  protected void fireActionPerformed(ActionEvent e)
294  {
295    ActionListener[] ll = getActionListeners();
296
297    for (int i = 0; i < ll.length; i++)
298      ll[i].actionPerformed(e);
299  }
300
301  /**
302   * Inform each ChangeListener in the {@link #listenerList} that a ChangeEvent
303   * has occurred. This happens in response to the any change to a property
304   * of the model.
305   */
306  protected void fireStateChanged()
307  {
308    ChangeListener[] ll = getChangeListeners();
309
310    for (int i = 0; i < ll.length; i++)
311      ll[i].stateChanged(changeEvent);
312  }
313
314  /**
315   * Get the value of the model's "armed" property.
316   *
317   * @return The current "armed" property
318   */
319  public boolean isArmed()
320  {
321    return (stateMask & ARMED) == ARMED;
322  }
323
324  /**
325   * Set the value of the model's "armed" property.
326   *
327   * @param a The new "armed" property
328   */
329  public void setArmed(boolean a)
330  {
331    // if this call does not represent a CHANGE in state, then return
332    if ((a && isArmed()) || (!a && !isArmed()))
333      return;
334
335    // cannot change ARMED state unless button is enabled
336    if (!isEnabled())
337      return;
338
339    // make the change
340    if (a)
341      stateMask = stateMask | ARMED;
342    else
343      stateMask = stateMask & (~ARMED);
344
345    // notify interested ChangeListeners
346    fireStateChanged();
347  }
348
349  /**
350   * Get the value of the model's "enabled" property.
351   *
352   * @return The current "enabled" property.
353   */
354  public boolean isEnabled()
355  {
356    return (stateMask & ENABLED) == ENABLED;
357  }
358
359  /**
360   * Set the value of the model's "enabled" property.
361   *
362   * @param e The new "enabled" property
363   */
364  public void setEnabled(boolean e)
365  {
366    // if this call does not represent a CHANGE in state, then return
367    if ((e && isEnabled()) || (!e && !isEnabled()))
368      return;
369
370    // make the change
371    if (e)
372      stateMask = stateMask | ENABLED;
373    else
374      stateMask = stateMask & (~ENABLED) & (~ARMED) & (~PRESSED);
375
376    // notify interested ChangeListeners
377    fireStateChanged();
378  }
379
380  /**
381   * Set the value of the model's "pressed" property.
382   *
383   * @param p The new "pressed" property
384   */
385  public void setPressed(boolean p)
386  {
387    // if this call does not represent a CHANGE in state, then return
388    if ((p && isPressed()) || (!p && !isPressed()))
389      return;
390
391    // cannot changed PRESSED state unless button is enabled
392    if (!isEnabled())
393      return;
394
395    // make the change
396    if (p)
397      stateMask = stateMask | PRESSED;
398    else
399      stateMask = stateMask & (~PRESSED);
400
401    // if button is armed and was released, fire action event
402    if (!p && isArmed())
403      fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
404                                          actionCommand));
405
406    // notify interested ChangeListeners
407    fireStateChanged();
408  }
409
410  /**
411   * Get the value of the model's "pressed" property.
412   *
413   * @return The current "pressed" property
414   */
415  public boolean isPressed()
416  {
417    return (stateMask & PRESSED) == PRESSED;
418  }
419
420  /**
421   * Set the value of the model's "rollover" property.
422   *
423   * @param r The new "rollover" property
424   */
425  public void setRollover(boolean r)
426  {
427    // if this call does not represent a CHANGE in state, then return
428    if (r == isRollover())
429      return;
430
431    // cannot set ROLLOVER property unless button is enabled
432    if (!isEnabled())
433      return;
434
435    // make the change
436    if (r)
437      stateMask = stateMask | ROLLOVER;
438    else
439      stateMask = stateMask & (~ROLLOVER);
440
441    // notify interested ChangeListeners
442    fireStateChanged();
443  }
444
445  /**
446   * Set the value of the model's "selected" property.
447   *
448   * @param s The new "selected" property
449   */
450  public void setSelected(boolean s)
451  {
452    // if this call does not represent a CHANGE in state, then return
453    if ((s && isSelected()) || (!s && !isSelected()))
454      return;
455
456    // make the change
457    if (s)
458      stateMask = stateMask | SELECTED;
459    else
460      stateMask = stateMask & (~SELECTED);
461
462    // notify interested ChangeListeners
463    fireStateChanged();
464
465    // fire ItemStateChanged events
466    if (s)
467      {
468        fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
469                                           this, ItemEvent.SELECTED));
470        if (group != null)
471          group.setSelected(this, true);
472      }
473    else
474      {
475        fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
476                                           this, ItemEvent.DESELECTED));
477        if (group != null)
478          group.setSelected(this, false);
479      }
480  }
481
482  /**
483   * Get the value of the model's "selected" property.
484   *
485   * @return The current "selected" property
486   */
487  public boolean isSelected()
488  {
489    return (stateMask & SELECTED) == SELECTED;
490  }
491
492  /**
493   * Get the value of the model's "rollover" property.
494   *
495   * @return The current "rollover" property
496   */
497  public boolean isRollover()
498  {
499    return (stateMask & ROLLOVER) == ROLLOVER;
500  }
501
502  /**
503   * Get the value of the model's "mnemonic" property.
504   *
505   * @return The current "mnemonic" property
506   */
507  public int getMnemonic()
508  {
509    return mnemonic;
510  }
511
512  /**
513   * Set the value of the model's "mnemonic" property.
514   *
515   * @param key The new "mnemonic" property
516   */
517  public void setMnemonic(int key)
518  {
519    if (mnemonic != key)
520      {
521        mnemonic = key;
522        fireStateChanged();
523      }
524  }
525
526  /**
527   * Set the value of the model's "actionCommand" property. This property is
528   * used as the "command" property of the {@link ActionEvent} fired from the
529   * model.
530   *
531   * @param s The new "actionCommand" property.
532   */
533  public void setActionCommand(String s)
534  {
535    if (actionCommand != s)
536      {
537        actionCommand = s;
538        fireStateChanged();
539      }
540  }
541
542  /**
543   * Returns the current value of the model's "actionCommand" property.
544   *
545   * @return The current "actionCommand" property
546   */
547  public String getActionCommand()
548  {
549    return actionCommand;
550  }
551
552  /**
553   * Set the value of the model's "group" property. The model is said to be a
554   * member of the {@link ButtonGroup} held in its "group" property, and only
555   * one model in a given group can have their "selected" property be
556   * <code>true</code> at a time.
557   *
558   * @param g The new "group" property (<code>null</code> permitted).
559   *
560   * @see #getGroup()
561   */
562  public void setGroup(ButtonGroup g)
563  {
564    group = g;
565  }
566
567  /**
568   * Returns the current value of the model's "group" property.
569   *
570   * @return The value of the "group" property
571   *
572   * @see #setGroup(ButtonGroup)
573   */
574  public ButtonGroup getGroup()
575  {
576    return group;
577  }
578}