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.SimpleObjectProperty; 31 import javafx.beans.value.ObservableValue; 32 import javafx.geometry.Pos; 33 import javafx.scene.control.CheckBox; 34 import javafx.scene.control.ContentDisplay; 35 import javafx.scene.control.ListCell; 36 import javafx.scene.control.ListView; 37 import javafx.util.Callback; 38 import javafx.util.StringConverter; 39 40 /** 41 * A class containing a {@link ListCell} implementation that draws a 42 * {@link CheckBox} node inside the cell, optionally with a label to indicate 43 * what the checkbox represents. 44 * 45 * <p>The CheckBoxListCell is rendered with a CheckBox on the left-hand side of 46 * the {@link ListView}, and the text related to the list item taking up all 47 * remaining horizontal space. 48 * 49 * <p>To construct an instance of this class, it is necessary to provide a 50 * {@link Callback} that, given an object of type T, will return a 51 * {@code ObservableValue<Boolean>} that represents whether the given item is 52 * selected or not. This ObservableValue will be bound bidirectionally (meaning 53 * that the CheckBox in the cell will set/unset this property based on user 54 * interactions, and the CheckBox will reflect the state of the 55 * ObservableValue<Boolean>, if it changes externally). 56 * 57 * <p>Note that the CheckBoxListCell renders the CheckBox 'live', meaning that 58 * the CheckBox is always interactive and can be directly toggled by the user. 59 * This means that it is not necessary that the cell enter its 60 * {@link #editingProperty() editing state} (usually by the user double-clicking 61 * on the cell). A side-effect of this is that the usual editing callbacks 62 * (such as {@link javafx.scene.control.ListView#onEditCommitProperty() on edit commit}) 63 * will <strong>not</strong> be called. If you want to be notified of changes, 64 * it is recommended to directly observe the boolean properties that are 65 * manipulated by the CheckBox.</p> 66 * 67 * @see CheckBox 68 * @see ListCell 69 * @param <T> The type of the elements contained within the ListView. 70 * @since JavaFX 2.2 71 */ 72 public class CheckBoxListCell<T> extends ListCell<T> { 73 74 /*************************************************************************** 75 * * 76 * Static cell factories * 77 * * 78 **************************************************************************/ 79 80 /** 81 * Creates a cell factory for use in ListView controls. When used in a 82 * ListView, the {@link CheckBoxListCell} is rendered with a CheckBox on the 83 * left-hand side of the ListView, with the text related to the list item 84 * taking up all remaining horizontal space. 85 * 86 * @param <T> The type of the elements contained within the ListView. 87 * @param getSelectedProperty A {@link Callback} that, given an object of 88 * type T (which is a value taken out of the 89 * {@code ListView<T>.items} list), 90 * will return an {@code ObservableValue<Boolean>} that represents 91 * whether the given item is selected or not. This ObservableValue will 92 * be bound bidirectionally (meaning that the CheckBox in the cell will 93 * set/unset this property based on user interactions, and the CheckBox 94 * will reflect the state of the ObservableValue, if it changes 95 * externally). 96 * @return A {@link Callback} that will return a ListCell that is able to 97 * work on the type of element contained within the ListView items list. 98 */ 99 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 100 final Callback<T, ObservableValue<Boolean>> getSelectedProperty) { 101 return forListView(getSelectedProperty, CellUtils.<T>defaultStringConverter()); 102 } 103 104 /** 105 * Creates a cell factory for use in ListView controls. When used in a 106 * ListView, the {@link CheckBoxListCell} is rendered with a CheckBox on the 107 * left-hand side of the ListView, with the text related to the list item 108 * taking up all remaining horizontal space. 109 * 110 * @param <T> The type of the elements contained within the ListView. 111 * @param getSelectedProperty A {@link Callback} that, given an object 112 * of type T (which is a value taken out of the 113 * {@code ListView<T>.items} list), 114 * will return an {@code ObservableValue<Boolean>} that represents 115 * whether the given item is selected or not. This ObservableValue will 116 * be bound bidirectionally (meaning that the CheckBox in the cell will 117 * set/unset this property based on user interactions, and the CheckBox 118 * will reflect the state of the ObservableValue, if it changes 119 * externally). 120 * @param converter A StringConverter that, give an object of type T, will 121 * return a String that can be used to represent the object visually. 122 * @return A {@link Callback} that will return a ListCell that is able to 123 * work on the type of element contained within the ListView. 124 */ 125 public static <T> Callback<ListView<T>, ListCell<T>> forListView( 126 final Callback<T, ObservableValue<Boolean>> getSelectedProperty, 127 final StringConverter<T> converter) { 128 return list -> new CheckBoxListCell<T>(getSelectedProperty, converter); 129 } 130 131 /*************************************************************************** 132 * * 133 * Fields * 134 * * 135 **************************************************************************/ 136 137 private final CheckBox checkBox; 138 139 private ObservableValue<Boolean> booleanProperty; 140 141 142 143 /*************************************************************************** 144 * * 145 * Constructors * 146 * * 147 **************************************************************************/ 148 149 /** 150 * Creates a default CheckBoxListCell. 151 */ 152 public CheckBoxListCell() { 153 this(null); 154 } 155 156 /** 157 * Creates a default CheckBoxListCell. 158 * 159 * @param getSelectedProperty A {@link Callback} that will return an 160 * {@code ObservableValue<Boolean>} given an item from the ListView. 161 */ 162 public CheckBoxListCell( 163 final Callback<T, ObservableValue<Boolean>> getSelectedProperty) { 164 this(getSelectedProperty, CellUtils.<T>defaultStringConverter()); 165 } 166 167 /** 168 * Creates a CheckBoxListCell with a custom string converter. 169 * 170 * @param getSelectedProperty A {@link Callback} that will return an 171 * {@code ObservableValue<Boolean>} given an item from the ListView. 172 * @param converter A StringConverter that, given an object of type T, will 173 * return a String that can be used to represent the object visually. 174 */ 175 public CheckBoxListCell( 176 final Callback<T, ObservableValue<Boolean>> getSelectedProperty, 177 final StringConverter<T> converter) { 178 this.getStyleClass().add("check-box-list-cell"); 179 setSelectedStateCallback(getSelectedProperty); 180 setConverter(converter); 181 182 this.checkBox = new CheckBox(); 183 184 setAlignment(Pos.CENTER_LEFT); 185 setContentDisplay(ContentDisplay.LEFT); 186 187 // by default the graphic is null until the cell stops being empty 188 setGraphic(null); 189 } 190 191 192 /*************************************************************************** 193 * * 194 * Properties * 195 * * 196 **************************************************************************/ 197 198 // --- converter 199 private ObjectProperty<StringConverter<T>> converter = 200 new SimpleObjectProperty<StringConverter<T>>(this, "converter"); 201 202 /** 203 * The {@link StringConverter} property. 204 */ 205 public final ObjectProperty<StringConverter<T>> converterProperty() { 206 return converter; 207 } 208 209 /** 210 * Sets the {@link StringConverter} to be used in this cell. 211 */ 212 public final void setConverter(StringConverter<T> value) { 213 converterProperty().set(value); 214 } 215 216 /** 217 * Returns the {@link StringConverter} used in this cell. 218 */ 219 public final StringConverter<T> getConverter() { 220 return converterProperty().get(); 221 } 222 223 224 // --- selected state callback property 225 private ObjectProperty<Callback<T, ObservableValue<Boolean>>> 226 selectedStateCallback = 227 new SimpleObjectProperty<Callback<T, ObservableValue<Boolean>>>( 228 this, "selectedStateCallback"); 229 230 /** 231 * Property representing the {@link Callback} that is bound to by the 232 * CheckBox shown on screen. 233 */ 234 public final ObjectProperty<Callback<T, ObservableValue<Boolean>>> selectedStateCallbackProperty() { 235 return selectedStateCallback; 236 } 237 238 /** 239 * Sets the {@link Callback} that is bound to by the CheckBox shown on screen. 240 */ 241 public final void setSelectedStateCallback(Callback<T, ObservableValue<Boolean>> value) { 242 selectedStateCallbackProperty().set(value); 243 } 244 245 /** 246 * Returns the {@link Callback} that is bound to by the CheckBox shown on screen. 247 */ 248 public final Callback<T, ObservableValue<Boolean>> getSelectedStateCallback() { 249 return selectedStateCallbackProperty().get(); 250 } 251 252 253 254 /*************************************************************************** 255 * * 256 * Public API * 257 * * 258 **************************************************************************/ 259 260 /** {@inheritDoc} */ 261 @Override public void updateItem(T item, boolean empty) { 262 super.updateItem(item, empty); 263 264 if (! empty) { 265 StringConverter<T> c = getConverter(); 266 Callback<T, ObservableValue<Boolean>> callback = getSelectedStateCallback(); 267 if (callback == null) { 268 throw new NullPointerException( 269 "The CheckBoxListCell selectedStateCallbackProperty can not be null"); 270 } 271 272 setGraphic(checkBox); 273 setText(c != null ? c.toString(item) : (item == null ? "" : item.toString())); 274 275 if (booleanProperty != null) { 276 checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty); 277 } 278 booleanProperty = callback.call(item); 279 if (booleanProperty != null) { 280 checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty); 281 } 282 } else { 283 setGraphic(null); 284 setText(null); 285 } 286 } 287 }