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