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 }