1 /* DefaultButtonModel.java --
2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing;
40
41 import java.awt.ItemSelectable;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.ItemEvent;
45 import java.awt.event.ItemListener;
46 import java.awt.event.KeyEvent;
47 import java.io.Serializable;
48 import java.util.EventListener;
49
50 import javax.swing.event.ChangeEvent;
51 import javax.swing.event.ChangeListener;
52 import javax.swing.event.EventListenerList;
53
54 /**
55 * The default implementation of {@link ButtonModel}.
56 * The purpose of this class is to model the dynamic state of an abstract
57 * button. The concrete button type holding this state may be a a "toggle"
58 * button (checkbox, radio button) or a "push" button (menu button, button).
59 * If the model is disabled, only the "selected" property can be changed. An
60 * attempt to change the "armed", "rollover" or "pressed" properties while
61 * the model is disabled will be blocked. Any successful (non-blocked) change
62 * to the model's properties will trigger the firing of a ChangeEvent. Any
63 * change to the "selected" property will trigger the firing of an ItemEvent
64 * in addition to ChangeEvent. This is true whether the model is enabled or
65 * not. One other state change is special: the transition from "enabled,
66 * armed and pressd" to "enabled, armed and not-pressed". This is considered
67 * the "trailing edge" of a successful mouse click, and therefore fires an
68 * ActionEvent in addition to a ChangeEvent. In all other respects this class
69 * is just a container of boolean flags.
70 *
71 * @author Graydon Hoare (graydon_at_redhat.com)
72 */
73 public class DefaultButtonModel implements ButtonModel, Serializable
74 {
75 /** DOCUMENT ME! */
76 private static final long serialVersionUID = -5342609566534980231L;
77
78 /**
79 * Indicates that the button is <em>partially</em> committed to being
80 * pressed, but not entirely. This usually happens when a user has pressed
81 * but not yet released the mouse button.
82 */
83 public static final int ARMED = 1;
84
85 /**
86 * State constant indicating that the button is enabled. Buttons cannot be
87 * pressed or selected unless they are enabled.
88 */
89 public static final int ENABLED = 8;
90
91 /**
92 * State constant indicating that the user is holding down the button. When
93 * this transitions from true to false, an ActionEvent may be fired,
94 * depending on the value of the "armed" property.
95 */
96 public static final int PRESSED = 4;
97
98 /**
99 * 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 EventListener[] getListeners(Class 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);
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()) || (!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 null, 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 null, 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
559 */
560 public void setGroup(ButtonGroup g)
561 {
562 if (group != g)
563 {
564 group = g;
565 fireStateChanged();
566 }
567 }
568
569 /**
570 * Returns the current value of the model's "group" property.
571 *
572 * @return The value of the "group" property
573 */
574 public ButtonGroup getGroup()
575 {
576 return group;
577 }
578 }