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<T> 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<T> 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<T> is sought. 692 * @return An ObservableValue<T> for this specific table column. 693 */ 694 public abstract ObservableValue<T> getCellObservableValue(int index); 695 696 /** 697 * Attempts to return an ObservableValue<T> 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<T> 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<T> is 710 * sought. 711 * @return An ObservableValue<T> 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 }