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 362 return replaceButton; 363 } 364 365 VBox getAddNestedColumnVBox() { 366 final TextField nestedColumnAtIndexTF = new TextField("0"); 367 nestedColumnAtIndexTF.setPromptText("index"); 368 nestedColumnAtIndexTF.setId(NESTED_COLUMN_INDEX_TEXT_FIELD_ID); 369 370 final TextField nestedColumnNameTF = new TextField(); 371 nestedColumnNameTF.setPromptText("namex"); 372 nestedColumnNameTF.setId(NESTED_COLUMN_NAME_TEXT_FIELD_ID); 373 374 HBox nestedColumnAdditionalNodes = new HBox(); 375 nestedColumnAdditionalNodes.getChildren().addAll(nestedColumnAtIndexTF, nestedColumnNameTF); 376 377 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 378 public void onAction(int[] indices) { 379 int[] reversed = new int[indices.length]; 380 for (int i = 0; i < indices.length; i++) { 381 reversed[i] = indices[indices.length - i - 1]; 382 } 383 TableColumn nestedColumn = new TableColumn(nestedColumnNameTF.getText()); 384 for (int i : indices) { 385 nestedColumn.getColumns().add(testedTableView.getColumns().get(i)); 386 } 387 for (int i : reversed) { 388 testedTableView.getColumns().remove(i); 389 } 390 tabPane.addPropertiesTable(nestedColumnNameTF.getText(), NodeControllerFactory.createFullController(nestedColumn, tabPane)); 391 testedTableView.getColumns().add(Integer.valueOf(nestedColumnAtIndexTF.getText()), nestedColumn); 392 } 393 }; 394 395 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 396 "Add nested column", 397 nestedColumnAdditionalNodes, 398 executor, 399 CREATE_NESTED_COLUMN_MULTIPLE_ACTION_BUTTON_ID, 400 CREATE_NESTED_COLUMN_MULTIPLE_TEXTFIELD_ID); 401 402 return multipleIndexForm; 403 } 404 405 VBox getRemoveColumnsVBox() { 406 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 407 public void onAction(int[] indices) { 408 ObservableList onRemoving = FXCollections.observableArrayList(); 409 for (int index : indices) { 410 onRemoving.add(testedTableView.getColumns().get(index)); 411 } 412 testedTableView.getColumns().removeAll(onRemoving); 413 } 414 }; 415 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 416 "Remove columns", null, executor, 417 REMOVE_MULTIPLE_COLUMNS_ACTION_BUTTON_ID, 418 REMOVE_MULTIPLE_COLUMNS_TEXT_FIELD_ID); 419 420 return multipleIndexForm; 421 } 422 423 VBox getRemoveDataVBox() { 424 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 425 public void onAction(int[] indices) { 426 ObservableList onRemoving = FXCollections.observableArrayList(); 427 for (int index : indices) { 428 onRemoving.add(testedTableView.getItems().get(index)); 429 } 430 testedTableView.getItems().removeAll(onRemoving); 431 } 432 }; 433 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 434 "Remove data items", null, executor, 435 REMOVE_DATA_ITEMS_MULTIPLE_ACTION_BUTTON_ID, 436 REMOVE_DATA_ITEMS_MULTIPLE_TEXT_FIELD_ID); 437 438 return multipleIndexForm; 439 } 440 441 private Pane controlsForEditing() { 442 443 VBox topContainer = new VBox(3.5); 444 HBox hb = new HBox(3.0); 445 446 final ComboBox cmbEditors = new ComboBox(); 447 cmbEditors.setId(CMB_EDITORS_ID); 448 cmbEditors.getItems().addAll((Object[]) CellEditorType.values()); 449 450 final CheckBox chbCustom = new CheckBox("Custom"); 451 chbCustom.setId(CHB_IS_CUSTOM_ID); 452 453 Button btnSetEditor = new Button("Set editor"); 454 btnSetEditor.setId(BTN_SET_CELLS_EDITOR_ID); 455 btnSetEditor.setOnAction(new EventHandler<ActionEvent>() { 456 public void handle(ActionEvent t) { 457 CellEditorType editor = (CellEditorType) cmbEditors.getSelectionModel().getSelectedItem(); 458 setCellEditor(editor, chbCustom.isSelected()); 459 } 460 }); 461 462 hb.getChildren().addAll(cmbEditors, chbCustom, btnSetEditor); 463 464 Button btn = new Button("Set onEdit event hadlers"); 465 btn.setId(BTN_SET_ON_EDIT_EVENT_HANDLERS); 466 467 btn.setOnAction(new EventHandler<ActionEvent>() { 468 public void handle(ActionEvent t) { 469 final EventHandler eventHandlerOnEditStart = new EventHandler() { 470 public void handle(Event t) { 471 // new Throwable().printStackTrace(); 472 tb.incrementCounter(COUNTER_EDIT_START); 473 } 474 }; 475 476 final EventHandler eventHandlerOnEditCommit = new EventHandler() { 477 public void handle(Event t) { 478 tb.incrementCounter(COUNTER_EDIT_COMMIT); 479 } 480 }; 481 482 final EventHandler eventHandlerOnEditCancel = new EventHandler() { 483 public void handle(Event t) { 484 t.consume(); 485 tb.incrementCounter(COUNTER_EDIT_CANCEL); 486 } 487 }; 488 489 for (TableColumn col : testedTableView.getColumns()) { 490 col.setOnEditStart(eventHandlerOnEditStart); 491 assertTrue(eventHandlerOnEditStart == col.getOnEditStart()); 492 493 col.setOnEditCommit(eventHandlerOnEditCommit); 494 assertTrue(eventHandlerOnEditCommit == col.getOnEditCommit()); 495 496 col.setOnEditCancel(eventHandlerOnEditCancel); 497 assertTrue(eventHandlerOnEditCancel == col.getOnEditCancel()); 498 } 499 } 500 }); 501 502 topContainer.getChildren().addAll(hb, btn); 503 return topContainer; 504 } 505 506 private void introduceColumn(String columnName, TableColumn column) { 507 existingColumns.put(columnName, column); 508 int counter = 0; 509 for (DataItem item : allData) { 510 item.add(columnName, new SimpleStringProperty(columnName + "-" + String.valueOf(counter))); 511 counter++; 512 } 513 } 514 515 /** 516 * This class contain HashMap, which contain dynamically generated data. 517 * For each line in the Table, when you add additional column, you 518 * should add additional key-value pair in map for all dataItems in 519 * allData observable list. 520 */ 521 public class DataItem implements Comparable { 522 523 private HashMap<String, StringProperty> data = new HashMap<String, StringProperty>(); 524 525 public void add(String string, StringProperty property) { 526 data.put(string, property); 527 } 528 529 public StringProperty get(String name) { 530 return data.get(name); 531 } 532 533 public int compareTo(Object that) { 534 if (this == that) { 535 return 0; 536 } 537 538 DataItem other = (DataItem) that; 539 540 if (data.size() != other.data.size()) { 541 throw new IllegalStateException("[All data items in the table must have equal number of fields]"); 542 } 543 544 int res = 0; 545 546 for (String key : data.keySet()) { 547 res = data.get(key).get().compareTo(other.data.get(key).get()); 548 if (res != 0) { 549 break; 550 } 551 } 552 553 return res; 554 } 555 } 556 557 private void setCellEditor(CellEditorType editor, boolean isCustom) { 558 if (null == editor) { 559 System.out.println("Editor is not selected"); 560 return; 561 } 562 563 switch (editor) { 564 case TEXT_FIELD: 565 setTextFieldCellEditor(isCustom); 566 break; 567 case COMBOBOX: 568 setComboboxCellEditor(isCustom); 569 break; 570 case CHOICEBOX: 571 setChoiceboxCellEditor(isCustom); 572 break; 573 default: 574 throw new UnsupportedOperationException(editor.toString()); 575 } 576 577 System.out.println(String.format("Editor set: %s %s", 578 isCustom ? "custom" : "", 579 editor.toString())); 580 } 581 582 private void setTextFieldCellEditor(boolean isCustom) { 583 for (TableColumn col : testedTableView.getColumns()) { 584 if (!isCustom) { 585 col.setCellFactory(TextFieldTableCell.forTableColumn()); 586 } else { 587 col.setCellFactory(new Callback() { 588 public Object call(Object p) { 589 return new EditingTextFieldCell(); 590 } 591 }); 592 } 593 } 594 } 595 596 private void setComboboxCellEditor(boolean isCustom) { 597 598 for (int i = 0; i < testedTableView.getColumns().size(); i++) { 599 TableColumn col = testedTableView.getColumns().get(i); 600 String colName = col.getText(); 601 final ObservableList<String> items = FXCollections.observableArrayList(); 602 603 for (DataItem dataItem : allData) { 604 items.add(dataItem.get(colName).get()); 605 } 606 if (!isCustom) { 607 col.setCellFactory(ComboBoxTableCell.forTableColumn(items)); 608 } else { 609 col.setCellFactory(new Callback() { 610 public Object call(Object p) { 611 return new EditingComboBoxCell(items); 612 } 613 }); 614 } 615 } 616 } 617 618 private void setChoiceboxCellEditor(boolean isCustom) { 619 620 for (int i = 0; i < testedTableView.getColumns().size(); i++) { 621 TableColumn col = testedTableView.getColumns().get(i); 622 String colName = col.getText(); 623 final ObservableList<String> items = FXCollections.observableArrayList(); 624 625 for (DataItem dataItem : allData) { 626 items.add(dataItem.get(colName).get()); 627 } 628 if (!isCustom) { 629 col.setCellFactory(ChoiceBoxTableCell.forTableColumn(items)); 630 } else { 631 col.setCellFactory(new Callback() { 632 public Object call(Object p) { 633 return new EditingChoiceBoxCell(items); 634 } 635 }); 636 } 637 } 638 } 639 640 private class EditingTextFieldCell extends TableCell { 641 642 private TextField textField; 643 644 public EditingTextFieldCell() { 645 setId(EDITING_TEXTFIELD_CELL_ID); 646 } 647 648 @Override 649 public void startEdit() { 650 if (!isEmpty()) { 651 super.startEdit(); 652 createTextField(); 653 654 setText(null); 655 setGraphic(textField); 656 } 657 } 658 659 @Override 660 public void cancelEdit() { 661 super.cancelEdit(); 662 663 setText(getString()); 664 setGraphic(null); 665 } 666 667 @Override 668 public void updateItem(Object item, boolean empty) { 669 super.updateItem(item, empty); 670 671 if (empty) { 672 setText(null); 673 setGraphic(null); 674 } else { 675 if (isEditing()) { 676 if (textField != null) { 677 textField.setText(getString()); 678 } 679 setText(null); 680 setGraphic(textField); 681 } else { 682 setText(getString()); 683 setGraphic(null); 684 } 685 } 686 } 687 688 private void createTextField() { 689 textField = new TextField(getString()); 690 textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); 691 692 textField.setOnKeyReleased(new EventHandler<KeyEvent>() { 693 @Override 694 public void handle(KeyEvent t) { 695 if (t.getCode() == KeyCode.ENTER) { 696 commitEdit(textField.getText()); 697 } else if (t.getCode() == KeyCode.ESCAPE) { 698 cancelEdit(); 699 } 700 } 701 }); 702 } 703 704 private String getString() { 705 return getItem() == null ? "" : getItem().toString(); 706 } 707 } 708 709 private class EditingComboBoxCell extends TableCell { 710 711 ObservableList items; 712 ComboBox comboBox; 713 714 public EditingComboBoxCell(ObservableList _items) { 715 items = _items; 716 setId(EDITING_COMBOBOX_CELL_ID); 717 } 718 719 @Override 720 public void startEdit() { 721 if (isEmpty()) { 722 return; 723 } 724 createComboBox(); 725 comboBox.getSelectionModel().select(getItem()); 726 727 super.startEdit(); 728 729 setText(null); 730 setGraphic(comboBox); 731 } 732 733 @Override 734 public void cancelEdit() { 735 super.cancelEdit(); 736 setGraphic(null); 737 setText(getString()); 738 } 739 740 @Override 741 public void updateItem(Object item, boolean isEmpty) { 742 super.updateItem(item, isEmpty); 743 if (isEmpty()) { 744 setText(null); 745 setGraphic(null); 746 } else { 747 if (isEditing()) { 748 if (comboBox != null) { 749 comboBox.getSelectionModel().select(getItem()); 750 } 751 setText(null); 752 setGraphic(comboBox); 753 } else { 754 setText(getString()); 755 setGraphic(null); 756 } 757 } 758 } 759 760 private String getString() { 761 return getItem() == null ? "" : getItem().toString(); 762 } 763 764 private void createComboBox() { 765 if (null == comboBox) { 766 comboBox = new ComboBox(items); 767 comboBox.setMaxWidth(Double.MAX_VALUE); 768 comboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 769 @Override 770 public void changed(ObservableValue ov, Object oldValue, Object newValue) { 771 if (isEditing()) { 772 commitEdit(newValue); 773 } 774 } 775 }); 776 } 777 } 778 } 779 780 private class EditingChoiceBoxCell extends TableCell { 781 782 ObservableList items; 783 ChoiceBox choiceBox; 784 785 public EditingChoiceBoxCell(ObservableList _items) { 786 items = _items; 787 setId(EDITING_CHOICEBOX_CELL_ID); 788 } 789 790 @Override 791 public void startEdit() { 792 if (isEmpty()) { 793 return; 794 } 795 796 createComboBox(); 797 798 choiceBox.getSelectionModel().select(getItem()); 799 800 super.startEdit(); 801 setText(null); 802 setGraphic(choiceBox); 803 } 804 805 @Override 806 public void cancelEdit() { 807 super.cancelEdit(); 808 setGraphic(null); 809 setText(getString()); 810 } 811 812 @Override 813 public void updateItem(Object item, boolean isEmpty) { 814 super.updateItem(item, isEmpty); 815 if (isEmpty()) { 816 setText(null); 817 setGraphic(null); 818 } else { 819 if (isEditing()) { 820 if (choiceBox != null) { 821 choiceBox.getSelectionModel().select(getItem()); 822 } 823 setText(null); 824 setGraphic(choiceBox); 825 } else { 826 setText(getString()); 827 setGraphic(null); 828 } 829 } 830 } 831 832 private String getString() { 833 return getItem() == null ? "" : getItem().toString(); 834 } 835 836 private void createComboBox() { 837 if (null == choiceBox) { 838 choiceBox = new ChoiceBox(items); 839 choiceBox.setMaxWidth(Double.MAX_VALUE); 840 choiceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 841 @Override 842 public void changed(ObservableValue ov, Object oldValue, Object newValue) { 843 if (isEditing()) { 844 commitEdit(newValue); 845 } 846 } 847 }); 848 } 849 } 850 } 851 } 852 853 /** 854 * Produces 2D array of strings to facilitate multiple columns sorting 855 * tests. 856 * 857 * @param COLS number of columns in the table 858 * @return 2D array of strings 859 */ 860 public static String[][] getDataForSorting(final int COLS) { 861 int rows; 862 if (COLS < 32) { 863 rows = 1 << COLS; 864 } else { 865 rows = (int) Math.pow(2, COLS); 866 } 867 868 char alphabet[] = new char[52]; 869 for (int i = 0; i < 26; i++) { 870 alphabet[i] = (char) ('A' + i); 871 } 872 for (int i = 0; i < 26; i++) { 873 alphabet[i + 26] = (char) ('a' + i); 874 } 875 876 String[][] testData = new String[rows][]; 877 for (int x = 0; x < rows; x++) { 878 879 testData[x] = new String[COLS]; 880 881 for (int y = 0; y < COLS; y++) { 882 int pos = x / (rows >> y + 1); 883 if (pos >= 52) { 884 885 StringBuilder sb = new StringBuilder(); 886 887 int quotient = pos / 52; 888 while (quotient-- > 0) { 889 sb.append("z"); 890 } 891 892 sb.append(alphabet[pos % 52]); 893 testData[x][y] = sb.toString(); 894 895 } else { 896 testData[x][y] = String.valueOf(alphabet[pos]); 897 } 898 } 899 } 900 return testData; 901 } 902 } 903