001    /* DefaultButtonModel.java --
002       Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing;
040    
041    import java.awt.ItemSelectable;
042    import java.awt.event.ActionEvent;
043    import java.awt.event.ActionListener;
044    import java.awt.event.ItemEvent;
045    import java.awt.event.ItemListener;
046    import java.awt.event.KeyEvent;
047    import java.io.Serializable;
048    import java.util.EventListener;
049    
050    import javax.swing.event.ChangeEvent;
051    import javax.swing.event.ChangeListener;
052    import 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     */
073    public 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    }