Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.control; 027 028import com.sun.javafx.scene.control.skin.NestedTableColumnHeader; 029import com.sun.javafx.scene.control.skin.TableColumnHeader; 030import com.sun.javafx.scene.control.skin.TableHeaderRow; 031import com.sun.javafx.scene.control.skin.TableViewSkin; 032import com.sun.javafx.scene.control.skin.TableViewSkinBase; 033 034import javafx.beans.property.ObjectProperty; 035import javafx.beans.property.SimpleObjectProperty; 036import javafx.collections.FXCollections; 037import javafx.collections.ListChangeListener; 038import javafx.collections.ObservableList; 039import javafx.css.CssMetaData; 040import javafx.css.Styleable; 041import javafx.event.Event; 042import javafx.event.EventHandler; 043import javafx.event.EventTarget; 044import javafx.event.EventType; 045import javafx.scene.Node; 046import javafx.util.Callback; 047 048import javafx.collections.WeakListChangeListener; 049import java.util.Collections; 050 051import java.util.List; 052import java.util.Map; 053import javafx.beans.InvalidationListener; 054import javafx.beans.Observable; 055import javafx.beans.property.ReadOnlyObjectProperty; 056import javafx.beans.property.ReadOnlyObjectWrapper; 057import javafx.beans.value.ObservableValue; 058import javafx.beans.value.WritableValue; 059 060/** 061 * A {@link TableView} is made up of a number of TableColumn instances. Each 062 * TableColumn in a table is responsible for displaying (and editing) the contents 063 * of that column. As well as being responsible for displaying and editing data 064 * for a single column, a TableColumn also contains the necessary properties to: 065 * <ul> 066 * <li>Be resized (using {@link #minWidthProperty() minWidth}/{@link #prefWidthProperty() prefWidth}/{@link #maxWidthProperty() maxWidth} 067 * and {@link #widthProperty() width} properties) 068 * <li>Have its {@link #visibleProperty() visibility} toggled 069 * <li>Display {@link #textProperty() header text} 070 * <li>Display any {@link #getColumns() nested columns} it may contain 071 * <li>Have a {@link #contextMenuProperty() context menu} when the user 072 * right-clicks the column header area 073 * <li>Have the contents of the table be sorted (using 074 * {@link #comparatorProperty() comparator}, {@link #sortable sortable} and 075 * {@link #sortTypeProperty() sortType}) 076 * </ul> 077 * </p> 078 * 079 * When creating a TableColumn instance, perhaps the two most important properties 080 * to set are the column {@link #textProperty() text} (what to show in the column 081 * header area), and the column {@link #cellValueFactory cell value factory} 082 * (which is used to populate individual cells in the column). This can be 083 * achieved using some variation on the following code: 084 * 085 * <pre> 086 * {@code 087 * ObservableList<Person> data = ... 088 * TableView<Person> tableView = new TableView<Person>(data); 089 * 090 * TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name"); 091 * firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() { 092 * public ObservableValue<String> call(CellDataFeatures<Person, String> p) { 093 * // p.getValue() returns the Person instance for a particular TableView row 094 * return p.getValue().firstNameProperty(); 095 * } 096 * }); 097 * } 098 * tableView.getColumns().add(firstNameCol);}</pre> 099 * 100 * This approach assumes that the object returned from <code>p.getValue()</code> 101 * has a JavaFX {@link ObservableValue} that can simply be returned. The benefit of this 102 * is that the TableView will internally create bindings to ensure that, 103 * should the returned {@link ObservableValue} change, the cell contents will be 104 * automatically refreshed. 105 * 106 * <p>In situations where a TableColumn must interact with classes created before 107 * JavaFX, or that generally do not wish to use JavaFX apis for properties, it is 108 * possible to wrap the returned value in a {@link ReadOnlyObjectWrapper} instance. For 109 * example: 110 * 111 * <pre> 112 * {@code 113 * firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() { 114 * public ObservableValue<String> call(CellDataFeatures<Person, String> p) { 115 * return new ReadOnlyObjectWrapper(p.getValue().getFirstName()); 116 * } 117 * });}</pre> 118 * 119 * It is hoped that over time there will be convenience cell value factories 120 * developed and made available to developers. As of the JavaFX 2.0 release, 121 * there is one such convenience class: {@link PropertyValueFactory}. This class 122 * removes the need to write the code above, instead relying on reflection to 123 * look up a given property from a String. Refer to the 124 * <code>PropertyValueFactory</code> class documentation for more information 125 * on how to use this with a TableColumn. 126 * 127 * Finally, for more detail on how to use TableColumn, there is further documentation in 128 * the {@link TableView} class documentation. 129 * 130 * @param <S> The type of the TableView generic type (i.e. S == TableView<S>) 131 * @param <T> The type of the content in all cells in this TableColumn. 132 * @see TableView 133 * @see TableCell 134 * @see TablePosition 135 */ 136public class TableColumn<S,T> extends TableColumnBase<S,T> implements EventTarget { 137 138 /*************************************************************************** 139 * * 140 * Static properties and methods * 141 * * 142 **************************************************************************/ 143 144 /** 145 * Parent event for any TableColumn edit event. 146 */ 147 @SuppressWarnings("unchecked") 148 public static <S,T> EventType<CellEditEvent<S,T>> editAnyEvent() { 149 return (EventType<CellEditEvent<S,T>>) EDIT_ANY_EVENT; 150 } 151 private static final EventType<?> EDIT_ANY_EVENT = 152 new EventType(Event.ANY, "TABLE_COLUMN_EDIT"); 153 154 /** 155 * Indicates that the user has performed some interaction to start an edit 156 * event, or alternatively the {@link TableView#edit(int, javafx.scene.control.TableColumn)} 157 * method has been called. 158 */ 159 @SuppressWarnings("unchecked") 160 public static <S,T> EventType<CellEditEvent<S,T>> editStartEvent() { 161 return (EventType<CellEditEvent<S,T>>) EDIT_START_EVENT; 162 } 163 private static final EventType<?> EDIT_START_EVENT = 164 new EventType(editAnyEvent(), "EDIT_START"); 165 166 /** 167 * Indicates that the editing has been canceled, meaning that no change should 168 * be made to the backing data source. 169 */ 170 @SuppressWarnings("unchecked") 171 public static <S,T> EventType<CellEditEvent<S,T>> editCancelEvent() { 172 return (EventType<CellEditEvent<S,T>>) EDIT_CANCEL_EVENT; 173 } 174 private static final EventType<?> EDIT_CANCEL_EVENT = 175 new EventType(editAnyEvent(), "EDIT_CANCEL"); 176 177 /** 178 * Indicates that the editing has been committed by the user, meaning that 179 * a change should be made to the backing data source to reflect the new 180 * data. 181 */ 182 @SuppressWarnings("unchecked") 183 public static <S,T> EventType<CellEditEvent<S,T>> editCommitEvent() { 184 return (EventType<CellEditEvent<S,T>>) EDIT_COMMIT_EVENT; 185 } 186 private static final EventType<?> EDIT_COMMIT_EVENT = 187 new EventType(editAnyEvent(), "EDIT_COMMIT"); 188 189 190 191 /** 192 * If no cellFactory is specified on a TableColumn instance, then this one 193 * will be used by default. At present it simply renders the TableCell item 194 * property within the {@link TableCell#graphicProperty() graphic} property 195 * if the {@link Cell#item item} is a Node, or it simply calls 196 * <code>toString()</code> if it is not null, setting the resulting string 197 * inside the {@link Cell#textProperty() text} property. 198 */ 199 public static final Callback<TableColumn<?,?>, TableCell<?,?>> DEFAULT_CELL_FACTORY = 200 new Callback<TableColumn<?,?>, TableCell<?,?>>() { 201 202 @Override public TableCell<?,?> call(TableColumn<?,?> param) { 203 return new TableCell<Object,Object>() { 204 @Override protected void updateItem(Object item, boolean empty) { 205 if (item == getItem()) return; 206 207 super.updateItem(item, empty); 208 209 if (item == null) { 210 super.setText(null); 211 super.setGraphic(null); 212 } else if (item instanceof Node) { 213 super.setText(null); 214 super.setGraphic((Node)item); 215 } else { 216 super.setText(item.toString()); 217 super.setGraphic(null); 218 } 219 } 220 }; 221 } 222 }; 223 224 225 226 /*************************************************************************** 227 * * 228 * Constructors * 229 * * 230 **************************************************************************/ 231 232 /** 233 * Creates a default TableColumn with default cell factory, comparator, and 234 * onEditCommit implementation. 235 */ 236 public TableColumn() { 237 getStyleClass().add(DEFAULT_STYLE_CLASS); 238 239 setOnEditCommit(DEFAULT_EDIT_COMMIT_HANDLER); 240 241 // we listen to the columns list here to ensure that widths are 242 // maintained properly, and to also set the column hierarchy such that 243 // all children columns know that this TableColumn is their parent. 244 getColumns().addListener(weakColumnsListener); 245 246 tableViewProperty().addListener(new InvalidationListener() { 247 @Override public void invalidated(Observable observable) { 248 // set all children of this tableView to have the same TableView 249 // as this column 250 for (TableColumn<S, ?> tc : getColumns()) { 251 tc.setTableView(getTableView()); 252 } 253 254 // This code was commented out due to RT-22391, with this enabled 255 // the parent column will be null, which is not desired 256// // set the parent of this column to also have this tableView 257// if (getParentColumn() != null) { 258// getParentColumn().setTableView(getTableView()); 259// } 260 } 261 }); 262 } 263 264 /** 265 * Creates a TableColumn with the text set to the provided string, with 266 * default cell factory, comparator, and onEditCommit implementation. 267 * @param text The string to show when the TableColumn is placed within the TableView. 268 */ 269 public TableColumn(String text) { 270 this(); 271 setText(text); 272 } 273 274 275 276 /*************************************************************************** 277 * * 278 * Listeners * 279 * * 280 **************************************************************************/ 281 282 private EventHandler<CellEditEvent<S,T>> DEFAULT_EDIT_COMMIT_HANDLER = new EventHandler<CellEditEvent<S,T>>() { 283 @Override public void handle(CellEditEvent<S,T> t) { 284 int index = t.getTablePosition().getRow(); 285 List<S> list = t.getTableView().getItems(); 286 if (list == null || index < 0 || index >= list.size()) return; 287 S rowData = list.get(index); 288 ObservableValue<T> ov = getCellObservableValue(rowData); 289 290 if (ov instanceof WritableValue) { 291 ((WritableValue)ov).setValue(t.getNewValue()); 292 } 293 } 294 }; 295 296 private ListChangeListener<TableColumn<S,?>> columnsListener = new ListChangeListener<TableColumn<S,?>>() { 297 @Override public void onChanged(Change<? extends TableColumn<S,?>> c) { 298 while (c.next()) { 299 // update the TableColumn.tableView property 300 for (TableColumn<S,?> tc : c.getRemoved()) { 301 // Fix for RT-16978. In TableColumnHeader we add before we 302 // remove when moving a TableColumn. This means that for 303 // a very brief moment the tc is duplicated, and we can prevent 304 // nulling out the tableview and parent column. Without this 305 // here, in a very special circumstance it is possible to null 306 // out the entire content of a column by reordering and then 307 // sorting another column. 308 if (getColumns().contains(tc)) continue; 309 310 tc.setTableView(null); 311 tc.setParentColumn(null); 312 } 313 for (TableColumn<S,?> tc : c.getAddedSubList()) { 314 tc.setTableView(getTableView()); 315 } 316 317 updateColumnWidths(); 318 } 319 } 320 }; 321 322 private WeakListChangeListener<TableColumn<S,?>> weakColumnsListener = 323 new WeakListChangeListener<TableColumn<S,?>>(columnsListener); 324 325 326 327 /*************************************************************************** 328 * * 329 * Instance Variables * 330 * * 331 **************************************************************************/ 332 333 // Contains any children columns that should be nested within this column 334 private final ObservableList<TableColumn<S,?>> columns = FXCollections.<TableColumn<S,?>>observableArrayList(); 335 336 337 338 /*************************************************************************** 339 * * 340 * Properties * 341 * * 342 **************************************************************************/ 343 344 // --- TableView 345 /** 346 * The TableView that this TableColumn belongs to. 347 */ 348 private ReadOnlyObjectWrapper<TableView<S>> tableView = new ReadOnlyObjectWrapper<TableView<S>>(this, "tableView"); 349 public final ReadOnlyObjectProperty<TableView<S>> tableViewProperty() { 350 return tableView.getReadOnlyProperty(); 351 } 352 final void setTableView(TableView<S> value) { tableView.set(value); } 353 public final TableView<S> getTableView() { return tableView.get(); } 354 355 356 357 // --- Cell value factory 358 /** 359 * The cell value factory needs to be set to specify how to populate all 360 * cells within a single TableColumn. A cell value factory is a {@link Callback} 361 * that provides a {@link CellDataFeatures} instance, and expects an 362 * {@link ObservableValue} to be returned. The returned ObservableValue instance 363 * will be observed internally to allow for immediate updates to the value 364 * to be reflected on screen. 365 * 366 * An example of how to set a cell value factory is: 367 * 368 * <pre><code> 369 * lastNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() { 370 * public ObservableValue<String> call(CellDataFeatures<Person, String> p) { 371 * // p.getValue() returns the Person instance for a particular TableView row 372 * return p.getValue().lastNameProperty(); 373 * } 374 * }); 375 * } 376 * </code></pre> 377 * 378 * A common approach is to want to populate cells in a TableColumn using 379 * a single value from a Java bean. To support this common scenario, there 380 * is the {@link PropertyValueFactory} class. Refer to this class for more 381 * information on how to use it, but briefly here is how the above use case 382 * could be simplified using the PropertyValueFactory class: 383 * 384 * <pre><code> 385 * lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName")); 386 * </code></pre> 387 * 388 * @see PropertyValueFactory 389 */ 390 private ObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>> cellValueFactory; 391 public final void setCellValueFactory(Callback<CellDataFeatures<S,T>, ObservableValue<T>> value) { 392 cellValueFactoryProperty().set(value); 393 } 394 public final Callback<CellDataFeatures<S,T>, ObservableValue<T>> getCellValueFactory() { 395 return cellValueFactory == null ? null : cellValueFactory.get(); 396 } 397 public final ObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>> cellValueFactoryProperty() { 398 if (cellValueFactory == null) { 399 cellValueFactory = new SimpleObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>>(this, "cellValueFactory"); 400 } 401 return cellValueFactory; 402 } 403 404 405 // --- Cell Factory 406 /** 407 * The cell factory for all cells in this column. The cell factory 408 * is responsible for rendering the data contained within each TableCell for 409 * a single table column. 410 * 411 * <p>By default TableColumn uses the {@link #DEFAULT_CELL_FACTORY default cell 412 * factory}, but this can be replaced with a custom implementation, for 413 * example to show data in a different way or to support editing.There is a 414 * lot of documentation on creating custom cell factories 415 * elsewhere (see {@link Cell} and {@link TableView} for example).</p> 416 * 417 * <p>Finally, there are a number of pre-built cell factories available in the 418 * {@link javafx.scene.control.cell} package. 419 */ 420 private final ObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>> cellFactory = 421 new SimpleObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>>( 422 this, "cellFactory", (Callback<TableColumn<S,T>, TableCell<S,T>>) ((Callback) DEFAULT_CELL_FACTORY)) { 423 @Override protected void invalidated() { 424 TableView table = getTableView(); 425 if (table == null) return; 426 Map<Object,Object> properties = table.getProperties(); 427 if (properties.containsKey(TableViewSkinBase.RECREATE)) { 428 properties.remove(TableViewSkinBase.RECREATE); 429 } 430 properties.put(TableViewSkinBase.RECREATE, Boolean.TRUE); 431 } 432 }; 433 434 public final void setCellFactory(Callback<TableColumn<S,T>, TableCell<S,T>> value) { 435 cellFactory.set(value); 436 } 437 438 public final Callback<TableColumn<S,T>, TableCell<S,T>> getCellFactory() { 439 return cellFactory.get(); 440 } 441 442 public final ObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>> cellFactoryProperty() { 443 return cellFactory; 444 } 445 446 447 448 // --- Sort Type 449 /** 450 * Used to state whether this column, if it is part of a sort order (see 451 * {@link TableView#getSortOrder()} for more details), should be sorted in 452 * ascending or descending order. 453 * Simply toggling this property will result in the sort order changing in 454 * the TableView, assuming of course that this column is in the 455 * sortOrder ObservableList to begin with. 456 */ 457 private ObjectProperty<SortType> sortType; 458 public final ObjectProperty<SortType> sortTypeProperty() { 459 if (sortType == null) { 460 sortType = new SimpleObjectProperty<SortType>(this, "sortType", SortType.ASCENDING); 461 } 462 return sortType; 463 } 464 public final void setSortType(SortType value) { 465 sortTypeProperty().set(value); 466 } 467 public final SortType getSortType() { 468 return sortType == null ? SortType.ASCENDING : sortType.get(); 469 } 470 471 472 473 // --- On Edit Start 474 private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditStart; 475 public final void setOnEditStart(EventHandler<CellEditEvent<S,T>> value) { 476 onEditStartProperty().set(value); 477 } 478 public final EventHandler<CellEditEvent<S,T>> getOnEditStart() { 479 return onEditStart == null ? null : onEditStart.get(); 480 } 481 /** 482 * This event handler will be fired when the user successfully initiates 483 * editing. 484 */ 485 public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditStartProperty() { 486 if (onEditStart == null) { 487 onEditStart = new SimpleObjectProperty<EventHandler<CellEditEvent<S,T>>>(this, "onEditStart") { 488 @Override protected void invalidated() { 489 eventHandlerManager.setEventHandler(TableColumn.<S,T>editStartEvent(), get()); 490 } 491 }; 492 } 493 return onEditStart; 494 } 495 496 497 // --- On Edit Commit 498 private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCommit; 499 public final void setOnEditCommit(EventHandler<CellEditEvent<S,T>> value) { 500 onEditCommitProperty().set(value); 501 } 502 public final EventHandler<CellEditEvent<S,T>> getOnEditCommit() { 503 return onEditCommit == null ? null : onEditCommit.get(); 504 } 505 /** 506 * This event handler will be fired when the user successfully commits their 507 * editing. 508 */ 509 public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCommitProperty() { 510 if (onEditCommit == null) { 511 onEditCommit = new SimpleObjectProperty<EventHandler<CellEditEvent<S,T>>>(this, "onEditCommit") { 512 @Override protected void invalidated() { 513 eventHandlerManager.setEventHandler(TableColumn.<S,T>editCommitEvent(), get()); 514 } 515 }; 516 } 517 return onEditCommit; 518 } 519 520 521 // --- On Edit Cancel 522 private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCancel; 523 public final void setOnEditCancel(EventHandler<CellEditEvent<S,T>> value) { 524 onEditCancelProperty().set(value); 525 } 526 public final EventHandler<CellEditEvent<S,T>> getOnEditCancel() { 527 return onEditCancel == null ? null : onEditCancel.get(); 528 } 529 /** 530 * This event handler will be fired when the user cancels editing a cell. 531 */ 532 public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCancelProperty() { 533 if (onEditCancel == null) { 534 onEditCancel = new SimpleObjectProperty<EventHandler<CellEditEvent<S, T>>>(this, "onEditCancel") { 535 @Override protected void invalidated() { 536 eventHandlerManager.setEventHandler(TableColumn.<S,T>editCancelEvent(), get()); 537 } 538 }; 539 } 540 return onEditCancel; 541 } 542 543 544 545 /*************************************************************************** 546 * * 547 * Public API * 548 * * 549 **************************************************************************/ 550 551 /** {@inheritDoc} */ 552 @Override public final ObservableList<TableColumn<S,?>> getColumns() { 553 return columns; 554 } 555 556 /** {@inheritDoc} */ 557 @Override public final ObservableValue<T> getCellObservableValue(int index) { 558 if (index < 0) return null; 559 560 // Get the table 561 final TableView<S> table = getTableView(); 562 if (table == null || table.getItems() == null) return null; 563 564 // Get the rowData 565 final List<S> items = table.getItems(); 566 if (index >= items.size()) return null; // Out of range 567 568 final S rowData = items.get(index); 569 return getCellObservableValue(rowData); 570 } 571 572 /** {@inheritDoc} */ 573 @Override public final ObservableValue<T> getCellObservableValue(S item) { 574 // Get the factory 575 final Callback<CellDataFeatures<S,T>, ObservableValue<T>> factory = getCellValueFactory(); 576 if (factory == null) return null; 577 578 // Get the table 579 final TableView<S> table = getTableView(); 580 if (table == null) return null; 581 582 // Call the factory 583 final CellDataFeatures<S,T> cdf = new CellDataFeatures<S,T>(table, this, item); 584 return factory.call(cdf); 585 } 586 587 588 589 /*************************************************************************** 590 * * 591 * Stylesheet Handling * 592 * * 593 **************************************************************************/ 594 595 private static final String DEFAULT_STYLE_CLASS = "table-column"; 596 597 /** 598 * {@inheritDoc} 599 * @return "TableColumn" 600 */ 601 @Override 602 public String getTypeSelector() { 603 return "TableColumn"; 604 } 605 606 /** 607 * {@inheritDoc} 608 * @return {@code getTableView()} 609 */ 610 @Override 611 public Styleable getStyleableParent() { 612 return getTableView(); } 613 614 615 /** 616 * {@inheritDoc} 617 */ 618 @Override 619 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 620 return getClassCssMetaData(); 621 } 622 623 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 624 return Collections.emptyList(); 625 } 626 627 /** 628 * @treatAsPrivate implementation detail 629 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 630 */ 631 @Deprecated 632 // SB-dependency: RT-21094 has been filed to track this 633 public Node impl_styleableGetNode() { 634 if (! (getTableView().getSkin() instanceof TableViewSkin)) return null; 635 TableViewSkin<?> skin = (TableViewSkin<?>) getTableView().getSkin(); 636 637 TableHeaderRow tableHeader = skin.getTableHeaderRow(); 638 NestedTableColumnHeader rootHeader = tableHeader.getRootHeader(); 639 640 // we now need to do a search for the header. We'll go depth-first. 641 return scan(rootHeader); 642 } 643 644 private TableColumnHeader scan(TableColumnHeader header) { 645 // firstly test that the parent isn't what we are looking for 646 if (TableColumn.this.equals(header.getTableColumn())) { 647 return header; 648 } 649 650 if (header instanceof NestedTableColumnHeader) { 651 NestedTableColumnHeader parent = (NestedTableColumnHeader) header; 652 for (int i = 0; i < parent.getColumnHeaders().size(); i++) { 653 TableColumnHeader result = scan(parent.getColumnHeaders().get(i)); 654 if (result != null) { 655 return result; 656 } 657 } 658 } 659 660 return null; 661 } 662 663 664 665 /*************************************************************************** 666 * * 667 * Support Interfaces * 668 * * 669 **************************************************************************/ 670 671 /** 672 * A support class used in TableColumn as a wrapper class 673 * to provide all necessary information for a particular {@link Cell}. Once 674 * instantiated, this class is immutable. 675 * 676 * @param <S> The TableView type 677 * @param <T> The TableColumn type 678 */ 679 public static class CellDataFeatures<S,T> { 680 private final TableView<S> tableView; 681 private final TableColumn<S,T> tableColumn; 682 private final S value; 683 684 /** 685 * Instantiates a CellDataFeatures instance with the given properties 686 * set as read-only values of this instance. 687 * 688 * @param tableView The TableView that this instance refers to. 689 * @param tableColumn The TableColumn that this instance refers to. 690 * @param value The value for a row in the TableView. 691 */ 692 public CellDataFeatures(TableView<S> tableView, 693 TableColumn<S,T> tableColumn, S value) { 694 this.tableView = tableView; 695 this.tableColumn = tableColumn; 696 this.value = value; 697 } 698 699 /** 700 * Returns the value passed in to the constructor. 701 */ 702 public S getValue() { 703 return value; 704 } 705 706 /** 707 * Returns the {@link TableColumn} passed in to the constructor. 708 */ 709 public TableColumn<S,T> getTableColumn() { 710 return tableColumn; 711 } 712 713 /** 714 * Returns the {@link TableView} passed in to the constructor. 715 */ 716 public TableView<S> getTableView() { 717 return tableView; 718 } 719 } 720 721 722 723 /** 724 * An event that is fired when a user performs an edit on a table cell. 725 */ 726 public static class CellEditEvent<S,T> extends Event { 727 private static final long serialVersionUID = -609964441682677579L; 728 729 /** 730 * Common supertype for all cell edit event types. 731 */ 732 public static final EventType<?> ANY = EDIT_ANY_EVENT; 733 734 // represents the new value input by the end user. This is NOT the value 735 // to go back into the TableView.items list - this new value represents 736 // just the input for a single cell, so it is likely that it needs to go 737 // back into a property within an item in the TableView.items list. 738 private final T newValue; 739 740 // The location of the edit event 741 private transient final TablePosition<S,T> pos; 742 743 /** 744 * Creates a new event that can be subsequently fired to the relevant listeners. 745 * 746 * @param table The TableView on which this event occurred. 747 * @param pos The position upon which this event occurred. 748 * @param eventType The type of event that occurred. 749 * @param newValue The value input by the end user. 750 */ 751 public CellEditEvent(TableView<S> table, TablePosition<S,T> pos, 752 EventType<CellEditEvent<S,T>> eventType, T newValue) { 753 super(table, Event.NULL_SOURCE_TARGET, eventType); 754 755 if (table == null) { 756 throw new NullPointerException("TableView can not be null"); 757 } 758 if (pos == null) { 759 throw new NullPointerException("TablePosition can not be null"); 760 } 761 this.pos = pos; 762 this.newValue = newValue; 763 } 764 765 /** 766 * Returns the TableView upon which this event occurred. 767 * @return The TableView control upon which this event occurred. 768 */ 769 public TableView<S> getTableView() { 770 return pos.getTableView(); 771 } 772 773 /** 774 * Returns the TableColumn upon which this event occurred. 775 * 776 * @return The TableColumn that the edit occurred in. 777 */ 778 public TableColumn<S,T> getTableColumn() { 779 return pos.getTableColumn(); 780 } 781 782 /** 783 * Returns the position upon which this event occurred. 784 * @return The position upon which this event occurred. 785 */ 786 public TablePosition<S,T> getTablePosition() { 787 return pos; 788 } 789 790 /** 791 * Returns the new value input by the end user. This is <b>not</b> the value 792 * to go back into the TableView.items list - this new value represents 793 * just the input for a single cell, so it is likely that it needs to go 794 * back into a property within an item in the TableView.items list. 795 * 796 * @return An Object representing the new value input by the user. 797 */ 798 public T getNewValue() { 799 return newValue; 800 } 801 802 /** 803 * Attempts to return the old value at the position referred to in the 804 * TablePosition returned by {@link #getTablePosition()}. This may return 805 * null for a number of reasons. 806 * 807 * @return Returns the value stored in the position being edited, or null 808 * if it can not be retrieved. 809 */ 810 public T getOldValue() { 811 S rowData = getRowValue(); 812 if (rowData == null || pos.getTableColumn() == null) { 813 return null; 814 } 815 816 // if we are here, we now need to get the data for the specific column 817 return (T) pos.getTableColumn().getCellData(rowData); 818 } 819 820 /** 821 * Convenience method that returns the value for the row (that is, from 822 * the TableView {@link TableView#itemsProperty() items} list), for the 823 * row contained within the {@link TablePosition} returned in 824 * {@link #getTablePosition()}. 825 */ 826 public S getRowValue() { 827 List<S> items = getTableView().getItems(); 828 if (items == null) return null; 829 830 int row = pos.getRow(); 831 if (row < 0 || row >= items.size()) return null; 832 833 return items.get(row); 834 } 835 } 836 837 /** 838 * Enumeration that specifies the type of sorting being applied to a specific 839 * column. 840 */ 841 public static enum SortType { 842 /** 843 * Column will be sorted in an ascending order. 844 */ 845 ASCENDING, 846 847 /** 848 * Column will be sorted in a descending order. 849 */ 850 DESCENDING; 851 852 // UNSORTED 853 } 854}