/*********************************************************************************** * * Copyright (c) 2015 Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.spy.ui.controls; import javafx.event.EventHandler; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.ImageView; import javafx.scene.input.ClipboardContent; import javafx.scene.input.DataFormat; import javafx.scene.input.DragEvent; import javafx.scene.input.Dragboard; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.baczkowicz.spy.common.generated.ConnectionGroupReference; import pl.baczkowicz.spy.common.generated.ConnectionReference; import pl.baczkowicz.spy.configuration.BaseConfigurationUtils; import pl.baczkowicz.spy.ui.configuration.ConfiguredConnectionGroupDetails; import pl.baczkowicz.spy.ui.events.observers.ItemsReorderedObserver; import pl.baczkowicz.spy.ui.properties.ConnectionTreeItemProperties; import pl.baczkowicz.spy.ui.properties.ModifiableItem; import pl.baczkowicz.spy.ui.utils.ImageUtils; public class DragAndDropTreeViewCell extends TreeCell<ConnectionTreeItemProperties> { /** Diagnostic logger. */ private final static Logger logger = LoggerFactory.getLogger(DragAndDropTreeViewCell.class); private ConnectionTreeItemProperties item; public DragAndDropTreeViewCell(final TreeView<ConnectionTreeItemProperties> treeView, final ItemsReorderedObserver controller) { DragAndDropTreeViewCell thisCell = this; setOnDragDetected(new EventHandler<MouseEvent>() { @Override public void handle(final MouseEvent event) { if (item == null) { return; } logger.debug("Drag detected on item = " + item); final Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE); final ClipboardContent content = new ClipboardContent(); content.put(DataFormat.PLAIN_TEXT, item.getId()); dragBoard.setContent(content); event.consume(); } }); setOnDragEntered(event -> { if (event.getGestureSource() != thisCell && event.getDragboard().hasString()) { setOpacity(0.3); } }); setOnDragExited(event -> { if (event.getGestureSource() != thisCell && event.getDragboard().hasString()) { setOpacity(1); } }); setOnDragDone(new EventHandler<DragEvent>() { @Override public void handle(final DragEvent dragEvent) { logger.debug("Drag done on item = " + item); dragEvent.consume(); } }); setOnDragOver(new EventHandler<DragEvent>() { @Override public void handle(final DragEvent dragEvent) { logger.debug("Drag over on item = " + item); if (dragEvent.getDragboard().hasString()) { final String idToMove = dragEvent.getDragboard().getString(); if (!idToMove.equals(item.getId())) { dragEvent.acceptTransferModes(TransferMode.MOVE); } } dragEvent.consume(); } }); setOnDragDropped(new EventHandler<DragEvent>() { @Override public void handle(DragEvent dragEvent) { logger.debug("Drag dropped on item = " + item); if (item == null) { return; } final String idToMove = dragEvent.getDragboard().getString(); // Only move if the new parent is not the current item and the new item is a group if (!idToMove.equals(item.getId())) { final TreeItem<ConnectionTreeItemProperties> treeItemToMove = findNode(treeView.getRoot(), idToMove); final ConnectionTreeItemProperties treeItemPropertiesToMove = treeItemToMove.getValue(); TreeItem<ConnectionTreeItemProperties> newParentTreeItem = findNode(treeView.getRoot(), item.getId()); TreeItem<ConnectionTreeItemProperties> requestedNewParentTreeItem = newParentTreeItem; if (checkIfParentIsSubitem(newParentTreeItem, treeItemToMove)) { return; } // Remove from the old parent treeItemToMove.getParent().getChildren().remove(treeItemToMove); // Re-map helper refs treeItemPropertiesToMove.getParent().getChildren().remove(treeItemPropertiesToMove); int insertIndex = newParentTreeItem.getChildren().size(); // Regroup if (item.isGroup()) { } else { // Reorder newParentTreeItem = newParentTreeItem.getParent(); insertIndex = newParentTreeItem.getChildren().indexOf(requestedNewParentTreeItem); } final ConnectionTreeItemProperties newParentTreeItemProperties = newParentTreeItem.getValue(); final ConnectionTreeItemProperties oldParentTreeItemProperties = treeItemPropertiesToMove.getParent(); final ConfiguredConnectionGroupDetails newParentGroup = (ConfiguredConnectionGroupDetails) newParentTreeItemProperties.getGroup(); final ConfiguredConnectionGroupDetails oldParentGroup = (ConfiguredConnectionGroupDetails) oldParentTreeItemProperties.getGroup(); // Add to the new parent newParentTreeItem.getChildren().add(insertIndex, treeItemToMove); // Re-map helper refs newParentTreeItemProperties.getChildren().add(insertIndex, treeItemPropertiesToMove); treeItemPropertiesToMove.setParent(newParentTreeItemProperties); // Re-map connections and groups if (treeItemPropertiesToMove.isGroup()) { final ConfiguredConnectionGroupDetails groupToMove = (ConfiguredConnectionGroupDetails) treeItemPropertiesToMove.getGroup(); // Remove old child ConfiguredConnectionGroupDetails.removeFromGroup(groupToMove, oldParentGroup); // Set new parent groupToMove.setGroup(new ConnectionGroupReference(newParentTreeItemProperties.getGroup())); // Add new child newParentGroup.getSubgroups().add(new ConnectionGroupReference(groupToMove)); // checkGroupForParentChanges(groupToMove); groupToMove.setGroupingModified(true); oldParentGroup.setGroupingModified(true); newParentGroup.setGroupingModified(true); } else { final ModifiableItem connectionToMove = treeItemPropertiesToMove.getConnection(); // Remove old child ConfiguredConnectionGroupDetails.removeFromGroup(connectionToMove, oldParentGroup); // Set new parent connectionToMove.setGroup(new ConnectionGroupReference(newParentTreeItemProperties.getGroup())); // Add new child newParentGroup.getConnections().add(insertIndex, new ConnectionReference(connectionToMove)); connectionToMove.setGroupingModified(true); oldParentGroup.setGroupingModified(true); newParentGroup.setGroupingModified(true); //checkConnectionForParentChanges(connectionToMove); } newParentTreeItem.setExpanded(true); // Select the current item getTreeView().getSelectionModel().select(treeItemToMove); } dragEvent.consume(); controller.onItemsReordered(); } }); } private boolean checkIfParentIsSubitem( TreeItem<ConnectionTreeItemProperties> newParent, TreeItem<ConnectionTreeItemProperties> treeItemToMove) { newParent = newParent.getParent(); while (newParent != null) { if (newParent.equals(treeItemToMove)) { logger.warn("Cannot move the object it its child!"); return true; } newParent = newParent.getParent(); } return false; } private TreeItem<ConnectionTreeItemProperties> findNode( final TreeItem<ConnectionTreeItemProperties> currentNode, final String idToSearch) { TreeItem<ConnectionTreeItemProperties> result = null; if (currentNode.getValue().getId().equals(idToSearch)) { result = currentNode; } else if (!currentNode.isLeaf()) { for (TreeItem<ConnectionTreeItemProperties> child : currentNode.getChildren()) { result = findNode(child, idToSearch); if (result != null) { break; } } } return result; } @Override protected void updateItem(final ConnectionTreeItemProperties item, final boolean empty) { super.updateItem(item, empty); this.item = item; if (item == null) { setText(null); setGraphic(null); } else { ImageView image; if (!item.isGroup()) { image = ImageUtils.createIcon("mqtt-icon", 18); } else { if (item.getGroup().getID().equals(BaseConfigurationUtils.DEFAULT_GROUP)) { setDisclosureNode(null); } if (item.getChildren().isEmpty()) { image = ImageUtils.createIcon("folder-grey", 18); } else { image = ImageUtils.createIcon("folder-yellow", 18); } } setText(item.getName()); setGraphic(image); } } }