Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.control.cell; 027 028import javafx.css.CssMetaData; 029import javafx.beans.binding.Bindings; 030import javafx.beans.property.BooleanProperty; 031import javafx.beans.property.ObjectProperty; 032import javafx.beans.property.SimpleObjectProperty; 033import javafx.beans.value.ObservableValue; 034import javafx.geometry.Pos; 035import javafx.scene.control.CheckBox; 036import javafx.scene.control.TableCell; 037import javafx.scene.control.TableColumn; 038import javafx.util.Callback; 039import javafx.util.StringConverter; 040 041/** 042 * A class containing a {@link TableCell} implementation that draws a 043 * {@link CheckBox} node inside the cell, optionally with a label to indicate 044 * what the checkbox represents. 045 * 046 * <p>By default, the CheckBoxTableCell is rendered with a CheckBox centred in 047 * the TableColumn. If a label is required, it is necessary to provide a 048 * non-null StringConverter instance to the 049 * {@link #CheckBoxTableCell(Callback, StringConverter)} constructor. 050 * 051 * <p>To construct an instance of this class, it is necessary to provide a 052 * {@link Callback} that, given an object of type T, will return an 053 * {@code ObservableProperty<Boolean>} that represents whether the given item is 054 * selected or not. This ObservableValue will be bound bidirectionally (meaning 055 * that the CheckBox in the cell will set/unset this property based on user 056 * interactions, and the CheckBox will reflect the state of the ObservableValue, 057 * if it changes externally). 058 * 059 * @param <T> The type of the elements contained within the TableColumn. 060 * @since 2.2 061 */ 062public class CheckBoxTableCell<S,T> extends TableCell<S,T> { 063 064 /*************************************************************************** 065 * * 066 * Static cell factories * 067 * * 068 **************************************************************************/ 069 070 /** 071 * Creates a cell factory for use in a {@link TableColumn} cell factory. 072 * This method requires that the TableColumn be of type {@link Boolean}. 073 * 074 * <p>When used in a TableColumn, the CheckBoxCell is rendered with a 075 * CheckBox centered in the column. 076 * 077 * <p>The {@code ObservableValue<Boolean>} contained within each cell in the 078 * column will be bound bidirectionally. This means that the CheckBox in 079 * the cell will set/unset this property based on user interactions, and the 080 * CheckBox will reflect the state of the {@code ObservableValue<Boolean>}, 081 * if it changes externally).</li> 082 * 083 * @return A {@link Callback} that will return a {@link TableCell} that is 084 * able to work on the type of element contained within the TableColumn. 085 */ 086 public static <S> Callback<TableColumn<S,Boolean>, TableCell<S,Boolean>> forTableColumn( 087 final TableColumn<S, Boolean> column) { 088 return forTableColumn(null, null); 089 } 090 091 /** 092 * Creates a cell factory for use in a {@link TableColumn} cell factory. 093 * This method requires that the TableColumn be of type 094 * {@code ObservableValue<Boolean>}. 095 * 096 * <p>When used in a TableColumn, the CheckBoxCell is rendered with a 097 * CheckBox centered in the column. 098 * 099 * @param <T> The type of the elements contained within the {@link TableColumn} 100 * instance. 101 * @param getSelectedProperty A Callback that, given an object of 102 * type {@code TableColumn<S,T>}, will return an 103 * {@code ObservableValue<Boolean>} 104 * that represents whether the given item is selected or not. This 105 * {@code ObservableValue<Boolean>} will be bound bidirectionally 106 * (meaning that the CheckBox in the cell will set/unset this property 107 * based on user interactions, and the CheckBox will reflect the state of 108 * the {@code ObservableValue<Boolean>}, if it changes externally). 109 * @return A {@link Callback} that will return a {@link TableCell} that is 110 * able to work on the type of element contained within the TableColumn. 111 */ 112 public static <S,T> Callback<TableColumn<S,T>, TableCell<S,T>> forTableColumn( 113 final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty) { 114 return forTableColumn(getSelectedProperty, null); 115 } 116 117 /** 118 * Creates a cell factory for use in a {@link TableColumn} cell factory. 119 * This method requires that the TableColumn be of type 120 * {@code ObservableValue<Boolean>}. 121 * 122 * <p>When used in a TableColumn, the CheckBoxCell is rendered with a 123 * CheckBox centered in the column. 124 * 125 * @param <T> The type of the elements contained within the {@link TableColumn} 126 * instance. 127 * @param getSelectedProperty A Callback that, given an object of 128 * type {@code TableColumn<S,T>}, will return an 129 * {@code ObservableValue<Boolean>} 130 * that represents whether the given item is selected or not. This 131 * {@code ObservableValue<Boolean>} will be bound bidirectionally 132 * (meaning that the CheckBox in the cell will set/unset this property 133 * based on user interactions, and the CheckBox will reflect the state of 134 * the {@code ObservableValue<Boolean>}, if it changes externally). 135 * @param showLabel In some cases, it may be desirable to show a label in 136 * the TableCell beside the {@link CheckBox}. By default a label is not 137 * shown, but by setting this to true the item in the cell will also 138 * have toString() called on it. If this is not the desired behavior, 139 * consider using 140 * {@link #forTableColumn(javafx.util.Callback, javafx.util.StringConverter) }, 141 * which allows for you to provide a callback that specifies the label for a 142 * given row item. 143 * @return A {@link Callback} that will return a {@link TableCell} that is 144 * able to work on the type of element contained within the TableColumn. 145 */ 146 public static <S,T> Callback<TableColumn<S,T>, TableCell<S,T>> forTableColumn( 147 final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty, 148 final boolean showLabel) { 149 StringConverter<T> converter = ! showLabel ? 150 null : CellUtils.<T>defaultStringConverter(); 151 return forTableColumn(getSelectedProperty, converter); 152 } 153 154 /** 155 * Creates a cell factory for use in a {@link TableColumn} cell factory. 156 * This method requires that the TableColumn be of type 157 * {@code ObservableValue<Boolean>}. 158 * 159 * <p>When used in a TableColumn, the CheckBoxCell is rendered with a 160 * CheckBox centered in the column. 161 * 162 * @param <T> The type of the elements contained within the {@link TableColumn} 163 * instance. 164 * @param getSelectedProperty A Callback that, given an object of type 165 * {@code TableColumn<S,T>}, will return an 166 * {@code ObservableValue<Boolean>} that represents whether the given 167 * item is selected or not. This {@code ObservableValue<Boolean>} will 168 * be bound bidirectionally (meaning that the CheckBox in the cell will 169 * set/unset this property based on user interactions, and the CheckBox 170 * will reflect the state of the {@code ObservableValue<Boolean>}, if 171 * it changes externally). 172 * @param converter A StringConverter that, give an object of type T, will return a 173 * String that can be used to represent the object visually. The default 174 * implementation in {@link #forTableColumn(Callback, boolean)} (when 175 * showLabel is true) is to simply call .toString() on all non-null 176 * items (and to just return an empty string in cases where the given 177 * item is null). 178 * @return A {@link Callback} that will return a {@link TableCell} that is 179 * able to work on the type of element contained within the TableColumn. 180 */ 181 public static <S,T> Callback<TableColumn<S,T>, TableCell<S,T>> forTableColumn( 182 final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty, 183 final StringConverter<T> converter) { 184 return new Callback<TableColumn<S,T>, TableCell<S,T>>() { 185 @Override public TableCell<S,T> call(TableColumn<S,T> list) { 186 return new CheckBoxTableCell<S,T>(getSelectedProperty, converter); 187 } 188 }; 189 } 190 191 192 193 /*************************************************************************** 194 * * 195 * Fields * 196 * * 197 **************************************************************************/ 198 private final CheckBox checkBox; 199 200 private boolean showLabel; 201 202 private ObservableValue<Boolean> booleanProperty; 203 204 205 206 /*************************************************************************** 207 * * 208 * Constructors * 209 * * 210 **************************************************************************/ 211 212 /** 213 * Creates a default CheckBoxTableCell. 214 */ 215 public CheckBoxTableCell() { 216 this(null, null); 217 } 218 219 /** 220 * Creates a default CheckBoxTableCell with a custom {@link Callback} to 221 * retrieve an ObservableValue for a given cell index. 222 * 223 * @param getSelectedProperty A {@link Callback} that will return an {@link 224 * ObservableValue} given an index from the TableColumn. 225 */ 226 public CheckBoxTableCell( 227 final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty) { 228 this(getSelectedProperty, null); 229 } 230 231 /** 232 * Creates a CheckBoxTableCell with a custom string converter. 233 * 234 * @param getSelectedProperty A {@link Callback} that will return a {@link 235 * ObservableValue} given an index from the TableColumn. 236 * @param converter A StringConverter that, given an object of type T, will return a 237 * String that can be used to represent the object visually. 238 */ 239 public CheckBoxTableCell( 240 final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty, 241 final StringConverter<T> converter) { 242 // we let getSelectedProperty be null here, as we can always defer to the 243 // TableColumn 244 this.getStyleClass().add("check-box-table-cell"); 245 246 this.checkBox = new CheckBox(); 247 setGraphic(checkBox); 248 249 setSelectedStateCallback(getSelectedProperty); 250 setConverter(converter); 251 252// // alignment is styleable through css. Calling setAlignment 253// // makes it look to css like the user set the value and css will not 254// // override. Initializing alignment by calling set on the 255// // CssMetaData ensures that css will be able to override the value. 256// final CssMetaData prop = CssMetaData.getCssMetaData(alignmentProperty()); 257// prop.set(this, Pos.CENTER); 258 259 260 } 261 262 263 /*************************************************************************** 264 * * 265 * Properties * 266 * * 267 **************************************************************************/ 268 269 // --- converter 270 private ObjectProperty<StringConverter<T>> converter = 271 new SimpleObjectProperty<StringConverter<T>>(this, "converter") { 272 protected void invalidated() { 273 updateShowLabel(); 274 } 275 }; 276 277 /** 278 * The {@link StringConverter} property. 279 */ 280 public final ObjectProperty<StringConverter<T>> converterProperty() { 281 return converter; 282 } 283 284 /** 285 * Sets the {@link StringConverter} to be used in this cell. 286 */ 287 public final void setConverter(StringConverter<T> value) { 288 converterProperty().set(value); 289 } 290 291 /** 292 * Returns the {@link StringConverter} used in this cell. 293 */ 294 public final StringConverter<T> getConverter() { 295 return converterProperty().get(); 296 } 297 298 299 300 // --- selected state callback property 301 private ObjectProperty<Callback<Integer, ObservableValue<Boolean>>> 302 selectedStateCallback = 303 new SimpleObjectProperty<Callback<Integer, ObservableValue<Boolean>>>( 304 this, "selectedStateCallback"); 305 306 /** 307 * Property representing the {@link Callback} that is bound to by the 308 * CheckBox shown on screen. 309 */ 310 public final ObjectProperty<Callback<Integer, ObservableValue<Boolean>>> selectedStateCallbackProperty() { 311 return selectedStateCallback; 312 } 313 314 /** 315 * Sets the {@link Callback} that is bound to by the CheckBox shown on screen. 316 */ 317 public final void setSelectedStateCallback(Callback<Integer, ObservableValue<Boolean>> value) { 318 selectedStateCallbackProperty().set(value); 319 } 320 321 /** 322 * Returns the {@link Callback} that is bound to by the CheckBox shown on screen. 323 */ 324 public final Callback<Integer, ObservableValue<Boolean>> getSelectedStateCallback() { 325 return selectedStateCallbackProperty().get(); 326 } 327 328 329 330 /*************************************************************************** 331 * * 332 * Public API * 333 * * 334 **************************************************************************/ 335 336 /** {@inheritDoc} */ 337 @Override public void updateItem(T item, boolean empty) { 338 super.updateItem(item, empty); 339 340 if (empty) { 341 setText(null); 342 setGraphic(null); 343 } else { 344 StringConverter c = getConverter(); 345 346 if (showLabel) { 347 setText(c.toString(item)); 348 } 349 setGraphic(checkBox); 350 351 if (booleanProperty instanceof BooleanProperty) { 352 checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty); 353 } 354 ObservableValue obsValue = getSelectedProperty(); 355 if (obsValue instanceof BooleanProperty) { 356 booleanProperty = obsValue; 357 checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty); 358 } 359 360 checkBox.disableProperty().bind(Bindings.not( 361 getTableView().editableProperty().and( 362 getTableColumn().editableProperty()).and( 363 editableProperty()) 364 )); 365 } 366 } 367 368 369 370 /*************************************************************************** 371 * * 372 * Private implementation * 373 * * 374 **************************************************************************/ 375 376 private void updateShowLabel() { 377 this.showLabel = converter != null; 378 this.checkBox.setAlignment(showLabel ? Pos.CENTER_LEFT : Pos.CENTER); 379 } 380 381 private ObservableValue getSelectedProperty() { 382 return getSelectedStateCallback() != null ? 383 getSelectedStateCallback().call(getIndex()) : 384 getTableColumn().getCellObservableValue(getIndex()); 385 } 386}