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;
027
028import java.text.Collator;
029import java.util.Collections;
030import java.util.Comparator;
031
032import javafx.beans.property.BooleanProperty;
033import javafx.beans.property.DoubleProperty;
034import javafx.beans.property.ObjectProperty;
035import javafx.beans.property.SimpleBooleanProperty;
036import javafx.beans.property.SimpleObjectProperty;
037import javafx.beans.property.SimpleStringProperty;
038import javafx.beans.property.StringProperty;
039import javafx.collections.FXCollections;
040import javafx.collections.ObservableList;
041import javafx.collections.ObservableSet;
042import javafx.css.PseudoClass;
043import javafx.css.Styleable;
044import javafx.event.Event;
045import javafx.event.EventDispatchChain;
046import javafx.event.EventHandler;
047import javafx.event.EventTarget;
048import javafx.event.EventType;
049import javafx.scene.Node;
050
051import com.sun.javafx.scene.control.skin.Utils;
052import com.sun.javafx.event.EventHandlerManager;
053import java.util.HashMap;
054
055import javafx.beans.property.ReadOnlyDoubleProperty;
056import javafx.beans.property.ReadOnlyDoubleWrapper;
057import javafx.beans.property.ReadOnlyObjectProperty;
058import javafx.beans.property.ReadOnlyObjectWrapper;
059import javafx.beans.property.SimpleDoubleProperty;
060import javafx.beans.value.ObservableValue;
061import javafx.collections.ObservableMap;
062
063/**
064 * Table-like controls (such as {@link TableView} and {@link TreeTableView}) are
065 * made up of zero or more instances of a concrete TableColumnBase subclass 
066 * ({@link TableColumn} and {@link TreeTableColumn}, respectively). Each
067 * table column in a table is responsible for displaying (and editing) the contents
068 * of that column. As well as being responsible for displaying and editing data 
069 * for a single column, a table column also contains the necessary properties to:
070 * <ul>
071 *    <li>Be resized (using {@link #minWidthProperty() minWidth}/{@link #prefWidthProperty() prefWidth}/{@link #maxWidthProperty() maxWidth}
072 *      and {@link #widthProperty() width} properties)
073 *    <li>Have its {@link #visibleProperty() visibility} toggled
074 *    <li>Display {@link #textProperty() header text}
075 *    <li>Display any {@link #getColumns() nested columns} it may contain
076 *    <li>Have a {@link #contextMenuProperty() context menu} when the user 
077 *      right-clicks the column header area
078 *    <li>Have the contents of the table be sorted (using 
079 *      {@link #comparatorProperty() comparator}, {@link #sortable sortable} and
080 *      {@link #sortTypeProperty() sortType})
081 * </ul>
082 * </p>
083 * 
084 * When instantiating a concrete subclass of TableColumnBase, perhaps the two 
085 * most important properties to set are the column {@link #textProperty() text} 
086 * (what to show in the column header area), and the column 
087 * {@code cell value factory} (which is used to populate individual cells in the 
088 * column). Refer to the class documentation for {@link TableColumn} and 
089 * {@link TreeTableColumn} for more information.
090 * 
091 * @param <S> The type of the UI control (e.g. the type of the 'row').
092 * @param <T> The type of the content in all cells in this table column.
093 * @see TableColumn
094 * @see TreeTableColumn
095 * @see TablePositionBase
096 */
097public abstract class TableColumnBase<S,T> implements EventTarget, Styleable {
098    
099    /***************************************************************************
100     *                                                                         *
101     * Static properties and methods                                           *
102     *                                                                         *
103     **************************************************************************/
104    
105    // NOTE: If these numbers change, update the copy of this value in TableColumnHeader
106    static final double DEFAULT_WIDTH = 80.0F;
107    static final double DEFAULT_MIN_WIDTH = 10.0F;
108    static final double DEFAULT_MAX_WIDTH = 5000.0F;
109    
110    /**
111     * By default all columns will use this comparator to perform sorting. This
112     * comparator simply performs null checks, and checks if the object is 
113     * {@link Comparable}. If it is, the {@link Comparable#compareTo(java.lang.Object)}
114     * method is called, otherwise this method will defer to
115     * {@link Collator#compare(java.lang.String, java.lang.String)}.
116     */
117    public static final Comparator DEFAULT_COMPARATOR = new Comparator() {
118        @Override public int compare(Object obj1, Object obj2) {
119            if (obj1 == null && obj2 == null) return 0;
120            if (obj1 == null) return -1;
121            if (obj2 == null) return 1;
122            
123            if (obj1 instanceof Comparable && (obj1.getClass() == obj2.getClass() || obj1.getClass().isAssignableFrom(obj2.getClass()))) {
124                return ((Comparable)obj1).compareTo(obj2);
125            }
126
127            return Collator.getInstance().compare(obj1.toString(), obj2.toString());
128        }
129    };
130    
131    
132    
133    /***************************************************************************
134     *                                                                         *
135     * Constructors                                                            *
136     *                                                                         *
137     **************************************************************************/    
138
139    /**
140     * Creates a default TableColumn with default cell factory, comparator, and
141     * onEditCommit implementation.
142     */
143    protected TableColumnBase() {
144        this("");
145    }
146
147    /**
148     * Creates a TableColumn with the text set to the provided string, with
149     * default cell factory, comparator, and onEditCommit implementation.
150     * @param text The string to show when the TableColumn is placed within the TableView.
151     */
152    protected TableColumnBase(String text) {
153        setText(text);
154    }
155    
156    
157    
158    /***************************************************************************
159     *                                                                         *
160     * Listeners                                                               *
161     *                                                                         *
162     **************************************************************************/
163    
164    
165    
166    /***************************************************************************
167     *                                                                         *
168     * Instance Variables                                                      *
169     *                                                                         *
170     **************************************************************************/
171    
172    final EventHandlerManager eventHandlerManager = new EventHandlerManager(this);
173    
174    
175    
176    /***************************************************************************
177     *                                                                         *
178     * Properties                                                              *
179     *                                                                         *
180     **************************************************************************/
181    
182    
183    // --- Text
184    /**
185     * This is the text to show in the header for this column.
186     */
187    private StringProperty text = new SimpleStringProperty(this, "text", "");
188    public final StringProperty textProperty() { return text; }
189    public final void setText(String value) { text.set(value); }
190    public final String getText() { return text.get(); }
191    
192    
193    // --- Visible
194    /**
195     * Toggling this will immediately toggle the visibility of this column,
196     * and all children columns.
197     */
198    private BooleanProperty visible = new SimpleBooleanProperty(this, "visible", true) {
199        @Override protected void invalidated() {
200            // set all children columns to be the same visibility. This isn't ideal,
201            // for example if a child column is hidden, then the parent hidden and
202            // shown, all columns will be visible again.
203            //
204            // TODO It may make sense for us to cache the visibility so that we may
205            // return to exactly the same state.
206            // set all children columns to be the same visibility. This isn't ideal,
207            // for example if a child column is hidden, then the parent hidden and
208            // shown, all columns will be visible again.
209            //
210            // TODO It may make sense for us to cache the visibility so that we may
211            // return to exactly the same state.
212            for (TableColumnBase<S,?> col : getColumns()) {
213                col.setVisible(isVisible());
214            }
215        }
216    };
217    public final void setVisible(boolean value) { visibleProperty().set(value); }
218    public final boolean isVisible() { return visible.get(); }
219    public final BooleanProperty visibleProperty() { return visible; }
220    
221    
222    // --- Parent Column
223    /**
224     * This read-only property will always refer to the parent of this column,
225     * in the situation where nested columns are being used. To create a nested
226     * column is simply a matter of placing TableColumnBase instances inside the
227     * {@link #columns} ObservableList of a TableColumnBase.
228     */
229    private ReadOnlyObjectWrapper<TableColumnBase<S,?>> parentColumn;
230    void setParentColumn(TableColumnBase<S,?> value) { parentColumnPropertyImpl().set(value); }
231    public final TableColumnBase<S,?> getParentColumn() {
232        return parentColumn == null ? null : parentColumn.get();
233    }
234
235    public final ReadOnlyObjectProperty<TableColumnBase<S,?>> parentColumnProperty() {
236        return parentColumnPropertyImpl().getReadOnlyProperty();
237    }
238
239    private ReadOnlyObjectWrapper<TableColumnBase<S,?>> parentColumnPropertyImpl() {
240        if (parentColumn == null) {
241            parentColumn = new ReadOnlyObjectWrapper<TableColumnBase<S,?>>(this, "parentColumn");
242        }
243        return parentColumn;
244    }
245    
246    
247    // --- Menu
248    /**
249     * This menu will be shown whenever the user right clicks within the header
250     * area of this TableColumnBase.
251     */
252    private ObjectProperty<ContextMenu> contextMenu;
253    public final void setContextMenu(ContextMenu value) { contextMenuProperty().set(value); }
254    public final ContextMenu getContextMenu() { return contextMenu == null ? null : contextMenu.get(); }
255    public final ObjectProperty<ContextMenu> contextMenuProperty() {
256        if (contextMenu == null) {
257            contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu");
258        }
259        return contextMenu;
260    }
261    
262    
263    // --- Id
264    /**
265     * The id of this TableColumnBase. This simple string identifier is useful 
266     * for finding a specific TableColumnBase within a UI control that uses 
267     * TableColumnBase instances. The default value is {@code null}.
268     * @since 2.2
269     */
270    private StringProperty id;
271    public final void setId(String value) { idProperty().set(value); }
272    @Override public final String getId() { return id == null ? null : id.get(); }
273    public final StringProperty idProperty() {
274        if (id == null) {
275            id = new SimpleStringProperty(this, "id");
276        }
277        return id;
278    }
279
280    
281    // --- style
282    /**
283     * A string representation of the CSS style associated with this
284     * TableColumnBase instance. This is analogous to the "style" attribute of an
285     * HTML element. Note that, like the HTML style attribute, this
286     * variable contains style properties and values and not the
287     * selector portion of a style rule.
288     * <p>
289     * Parsing this style might not be supported on some limited
290     * platforms. It is recommended to use a standalone CSS file instead.
291     * @since 2.2
292     */
293    private StringProperty style;
294    public final void setStyle(String value) { styleProperty().set(value); }
295    @Override public final String getStyle() { return style == null ? null : style.get(); }
296    public final StringProperty styleProperty() {
297        if (style == null) {
298            style = new SimpleStringProperty(this, "style");
299        }
300        return style;
301    }
302    
303    
304    // --- Style class
305    private final ObservableList<String> styleClass = FXCollections.observableArrayList();
306    /**
307     * A list of String identifiers which can be used to logically group
308     * Nodes, specifically for an external style engine. This variable is
309     * analogous to the "class" attribute on an HTML element and, as such,
310     * each element of the list is a style class to which this Node belongs.
311     *
312     * @see <a href="http://www.w3.org/TR/css3-selectors/#class-html">CSS3 class selectors</a>
313     * @since 2.2
314     */
315    @Override public ObservableList<String> getStyleClass() {
316        return styleClass;
317    }
318    
319    
320    // --- Graphic
321    /**
322     * <p>The graphic to show in the table column to allow the user to
323     * indicate graphically what is in the column. </p>
324     * @since 2.2
325     */
326    private ObjectProperty<Node> graphic;
327    public final void setGraphic(Node value) {
328        graphicProperty().set(value);
329    }
330    public final Node getGraphic() {
331        return graphic == null ? null : graphic.get();
332    }
333    public final ObjectProperty<Node> graphicProperty() {
334        if (graphic == null) {
335            graphic = new SimpleObjectProperty<Node>(this, "graphic");
336        }
337        return graphic;
338    }
339    
340    
341    // --- Sort node
342    /**
343     * <p>The node to use as the "sort arrow", shown to the user in situations where
344     * the table column is part of the sort order. It may be the only item in
345     * the sort order, or it may be a secondary, tertiary, or latter sort item, 
346     * and the node should reflect this visually. This is only used in the case of
347     * the table column being in the sort order (refer to, for example, 
348     * {@link TableView#getSortOrder()} and {@link TreeTableView#getSortOrder()}).
349     * If not specified, the table column skin implementation is responsible for 
350     * providing a default sort node.
351     * 
352     * <p>The sort node is commonly seen represented as a triangle that rotates
353     * on screen to indicate whether the table column is part of the sort order, 
354     * and if so, whether the sort is ascending or descending, and what position in 
355     * the sort order it is in.
356     * 
357     * @since 2.2
358     */
359    private ObjectProperty<Node> sortNode = new SimpleObjectProperty<Node>(this, "sortNode");
360    public final void setSortNode(Node value) { sortNodeProperty().set(value); }
361    public final Node getSortNode() { return sortNode.get(); }
362    public final ObjectProperty<Node> sortNodeProperty() { return sortNode; }
363    
364    
365    // --- Width
366    /**
367     * The width of this column. Modifying this will result in the column width
368     * adjusting visually. It is recommended to not bind this property to an
369     * external property, as that will result in the column width not being
370     * adjustable by the user through dragging the left and right borders of
371     * column headers.
372     */
373    public final ReadOnlyDoubleProperty widthProperty() { return width.getReadOnlyProperty(); }
374    public final double getWidth() { return width.get(); }
375    void setWidth(double value) { width.set(value); }
376    private ReadOnlyDoubleWrapper width = new ReadOnlyDoubleWrapper(this, "width", DEFAULT_WIDTH);
377    
378    
379    // --- Minimum Width
380    /**
381     * The minimum width the table column is permitted to be resized to.
382     */
383    private DoubleProperty minWidth;
384    public final void setMinWidth(double value) { minWidthProperty().set(value); }
385    public final double getMinWidth() { return minWidth == null ? DEFAULT_MIN_WIDTH : minWidth.get(); }
386    public final DoubleProperty minWidthProperty() {
387        if (minWidth == null) {
388            minWidth = new SimpleDoubleProperty(this, "minWidth", DEFAULT_MIN_WIDTH) {
389                @Override protected void invalidated() {
390                    if (getMinWidth() < 0) {
391                        setMinWidth(0.0F);
392                    }
393
394                    impl_setWidth(getWidth());
395                }
396            };
397        }
398        return minWidth;
399    }
400    
401    
402    // --- Preferred Width
403    /**
404     * The preferred width of the TableColumn.
405     */
406    public final DoubleProperty prefWidthProperty() { return prefWidth; }
407    public final void setPrefWidth(double value) { prefWidthProperty().set(value); }
408    public final double getPrefWidth() { return prefWidth.get(); }
409    private final DoubleProperty prefWidth = new SimpleDoubleProperty(this, "prefWidth", DEFAULT_WIDTH) {
410        @Override protected void invalidated() {
411            impl_setWidth(getPrefWidth());
412        }
413    };
414    
415    
416    // --- Maximum Width
417    // The table does not resize properly if this is set to Number.MAX_VALUE,
418    // so I've arbitrarily chosen a better, smaller number.
419    /**
420     * The maximum width the table column is permitted to be resized to.
421     */
422    public final DoubleProperty maxWidthProperty() { return maxWidth; }
423    public final void setMaxWidth(double value) { maxWidthProperty().set(value); }
424    public final double getMaxWidth() { return maxWidth.get(); }
425    private DoubleProperty maxWidth = new SimpleDoubleProperty(this, "maxWidth", DEFAULT_MAX_WIDTH) {
426        @Override protected void invalidated() {
427            impl_setWidth(getWidth());
428        }
429    };
430    
431    
432    // --- Resizable
433    /**
434     * Used to indicate whether the width of this column can change. It is up
435     * to the resizing policy to enforce this however.
436     */
437    private BooleanProperty resizable;
438    public final BooleanProperty resizableProperty() {
439        if (resizable == null) {
440            resizable = new SimpleBooleanProperty(this, "resizable", true);
441        }
442        return resizable;
443    }
444    public final void setResizable(boolean value) {
445        resizableProperty().set(value);
446    }
447    public final boolean isResizable() {
448        return resizable == null ? true : resizable.get();
449    }
450
451    
452    
453    // --- Sortable
454    /**
455     * <p>A boolean property to toggle on and off the 'sortability' of this column.
456     * When this property is true, this column can be included in sort
457     * operations. If this property is false, it will not be included in sort
458     * operations, even if it is contained within the sort order list of the
459     * underlying UI control (e.g. {@link TableView#getSortOrder()} or
460     * {@link TreeTableView#getSortOrder()}).</p>
461     *
462     * <p>For example, iIf a TableColumn instance is contained within the TableView sortOrder
463     * ObservableList, and its sortable property toggles state, it will force the
464     * TableView to perform a sort, as it is likely the view will need updating.</p>
465     */
466    private BooleanProperty sortable;
467    public final BooleanProperty sortableProperty() {
468        if (sortable == null) {
469            sortable = new SimpleBooleanProperty(this, "sortable", true);
470        }
471        return sortable;
472    }
473    public final void setSortable(boolean value) {
474        sortableProperty().set(value);
475    }
476    public final boolean isSortable() {
477        return sortable == null ? true : sortable.get();
478    }
479    
480    
481    
482    // --- Reorderable
483    private BooleanProperty reorderable;
484    /**
485     * @treatAsPrivate implementation detail
486     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
487     */
488    @Deprecated
489    public final BooleanProperty impl_reorderableProperty() {
490        if (reorderable == null) {
491            reorderable = new SimpleBooleanProperty(this, "reorderable", true);
492        }
493        return reorderable;
494    }
495    /**
496     * @treatAsPrivate implementation detail
497     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
498     */
499    @Deprecated
500    public final void impl_setReorderable(boolean value) {
501        impl_reorderableProperty().set(value);
502    }
503    /**
504     * @treatAsPrivate implementation detail
505     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
506     */
507    @Deprecated
508    public final boolean impl_isReorderable() {
509        return reorderable == null ? false : reorderable.get();
510    }
511    
512    
513    
514    // --- Comparator
515    /**
516     * Comparator function used when sorting this table column. The two Objects
517     * given as arguments are the cell data for two individual cells in this
518     * column.
519     */
520    private ObjectProperty<Comparator<T>> comparator;
521    public final ObjectProperty<Comparator<T>> comparatorProperty() {
522        if (comparator == null) {
523            comparator = new SimpleObjectProperty<Comparator<T>>(this, "comparator", DEFAULT_COMPARATOR);
524        }
525        return comparator;
526    }
527    public final void setComparator(Comparator<T> value) {
528        comparatorProperty().set(value);
529    }
530    public final Comparator<T> getComparator() {
531        return comparator == null ? DEFAULT_COMPARATOR : comparator.get();
532    }
533
534    
535    // --- Editable
536    /**
537     * Specifies whether this table column allows editing. This, unlike 
538     * {@link TableView#editableProperty()} and 
539     * {@link TreeTableView#editableProperty()}, is true by default.
540     */
541    private BooleanProperty editable;
542    public final void setEditable(boolean value) {
543        editableProperty().set(value);
544    }
545    public final boolean isEditable() {
546        return editable == null ? true : editable.get();
547    }
548    public final BooleanProperty editableProperty() {
549        if (editable == null) {
550            editable = new SimpleBooleanProperty(this, "editable", true);
551        }
552        return editable;
553    }
554    
555    
556    // --- Properties
557    private static final Object USER_DATA_KEY = new Object();
558    
559    // A map containing a set of properties for this TableColumn
560    private ObservableMap<Object, Object> properties;
561
562    /**
563      * Returns an observable map of properties on this table column for use 
564      * primarily by application developers.
565      *
566      * @return an observable map of properties on this table column for use 
567      * primarily by application developers
568     * @since 2.2
569     */
570    public final ObservableMap<Object, Object> getProperties() {
571        if (properties == null) {
572            properties = FXCollections.observableMap(new HashMap<Object, Object>());
573        }
574        return properties;
575    }
576    
577    /**
578     * Tests if this table column has properties.
579     * @return true if node has properties.
580     * @since 2.2
581     */
582    public boolean hasProperties() {
583        return properties != null && ! properties.isEmpty();
584    }
585
586     
587    // --- UserData
588    /**
589     * Convenience method for setting a single Object property that can be
590     * retrieved at a later date. This is functionally equivalent to calling
591     * the getProperties().put(Object key, Object value) method. This can later
592     * be retrieved by calling {@link TableColumnBase#getUserData()}.
593     *
594     * @param value The value to be stored - this can later be retrieved by calling
595     *          {@link TableColumnBase#getUserData()}.
596     * @since 2.2
597     */
598    public void setUserData(Object value) {
599        getProperties().put(USER_DATA_KEY, value);
600    }
601
602    /**
603     * Returns a previously set Object property, or null if no such property
604     * has been set using the {@link TableColumnBase#setUserData(java.lang.Object)} method.
605     *
606     * @return The Object that was previously set, or null if no property
607     *          has been set or if null was set.
608     * @since 2.2
609     */
610    public Object getUserData() {
611        return getProperties().get(USER_DATA_KEY);
612    }
613    
614    
615    /***************************************************************************
616     *                                                                         *
617     * Public API                                                              *
618     *                                                                         *
619     **************************************************************************/    
620    
621    /**
622     * This enables support for nested columns, which can be useful to group
623     * together related data. For example, we may have a 'Name' column with
624     * two nested columns for 'First' and 'Last' names.
625     *
626     * <p>This has no impact on the table as such - all column indices point to the
627     * leaf columns only, and it isn't possible to sort using the parent column,
628     * just the leaf columns. In other words, this is purely a visual feature.</p>
629     *
630     * @return An ObservableList containing TableColumnBase instances (or subclasses)
631     *      that are the children of this TableColumnBase. If these children 
632     *      TableColumnBase instances are set as visible, they will appear 
633     *      beneath this table column.
634     */
635    public abstract ObservableList<? extends TableColumnBase<S,?>> getColumns();
636    
637    /**
638     * Returns the actual value for a cell at a given row index (and which 
639     * belongs to this table column).
640     * 
641     * @param index The row index for which the data is required.
642     * @return The data that belongs to the cell at the intersection of the given
643     *      row index and the table column that this method is called on.
644     */
645    public final T getCellData(final int index) {
646        ObservableValue<T> result = getCellObservableValue(index);
647        return result == null ? null : result.getValue();
648    }
649
650    /**
651     * Returns the actual value for a cell from the given item.
652     * 
653     * @param item The item from which a value of type T should be extracted.
654     * @return The data that should be used in a specific cell in this 
655     *      column, based on the item passed in as an argument.
656     */
657    public final T getCellData(final S item) {
658        ObservableValue<T> result = getCellObservableValue(item);
659        return result == null ? null : result.getValue();
660    }
661
662    /**
663     * Attempts to return an ObservableValue&lt;T&gt; for the item in the given
664     * index (which is of type S). In other words, this method expects to receive
665     * an integer value that is greater than or equal to zero, and less than the
666     * size of the underlying data model. If the index is
667     * valid, this method will return an ObservableValue&lt;T&gt; for this 
668     * specific column.
669     * 
670     * <p>This is achieved by calling the {@code cell value factory}, and 
671     * returning whatever it returns when passed a {@code CellDataFeatures} (see,
672     * for example, the CellDataFeatures classes belonging to 
673     * {@link TableColumn.CellDataFeatures TableColumn} and 
674     * {@link TreeTableColumn.CellDataFeatures TreeTableColumn} for more 
675     * information).
676     * 
677     * @param index The index of the item (of type S) for which an 
678     *      ObservableValue&lt;T&gt; is sought.
679     * @return An ObservableValue&lt;T&gt; for this specific table column.
680     */
681    public abstract ObservableValue<T> getCellObservableValue(int index);
682    
683    /**
684     * Attempts to return an ObservableValue&lt;T&gt; for the given item (which
685     * is of type S). In other words, this method expects to receive an object from
686     * the underlying data model for the entire 'row' in the table, and it must 
687     * return an ObservableValue&lt;T&gt; for the value in this specific column.
688     * 
689     * <p>This is achieved by calling the {@code cell value factory}, and 
690     * returning whatever it returns when passed a {@code CellDataFeatures} (see,
691     * for example, the CellDataFeatures classes belonging to 
692     * {@link TableColumn.CellDataFeatures TableColumn} and 
693     * {@link TreeTableColumn.CellDataFeatures TreeTableColumn} for more 
694     * information).
695     * 
696     * @param item The item (of type S) for which an ObservableValue&lt;T&gt; is
697     *      sought.
698     * @return An ObservableValue&lt;T&gt; for this specific table column.
699     */
700    public abstract ObservableValue<T> getCellObservableValue(S item);
701
702    /** {@inheritDoc} */
703    @Override public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
704        return tail.prepend(eventHandlerManager);
705    }
706
707    /**
708     * Registers an event handler to this table column. The TableColumnBase class allows 
709     * registration of listeners which will be notified when editing occurs.
710     * Note however that TableColumnBase is <b>not</b> a Node, and therefore no visual
711     * events will be fired on it.
712     *
713     * @param eventType the type of the events to receive by the handler
714     * @param eventHandler the handler to register
715     * @throws NullPointerException if the event type or handler is null
716     */
717    public <E extends Event> void addEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
718        eventHandlerManager.addEventHandler(eventType, eventHandler);
719    }
720    
721    /**
722     * Unregisters a previously registered event handler from this table column. One
723     * handler might have been registered for different event types, so the
724     * caller needs to specify the particular event type from which to
725     * unregister the handler.
726     *
727     * @param eventType the event type from which to unregister
728     * @param eventHandler the handler to unregister
729     * @throws NullPointerException if the event type or handler is null
730     */
731    public <E extends Event> void removeEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
732        eventHandlerManager.removeEventHandler(eventType, eventHandler);
733    }
734
735    
736    
737    /***************************************************************************
738     *                                                                         *
739     * Private Implementation                                                  *
740     *                                                                         *
741     **************************************************************************/        
742
743    /**
744     * @treatAsPrivate implementation detail
745     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
746     */
747    @Deprecated
748    public void impl_setWidth(double width) {
749        setWidth(Utils.boundedSize(width, getMinWidth(), getMaxWidth()));
750    }
751
752    void updateColumnWidths() {
753        if (! getColumns().isEmpty()) {
754            // zero out the width and min width values, and iterate to 
755            // ensure the new value is equal to the sum of all children
756            // columns
757            double _minWidth = 0.0f;
758            double _prefWidth = 0.0f;
759            double _maxWidth = 0.0f;
760
761            for (TableColumnBase col : getColumns()) {
762                col.setParentColumn(this);
763
764                _minWidth += col.getMinWidth();
765                _prefWidth += col.getPrefWidth();
766                _maxWidth += col.getMaxWidth();
767            }
768
769            setMinWidth(_minWidth);
770            setPrefWidth(_prefWidth);
771            setMaxWidth(_maxWidth);
772        }
773    }
774
775    
776    /***************************************************************************
777     *                                                                         *
778     * Stylesheet Handling                                                     *
779     *                                                                         *
780     **************************************************************************/
781
782    /**
783     * {@inheritDoc}
784     */
785    public final ObservableSet<PseudoClass> getPseudoClassStates() {
786        return FXCollections.emptyObservableSet();
787    }
788
789    
790    
791    /***************************************************************************
792     *                                                                         *
793     * Support Interfaces                                                      *
794     *                                                                         *
795     **************************************************************************/
796
797}