1 /* 2 * Copyright (c) 2014, 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 package javafx.scene.control.test.tableview; 26 27 import javafx.scene.control.skin.NestedTableColumnHeader; 28 import javafx.scene.control.skin.TableColumnHeader; 29 import javafx.scene.control.skin.TableHeaderRow; 30 import javafx.scene.control.skin.TableViewSkin; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 import java.util.Map; 34 import javafx.beans.property.SimpleStringProperty; 35 import javafx.beans.property.StringProperty; 36 import javafx.beans.value.ChangeListener; 37 import javafx.beans.value.ObservableValue; 38 import javafx.collections.FXCollections; 39 import javafx.collections.ObservableList; 40 import javafx.collections.transformation.SortedList; 41 import javafx.event.ActionEvent; 42 import javafx.event.Event; 43 import javafx.event.EventHandler; 44 import javafx.geometry.Orientation; 45 import javafx.scene.Scene; 46 import javafx.scene.control.*; 47 import javafx.scene.control.TableColumn.CellDataFeatures; 48 import javafx.scene.control.cell.ChoiceBoxTableCell; 49 import javafx.scene.control.cell.ComboBoxTableCell; 50 import javafx.scene.control.cell.TextFieldTableCell; 51 import javafx.scene.control.test.treetable.ResetButtonNames; 52 import javafx.scene.control.test.utils.CommonPropertiesScene; 53 import javafx.scene.control.test.utils.ComponentsFactory.MultipleIndexFormComponent; 54 import javafx.scene.control.test.utils.ptables.NodeControllerFactory; 55 import javafx.scene.control.test.utils.ptables.PropertiesTable; 56 import javafx.scene.control.test.utils.ptables.PropertyTablesFactory; 57 import javafx.scene.control.test.utils.ptables.TabPaneWithControl; 58 import javafx.scene.input.KeyCode; 59 import javafx.scene.input.KeyEvent; 60 import javafx.scene.layout.HBox; 61 import javafx.scene.layout.Pane; 62 import javafx.scene.layout.VBox; 63 import javafx.util.Callback; 64 import test.javaclient.shared.InteroperabilityApp; 65 import test.javaclient.shared.Utils; 66 import static javafx.commons.Consts.*; 67 import static javafx.commons.Consts.Cell.*; 68 import javafx.scene.Node; 69 import static javafx.scene.control.test.treetable.ResetButtonNames.HARD_RESET_BUTTON_ID; 70 import static javafx.scene.control.test.treetable.ResetButtonNames.SOFT_RESET_BUTTON_ID; 71 import javafx.scene.control.test.utils.ComponentsFactory.MultipleIndexFormComponent.MultipleIndicesAction; 72 import static org.junit.Assert.assertTrue; 73 74 /** 75 * @author Alexander Kirov 76 */ 77 public class NewTableViewApp extends InteroperabilityApp implements ResetButtonNames { 78 79 public final static String TESTED_TABLE_VIEW_ID = "TESTED_TABLEVIEW_ID"; 80 81 public static void main(String[] args) { 82 Utils.launch(NewTableViewApp.class, args); 83 } 84 85 @Override 86 protected Scene getScene() { 87 Utils.setTitleToStage(stage, "TableViewTestApp"); 88 Scene scene = new TableViewScene(); 89 Utils.addBrowser(scene); 90 return scene; 91 } 92 93 class TableViewScene extends CommonPropertiesScene { 94 95 //TabPane with properties tables. 96 TabPaneWithControl tabPane; 97 //Tableview to be tested. 98 TableView<DataItem> testedTableView; 99 //Container for all the data. 100 ObservableList<DataItem> allData = FXCollections.observableArrayList(); 101 //List of existing columnsin current tableView. 102 Map<String, TableColumn> existingColumns = new HashMap<String, TableColumn>(); 103 //This list contains all properties tables, which were created during testing. 104 ArrayList<PropertiesTable> allPropertiesTables = new ArrayList<PropertiesTable>(); 105 private PropertiesTable tb; 106 107 public TableViewScene() { 108 super("TableView", 800, 600); 109 prepareScene(); 110 } 111 112 @Override 113 final protected void prepareScene() { 114 testedTableView = new TableView<DataItem>(allData); 115 testedTableView.setId(TESTED_TABLE_VIEW_ID); 116 117 tb = new PropertiesTable(testedTableView); 118 119 PropertyTablesFactory.explorePropertiesList(testedTableView, tb); 120 121 tb.addCounter(COUNTER_EDIT_START); 122 tb.addCounter(COUNTER_EDIT_COMMIT); 123 tb.addCounter(COUNTER_EDIT_CANCEL); 124 tb.addCounter(COUNTER_ON_SORT); 125 testedTableView.setOnSort(new EventHandler<SortEvent<TableView<DataItem>>>() { 126 public void handle(SortEvent<TableView<DataItem>> event) { 127 tb.incrementCounter(COUNTER_ON_SORT); 128 } 129 }); 130 131 tabPane = new TabPaneWithControl("TableView", tb); 132 tabPane.setMinSize(1000, 1000); 133 134 Button hardResetButton = new Button("Hard reset"); 135 hardResetButton.setId(HARD_RESET_BUTTON_ID); 136 hardResetButton.setOnAction(new EventHandler<ActionEvent>() { 137 public void handle(ActionEvent t) { 138 HBox hb = (HBox) getRoot(); 139 hb.getChildren().clear(); 140 prepareMainSceneStructure(); 141 prepareScene(); 142 } 143 }); 144 145 Button softResetButton = new Button("Soft reset"); 146 softResetButton.setId(SOFT_RESET_BUTTON_ID); 147 softResetButton.setOnAction(new EventHandler<ActionEvent>() { 148 public void handle(ActionEvent t) { 149 TableView newOne = new TableView(); 150 refreshProcedure(1); 151 allData.clear(); 152 existingColumns.clear(); 153 testedTableView.setPrefHeight(newOne.getPrefHeight()); 154 testedTableView.setPrefWidth(newOne.getPrefWidth()); 155 testedTableView.getColumns().clear(); 156 testedTableView.setPlaceholder(newOne.getPlaceholder()); 157 testedTableView.setEditable(newOne.isEditable()); 158 testedTableView.setVisible(newOne.isVisible()); 159 testedTableView.setTableMenuButtonVisible(newOne.isTableMenuButtonVisible()); 160 testedTableView.setDisable(newOne.isDisable()); 161 testedTableView.setContextMenu(newOne.getContextMenu()); 162 } 163 }); 164 165 HBox resetButtonsHBox = new HBox(); 166 resetButtonsHBox.getChildren().addAll(hardResetButton, softResetButton); 167 168 VBox vb = new VBox(); 169 vb.setSpacing(5); 170 vb.getChildren().addAll(resetButtonsHBox, new Separator(Orientation.HORIZONTAL), 171 getAddColumnForm(), new Separator(Orientation.HORIZONTAL), 172 getChangeDataSizeForm(), new Separator(Orientation.HORIZONTAL), 173 getRemoveColumnsVBox(), new Separator(Orientation.HORIZONTAL), 174 getRemoveDataVBox(), new Separator(Orientation.HORIZONTAL), 175 getAddNestedColumnVBox(), new Separator(Orientation.HORIZONTAL), 176 controlsForEditing(), new Separator(Orientation.HORIZONTAL), 177 getReplaceTableHeaderImplementationButton(), new Separator(Orientation.HORIZONTAL)); 178 setControllersContent(vb); 179 setPropertiesContent(tabPane); 180 setTestedControl(testedTableView); 181 } 182 183 /* 184 * In this function TabPane with control will be refreshed, and all 185 * properties table will be refreshed and cleared. 186 */ 187 private void refreshProcedure(int exceptFirstPropertiesTable) { 188 for (int i = allPropertiesTables.size() - 1; i >= 0; i--) { 189 allPropertiesTables.get(i).refresh(); 190 if (i >= exceptFirstPropertiesTable) { 191 allPropertiesTables.remove(i); 192 } 193 } 194 195 tabPane.removePropertiesTablesExceptFirstOnes(exceptFirstPropertiesTable); 196 } 197 198 private HBox getChangeDataSizeForm() { 199 final TextField sizeTf = new TextField(); 200 sizeTf.setId(NEW_DATA_SIZE_TEXTFIELD_ID); 201 sizeTf.setPromptText("new size (rows)"); 202 203 Button button = new Button("set"); 204 button.setId(NEW_DATA_SIZE_BUTTON_ID); 205 button.setOnAction(new EventHandler<ActionEvent>() { 206 public void handle(ActionEvent t) { 207 int actualSize = allData.size(); 208 int newSize = Integer.parseInt(sizeTf.getText()); 209 if (actualSize > newSize) { 210 int toRemove = actualSize - newSize; 211 for (int i = 0; i < toRemove; i++) { 212 allData.remove(0); 213 } 214 } else if (actualSize < newSize) { 215 int toAdd = newSize - actualSize; 216 for (int i = 0; i < toAdd; i++) { 217 DataItem dataItem = new DataItem(); 218 for (String columnName : existingColumns.keySet()) { 219 dataItem.add(columnName, new SimpleStringProperty(columnName + "-" + String.valueOf(actualSize + i))); 220 } 221 allData.add(dataItem); 222 } 223 } 224 } 225 }); 226 227 HBox hb1 = new HBox(3); 228 hb1.getChildren().addAll(new Label("New size:"), sizeTf, button); 229 230 Button addSortableRows = new Button("Add sortable rows"); 231 addSortableRows.setId(BTN_CREATE_SORTABLE_ROWS_ID); 232 addSortableRows.setOnAction(new EventHandler<ActionEvent>() { 233 public void handle(ActionEvent t) { 234 allData.clear(); 235 ObservableList<TableColumn<DataItem, ?>> columns = testedTableView.getColumns(); 236 237 String[][] testData = getDataForSorting(columns.size()); 238 239 for (int x = 0; x < testData.length; x++) { 240 DataItem dataItem = new DataItem(); 241 int z = 0; 242 //Use the correct order of columns as they are placed. 243 //From left to right 244 for (int i = 0; i < testData[i].length; i++) { 245 TableColumn col = columns.get(i); 246 dataItem.add(col.getText(), new SimpleStringProperty(testData[x][z++])); 247 } 248 allData.add(dataItem); 249 } 250 } 251 }); 252 253 Button setSortedListForModel = new Button("Change model to sortable list"); 254 setSortedListForModel.setId(BTN_SET_SORTED_LIST_FOR_MODEL_ID); 255 setSortedListForModel.setOnAction((e) -> { 256 SortedList sortedList = new SortedList(allData); 257 sortedList.comparatorProperty().bind(testedTableView.comparatorProperty()); 258 testedTableView.setItems(sortedList); 259 }); 260 261 HBox topContainer = new HBox(); 262 263 VBox internalContainer = new VBox(5.0); 264 internalContainer.getChildren().addAll(hb1, addSortableRows, setSortedListForModel); 265 266 topContainer.getChildren().add(internalContainer); 267 return topContainer; 268 // return hb1; 269 } 270 271 private VBox getAddColumnForm() { 272 final TextField tfColumnName = new TextField(); 273 tfColumnName.setPromptText("new column name"); 274 tfColumnName.setId(NEW_COLUMN_NAME_TEXTFIELD_ID); 275 276 final TextField indexTf = new TextField(); 277 indexTf.setPromptText("at index"); 278 indexTf.setId(NEW_COLUMN_INDEX_TEXTFIELD_UD); 279 280 final CheckBox addDataPropertiesTabToTab = new CheckBox("with properties table for data"); 281 addDataPropertiesTabToTab.setId(NEW_COLUMN_GET_DATA_PROPERTIES_TAB_ID); 282 283 final CheckBox addColumnPropertiesTableToTab = new CheckBox("with properties table for column"); 284 addColumnPropertiesTableToTab.setId(NEW_COLUMN_GET_COLUMN_PROPERTIES_TAB_ID); 285 286 Button button = new Button("Add"); 287 button.setId(NEW_COLUMN_ADD_BUTTON_ID); 288 button.setOnAction(new EventHandler<ActionEvent>() { 289 public void handle(ActionEvent t) { 290 final String name = tfColumnName.getText(); 291 int index = Integer.parseInt(indexTf.getText()); 292 TableColumn column = new TableColumn(name); 293 column.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<DataItem, String>, ObservableValue<String>>() { 294 public ObservableValue<String> call(CellDataFeatures<DataItem, String> p) { 295 return p.getValue().get(name); 296 } 297 }); 298 introduceColumn(name, column); 299 if (addColumnPropertiesTableToTab.isSelected()) { 300 tabPane.addPropertiesTable(name, NodeControllerFactory.createFullController(column, tabPane)); 301 } 302 testedTableView.getColumns().add(index, column); 303 if (addDataPropertiesTabToTab.isSelected()) { 304 final PropertiesTable forData = new PropertiesTable(null); 305 for (DataItem item : allData) { 306 forData.addStringLine(item.get(name), "new value"); 307 } 308 tabPane.addPropertiesTable(name + " data", forData.getVisualRepresentation()); 309 } 310 } 311 }); 312 313 HBox hb1 = new HBox(3); 314 hb1.getChildren().addAll(new Label("With name"), tfColumnName); 315 316 HBox hb2 = new HBox(3); 317 hb2.getChildren().addAll(new Label("At index"), indexTf, button); 318 319 VBox vb = new VBox(); 320 vb.getChildren().addAll(hb1, addDataPropertiesTabToTab, addColumnPropertiesTableToTab, hb2); 321 return vb; 322 } 323 324 Node getReplaceTableHeaderImplementationButton() { 325 Button replaceButton = new Button("Replace skin implementation"); 326 replaceButton.setId(REPLACE_SKIN_IMPLEMENTATION_BUTTON_ID); 327 replaceButton.setOnAction(new EventHandler<ActionEvent>() { 328 public void handle(ActionEvent t) { 329 testedTableView.setSkin(new TableViewSkin(testedTableView) { 330 @Override 331 public String toString() { 332 return "CUSTOM " + super.toString(); 333 } 334 335 @Override 336 protected TableHeaderRow createTableHeaderRow() { 337 return new TableHeaderRow(this) { 338 @Override 339 protected NestedTableColumnHeader createRootHeader() { 340 return new NestedTableColumnHeader((TableViewSkin) testedTableView.getSkin(), null) { 341 @Override 342 protected TableColumnHeader createTableColumnHeader(TableColumnBase col) { 343 if (col.getColumns().isEmpty()) { 344 final TableColumnHeader tableColumnHeader = new TableColumnHeader(getTableViewSkin(), col); 345 tableColumnHeader.setId(CUSTOM_IMPLEMENTATION_MARKER); 346 return tableColumnHeader; 347 } else { 348 final NestedTableColumnHeader nestedTableColumnHeader = new NestedTableColumnHeader(getTableViewSkin(), col); 349 nestedTableColumnHeader.setId(CUSTOM_IMPLEMENTATION_MARKER); 350 return nestedTableColumnHeader; 351 } 352 } 353 }; 354 } 355 }; 356 } 357 }); 358 } 359 }); 360 361 return replaceButton; 362 } 363 364 VBox getAddNestedColumnVBox() { 365 final TextField nestedColumnAtIndexTF = new TextField("0"); 366 nestedColumnAtIndexTF.setPromptText("index"); 367 nestedColumnAtIndexTF.setId(NESTED_COLUMN_INDEX_TEXT_FIELD_ID); 368 369 final TextField nestedColumnNameTF = new TextField(); 370 nestedColumnNameTF.setPromptText("namex"); 371 nestedColumnNameTF.setId(NESTED_COLUMN_NAME_TEXT_FIELD_ID); 372 373 HBox nestedColumnAdditionalNodes = new HBox(); 374 nestedColumnAdditionalNodes.getChildren().addAll(nestedColumnAtIndexTF, nestedColumnNameTF); 375 376 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 377 public void onAction(int[] indices) { 378 int[] reversed = new int[indices.length]; 379 for (int i = 0; i < indices.length; i++) { 380 reversed[i] = indices[indices.length - i - 1]; 381 } 382 TableColumn nestedColumn = new TableColumn(nestedColumnNameTF.getText()); 383 for (int i : indices) { 384 nestedColumn.getColumns().add(testedTableView.getColumns().get(i)); 385 } 386 for (int i : reversed) { 387 testedTableView.getColumns().remove(i); 388 } 389 tabPane.addPropertiesTable(nestedColumnNameTF.getText(), NodeControllerFactory.createFullController(nestedColumn, tabPane)); 390 testedTableView.getColumns().add(Integer.valueOf(nestedColumnAtIndexTF.getText()), nestedColumn); 391 } 392 }; 393 394 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 395 "Add nested column", 396 nestedColumnAdditionalNodes, 397 executor, 398 CREATE_NESTED_COLUMN_MULTIPLE_ACTION_BUTTON_ID, 399 CREATE_NESTED_COLUMN_MULTIPLE_TEXTFIELD_ID); 400 401 return multipleIndexForm; 402 } 403 404 VBox getRemoveColumnsVBox() { 405 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 406 public void onAction(int[] indices) { 407 ObservableList onRemoving = FXCollections.observableArrayList(); 408 for (int index : indices) { 409 onRemoving.add(testedTableView.getColumns().get(index)); 410 } 411 testedTableView.getColumns().removeAll(onRemoving); 412 } 413 }; 414 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 415 "Remove columns", null, executor, 416 REMOVE_MULTIPLE_COLUMNS_ACTION_BUTTON_ID, 417 REMOVE_MULTIPLE_COLUMNS_TEXT_FIELD_ID); 418 419 return multipleIndexForm; 420 } 421 422 VBox getRemoveDataVBox() { 423 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 424 public void onAction(int[] indices) { 425 ObservableList onRemoving = FXCollections.observableArrayList(); 426 for (int index : indices) { 427 onRemoving.add(testedTableView.getItems().get(index)); 428 } 429 testedTableView.getItems().removeAll(onRemoving); 430 } 431 }; 432 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 433 "Remove data items", null, executor, 434 REMOVE_DATA_ITEMS_MULTIPLE_ACTION_BUTTON_ID, 435 REMOVE_DATA_ITEMS_MULTIPLE_TEXT_FIELD_ID); 436 437 return multipleIndexForm; 438 } 439 440 private Pane controlsForEditing() { 441 442 VBox topContainer = new VBox(3.5); 443 HBox hb = new HBox(3.0); 444 445 final ComboBox cmbEditors = new ComboBox(); 446 cmbEditors.setId(CMB_EDITORS_ID); 447 cmbEditors.getItems().addAll((Object[]) CellEditorType.values()); 448 449 final CheckBox chbCustom = new CheckBox("Custom"); 450 chbCustom.setId(CHB_IS_CUSTOM_ID); 451 452 Button btnSetEditor = new Button("Set editor"); 453 btnSetEditor.setId(BTN_SET_CELLS_EDITOR_ID); 454 btnSetEditor.setOnAction(new EventHandler<ActionEvent>() { 455 public void handle(ActionEvent t) { 456 CellEditorType editor = (CellEditorType) cmbEditors.getSelectionModel().getSelectedItem(); 457 setCellEditor(editor, chbCustom.isSelected()); 458 } 459 }); 460 461 hb.getChildren().addAll(cmbEditors, chbCustom, btnSetEditor); 462 463 Button btn = new Button("Set onEdit event hadlers"); 464 btn.setId(BTN_SET_ON_EDIT_EVENT_HANDLERS); 465 466 btn.setOnAction(new EventHandler<ActionEvent>() { 467 public void handle(ActionEvent t) { 468 final EventHandler eventHandlerOnEditStart = new EventHandler() { 469 public void handle(Event t) { 470 // new Throwable().printStackTrace(); 471 tb.incrementCounter(COUNTER_EDIT_START); 472 } 473 }; 474 475 final EventHandler eventHandlerOnEditCommit = new EventHandler() { 476 public void handle(Event t) { 477 tb.incrementCounter(COUNTER_EDIT_COMMIT); 478 } 479 }; 480 481 final EventHandler eventHandlerOnEditCancel = new EventHandler() { 482 public void handle(Event t) { 483 t.consume(); 484 tb.incrementCounter(COUNTER_EDIT_CANCEL); 485 } 486 }; 487 488 for (TableColumn col : testedTableView.getColumns()) { 489 col.setOnEditStart(eventHandlerOnEditStart); 490 assertTrue(eventHandlerOnEditStart == col.getOnEditStart()); 491 492 col.setOnEditCommit(eventHandlerOnEditCommit); 493 assertTrue(eventHandlerOnEditCommit == col.getOnEditCommit()); 494 495 col.setOnEditCancel(eventHandlerOnEditCancel); 496 assertTrue(eventHandlerOnEditCancel == col.getOnEditCancel()); 497 } 498 } 499 }); 500 501 topContainer.getChildren().addAll(hb, btn); 502 return topContainer; 503 } 504 505 private void introduceColumn(String columnName, TableColumn column) { 506 existingColumns.put(columnName, column); 507 int counter = 0; 508 for (DataItem item : allData) { 509 item.add(columnName, new SimpleStringProperty(columnName + "-" + String.valueOf(counter))); 510 counter++; 511 } 512 } 513 514 /** 515 * This class contain HashMap, which contain dynamically generated data. 516 * For each line in the Table, when you add additional column, you 517 * should add additional key-value pair in map for all dataItems in 518 * allData observable list. 519 */ 520 public class DataItem implements Comparable { 521 522 private HashMap<String, StringProperty> data = new HashMap<String, StringProperty>(); 523 524 public void add(String string, StringProperty property) { 525 data.put(string, property); 526 } 527 528 public StringProperty get(String name) { 529 return data.get(name); 530 } 531 532 public int compareTo(Object that) { 533 if (this == that) { 534 return 0; 535 } 536 537 DataItem other = (DataItem) that; 538 539 if (data.size() != other.data.size()) { 540 throw new IllegalStateException("[All data items in the table must have equal number of fields]"); 541 } 542 543 int res = 0; 544 545 for (String key : data.keySet()) { 546 res = data.get(key).get().compareTo(other.data.get(key).get()); 547 if (res != 0) { 548 break; 549 } 550 } 551 552 return res; 553 } 554 } 555 556 private void setCellEditor(CellEditorType editor, boolean isCustom) { 557 if (null == editor) { 558 System.out.println("Editor is not selected"); 559 return; 560 } 561 562 switch (editor) { 563 case TEXT_FIELD: 564 setTextFieldCellEditor(isCustom); 565 break; 566 case COMBOBOX: 567 setComboboxCellEditor(isCustom); 568 break; 569 case CHOICEBOX: 570 setChoiceboxCellEditor(isCustom); 571 break; 572 default: 573 throw new UnsupportedOperationException(editor.toString()); 574 } 575 576 System.out.println(String.format("Editor set: %s %s", 577 isCustom ? "custom" : "", 578 editor.toString())); 579 } 580 581 private void setTextFieldCellEditor(boolean isCustom) { 582 for (TableColumn col : testedTableView.getColumns()) { 583 if (!isCustom) { 584 col.setCellFactory(TextFieldTableCell.forTableColumn()); 585 } else { 586 col.setCellFactory(new Callback() { 587 public Object call(Object p) { 588 return new EditingTextFieldCell(); 589 } 590 }); 591 } 592 } 593 } 594 595 private void setComboboxCellEditor(boolean isCustom) { 596 597 for (int i = 0; i < testedTableView.getColumns().size(); i++) { 598 TableColumn col = testedTableView.getColumns().get(i); 599 String colName = col.getText(); 600 final ObservableList<String> items = FXCollections.observableArrayList(); 601 602 for (DataItem dataItem : allData) { 603 items.add(dataItem.get(colName).get()); 604 } 605 if (!isCustom) { 606 col.setCellFactory(ComboBoxTableCell.forTableColumn(items)); 607 } else { 608 col.setCellFactory(new Callback() { 609 public Object call(Object p) { 610 return new EditingComboBoxCell(items); 611 } 612 }); 613 } 614 } 615 } 616 617 private void setChoiceboxCellEditor(boolean isCustom) { 618 619 for (int i = 0; i < testedTableView.getColumns().size(); i++) { 620 TableColumn col = testedTableView.getColumns().get(i); 621 String colName = col.getText(); 622 final ObservableList<String> items = FXCollections.observableArrayList(); 623 624 for (DataItem dataItem : allData) { 625 items.add(dataItem.get(colName).get()); 626 } 627 if (!isCustom) { 628 col.setCellFactory(ChoiceBoxTableCell.forTableColumn(items)); 629 } else { 630 col.setCellFactory(new Callback() { 631 public Object call(Object p) { 632 return new EditingChoiceBoxCell(items); 633 } 634 }); 635 } 636 } 637 } 638 639 private class EditingTextFieldCell extends TableCell { 640 641 private TextField textField; 642 643 public EditingTextFieldCell() { 644 setId(EDITING_TEXTFIELD_CELL_ID); 645 } 646 647 @Override 648 public void startEdit() { 649 if (!isEmpty()) { 650 super.startEdit(); 651 createTextField(); 652 653 setText(null); 654 setGraphic(textField); 655 } 656 } 657 658 @Override 659 public void cancelEdit() { 660 super.cancelEdit(); 661 662 setText(getString()); 663 setGraphic(null); 664 } 665 666 @Override 667 public void updateItem(Object item, boolean empty) { 668 super.updateItem(item, empty); 669 670 if (empty) { 671 setText(null); 672 setGraphic(null); 673 } else { 674 if (isEditing()) { 675 if (textField != null) { 676 textField.setText(getString()); 677 } 678 setText(null); 679 setGraphic(textField); 680 } else { 681 setText(getString()); 682 setGraphic(null); 683 } 684 } 685 } 686 687 private void createTextField() { 688 textField = new TextField(getString()); 689 textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); 690 691 textField.setOnKeyReleased(new EventHandler<KeyEvent>() { 692 @Override 693 public void handle(KeyEvent t) { 694 if (t.getCode() == KeyCode.ENTER) { 695 commitEdit(textField.getText()); 696 } else if (t.getCode() == KeyCode.ESCAPE) { 697 cancelEdit(); 698 } 699 } 700 }); 701 } 702 703 private String getString() { 704 return getItem() == null ? "" : getItem().toString(); 705 } 706 } 707 708 private class EditingComboBoxCell extends TableCell { 709 710 ObservableList items; 711 ComboBox comboBox; 712 713 public EditingComboBoxCell(ObservableList _items) { 714 items = _items; 715 setId(EDITING_COMBOBOX_CELL_ID); 716 } 717 718 @Override 719 public void startEdit() { 720 if (isEmpty()) { 721 return; 722 } 723 createComboBox(); 724 comboBox.getSelectionModel().select(getItem()); 725 726 super.startEdit(); 727 728 setText(null); 729 setGraphic(comboBox); 730 } 731 732 @Override 733 public void cancelEdit() { 734 super.cancelEdit(); 735 setGraphic(null); 736 setText(getString()); 737 } 738 739 @Override 740 public void updateItem(Object item, boolean isEmpty) { 741 super.updateItem(item, isEmpty); 742 if (isEmpty()) { 743 setText(null); 744 setGraphic(null); 745 } else { 746 if (isEditing()) { 747 if (comboBox != null) { 748 comboBox.getSelectionModel().select(getItem()); 749 } 750 setText(null); 751 setGraphic(comboBox); 752 } else { 753 setText(getString()); 754 setGraphic(null); 755 } 756 } 757 } 758 759 private String getString() { 760 return getItem() == null ? "" : getItem().toString(); 761 } 762 763 private void createComboBox() { 764 if (null == comboBox) { 765 comboBox = new ComboBox(items); 766 comboBox.setMaxWidth(Double.MAX_VALUE); 767 comboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 768 @Override 769 public void changed(ObservableValue ov, Object oldValue, Object newValue) { 770 if (isEditing()) { 771 commitEdit(newValue); 772 } 773 } 774 }); 775 } 776 } 777 } 778 779 private class EditingChoiceBoxCell extends TableCell { 780 781 ObservableList items; 782 ChoiceBox choiceBox; 783 784 public EditingChoiceBoxCell(ObservableList _items) { 785 items = _items; 786 setId(EDITING_CHOICEBOX_CELL_ID); 787 } 788 789 @Override 790 public void startEdit() { 791 if (isEmpty()) { 792 return; 793 } 794 795 createComboBox(); 796 797 choiceBox.getSelectionModel().select(getItem()); 798 799 super.startEdit(); 800 setText(null); 801 setGraphic(choiceBox); 802 } 803 804 @Override 805 public void cancelEdit() { 806 super.cancelEdit(); 807 setGraphic(null); 808 setText(getString()); 809 } 810 811 @Override 812 public void updateItem(Object item, boolean isEmpty) { 813 super.updateItem(item, isEmpty); 814 if (isEmpty()) { 815 setText(null); 816 setGraphic(null); 817 } else { 818 if (isEditing()) { 819 if (choiceBox != null) { 820 choiceBox.getSelectionModel().select(getItem()); 821 } 822 setText(null); 823 setGraphic(choiceBox); 824 } else { 825 setText(getString()); 826 setGraphic(null); 827 } 828 } 829 } 830 831 private String getString() { 832 return getItem() == null ? "" : getItem().toString(); 833 } 834 835 private void createComboBox() { 836 if (null == choiceBox) { 837 choiceBox = new ChoiceBox(items); 838 choiceBox.setMaxWidth(Double.MAX_VALUE); 839 choiceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 840 @Override 841 public void changed(ObservableValue ov, Object oldValue, Object newValue) { 842 if (isEditing()) { 843 commitEdit(newValue); 844 } 845 } 846 }); 847 } 848 } 849 } 850 } 851 852 /** 853 * Produces 2D array of strings to facilitate multiple columns sorting 854 * tests. 855 * 856 * @param COLS number of columns in the table 857 * @return 2D array of strings 858 */ 859 public static String[][] getDataForSorting(final int COLS) { 860 int rows; 861 if (COLS < 32) { 862 rows = 1 << COLS; 863 } else { 864 rows = (int) Math.pow(2, COLS); 865 } 866 867 char alphabet[] = new char[52]; 868 for (int i = 0; i < 26; i++) { 869 alphabet[i] = (char) ('A' + i); 870 } 871 for (int i = 0; i < 26; i++) { 872 alphabet[i + 26] = (char) ('a' + i); 873 } 874 875 String[][] testData = new String[rows][]; 876 for (int x = 0; x < rows; x++) { 877 878 testData[x] = new String[COLS]; 879 880 for (int y = 0; y < COLS; y++) { 881 int pos = x / (rows >> y + 1); 882 if (pos >= 52) { 883 884 StringBuilder sb = new StringBuilder(); 885 886 int quotient = pos / 52; 887 while (quotient-- > 0) { 888 sb.append("z"); 889 } 890 891 sb.append(alphabet[pos % 52]); 892 testData[x][y] = sb.toString(); 893 894 } else { 895 testData[x][y] = String.valueOf(alphabet[pos]); 896 } 897 } 898 } 899 return testData; 900 } 901 }