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