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