1 /* 2 * Copyright (c) 2012, 2014, 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.cell; 27 28 import javafx.beans.property.BooleanProperty; 29 import javafx.beans.property.ObjectProperty; 30 import javafx.beans.property.SimpleBooleanProperty; 31 import javafx.beans.property.SimpleObjectProperty; 32 import javafx.collections.FXCollections; 33 import javafx.collections.ObservableList; 34 import javafx.scene.Node; 35 import javafx.scene.control.*; 36 import javafx.scene.layout.HBox; 37 import javafx.util.Callback; 38 import javafx.util.StringConverter; 39 import static javafx.scene.control.cell.CellUtils.createComboBox; 40 41 /** 42 * A class containing a {@link TreeCell} implementation that draws a 43 * {@link ComboBox} node inside the cell. 44 * 45 * <p>By default, the ComboBoxTreeCell is rendered as a {@link Label} when not 46 * being edited, and as a ComboBox when in editing mode. The ComboBox will, by 47 * default, stretch to fill the entire tree cell. 48 * 49 * <p>To create a ComboBoxTreeCell, it is necessary to provide zero or more 50 * items that will be shown to the user when the {@link ComboBox} menu is 51 * showing. These items must be of the same type as the TreeView TreeItems, such 52 * that upon selection, they replace the existing value in the 53 * {@link TreeItem#valueProperty()}. 54 * 55 * @param <T> The type of the TreeItems contained within the TreeView. 56 * @since JavaFX 2.2 57 */ 58 public class ComboBoxTreeCell<T> extends DefaultTreeCell<T> { 59 60 /*************************************************************************** 61 * * 62 * Static cell factories * 63 * * 64 **************************************************************************/ 65 66 /** 67 * Creates a ComboBox cell factory for use in {@link TreeView} controls. By 68 * default, the ComboBoxCell is rendered as a {@link Label} when not being 69 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 70 * default, stretch to fill the entire tree cell. 71 * 72 * @param <T> The type of the elements contained within the TreeView. 73 * @param items Zero or more items that will be shown to the user when the 74 * {@link ComboBox} menu is showing. These items must be of the same 75 * type as the TreeView<T>, such that upon selection, they replace the 76 * existing value in the TreeItem {@link TreeItem#valueProperty() value} 77 * property. 78 * @return A {@link Callback} that will return a TreeCell that is able to 79 * work on the type of element contained within the TreeView. 80 */ 81 @SafeVarargs 82 public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView(T... items) { 83 return forTreeView(FXCollections.observableArrayList(items)); 84 } 85 86 /** 87 * Creates a ComboBox cell factory for use in {@link TreeView} controls. By 88 * default, the ComboBoxCell is rendered as a {@link Label} when not being 89 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 90 * default, stretch to fill the entire tree cell, excluding the space 91 * allocated to the tree cell indentation and disclosure node(i.e. the arrow). 92 * 93 * @param <T> The type of the {@link TreeItem} elements contained within the 94 * TreeView. 95 * @param items An {@link ObservableList} containing zero or more items that 96 * will be shown to the user when the {@link ComboBox} menu is showing. 97 * These items must be of the same type as the TreeView generic type, 98 * such that upon selection, they replace the existing value in the 99 * {@link TreeItem} that is being edited (as noted in the 100 * {@link TreeView#editingItemProperty()}. 101 * @return A {@link Callback} that will return a TreeCell that is able to 102 * work on the type of element contained within the TreeView. 103 */ 104 public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView( 105 final ObservableList<T> items) { 106 return forTreeView(null, items); 107 } 108 109 /** 110 * Creates a ComboBox cell factory for use in {@link TreeView} controls. By 111 * default, the ComboBoxCell is rendered as a {@link Label} when not being 112 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 113 * default, stretch to fill the entire tree cell. 114 * 115 * @param <T> The type of the elements contained within the TreeView. 116 * @param converter A {@link StringConverter} to convert the given item (of 117 * type T) to a String for displaying to the user. 118 * @param items Zero or more items that will be shown to the user when the 119 * {@link ComboBox} menu is showing. These items must be of the same 120 * type as the TreeView<T>, such that upon selection, they replace the 121 * existing value in the TreeItem {@link TreeItem#valueProperty() value} 122 * property. 123 * @return A {@link Callback} that will return a TreeCell that is able to 124 * work on the type of element contained within the TreeView. 125 */ 126 @SafeVarargs 127 public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView( 128 final StringConverter<T> converter, 129 final T... items) { 130 return forTreeView(converter, FXCollections.observableArrayList(items)); 131 } 132 133 /** 134 * Creates a ComboBox cell factory for use in {@link TreeView} controls. By 135 * default, the ComboBoxCell is rendered as a {@link Label} when not being 136 * edited, and as a ComboBox when in editing mode. The ComboBox will, by 137 * default, stretch to fill the entire tree cell. 138 * 139 * @param <T> The type of the elements contained within the TreeView. 140 * @param converter A {@link StringConverter} to convert the given item (of 141 * type T) to a String for displaying to the user. 142 * @param items An {@link ObservableList} containing zero or more items that 143 * will be shown to the user when the {@link ComboBox} menu is showing. 144 * These items must be of the same type as the TreeView generic type, 145 * such that upon selection, they replace the existing value in the 146 * {@link TreeItem} that is being edited (as noted in the 147 * {@link TreeView#editingItemProperty()}. 148 * @return A {@link Callback} that will return a TreeCell that is able to 149 * work on the type of element contained within the TreeView. 150 */ 151 public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView( 152 final StringConverter<T> converter, 153 final ObservableList<T> items) { 154 return list -> new ComboBoxTreeCell<T>(converter, items); 155 } 156 157 158 159 /*************************************************************************** 160 * * 161 * Fields * 162 * * 163 **************************************************************************/ 164 private final ObservableList<T> items; 165 166 private ComboBox<T> comboBox; 167 private HBox hbox; 168 169 170 171 /*************************************************************************** 172 * * 173 * Constructors * 174 * * 175 **************************************************************************/ 176 177 /** 178 * Creates a default ComboBoxTreeCell with an empty items list. 179 */ 180 public ComboBoxTreeCell() { 181 this(FXCollections.<T>observableArrayList()); 182 } 183 184 /** 185 * Creates a default {@link ComboBoxTreeCell} instance with the given items 186 * being used to populate the {@link ComboBox} when it is shown. 187 * 188 * @param items The items to show in the ComboBox popup menu when selected 189 * by the user. 190 */ 191 @SafeVarargs 192 public ComboBoxTreeCell(T... items) { 193 this(FXCollections.observableArrayList(items)); 194 } 195 196 /** 197 * Creates a {@link ComboBoxTreeCell} instance with the given items 198 * being used to populate the {@link ComboBox} when it is shown, and the 199 * {@link StringConverter} being used to convert the item in to a 200 * user-readable form. 201 * 202 * @param converter A {@link StringConverter} that can convert an item of 203 * type T into a user-readable string so that it may then be shown in 204 * the ComboBox popup menu. 205 * @param items The items to show in the ComboBox popup menu when selected 206 * by the user. 207 */ 208 @SafeVarargs 209 public ComboBoxTreeCell(StringConverter<T> converter, T... items) { 210 this(converter, FXCollections.observableArrayList(items)); 211 } 212 213 /** 214 * Creates a default {@link ComboBoxTreeCell} instance with the given items 215 * being used to populate the {@link ComboBox} when it is shown. 216 * 217 * @param items The items to show in the ComboBox popup menu when selected 218 * by the user. 219 */ 220 public ComboBoxTreeCell(ObservableList<T> items) { 221 this(null, items); 222 } 223 224 /** 225 * Creates a {@link ComboBoxTreeCell} instance with the given items 226 * being used to populate the {@link ComboBox} when it is shown, and the 227 * {@link StringConverter} being used to convert the item in to a 228 * user-readable form. 229 * 230 * @param converter A {@link StringConverter} that can convert an item of 231 * type T into a user-readable string so that it may then be shown in 232 * the ComboBox popup menu. 233 * @param items The items to show in the ComboBox popup menu when selected 234 * by the user. 235 */ 236 public ComboBoxTreeCell(StringConverter<T> converter, ObservableList<T> items) { 237 this.getStyleClass().add("combo-box-tree-cell"); 238 this.items = items; 239 setConverter(converter != null ? converter : CellUtils.<T>defaultStringConverter()); 240 } 241 242 243 244 /*************************************************************************** 245 * * 246 * Properties * 247 * * 248 **************************************************************************/ 249 250 // --- converter 251 private ObjectProperty<StringConverter<T>> converter = 252 new SimpleObjectProperty<StringConverter<T>>(this, "converter"); 253 254 /** 255 * The {@link StringConverter} property. 256 */ 257 public final ObjectProperty<StringConverter<T>> converterProperty() { 258 return converter; 259 } 260 261 /** 262 * Sets the {@link StringConverter} to be used in this cell. 263 */ 264 public final void setConverter(StringConverter<T> value) { 265 converterProperty().set(value); 266 } 267 268 /** 269 * Returns the {@link StringConverter} used in this cell. 270 */ 271 public final StringConverter<T> getConverter() { 272 return converterProperty().get(); 273 } 274 275 276 // --- comboBox editable 277 private BooleanProperty comboBoxEditable = 278 new SimpleBooleanProperty(this, "comboBoxEditable"); 279 280 /** 281 * A property representing whether the ComboBox, when shown to the user, 282 * is editable or not. 283 */ 284 public final BooleanProperty comboBoxEditableProperty() { 285 return comboBoxEditable; 286 } 287 288 /** 289 * Configures the ComboBox to be editable (to allow user input outside of the 290 * options provide in the dropdown list). 291 */ 292 public final void setComboBoxEditable(boolean value) { 293 comboBoxEditableProperty().set(value); 294 } 295 296 /** 297 * Returns true if the ComboBox is editable. 298 */ 299 public final boolean isComboBoxEditable() { 300 return comboBoxEditableProperty().get(); 301 } 302 303 304 305 /*************************************************************************** 306 * * 307 * Public API * 308 * * 309 **************************************************************************/ 310 311 /** 312 * Returns the items to be displayed in the ChoiceBox when it is showing. 313 */ 314 public ObservableList<T> getItems() { 315 return items; 316 } 317 318 /** {@inheritDoc} */ 319 @Override public void startEdit() { 320 if (! isEditable() || ! getTreeView().isEditable()) { 321 return; 322 } 323 324 TreeItem<T> treeItem = getTreeItem(); 325 if (treeItem == null) { 326 return; 327 } 328 329 if (comboBox == null) { 330 comboBox = createComboBox(this, items, converterProperty()); 331 comboBox.editableProperty().bind(comboBoxEditableProperty()); 332 } 333 if (hbox == null) { 334 hbox = new HBox(CellUtils.TREE_VIEW_HBOX_GRAPHIC_PADDING); 335 } 336 337 comboBox.getSelectionModel().select(treeItem.getValue()); 338 339 super.startEdit(); 340 341 if (isEditing()) { 342 setText(null); 343 344 Node graphic = CellUtils.getGraphic(treeItem); 345 if (graphic != null) { 346 hbox.getChildren().setAll(graphic, comboBox); 347 setGraphic(hbox); 348 } else { 349 setGraphic(comboBox); 350 } 351 } 352 } 353 354 /** {@inheritDoc} */ 355 @Override public void cancelEdit() { 356 super.cancelEdit(); 357 358 setText(getConverter().toString(getItem())); 359 setGraphic(null); 360 } 361 362 /** {@inheritDoc} */ 363 @Override public void updateItem(T item, boolean empty) { 364 super.updateItem(item, empty); 365 366 Node graphic = CellUtils.getGraphic(getTreeItem()); 367 CellUtils.updateItem(this, getConverter(), hbox, graphic, comboBox); 368 }; 369 }