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