1 /* 2 * Copyright (c) 2010, 2013, 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 26 package javafx.scene.control; 27 28 import javafx.beans.property.BooleanProperty; 29 import javafx.beans.property.BooleanPropertyBase; 30 import javafx.beans.property.ObjectProperty; 31 import javafx.beans.property.ObjectPropertyBase; 32 import javafx.scene.Node; 33 34 /** 35 * <p> 36 * A RadioMenuItem is a {@link MenuItem} that can be toggled (it uses 37 * the {@link javafx.scene.control.Toggle Toggle} mixin). This means that 38 * RadioMenuItem has an API very similar in nature to other controls that use 39 * {@link javafx.scene.control.Toggle Toggle}, such as 40 * {@link javafx.scene.control.RadioButton} and 41 * {@link javafx.scene.control.ToggleButton}. RadioMenuItem is 42 * specifically designed for use within a {@code Menu}, so refer to that class 43 * API documentation for more information on how to add a RadioMenuItem into it. 44 * <p> 45 * To create a simple, ungrouped RadioMenuItem, do the following: 46 <pre><code> 47 RadioMenuItem radioItem = new RadioMenuItem("radio text"); 48 radioItem.setSelected(false); 49 radioItem.setOnAction(new EventHandler<ActionEvent>() { 50 @Override public void handle(ActionEvent e) { 51 System.out.println("radio toggled"); 52 } 53 }); 54 </code></pre> 55 * <p> 56 * The problem with the example above is that this offers no benefit over using 57 * a normal MenuItem. As already mentioned, the purpose of a 58 * RadioMenuItem is to offer 59 * multiple choices to the user, and only allow for one of these choices to be 60 * selected at any one time (i.e. the selection should be <i>mutually exclusive</i>). 61 * To achieve this, you can place zero or more RadioMenuItem's into groups. When 62 * in groups, only one RadioMenuItem at a time within that group can be selected. 63 * To put two RadioMenuItem instances into the same group, simply assign them 64 * both the same value for {@code toggleGroup}. For example: 65 <pre><code> 66 ToggleGroup toggleGroup = new ToggleGroup(); 67 68 RadioMenuItem radioItem1 = new RadioMenuItem("Option 1"); 69 radioItem.setOnAction(new EventHandler<ActionEvent>() { 70 @Override public void handle(ActionEvent e) { 71 System.out.println("radio toggled"); 72 } 73 }); 74 radioItem1.setToggleGroup(toggleGroup); 75 RadioMenuItem radioItem2 = new RadioMenuItem("Option 2"); 76 radioItem.setOnAction(new EventHandler<ActionEvent>() { 77 @Override public void handle(ActionEvent e) { 78 System.out.println("radio toggled"); 79 } 80 }); 81 radioItem2.setToggleGroup(toggleGroup); 82 83 </code></pre> 84 * 85 * In this example, with both RadioMenuItem's assigned to the same 86 * {@link javafx.scene.control.ToggleGroup ToggleGroup}, only one item may be 87 * selected at any one time, and should 88 * the selection change, the ToggleGroup will take care of deselecting the 89 * previous item. 90 </code></pre> 91 * 92 * @see MenuItem 93 * @see Menu 94 * @since JavaFX 2.0 95 */ 96 public class RadioMenuItem extends MenuItem implements Toggle { 97 98 /*************************************************************************** 99 * * 100 * Constructors * 101 * * 102 **************************************************************************/ 103 104 /** 105 * Constructs a RadioMenuItem with no display text. 106 */ 107 public RadioMenuItem() { 108 this(null,null); 109 } 110 111 /** 112 * Constructs a RadioMenuItem and sets the display text with the specified text. 113 */ 114 public RadioMenuItem(String text) { 115 this(text,null); 116 } 117 118 /** 119 * Constructs a RadioMenuItem and sets the display text with the specified text 120 * and sets the graphic {@link Node} to the given node. 121 */ 122 public RadioMenuItem(String text, Node graphic) { 123 super(text,graphic); 124 getStyleClass().add(DEFAULT_STYLE_CLASS); 125 } 126 127 128 129 /*************************************************************************** 130 * * 131 * Properties * 132 * * 133 **************************************************************************/ 134 135 // --- Toggle Group 136 /** 137 * Represents the {@link ToggleGroup} that this RadioMenuItem belongs to. 138 */ 139 private ObjectProperty<ToggleGroup> toggleGroup; 140 @Override public final void setToggleGroup(ToggleGroup value) { 141 toggleGroupProperty().set(value); 142 } 143 144 @Override public final ToggleGroup getToggleGroup() { 145 return toggleGroup == null ? null : toggleGroup.get(); 146 } 147 148 @Override public final ObjectProperty<ToggleGroup> toggleGroupProperty() { 149 if (toggleGroup == null) { 150 toggleGroup = new ObjectPropertyBase<ToggleGroup>() { 151 private ToggleGroup old; 152 @Override protected void invalidated() { 153 if (old != null) { 154 old.getToggles().remove(RadioMenuItem.this); 155 } 156 old = get(); 157 if (get() != null && !get().getToggles().contains(RadioMenuItem.this)) { 158 get().getToggles().add(RadioMenuItem.this); 159 } 160 } 161 162 @Override 163 public Object getBean() { 164 return RadioMenuItem.this; 165 } 166 167 @Override 168 public String getName() { 169 return "toggleGroup"; 170 } 171 }; 172 } 173 return toggleGroup; 174 } 175 176 177 // --- Selected 178 private BooleanProperty selected; 179 @Override public final void setSelected(boolean value) { 180 selectedProperty().set(value); 181 } 182 183 @Override public final boolean isSelected() { 184 return selected == null ? false : selected.get(); 185 } 186 187 @Override public final BooleanProperty selectedProperty() { 188 if (selected == null) { 189 selected = new BooleanPropertyBase() { 190 @Override protected void invalidated() { 191 if (getToggleGroup() != null) { 192 if (get()) { 193 getToggleGroup().selectToggle(RadioMenuItem.this); 194 } else if (getToggleGroup().getSelectedToggle() == RadioMenuItem.this) { 195 getToggleGroup().selectToggle(null); 196 } 197 } 198 199 if (isSelected()) { 200 getStyleClass().add(STYLE_CLASS_SELECTED); 201 } else { 202 getStyleClass().remove(STYLE_CLASS_SELECTED); 203 } 204 } 205 206 @Override 207 public Object getBean() { 208 return RadioMenuItem.this; 209 } 210 211 @Override 212 public String getName() { 213 return "selected"; 214 } 215 }; 216 } 217 return selected; 218 } 219 220 221 222 /*************************************************************************** 223 * * 224 * Inherited Public API * 225 * * 226 **************************************************************************/ 227 228 229 /*************************************************************************** 230 * * 231 * Stylesheet Handling * 232 * * 233 **************************************************************************/ 234 235 private static final String DEFAULT_STYLE_CLASS = "radio-menu-item"; 236 private static final String STYLE_CLASS_SELECTED = "selected"; 237 }