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}