1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.beans.JavaBean; 30 import java.beans.BeanProperty; 31 32 import javax.swing.plaf.*; 33 import javax.accessibility.*; 34 35 import java.io.ObjectOutputStream; 36 import java.io.IOException; 37 import java.util.Enumeration; 38 39 /** 40 * An implementation of a two-state button. 41 * The <code>JRadioButton</code> and <code>JCheckBox</code> classes 42 * are subclasses of this class. 43 * For information on using them see 44 * <a 45 href="http://docs.oracle.com/javase/tutorial/uiswing/components/button.html">How to Use Buttons, Check Boxes, and Radio Buttons</a>, 46 * a section in <em>The Java Tutorial</em>. 47 * <p> 48 * Buttons can be configured, and to some degree controlled, by 49 * <code><a href="Action.html">Action</a></code>s. Using an 50 * <code>Action</code> with a button has many benefits beyond directly 51 * configuring a button. Refer to <a href="Action.html#buttonActions"> 52 * Swing Components Supporting <code>Action</code></a> for more 53 * details, and you can find more information in <a 54 * href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How 55 * to Use Actions</a>, a section in <em>The Java Tutorial</em>. 56 * <p> 57 * <strong>Warning:</strong> Swing is not thread safe. For more 58 * information see <a 59 * href="package-summary.html#threading">Swing's Threading 60 * Policy</a>. 61 * <p> 62 * <strong>Warning:</strong> 63 * Serialized objects of this class will not be compatible with 64 * future Swing releases. The current serialization support is 65 * appropriate for short term storage or RMI between applications running 66 * the same version of Swing. As of 1.4, support for long term storage 67 * of all JavaBeans™ 68 * has been added to the <code>java.beans</code> package. 69 * Please see {@link java.beans.XMLEncoder}. 70 * 71 * @see JRadioButton 72 * @see JCheckBox 73 * @author Jeff Dinkins 74 * @since 1.2 75 */ 76 @JavaBean(defaultProperty = "UIClassID", description = "An implementation of a two-state button.") 77 @SwingContainer(false) 78 @SuppressWarnings("serial") // Same-version serialization only 79 public class JToggleButton extends AbstractButton implements Accessible { 80 81 /** 82 * @see #getUIClassID 83 * @see #readObject 84 */ 85 private static final String uiClassID = "ToggleButtonUI"; 86 87 /** 88 * Creates an initially unselected toggle button 89 * without setting the text or image. 90 */ 91 public JToggleButton () { 92 this(null, null, false); 93 } 94 95 /** 96 * Creates an initially unselected toggle button 97 * with the specified image but no text. 98 * 99 * @param icon the image that the button should display 100 */ 101 public JToggleButton(Icon icon) { 102 this(null, icon, false); 103 } 104 105 /** 106 * Creates a toggle button with the specified image 107 * and selection state, but no text. 108 * 109 * @param icon the image that the button should display 110 * @param selected if true, the button is initially selected; 111 * otherwise, the button is initially unselected 112 */ 113 public JToggleButton(Icon icon, boolean selected) { 114 this(null, icon, selected); 115 } 116 117 /** 118 * Creates an unselected toggle button with the specified text. 119 * 120 * @param text the string displayed on the toggle button 121 */ 122 public JToggleButton (String text) { 123 this(text, null, false); 124 } 125 126 /** 127 * Creates a toggle button with the specified text 128 * and selection state. 129 * 130 * @param text the string displayed on the toggle button 131 * @param selected if true, the button is initially selected; 132 * otherwise, the button is initially unselected 133 */ 134 public JToggleButton (String text, boolean selected) { 135 this(text, null, selected); 136 } 137 138 /** 139 * Creates a toggle button where properties are taken from the 140 * Action supplied. 141 * 142 * @param a an instance of an {@code Action} 143 * @since 1.3 144 */ 145 public JToggleButton(Action a) { 146 this(); 147 setAction(a); 148 } 149 150 /** 151 * Creates a toggle button that has the specified text and image, 152 * and that is initially unselected. 153 * 154 * @param text the string displayed on the button 155 * @param icon the image that the button should display 156 */ 157 public JToggleButton(String text, Icon icon) { 158 this(text, icon, false); 159 } 160 161 /** 162 * Creates a toggle button with the specified text, image, and 163 * selection state. 164 * 165 * @param text the text of the toggle button 166 * @param icon the image that the button should display 167 * @param selected if true, the button is initially selected; 168 * otherwise, the button is initially unselected 169 */ 170 public JToggleButton (String text, Icon icon, boolean selected) { 171 // Create the model 172 setModel(new ToggleButtonModel()); 173 174 model.setSelected(selected); 175 176 // initialize 177 init(text, icon); 178 } 179 180 /** 181 * Resets the UI property to a value from the current look and feel. 182 * 183 * @see JComponent#updateUI 184 */ 185 public void updateUI() { 186 setUI((ButtonUI)UIManager.getUI(this)); 187 } 188 189 /** 190 * Returns a string that specifies the name of the l&f class 191 * that renders this component. 192 * 193 * @return String "ToggleButtonUI" 194 * @see JComponent#getUIClassID 195 * @see UIDefaults#getUI 196 */ 197 @BeanProperty(bound = false, description 198 = "A string that specifies the name of the L&F class") 199 public String getUIClassID() { 200 return uiClassID; 201 } 202 203 204 /** 205 * Overriden to return true, JToggleButton supports 206 * the selected state. 207 */ 208 boolean shouldUpdateSelectedStateFromAction() { 209 return true; 210 } 211 212 private JToggleButton getGroupSelection() { 213 ButtonModel model = getModel(); 214 JToggleButton selection = this; 215 if (model instanceof DefaultButtonModel) { 216 ButtonGroup group = ((DefaultButtonModel) model).getGroup(); 217 if (group != null && group.getSelection() != null 218 && !group.isSelected(model)) { 219 Enumeration<AbstractButton> elements = group.getElements(); 220 while (elements.hasMoreElements()) { 221 AbstractButton member = elements.nextElement(); 222 if (group.isSelected(member.getModel())) { 223 if (member instanceof JToggleButton && 224 member.isVisible() && member.isDisplayable() && 225 member.isEnabled() && member.isFocusable()) { 226 selection = (JToggleButton) member; 227 } 228 break; 229 } 230 } 231 } 232 } 233 return selection; 234 } 235 236 /** 237 * If this toggle button is a member of the {@link ButtonGroup} which has 238 * an another focusable toggle button selected, and the focus cause argument 239 * denotes window activation or focus traversal action of any direction 240 * the result of the method execution is the same as calling 241 * {@link Component#requestFocus(FocusEvent.Cause)} on the toggle button 242 * selected in the group. 243 * In all other cases the result of the method is the same as calling 244 * {@link Component#requestFocus(FocusEvent.Cause)} on this toggle button. 245 * 246 * @param cause the cause why the focus is requested 247 * @see ButtonGroup 248 * @see Component#requestFocus(FocusEvent.Cause) 249 * @see FocusEvent.Cause 250 * 251 * @since 9 252 */ 253 @Override 254 public void requestFocus(FocusEvent.Cause cause) { 255 switch (cause) { 256 case ACTIVATION: 257 case TRAVERSAL: 258 case TRAVERSAL_UP: 259 case TRAVERSAL_DOWN: 260 case TRAVERSAL_FORWARD: 261 case TRAVERSAL_BACKWARD: 262 getGroupSelection().requestFocusUnconditionally(cause); 263 break; 264 default: 265 requestFocusUnconditionally(cause); 266 } 267 } 268 269 private void requestFocusUnconditionally(FocusEvent.Cause cause) { 270 super.requestFocus(cause); 271 } 272 273 /** 274 * If this toggle button is a member of the {@link ButtonGroup} which has 275 * an another focusable button selected, and the focus cause argument 276 * denotes window activation or focus traversal action of any direction 277 * the result of the method execution is the same as calling 278 * {@link Component#requestFocusInWindow(FocusEvent.Cause)} on the group 279 * selection button. 280 * In all other cases the result of the method is the same as calling 281 * {@link Component#requestFocusInWindow(FocusEvent.Cause)} on this toggle 282 * button. 283 * 284 * @param cause the cause why the focus is requested 285 * @see ButtonGroup 286 * @see Component#requestFocusInWindow(FocusEvent.Cause) 287 * @see FocusEvent.Cause 288 * 289 * @since 9 290 */ 291 public boolean requestFocusInWindow(FocusEvent.Cause cause) { 292 switch (cause) { 293 case ACTIVATION: 294 case TRAVERSAL: 295 case TRAVERSAL_UP: 296 case TRAVERSAL_DOWN: 297 case TRAVERSAL_FORWARD: 298 case TRAVERSAL_BACKWARD: 299 return getGroupSelection(). 300 requestFocusInWindowUnconditionally(cause); 301 default: 302 return requestFocusInWindowUnconditionally(cause); 303 } 304 } 305 306 private boolean requestFocusInWindowUnconditionally(FocusEvent.Cause cause) { 307 return super.requestFocusInWindow(cause); 308 } 309 310 // ********************************************************************* 311 312 /** 313 * The ToggleButton model 314 * <p> 315 * <strong>Warning:</strong> 316 * Serialized objects of this class will not be compatible with 317 * future Swing releases. The current serialization support is 318 * appropriate for short term storage or RMI between applications running 319 * the same version of Swing. As of 1.4, support for long term storage 320 * of all JavaBeans™ 321 * has been added to the <code>java.beans</code> package. 322 * Please see {@link java.beans.XMLEncoder}. 323 */ 324 @SuppressWarnings("serial") // Same-version serialization only 325 public static class ToggleButtonModel extends DefaultButtonModel { 326 327 /** 328 * Creates a new ToggleButton Model 329 */ 330 public ToggleButtonModel () { 331 } 332 333 /** 334 * Checks if the button is selected. 335 */ 336 public boolean isSelected() { 337 // if(getGroup() != null) { 338 // return getGroup().isSelected(this); 339 // } else { 340 return (stateMask & SELECTED) != 0; 341 // } 342 } 343 344 345 /** 346 * Sets the selected state of the button. 347 * @param b true selects the toggle button, 348 * false deselects the toggle button. 349 */ 350 public void setSelected(boolean b) { 351 ButtonGroup group = getGroup(); 352 if (group != null) { 353 // use the group model instead 354 group.setSelected(this, b); 355 b = group.isSelected(this); 356 } 357 358 if (isSelected() == b) { 359 return; 360 } 361 362 if (b) { 363 stateMask |= SELECTED; 364 } else { 365 stateMask &= ~SELECTED; 366 } 367 368 // Send ChangeEvent 369 fireStateChanged(); 370 371 // Send ItemEvent 372 fireItemStateChanged( 373 new ItemEvent(this, 374 ItemEvent.ITEM_STATE_CHANGED, 375 this, 376 this.isSelected() ? ItemEvent.SELECTED : ItemEvent.DESELECTED)); 377 378 } 379 380 /** 381 * Sets the pressed state of the toggle button. 382 */ 383 public void setPressed(boolean b) { 384 if ((isPressed() == b) || !isEnabled()) { 385 return; 386 } 387 388 if (b == false && isArmed()) { 389 setSelected(!this.isSelected()); 390 } 391 392 if (b) { 393 stateMask |= PRESSED; 394 } else { 395 stateMask &= ~PRESSED; 396 } 397 398 fireStateChanged(); 399 400 if(!isPressed() && isArmed()) { 401 int modifiers = 0; 402 AWTEvent currentEvent = EventQueue.getCurrentEvent(); 403 if (currentEvent instanceof InputEvent) { 404 modifiers = ((InputEvent)currentEvent).getModifiers(); 405 } else if (currentEvent instanceof ActionEvent) { 406 modifiers = ((ActionEvent)currentEvent).getModifiers(); 407 } 408 fireActionPerformed( 409 new ActionEvent(this, ActionEvent.ACTION_PERFORMED, 410 getActionCommand(), 411 EventQueue.getMostRecentEventTime(), 412 modifiers)); 413 } 414 415 } 416 } 417 418 419 /** 420 * See readObject() and writeObject() in JComponent for more 421 * information about serialization in Swing. 422 */ 423 private void writeObject(ObjectOutputStream s) throws IOException { 424 s.defaultWriteObject(); 425 if (getUIClassID().equals(uiClassID)) { 426 byte count = JComponent.getWriteObjCounter(this); 427 JComponent.setWriteObjCounter(this, --count); 428 if (count == 0 && ui != null) { 429 ui.installUI(this); 430 } 431 } 432 } 433 434 435 /** 436 * Returns a string representation of this JToggleButton. This method 437 * is intended to be used only for debugging purposes, and the 438 * content and format of the returned string may vary between 439 * implementations. The returned string may be empty but may not 440 * be <code>null</code>. 441 * 442 * @return a string representation of this JToggleButton. 443 */ 444 protected String paramString() { 445 return super.paramString(); 446 } 447 448 449 ///////////////// 450 // Accessibility support 451 //////////////// 452 453 /** 454 * Gets the AccessibleContext associated with this JToggleButton. 455 * For toggle buttons, the AccessibleContext takes the form of an 456 * AccessibleJToggleButton. 457 * A new AccessibleJToggleButton instance is created if necessary. 458 * 459 * @return an AccessibleJToggleButton that serves as the 460 * AccessibleContext of this JToggleButton 461 */ 462 @BeanProperty(bound = false, expert = true, description 463 = "The AccessibleContext associated with this ToggleButton.") 464 public AccessibleContext getAccessibleContext() { 465 if (accessibleContext == null) { 466 accessibleContext = new AccessibleJToggleButton(); 467 } 468 return accessibleContext; 469 } 470 471 /** 472 * This class implements accessibility support for the 473 * <code>JToggleButton</code> class. It provides an implementation of the 474 * Java Accessibility API appropriate to toggle button user-interface 475 * elements. 476 * <p> 477 * <strong>Warning:</strong> 478 * Serialized objects of this class will not be compatible with 479 * future Swing releases. The current serialization support is 480 * appropriate for short term storage or RMI between applications running 481 * the same version of Swing. As of 1.4, support for long term storage 482 * of all JavaBeans™ 483 * has been added to the <code>java.beans</code> package. 484 * Please see {@link java.beans.XMLEncoder}. 485 */ 486 @SuppressWarnings("serial") // Same-version serialization only 487 protected class AccessibleJToggleButton extends AccessibleAbstractButton 488 implements ItemListener { 489 490 /** 491 * Constructs {@code AccessibleJToggleButton} 492 */ 493 public AccessibleJToggleButton() { 494 super(); 495 JToggleButton.this.addItemListener(this); 496 } 497 498 /** 499 * Fire accessible property change events when the state of the 500 * toggle button changes. 501 */ 502 public void itemStateChanged(ItemEvent e) { 503 JToggleButton tb = (JToggleButton) e.getSource(); 504 if (JToggleButton.this.accessibleContext != null) { 505 if (tb.isSelected()) { 506 JToggleButton.this.accessibleContext.firePropertyChange( 507 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 508 null, AccessibleState.CHECKED); 509 } else { 510 JToggleButton.this.accessibleContext.firePropertyChange( 511 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 512 AccessibleState.CHECKED, null); 513 } 514 } 515 } 516 517 /** 518 * Get the role of this object. 519 * 520 * @return an instance of AccessibleRole describing the role of the 521 * object 522 */ 523 public AccessibleRole getAccessibleRole() { 524 return AccessibleRole.TOGGLE_BUTTON; 525 } 526 } // inner class AccessibleJToggleButton 527 }