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}