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 * 91 * @see MenuItem 92 * @see Menu 93 * @since JavaFX 2.0 94 */ 95 public class RadioMenuItem extends MenuItem implements Toggle { 96 97 /*************************************************************************** 98 * * 99 * Constructors * 100 * * 101 **************************************************************************/ 102 103 /** 104 * Constructs a RadioMenuItem with no display text. 105 */ 106 public RadioMenuItem() { 107 this(null,null); 108 } 109 110 /** 111 * Constructs a RadioMenuItem and sets the display text with the specified text. 112 * @param text the display 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 * @param text the display text 122 * @param graphic the graphic node 123 */ 124 public RadioMenuItem(String text, Node graphic) { 125 super(text,graphic); 126 getStyleClass().add(DEFAULT_STYLE_CLASS); 127 } 128 129 130 131 /*************************************************************************** 132 * * 133 * Properties * 134 * * 135 **************************************************************************/ 136 137 // --- Toggle Group 138 /** 139 * Represents the {@link ToggleGroup} that this RadioMenuItem belongs to. 140 */ 141 private ObjectProperty<ToggleGroup> toggleGroup; 142 @Override public final void setToggleGroup(ToggleGroup value) { 143 toggleGroupProperty().set(value); 144 } 145 146 @Override public final ToggleGroup getToggleGroup() { 147 return toggleGroup == null ? null : toggleGroup.get(); 148 } 149 150 @Override public final ObjectProperty<ToggleGroup> toggleGroupProperty() { 151 if (toggleGroup == null) { 152 toggleGroup = new ObjectPropertyBase<ToggleGroup>() { 153 private ToggleGroup old; 154 @Override protected void invalidated() { 155 if (old != null) { 156 old.getToggles().remove(RadioMenuItem.this); 157 } 158 old = get(); 159 if (get() != null && !get().getToggles().contains(RadioMenuItem.this)) { 160 get().getToggles().add(RadioMenuItem.this); 161 } 162 } 163 164 @Override 165 public Object getBean() { 166 return RadioMenuItem.this; 167 } 168 169 @Override 170 public String getName() { 171 return "toggleGroup"; 172 } 173 }; 174 } 175 return toggleGroup; 176 } 177 178 179 // --- Selected 180 private BooleanProperty selected; 181 @Override public final void setSelected(boolean value) { 182 selectedProperty().set(value); 183 } 184 185 @Override public final boolean isSelected() { 186 return selected == null ? false : selected.get(); 187 } 188 189 @Override public final BooleanProperty selectedProperty() { 190 if (selected == null) { 191 selected = new BooleanPropertyBase() { 192 @Override protected void invalidated() { 193 if (getToggleGroup() != null) { 194 if (get()) { 195 getToggleGroup().selectToggle(RadioMenuItem.this); 196 } else if (getToggleGroup().getSelectedToggle() == RadioMenuItem.this) { 197 getToggleGroup().selectToggle(null); 198 } 199 } 200 201 if (isSelected()) { 202 getStyleClass().add(STYLE_CLASS_SELECTED); 203 } else { 204 getStyleClass().remove(STYLE_CLASS_SELECTED); 205 } 206 } 207 208 @Override 209 public Object getBean() { 210 return RadioMenuItem.this; 211 } 212 213 @Override 214 public String getName() { 215 return "selected"; 216 } 217 }; 218 } 219 return selected; 220 } 221 222 223 224 /*************************************************************************** 225 * * 226 * Inherited Public API * 227 * * 228 **************************************************************************/ 229 230 231 /*************************************************************************** 232 * * 233 * Stylesheet Handling * 234 * * 235 **************************************************************************/ 236 237 private static final String DEFAULT_STYLE_CLASS = "radio-menu-item"; 238 private static final String STYLE_CLASS_SELECTED = "selected"; 239 }