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