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