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