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.treetable; 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.TreeTableViewSkin; 31 import java.util.ArrayList; 32 import java.util.Comparator; 33 import java.util.HashMap; 34 import java.util.Iterator; 35 import java.util.Map; 36 import javafx.beans.property.SimpleStringProperty; 37 import javafx.beans.property.StringProperty; 38 import javafx.beans.value.ChangeListener; 39 import javafx.beans.value.ObservableValue; 40 import javafx.collections.FXCollections; 41 import javafx.collections.ObservableList; 42 import static javafx.commons.Consts.*; 43 import javafx.commons.Consts.CellEditorType; 44 import javafx.event.ActionEvent; 45 import javafx.event.Event; 46 import javafx.event.EventHandler; 47 import javafx.geometry.Insets; 48 import javafx.geometry.Orientation; 49 import javafx.scene.Node; 50 import javafx.scene.Scene; 51 import javafx.scene.control.*; 52 import javafx.scene.control.TreeTableColumn.CellDataFeatures; 53 import javafx.scene.control.cell.ChoiceBoxTreeTableCell; 54 import javafx.scene.control.cell.ComboBoxTreeTableCell; 55 import javafx.scene.control.cell.TextFieldTreeTableCell; 56 import javafx.scene.control.test.tableview.NewTableViewApp; 57 import static javafx.scene.control.test.treetable.ResetButtonNames.HARD_RESET_BUTTON_ID; 58 import javafx.scene.control.test.treeview.TreeViewConstants; 59 import javafx.scene.control.test.utils.CommonPropertiesScene; 60 import javafx.scene.control.test.utils.ComponentsFactory.MultipleIndexFormComponent; 61 import javafx.scene.control.test.utils.ComponentsFactory.MultipleIndexFormComponent.MultipleIndicesAction; 62 import javafx.scene.control.test.utils.ptables.NodeControllerFactory; 63 import javafx.scene.control.test.utils.ptables.PropertiesTable; 64 import javafx.scene.control.test.utils.ptables.PropertyTablesFactory; 65 import javafx.scene.control.test.utils.ptables.SpecialTablePropertiesProvider; 66 import javafx.scene.control.test.utils.ptables.TabPaneWithControl; 67 import javafx.scene.input.KeyCode; 68 import javafx.scene.input.KeyEvent; 69 import javafx.scene.layout.HBox; 70 import javafx.scene.layout.Pane; 71 import javafx.scene.layout.VBox; 72 import javafx.util.Callback; 73 import test.javaclient.shared.InteroperabilityApp; 74 import test.javaclient.shared.Utils; 75 import static org.junit.Assert.assertTrue; 76 77 /** 78 * @author Alexander Kirov 79 */ 80 public class TreeTableNewApp extends InteroperabilityApp implements TreeViewConstants, ResetButtonNames { 81 82 public final static String TESTED_TREETABLEVIEW_ID = "TESTED_TREETABLEVIEW_ID"; 83 public final static String TREE_DATA_COLUMN_NAME = "TreeData"; 84 public final static String REMOVE_MULTIPLE_COLUMNS_TEXT_FIELD_ID = "REMOVE_MULTIPLE_COLUMNS_TEXT_FIELD_ID"; 85 public final static String REMOVE_MULTIPLE_COLUMNS_ACTION_BUTTON_ID = "REMOVE_MULTIPLE_COLUMNS_ACTION_BUTTON_ID"; 86 public final static String REMOVE_DATA_ITEMS_MULTIPLE_TEXT_FIELD_ID = "REMOVE_DATA_ITEMS_MULTIPLE_TEXT_FIELD_ID"; 87 public final static String REMOVE_DATA_ITEMS_MULTIPLE_ACTION_BUTTON_ID = "REMOVE_DATA_ITEMS_MULTIPLE_ACTION_BUTTON_ID"; 88 public final static String CREATE_NESTED_COLUMN_MULTIPLE_TEXTFIELD_ID = "CREATE_NESTED_COLUMN_MULTIPLE_TEXTFIELD_ID"; 89 public final static String CREATE_NESTED_COLUMN_MULTIPLE_ACTION_BUTTON_ID = "CREATE_NESTED_COLUMN_MULTIPLE_ACTION_BUTTON_ID"; 90 public final static String TREE_TABLE_VIEW_TAB_NAME = "TreeTableView"; 91 public final static Comparator<TreeItem<TreeTableNewApp.DataItem>> DEFAULT_TREE_ITEM_COMPARATOR = new Comparator<TreeItem<TreeTableNewApp.DataItem>>() { 92 public int compare(TreeItem<TreeTableNewApp.DataItem> t1, TreeItem<TreeTableNewApp.DataItem> t2) { 93 DataItem first = t1.getValue(); 94 DataItem second = t2.getValue(); 95 return first.compareTo(second); 96 } 97 }; 98 99 public static void main(String[] args) { 100 Utils.launch(TreeTableNewApp.class, args); 101 } 102 103 @Override 104 protected Scene getScene() { 105 Utils.setTitleToStage(stage, "TreeTableTestApp"); 106 return new TreeTableScene(); 107 } 108 109 public static TreeItem searchTreeItem(TreeTableView treeView, String content) { 110 if (treeView.getRoot() != null) { 111 return recursiveSearch(content, treeView.getRoot()); 112 } 113 return null; 114 } 115 116 protected static TreeItem recursiveSearch(String content, TreeItem<? extends DataItem> itemToStart) { 117 if (null != itemToStart.getValue() 118 && (content.equals(itemToStart.getValue().getTreeValue().getValue()) 119 || itemToStart.getValue().contains(content))) { 120 return itemToStart; 121 } else { 122 for (TreeItem child : itemToStart.getChildren()) { 123 TreeItem recResult = recursiveSearch(content, child); 124 if (recResult != null) { 125 return recResult; 126 } 127 } 128 return null; 129 } 130 } 131 132 protected class TreeTableScene extends CommonPropertiesScene { 133 134 //TreeTableView instance to be tested. 135 private TreeTableView testedControl; 136 //TabPane with PropertiesTable. 137 private TabPaneWithControl tabPane; 138 //Properties table of the control 139 private PropertiesTable tb; 140 //Container for all the data. 141 private ObservableList<DataItem> allData; 142 //List of existing columnsin current tableView. 143 private Map<String, TreeTableColumn> existingColumns = new HashMap<String, TreeTableColumn>(); 144 //This list contains all properties tables, which were created during testing. 145 private ArrayList<PropertiesTable> allPropertiesTables; 146 147 public TreeTableScene() { 148 super("TreeTableControl", 800, 600); 149 prepareScene(); 150 } 151 152 protected TreeTableView getTestedTreeTable() { 153 TreeTableView tv = new TreeTableView(); 154 DataItem item = new DataItem(ROOT_NAME); 155 allData.add(item); 156 tv.setRoot(new TreeItem(item)); 157 tv.setShowRoot(true); 158 tv.setId(TESTED_TREETABLEVIEW_ID); 159 return tv; 160 } 161 162 @Override 163 final protected void prepareScene() { 164 allData = FXCollections.observableArrayList(); 165 testedControl = getTestedTreeTable(); 166 tb = new PropertiesTable(testedControl); 167 168 tb.addCounter(EDIT_START_COUNTER); 169 tb.addCounter(EDIT_COMMIT_COUNTER); 170 tb.addCounter(EDIT_CANCEL_COUNTER); 171 tb.addCounter(COUNTER_ON_SORT); 172 testedControl.setOnSort(new EventHandler<SortEvent<TableView<DataItem>>>() { 173 public void handle(SortEvent<TableView<DataItem>> event) { 174 tb.incrementCounter(COUNTER_ON_SORT); 175 } 176 }); 177 178 allPropertiesTables = new ArrayList<PropertiesTable>(); 179 180 PropertyTablesFactory.explorePropertiesList(testedControl, tb); 181 PropertyTablesFactory.explorePropertiesList(testedControl.getSelectionModel(), tb); 182 PropertyTablesFactory.explorePropertiesList(testedControl.getFocusModel(), tb); 183 SpecialTablePropertiesProvider.provideForControl(testedControl, tb); 184 185 tabPane = new TabPaneWithControl(TREE_TABLE_VIEW_TAB_NAME, tb); 186 getControlOverItem(ROOT_NAME); 187 188 tb.setStyle("-fx-border-color : yellow;"); 189 190 VBox vb = new VBox(); 191 vb.setSpacing(5); 192 193 HBox hb = (HBox) getRoot(); 194 hb.setPadding(new Insets(5, 5, 5, 5)); 195 hb.setStyle("-fx-border-color : green;"); 196 197 Button resetButton = new Button("Reset"); 198 resetButton.setId(HARD_RESET_BUTTON_ID); 199 resetButton.setOnAction(new EventHandler<ActionEvent>() { 200 public void handle(ActionEvent t) { 201 HBox hb = (HBox) getRoot(); 202 hb.getChildren().clear(); 203 prepareMainSceneStructure(); 204 prepareScene(); 205 } 206 }); 207 208 Button autosize = new Button("Autosize"); 209 autosize.setId(AUTOSIZE_BUTTON_ID); 210 autosize.setOnAction(new EventHandler<ActionEvent>() { 211 public void handle(ActionEvent t) { 212 testedControl.autosize(); 213 } 214 }); 215 216 VBox vbActionMakers = new VBox(); 217 218 vbActionMakers.getChildren().addAll(resetButton, autosize, new Separator(Orientation.HORIZONTAL), 219 getAddColumnForm(), new Separator(Orientation.HORIZONTAL), 220 getAddNestedColumnVBox(), new Separator(Orientation.HORIZONTAL), 221 getChangeDataSizeForm(), new Separator(Orientation.HORIZONTAL), 222 getRemoveColumnsVBox(), new Separator(Orientation.HORIZONTAL), 223 getTreeViewAddItemController(), new Separator(Orientation.HORIZONTAL), 224 getTreeViewRemoveItemController(), new Separator(Orientation.HORIZONTAL), 225 getTabOverItemThroughGetItemMethodController(), new Separator(Orientation.HORIZONTAL), 226 getTabOverItemController(), new Separator(Orientation.HORIZONTAL), 227 getObjectTitleThroughGetItemMethodController(), new Separator(Orientation.HORIZONTAL), 228 getLineOfItemController(), new Separator(Orientation.HORIZONTAL), 229 getScrollToHBox(), new Separator(Orientation.HORIZONTAL), 230 getEditHBox(), new Separator(Orientation.HORIZONTAL), 231 getReplaceTableHeaderImplementationButton(), new Separator(Orientation.HORIZONTAL), 232 setEventHandlersHBox(), new Separator(Orientation.HORIZONTAL), 233 controlsForEditing()); 234 235 ScrollPane scrollPaneWithActionMakers = new ScrollPane(); 236 scrollPaneWithActionMakers.setContent(vbActionMakers); 237 scrollPaneWithActionMakers.setPrefHeight(1000); 238 scrollPaneWithActionMakers.setPannable(true); 239 240 setTestedControl(testedControl); 241 setPropertiesContent(tabPane); 242 setControllersContent(scrollPaneWithActionMakers); 243 setTestedControlContainerSize(300, 220); 244 } 245 246 private HBox getChangeDataSizeForm() { 247 final TextField sizeTf = new TextField(); 248 sizeTf.setId(NEW_DATA_SIZE_TEXTFIELD_ID); 249 sizeTf.setPromptText("new size (rows)"); 250 251 Button button = new Button("set"); 252 button.setId(NEW_DATA_SIZE_BUTTON_ID); 253 button.setOnAction(new EventHandler<ActionEvent>() { 254 public void handle(ActionEvent t) { 255 if (!testedControl.getRoot().isExpanded()) { 256 testedControl.getRoot().setExpanded(true); 257 } 258 int actualSize = testedControl.getRoot().getChildren().size(); 259 int newSize = Integer.parseInt(sizeTf.getText()); 260 if (actualSize > newSize) { 261 int toRemove = actualSize - newSize; 262 for (int i = 0; i < toRemove; i++) { 263 allData.remove(0); 264 testedControl.getRoot().getChildren().remove(0); 265 } 266 } else if (actualSize < newSize) { 267 int toAdd = newSize - actualSize; 268 for (int i = 0; i < toAdd; i++) { 269 DataItem dataItem = new DataItem(); 270 for (String columnName : existingColumns.keySet()) { 271 dataItem.add(columnName, new SimpleStringProperty(columnName + "-" + String.valueOf(actualSize + i))); 272 } 273 allData.add(dataItem); 274 testedControl.getRoot().getChildren().add(new TreeItem(dataItem)); 275 } 276 } 277 } 278 }); 279 280 HBox hb1 = new HBox(3); 281 hb1.getChildren().addAll(new Label("New size:"), sizeTf, button); 282 283 Button addSortableRows = new Button("Add sortable rows"); 284 addSortableRows.setId(BTN_CREATE_SORTABLE_ROWS_ID); 285 addSortableRows.setOnAction(new EventHandler<ActionEvent>() { 286 public void handle(ActionEvent t) { 287 allData.clear(); 288 TreeItem root = testedControl.getRoot(); 289 root.getChildren().clear(); 290 root.setExpanded(true); 291 292 ObservableList<TableColumn<DataItem, ?>> columns = testedControl.getColumns(); 293 String[][] testData = NewTableViewApp.getDataForSorting(columns.size()); 294 295 for (int x = 0; x < testData.length; x++) { 296 DataItem dataItem = new DataItem(); 297 int z = 0; 298 //Use the correct order of columns as they are placed. 299 //From left to right 300 for (int i = 0; i < testData[i].length; i++) { 301 TableColumnBase col = columns.get(i); 302 dataItem.add(col.getText(), new SimpleStringProperty(testData[x][z++])); 303 } 304 allData.add(dataItem); 305 306 root.getChildren().add(new TreeItem(dataItem)); 307 } 308 } 309 }); 310 311 HBox topContainer = new HBox(); 312 313 VBox internalContainer = new VBox(5.0); 314 internalContainer.getChildren().addAll(hb1, addSortableRows); 315 316 topContainer.getChildren().add(internalContainer); 317 return topContainer; 318 319 // return hb1; 320 } 321 322 private VBox getAddColumnForm() { 323 final TextField tfColumnName = new TextField(); 324 tfColumnName.setPromptText("new column name"); 325 tfColumnName.setId(NEW_COLUMN_NAME_TEXTFIELD_ID); 326 327 final TextField indexTf = new TextField(); 328 indexTf.setPromptText("at index"); 329 indexTf.setId(NEW_COLUMN_INDEX_TEXTFIELD_UD); 330 331 final CheckBox addDataPropertiesTabToTab = new CheckBox("with properties table for data"); 332 addDataPropertiesTabToTab.setId(NEW_COLUMN_GET_DATA_PROPERTIES_TAB_ID); 333 334 final CheckBox addColumnPropertiesTableToTab = new CheckBox("with properties table for column"); 335 addColumnPropertiesTableToTab.setId(NEW_COLUMN_GET_COLUMN_PROPERTIES_TAB_ID); 336 337 Button button = new Button("Add"); 338 button.setId(NEW_COLUMN_ADD_BUTTON_ID); 339 button.setOnAction(new EventHandler<ActionEvent>() { 340 public void handle(ActionEvent t) { 341 final String name = tfColumnName.getText(); 342 int index = Integer.parseInt(indexTf.getText()); 343 TreeTableColumn column = new TreeTableColumn(name); 344 if (TREE_DATA_COLUMN_NAME.equals(name)) { 345 column.setCellValueFactory(new Callback<CellDataFeatures<DataItem, String>, ObservableValue<String>>() { 346 public ObservableValue<String> call(CellDataFeatures<DataItem, String> p) { 347 return p.getValue().getValue().getTreeValue(); 348 } 349 }); 350 } else { 351 column.setCellValueFactory(new Callback<CellDataFeatures<DataItem, String>, ObservableValue<String>>() { 352 public ObservableValue<String> call(CellDataFeatures<DataItem, String> p) { 353 return p.getValue().getValue().get(name); 354 } 355 }); 356 } 357 introduceColumn(name, column); 358 if (addColumnPropertiesTableToTab.isSelected()) { 359 tabPane.addPropertiesTable(name, NodeControllerFactory.createFullController(column, tabPane)); 360 } 361 testedControl.getColumns().add(index, column); 362 if (addDataPropertiesTabToTab.isSelected()) { 363 final PropertiesTable forData = new PropertiesTable(null); 364 for (DataItem item : allData) { 365 forData.addStringLine(item.get(name), "new value"); 366 } 367 tabPane.addPropertiesTable(name + " data", forData.getVisualRepresentation()); 368 } 369 } 370 }); 371 372 HBox hb1 = new HBox(3); 373 hb1.getChildren().addAll(new Label("With name"), tfColumnName); 374 375 HBox hb2 = new HBox(3); 376 hb2.getChildren().addAll(new Label("At index"), indexTf, button); 377 378 VBox vb = new VBox(); 379 vb.getChildren().addAll(hb1, addDataPropertiesTabToTab, addColumnPropertiesTableToTab, hb2); 380 return vb; 381 } 382 383 VBox getAddNestedColumnVBox() { 384 final TextField nestedColumnAtIndexTF = new TextField("0"); 385 nestedColumnAtIndexTF.setPromptText("index"); 386 nestedColumnAtIndexTF.setId(NESTED_COLUMN_INDEX_TEXT_FIELD_ID); 387 388 final TextField nestedColumnNameTF = new TextField(); 389 nestedColumnNameTF.setPromptText("namex"); 390 nestedColumnNameTF.setId(NESTED_COLUMN_NAME_TEXT_FIELD_ID); 391 392 HBox nestedColumnAdditionalNodes = new HBox(); 393 nestedColumnAdditionalNodes.getChildren().addAll(nestedColumnAtIndexTF, nestedColumnNameTF); 394 395 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 396 public void onAction(int[] indices) { 397 int[] reversed = new int[indices.length]; 398 for (int i = 0; i < indices.length; i++) { 399 reversed[i] = indices[indices.length - i - 1]; 400 } 401 TreeTableColumn nestedColumn = new TreeTableColumn(nestedColumnNameTF.getText()); 402 for (int i : indices) { 403 nestedColumn.getColumns().add(testedControl.getColumns().get(i)); 404 } 405 for (int i : reversed) { 406 testedControl.getColumns().remove(i); 407 } 408 tabPane.addPropertiesTable(nestedColumnNameTF.getText(), NodeControllerFactory.createFullController(nestedColumn, tabPane)); 409 testedControl.getColumns().add(Integer.valueOf(nestedColumnAtIndexTF.getText()), nestedColumn); 410 } 411 }; 412 413 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 414 "Add nested column", nestedColumnAdditionalNodes, executor, 415 CREATE_NESTED_COLUMN_MULTIPLE_ACTION_BUTTON_ID, 416 CREATE_NESTED_COLUMN_MULTIPLE_TEXTFIELD_ID); 417 418 return multipleIndexForm; 419 } 420 421 protected VBox getRemoveColumnsVBox() { 422 MultipleIndicesAction executor = new MultipleIndexFormComponent.MultipleIndicesAction() { 423 public void onAction(int[] indices) { 424 ObservableList onRemoving = FXCollections.observableArrayList(); 425 for (int index : indices) { 426 onRemoving.add(testedControl.getColumns().get(index)); 427 } 428 testedControl.getColumns().removeAll(onRemoving); 429 } 430 }; 431 MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent( 432 "Remove columns", null, executor, 433 REMOVE_MULTIPLE_COLUMNS_ACTION_BUTTON_ID, 434 REMOVE_MULTIPLE_COLUMNS_TEXT_FIELD_ID); 435 return multipleIndexForm; 436 } 437 // 438 // VBox getRemoveDataVBox() { 439 // MultipleIndexFormComponent multipleIndexForm = new MultipleIndexFormComponent("Remove data items", null, new MultipleIndexFormComponent.MultipleIndicesAction() { 440 // public void onAction(int[] indices) { 441 // ObservableList onRemoving = FXCollections.observableArrayList(); 442 // for (int index : indices) { 443 // onRemoving.add(testedControl.getTreeItem(index)); 444 // } 445 // testedControl.getItems().removeAll(onRemoving); 446 // } 447 // }); 448 // 449 // REMOVE_DATA_ITEMS_MULTIPLE_TEXT_FIELD_ID = multipleIndexForm.MULTIPLE_INDECES_TEXTFIELD_ID; 450 // REMOVE_DATA_ITEMS_MULTIPLE_ACTION_BUTTON_ID = multipleIndexForm.ACTION_BUTTON_ID; 451 // return multipleIndexForm; 452 // } 453 454 private void introduceColumn(String columnName, TreeTableColumn column) { 455 existingColumns.put(columnName, column); 456 int counter = 0; 457 for (DataItem item : allData) { 458 item.add(columnName, new SimpleStringProperty(columnName + "-" + String.valueOf(counter))); 459 counter++; 460 } 461 } 462 463 protected void getControlOverItem(String name) { 464 TreeItem<DataItem> treeItem = searchTreeItem(name); 465 NodeControllerFactory.NodesStorage node = NodeControllerFactory.createFullController(treeItem, tabPane); 466 allPropertiesTables.add(node.pt); 467 tabPane.addPropertiesTable(name, node); 468 } 469 470 protected VBox getTreeViewAddItemController() { 471 VBox vb = new VBox(3); 472 473 final TextField tfParentName = new TextField(); 474 tfParentName.setId(ADD_ITEM_PARENT_TEXT_FIELD_ID); 475 tfParentName.setPromptText("parent name"); 476 477 final TextField tfName = new TextField(); 478 tfName.setId(ADD_ITEM_TEXT_FIELD_ID); 479 tfName.setPromptText("new item name"); 480 481 final TextField tfPosition = new TextField(); 482 tfPosition.setId(ADD_ITEM_POSITION_TEXT_FIELD_ID);; 483 tfPosition.setPromptText("position"); 484 tfPosition.setPrefWidth(50); 485 486 Button button = new Button("add to pos"); 487 button.setId(ADD_ITEM_BUTTON_ID); 488 button.setOnAction(new EventHandler<ActionEvent>() { 489 public void handle(ActionEvent t) { 490 TreeItem<DataItem> treeItem = searchTreeItem(tfParentName.getText()); 491 treeItem.getChildren().add(Integer.parseInt(tfPosition.getText()), new TreeItem(new DataItem(tfName.getText()))); 492 } 493 }); 494 495 HBox hb1 = new HBox(3); 496 hb1.getChildren().addAll(new Label("Add child to "), tfParentName); 497 498 HBox hb2 = new HBox(3); 499 hb2.getChildren().addAll(new Label(" named "), tfName); 500 501 HBox hb3 = new HBox(3); 502 hb3.getChildren().addAll(tfPosition, button); 503 504 vb.getChildren().addAll(hb1, hb2, hb3); 505 506 Button makeHappy = new Button("Fill default data"); 507 makeHappy.setOnAction(new EventHandler<ActionEvent>() { 508 public void handle(ActionEvent event) { 509 testedControl.setRoot(new TreeItem(new DataItem("ROOT"))); 510 testedControl.getRoot().setExpanded(true); 511 512 for (int i = 0; i < 4; i++) { 513 TreeItem p = new TreeItem(new DataItem("Item-" + i)); 514 p.setExpanded(true); 515 testedControl.getRoot().getChildren().add(p); 516 for (int j = 0; j < 4; j++) { 517 p.getChildren().add(new TreeItem(new DataItem("Item-" + i + "-" + j))); 518 } 519 } 520 521 TreeTableColumn col = new TreeTableColumn("TreeData"); 522 col.setCellValueFactory(new Callback<CellDataFeatures<DataItem, String>, ObservableValue<String>>() { 523 public ObservableValue<String> call(CellDataFeatures<DataItem, String> p) { 524 return p.getValue().getValue().getTreeValue(); 525 } 526 }); 527 col.setMinWidth(144); 528 testedControl.getColumns().clear(); 529 testedControl.getColumns().add(col); 530 } 531 }); 532 533 vb.getChildren().add(makeHappy); 534 535 return vb; 536 } 537 538 protected VBox getTreeViewRemoveItemController() { 539 VBox vb = new VBox(3); 540 541 final TextField tfName = new TextField(); 542 tfName.setId(REMOVE_ITEM_TEXT_FIELD_ID); 543 tfName.setPromptText("new item name"); 544 545 Button button = new Button("remove!"); 546 button.setId(REMOVE_ITEM_BUTTON_ID); 547 button.setOnAction(new EventHandler<ActionEvent>() { 548 public void handle(ActionEvent t) { 549 TreeItem treeItem = searchTreeItem(tfName.getText()); 550 if (null != treeItem) { 551 if (testedControl.getRoot() == treeItem) { 552 testedControl.setRoot(null); 553 } else { 554 treeItem.getParent().getChildren().remove(treeItem); 555 } 556 } 557 } 558 }); 559 560 HBox hb1 = new HBox(3); 561 hb1.getChildren().addAll(new Label("Named item"), tfName); 562 563 HBox hb2 = new HBox(3); 564 hb2.getChildren().addAll(button); 565 566 vb.getChildren().addAll(hb1, hb2); 567 568 return vb; 569 } 570 571 protected VBox getTabOverItemController() { 572 final TextField tfParentName = new TextField(); 573 tfParentName.setId(GET_CONTROL_OVER_TREEITEM_TEXTFIELD_ID); 574 tfParentName.setPromptText("item name"); 575 576 Button button = new Button("get table over"); 577 button.setId(GET_CONTROL_OVER_TREEITEM_BUTTON_ID); 578 button.setOnAction(new EventHandler<ActionEvent>() { 579 public void handle(ActionEvent t) { 580 getControlOverItem(tfParentName.getText()); 581 } 582 }); 583 584 HBox hb1 = new HBox(3); 585 hb1.getChildren().addAll(tfParentName, button); 586 587 VBox vb = new VBox(); 588 vb.getChildren().addAll(hb1); 589 return vb; 590 } 591 592 protected VBox getObjectTitleThroughGetItemMethodController() { 593 final TextField tfParentName = new TextField(); 594 tfParentName.setPromptText("index"); 595 tfParentName.setPrefWidth(50); 596 597 final TextField content = new TextField(); 598 content.setPromptText("content"); 599 600 Button button = new Button("getTreeItem! index:"); 601 button.setOnAction(new EventHandler<ActionEvent>() { 602 public void handle(ActionEvent t) { 603 TreeItem item = testedControl.getTreeItem(Integer.parseInt(tfParentName.getText())); 604 if (item == null) { 605 content.setText("null"); 606 } else { 607 content.setText(item.getValue().toString()); 608 } 609 } 610 }); 611 612 HBox hb1 = new HBox(3); 613 hb1.getChildren().addAll(new Label("Get tree item at"), tfParentName); 614 615 HBox hb2 = new HBox(3); 616 hb2.getChildren().addAll(button, content); 617 618 VBox vb = new VBox(); 619 vb.getChildren().addAll(hb1, hb2); 620 return vb; 621 } 622 623 Node getReplaceTableHeaderImplementationButton() { 624 Button replaceButton = new Button("Replace skin implementation"); 625 replaceButton.setId(REPLACE_SKIN_IMPLEMENTATION_BUTTON_ID); 626 replaceButton.setOnAction(new EventHandler<ActionEvent>() { 627 public void handle(ActionEvent t) { 628 testedControl.setSkin(new TreeTableViewSkin(testedControl) { 629 @Override 630 public String toString() { 631 return "CUSTOM " + super.toString(); 632 } 633 /* 634 @Override 635 protected TableHeaderRow createTableHeaderRow() { 636 return new TableHeaderRow(this) { 637 @Override 638 protected NestedTableColumnHeader createRootHeader() { 639 return new NestedTableColumnHeader((TreeTableViewSkin) testedControl.getSkin(), null) { 640 @Override 641 protected TableColumnHeader createTableColumnHeader(TableColumnBase col) { 642 if (col.getColumns().isEmpty()) { 643 final TableColumnHeader tableColumnHeader = new TableColumnHeader(getTableViewSkin(), col); 644 tableColumnHeader.setId(CUSTOM_IMPLEMENTATION_MARKER); 645 return tableColumnHeader; 646 } else { 647 final NestedTableColumnHeader nestedTableColumnHeader = new NestedTableColumnHeader(getTableViewSkin(), col); 648 nestedTableColumnHeader.setId(CUSTOM_IMPLEMENTATION_MARKER); 649 return nestedTableColumnHeader; 650 } 651 } 652 }; 653 } 654 }; 655 } 656 */ 657 }); 658 } 659 }); 660 661 return replaceButton; 662 } 663 664 protected VBox getTabOverItemThroughGetItemMethodController() { 665 final TextField tfParentName = new TextField(); 666 tfParentName.setPromptText("index"); 667 tfParentName.setPrefWidth(50); 668 669 Button button = new Button("get table over"); 670 button.setOnAction(new EventHandler<ActionEvent>() { 671 public void handle(ActionEvent t) { 672 getControlOverItem(testedControl.getTreeItem(Integer.parseInt(tfParentName.getText())).getValue().toString()); 673 } 674 }); 675 676 HBox hb1 = new HBox(3); 677 hb1.getChildren().addAll(tfParentName, button); 678 679 VBox vb = new VBox(); 680 vb.getChildren().addAll(hb1); 681 return vb; 682 } 683 684 protected VBox getLineOfItemController() { 685 final TextField tfParentName = new TextField(); 686 tfParentName.setPromptText("name"); 687 688 final TextField index = new TextField(); 689 index.setPromptText("found index"); 690 index.setPrefWidth(50); 691 692 Button button = new Button("getRow! index :"); 693 button.setOnAction(new EventHandler<ActionEvent>() { 694 public void handle(ActionEvent t) { 695 int found = testedControl.getRow(searchTreeItem(tfParentName.getText())); 696 index.setText(((Integer) found).toString()); 697 } 698 }); 699 700 HBox hb1 = new HBox(3); 701 hb1.getChildren().addAll(new Label("Get index of"), tfParentName); 702 703 HBox hb2 = new HBox(3); 704 hb2.getChildren().addAll(index, button); 705 706 VBox vb = new VBox(); 707 vb.getChildren().addAll(hb1, hb2); 708 return vb; 709 } 710 711 private HBox getScrollToHBox() { 712 HBox hb = new HBox(); 713 Button button = new Button("ScrollTo"); 714 button.setId(SCROLL_TO_BUTTON_ID); 715 final TextField tf = new TextField("0"); 716 tf.setId(SCROLL_TO_TEXT_FIELD_ID); 717 tf.setPrefWidth(50); 718 719 button.setOnAction(new EventHandler<ActionEvent>() { 720 public void handle(ActionEvent t) { 721 testedControl.scrollTo(Integer.parseInt(tf.getText())); 722 } 723 }); 724 725 hb.getChildren().addAll(tf, button); 726 return hb; 727 } 728 729 private HBox getEditHBox() { 730 HBox hb = new HBox(); 731 Button button = new Button("Edit"); 732 button.setId(EDIT_BUTTON_ID); 733 final TextField tf = new TextField(); 734 tf.setPromptText("name"); 735 tf.setId(EDIT_TEXT_FIELD_ID); 736 tf.setPrefWidth(50); 737 738 button.setOnAction(new EventHandler<ActionEvent>() { 739 public void handle(ActionEvent t) { 740 throw new IllegalStateException("In build 101 was changed API of this function. Instead of 0, write index of edited treeItem please."); 741 //testedControl.edit(0/* searchTreeItem(tf.getText()) */, (TreeTableColumn) testedControl.getColumns().get(0)); 742 } 743 }); 744 745 hb.getChildren().addAll(tf, button); 746 return hb; 747 } 748 749 private VBox setEventHandlersHBox() { 750 VBox vb = new VBox(); 751 752 Button btn = new Button("Set onEdit event hadlers"); 753 btn.setId(BTN_SET_ON_EDIT_EVENT_HANDLERS); 754 btn.setOnAction(new EventHandler<ActionEvent>() { 755 final EventHandler eventHandlerOnEditStart = new EventHandler() { 756 public void handle(Event t) { 757 tb.incrementCounter(COUNTER_EDIT_START); 758 } 759 }; 760 final EventHandler eventHandlerOnEditCommit = new EventHandler<TreeTableColumn.CellEditEvent<DataItem, String>>() { 761 public void handle(TreeTableColumn.CellEditEvent<DataItem, String> event) { 762 tb.incrementCounter(COUNTER_EDIT_COMMIT); 763 DataItem data = event.getTreeTablePosition().getTreeItem().getValue(); 764 StringProperty prop = data.get(event.getTableColumn().getText()); 765 prop.setValue(event.getNewValue()); 766 } 767 }; 768 final EventHandler eventHandlerOnEditCancel = new EventHandler() { 769 public void handle(Event t) { 770 tb.incrementCounter(COUNTER_EDIT_CANCEL); 771 } 772 }; 773 774 public void handle(ActionEvent t) { 775 for (Iterator it = testedControl.getColumns().iterator(); it.hasNext();) { 776 TreeTableColumn col = (TreeTableColumn) it.next(); 777 778 col.setOnEditStart(eventHandlerOnEditStart); 779 assertTrue(eventHandlerOnEditStart == col.getOnEditStart()); 780 781 col.setOnEditCommit(eventHandlerOnEditCommit); 782 assertTrue(eventHandlerOnEditCommit == col.getOnEditCommit()); 783 784 col.setOnEditCancel(eventHandlerOnEditCancel); 785 assertTrue(eventHandlerOnEditCancel == col.getOnEditCancel()); 786 } 787 } 788 }); 789 790 Button btnAddFactory = new Button("Set cell factory for editing"); 791 btnAddFactory.setId(SET_CELL_FACTORY_FOR_EDITING); 792 btnAddFactory.setOnAction(new EventHandler<ActionEvent>() { 793 public void handle(ActionEvent t) { 794 for (Object obj : testedControl.getColumns()) { 795 TreeTableColumn col = (TreeTableColumn) obj; 796 col.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn()); 797 } 798 } 799 }); 800 801 vb.getChildren().addAll(btn, btnAddFactory); 802 return vb; 803 } 804 805 private TreeItem<DataItem> searchTreeItem(String content) { 806 return TreeTableNewApp.searchTreeItem(testedControl, content); 807 } 808 809 private Pane controlsForEditing() { 810 811 VBox topContainer = new VBox(3.5); 812 HBox hb = new HBox(3.0); 813 814 final ComboBox cmbEditors = new ComboBox(); 815 cmbEditors.setId(CMB_EDITORS_ID); 816 cmbEditors.getItems().addAll((Object[]) CellEditorType.values()); 817 818 final CheckBox chbCustom = new CheckBox("Custom"); 819 chbCustom.setId(CHB_IS_CUSTOM_ID); 820 821 Button btnSetEditor = new Button("Set editor"); 822 btnSetEditor.setId(BTN_SET_CELLS_EDITOR_ID); 823 btnSetEditor.setOnAction(new EventHandler<ActionEvent>() { 824 public void handle(ActionEvent t) { 825 CellEditorType editor = (CellEditorType) cmbEditors.getSelectionModel().getSelectedItem(); 826 setCellEditor(editor, chbCustom.isSelected()); 827 } 828 }); 829 830 hb.getChildren().addAll(cmbEditors, chbCustom, btnSetEditor); 831 832 topContainer.getChildren().addAll(hb); 833 return topContainer; 834 } 835 836 private void setCellEditor(CellEditorType editor, boolean isCustom) { 837 if (null == editor) { 838 System.out.println("Editor is not selected"); 839 return; 840 } 841 842 switch (editor) { 843 case TEXT_FIELD: 844 setTextFieldCellEditor(isCustom); 845 break; 846 case COMBOBOX: 847 setComboboxCellEditor(isCustom); 848 break; 849 case CHOICEBOX: 850 setChoiceboxCellEditor(isCustom); 851 break; 852 default: 853 throw new UnsupportedOperationException(editor.toString()); 854 } 855 856 System.out.println(String.format("Editor set: %s %s", 857 isCustom ? "custom" : "", 858 editor.toString())); 859 } 860 861 private void setTextFieldCellEditor(boolean isCustom) { 862 for (Iterator it = testedControl.getColumns().iterator(); it.hasNext();) { 863 TreeTableColumn col = (TreeTableColumn) it.next(); 864 if (!isCustom) { 865 col.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn()); 866 } else { 867 col.setCellFactory(new Callback() { 868 public Object call(Object p) { 869 return new EditingTextFieldCell(); 870 } 871 }); 872 } 873 } 874 } 875 876 private void setComboboxCellEditor(boolean isCustom) { 877 878 for (Iterator it = testedControl.getColumns().iterator(); it.hasNext();) { 879 TreeTableColumn col = (TreeTableColumn) it.next(); 880 String colName = col.getText(); 881 final ObservableList<String> items = FXCollections.observableArrayList(); 882 883 for (DataItem dataItem : allData) { 884 items.add(dataItem.get(colName).get()); 885 } 886 if (!isCustom) { 887 col.setCellFactory(ComboBoxTreeTableCell.forTreeTableColumn(items)); 888 } else { 889 col.setCellFactory(new Callback() { 890 public Object call(Object p) { 891 return new EditingComboBoxCell(items); 892 } 893 }); 894 } 895 } 896 } 897 898 private void setChoiceboxCellEditor(boolean isCustom) { 899 900 for (Iterator it = testedControl.getColumns().iterator(); it.hasNext();) { 901 TreeTableColumn col = (TreeTableColumn) it.next(); 902 String colName = col.getText(); 903 final ObservableList<String> items = FXCollections.observableArrayList(); 904 905 for (DataItem dataItem : allData) { 906 items.add(dataItem.get(colName).get()); 907 } 908 if (!isCustom) { 909 col.setCellFactory(ChoiceBoxTreeTableCell.forTreeTableColumn(items)); 910 } else { 911 col.setCellFactory(new Callback() { 912 public Object call(Object p) { 913 return new EditingChoiceBoxCell(items); 914 } 915 }); 916 } 917 } 918 } 919 920 private class EditingTextFieldCell extends TreeTableCell { 921 922 private TextField textField; 923 924 public EditingTextFieldCell() { 925 } 926 927 @Override 928 public void startEdit() { 929 if (!isEmpty()) { 930 super.startEdit(); 931 createTextField(); 932 933 super.startEdit(); 934 setText(null); 935 setGraphic(textField); 936 } 937 } 938 939 @Override 940 public void cancelEdit() { 941 super.cancelEdit(); 942 943 setText(getString()); 944 setGraphic(null); 945 } 946 947 @Override 948 public void updateItem(Object item, boolean empty) { 949 super.updateItem(item, empty); 950 951 if (empty) { 952 setText(null); 953 setGraphic(null); 954 } else { 955 if (isEditing()) { 956 if (textField != null) { 957 textField.setText(getString()); 958 } 959 setText(null); 960 setGraphic(textField); 961 } else { 962 setText(getString()); 963 setGraphic(null); 964 } 965 } 966 } 967 968 private void createTextField() { 969 textField = new TextField(getString()); 970 textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); 971 972 textField.setOnKeyReleased(new EventHandler<KeyEvent>() { 973 @Override 974 public void handle(KeyEvent t) { 975 if (t.getCode() == KeyCode.ENTER) { 976 commitEdit(textField.getText()); 977 } else if (t.getCode() == KeyCode.ESCAPE) { 978 cancelEdit(); 979 } 980 } 981 }); 982 } 983 984 private String getString() { 985 return getItem() == null ? "" : getItem().toString(); 986 } 987 } 988 989 private class EditingComboBoxCell extends TreeTableCell { 990 991 ObservableList items; 992 ComboBox comboBox; 993 994 public EditingComboBoxCell(ObservableList _items) { 995 items = _items; 996 } 997 998 @Override 999 public void startEdit() { 1000 if (isEmpty()) { 1001 return; 1002 } 1003 1004 createComboBox(); 1005 1006 comboBox.getSelectionModel().select(getItem()); 1007 1008 super.startEdit(); 1009 setText(null); 1010 setGraphic(comboBox); 1011 } 1012 1013 @Override 1014 public void cancelEdit() { 1015 super.cancelEdit(); 1016 setGraphic(null); 1017 setText(getString()); 1018 } 1019 1020 @Override 1021 public void updateItem(Object item, boolean isEmpty) { 1022 super.updateItem(item, isEmpty); 1023 if (isEmpty()) { 1024 setText(null); 1025 setGraphic(null); 1026 } else { 1027 if (isEditing()) { 1028 if (comboBox != null) { 1029 comboBox.getSelectionModel().select(getItem()); 1030 } 1031 setText(null); 1032 setGraphic(comboBox); 1033 } else { 1034 setText(getString()); 1035 setGraphic(null); 1036 } 1037 } 1038 } 1039 1040 private String getString() { 1041 return getItem() == null ? "" : getItem().toString(); 1042 } 1043 1044 private void createComboBox() { 1045 if (null == comboBox) { 1046 comboBox = new ComboBox(items); 1047 comboBox.setMaxWidth(Double.MAX_VALUE); 1048 comboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 1049 @Override 1050 public void changed(ObservableValue ov, Object oldValue, Object newValue) { 1051 if (isEditing()) { 1052 commitEdit(newValue); 1053 } 1054 } 1055 }); 1056 } 1057 } 1058 } 1059 1060 private class EditingChoiceBoxCell extends TreeTableCell { 1061 1062 ObservableList items; 1063 ChoiceBox choiceBox; 1064 1065 public EditingChoiceBoxCell(ObservableList _items) { 1066 items = _items; 1067 } 1068 1069 @Override 1070 public void startEdit() { 1071 if (isEmpty()) { 1072 return; 1073 } 1074 1075 createComboBox(); 1076 1077 choiceBox.getSelectionModel().select(getItem()); 1078 1079 super.startEdit(); 1080 setText(null); 1081 setGraphic(choiceBox); 1082 } 1083 1084 @Override 1085 public void cancelEdit() { 1086 super.cancelEdit(); 1087 setGraphic(null); 1088 setText(getString()); 1089 } 1090 1091 @Override 1092 public void updateItem(Object item, boolean isEmpty) { 1093 super.updateItem(item, isEmpty); 1094 if (isEmpty()) { 1095 setText(null); 1096 setGraphic(null); 1097 } else { 1098 if (isEditing()) { 1099 if (choiceBox != null) { 1100 choiceBox.getSelectionModel().select(getItem()); 1101 } 1102 setText(null); 1103 setGraphic(choiceBox); 1104 } else { 1105 setText(getString()); 1106 setGraphic(null); 1107 } 1108 } 1109 } 1110 1111 private String getString() { 1112 return getItem() == null ? "" : getItem().toString(); 1113 } 1114 1115 private void createComboBox() { 1116 if (null == choiceBox) { 1117 choiceBox = new ChoiceBox(items); 1118 choiceBox.setMaxWidth(Double.MAX_VALUE); 1119 choiceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 1120 @Override 1121 public void changed(ObservableValue ov, Object oldValue, Object newValue) { 1122 if (isEditing()) { 1123 commitEdit(newValue); 1124 } 1125 } 1126 }); 1127 } 1128 } 1129 } 1130 } 1131 1132 /** 1133 * This class contain HashMap, which contain dynamically generated data. For 1134 * each line in the Table, when you add additional column, you should add 1135 * additional key-value pair in map for all dataItems in allData observable 1136 * list. 1137 */ 1138 public static class DataItem implements Comparable { 1139 1140 private StringProperty treeData; 1141 private HashMap<String, StringProperty> data = new HashMap<String, StringProperty>(); 1142 1143 public DataItem() { 1144 this(null); 1145 } 1146 1147 public DataItem(String treeData) { 1148 this.treeData = new SimpleStringProperty(treeData); 1149 } 1150 1151 public void add(String string, StringProperty property) { 1152 data.put(string, property); 1153 } 1154 1155 public StringProperty get(String name) { 1156 return data.get(name); 1157 } 1158 1159 public StringProperty getTreeValue() { 1160 return treeData; 1161 } 1162 1163 @Override 1164 public String toString() { 1165 if (treeData != null) { 1166 return treeData.getValue(); 1167 } else { 1168 return data.toString(); 1169 } 1170 } 1171 1172 public boolean contains(String text) { 1173 boolean res = false; 1174 for (StringProperty prop : data.values()) { 1175 if (text.equals(prop.get())) { 1176 res = true; 1177 break; 1178 } 1179 } 1180 return res; 1181 } 1182 1183 public int compareTo(Object t) { 1184 if (!(DataItem.class.isAssignableFrom(t.getClass()) 1185 && ((DataItem) t).data.size() == data.size())) { 1186 throw new IllegalArgumentException("[It is assumed that" 1187 + "the data size is equal in botn data items]"); 1188 } 1189 1190 int res = 0; 1191 for (String key : data.keySet()) { 1192 res = data.get(key).get().compareTo(((DataItem) t).data.get(key).get()); 1193 if (res != 0) { 1194 return res; 1195 } 1196 } 1197 if (treeData.getValue() != null) { 1198 return treeData.getValue().compareTo(((DataItem) t).treeData.get()); 1199 } 1200 return 0; 1201 } 1202 } 1203 }