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