/*
* Freeplane - mind map editor
* Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev
*
* This file is modified by Dimitry Polivaev in 2008.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.freeplane.view.swing.ui.mindmapmode;
import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.swing.JOptionPane;
import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.clipboard.ClipboardController;
import org.freeplane.features.clipboard.MindMapNodesSelection;
import org.freeplane.features.clipboard.mindmapmode.MClipboardController;
import org.freeplane.features.link.LinkController;
import org.freeplane.features.link.mindmapmode.MLinkController;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.map.mindmapmode.MMapController;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.mode.ModeController;
import org.freeplane.view.swing.map.MainView;
import org.freeplane.view.swing.map.NodeView;
public class MNodeDropListener implements DropTargetListener {
// final private ModeController modeController;
public MNodeDropListener() {
}
/**
* The method is called when the cursor carrying the dragged item enteres
* the area of the node. The name "dragEnter" seems to be confusing to me. I
* think the difference between dragAcceptable and dropAcceptable is that in
* dragAcceptable, you tell if the type of the thing being dragged is OK,
* where in dropAcceptable, you tell if your really willing to accept the
* item.
*/
public void dragEnter(final DropTargetDragEvent dtde) {
if (isDragAcceptable(dtde)) {
dtde.acceptDrag(DnDConstants.ACTION_MOVE);
}
else {
dtde.rejectDrag();
}
}
public void dragExit(final DropTargetEvent e) {
final Component draggedNode = e.getDropTargetContext().getComponent();
((MainView) draggedNode).setDraggedOver(NodeView.DRAGGED_OVER_NO);
draggedNode.repaint();
}
public void dragOver(final DropTargetDragEvent e) {
final MainView draggedNode = (MainView) e.getDropTargetContext().getComponent();
final int oldDraggedOver = draggedNode.getDraggedOver();
draggedNode.setDraggedOver(e.getLocation());
final int newDraggedOver = draggedNode.getDraggedOver();
final boolean repaint = newDraggedOver != oldDraggedOver;
if (repaint) {
draggedNode.repaint();
}
}
public void dragScroll(final DropTargetDragEvent e) {
}
public void drop(final DropTargetDropEvent dtde) {
try {
int dropAction = dtde.getDropAction();
final Transferable t = dtde.getTransferable();
final MainView mainView = (MainView) dtde.getDropTargetContext().getComponent();
final NodeView targetNodeView = mainView.getNodeView();
final NodeModel targetNode = targetNodeView.getModel();
final Controller controller = Controller.getCurrentController();
if (dtde.isLocalTransfer() && t.isDataFlavorSupported(MindMapNodesSelection.dropActionFlavor)) {
final String sourceAction = (String) t.getTransferData(MindMapNodesSelection.dropActionFlavor);
if (sourceAction.equals("LINK")) {
dropAction = DnDConstants.ACTION_LINK;
}
if (sourceAction.equals("COPY")) {
dropAction = DnDConstants.ACTION_COPY;
}
}
mainView.setDraggedOver(NodeView.DRAGGED_OVER_NO);
mainView.repaint();
if (dtde.isLocalTransfer() && (dropAction == DnDConstants.ACTION_MOVE) && !isDropAcceptable(dtde)) {
dtde.rejectDrop();
return;
}
final boolean dropAsSibling = mainView.dropAsSibling(dtde.getLocation().getX());
ModeController modeController = controller.getModeController();
final MMapController mapController = (MMapController) modeController.getMapController();
if ((dropAction == DnDConstants.ACTION_MOVE || dropAction == DnDConstants.ACTION_COPY)) {
final NodeModel parent = dropAsSibling ? targetNode.getParentNode() : targetNode;
if (!mapController.isWriteable(parent)) {
dtde.rejectDrop();
final String message = TextUtils.getText("node_is_write_protected");
UITools.errorMessage(message);
return;
}
}
final boolean isLeft = mainView.dropLeft(dtde.getLocation().getX());
if (!dtde.isLocalTransfer()) {
dtde.acceptDrop(DnDConstants.ACTION_COPY);
((MClipboardController) ClipboardController.getController()).paste(t, targetNode, dropAsSibling, isLeft, dropAction);
dtde.dropComplete(true);
return;
}
dtde.acceptDrop(dropAction);
if (dropAction == DnDConstants.ACTION_LINK) {
int yesorno = JOptionPane.YES_OPTION;
if (controller.getSelection().size() >= 5) {
yesorno = JOptionPane.showConfirmDialog(controller.getViewController().getContentPane(), TextUtils
.getText("lots_of_links_warning"), Integer.toString(controller.getSelection().size())
+ " links to the same node", JOptionPane.YES_NO_OPTION);
}
if (yesorno == JOptionPane.YES_OPTION) {
for (final Iterator<NodeModel> it = controller.getSelection().getSelection().iterator(); it
.hasNext();) {
final NodeModel selectedNodeModel = (it.next());
((MLinkController) LinkController.getController(modeController)).addConnector(
selectedNodeModel, targetNode);
}
}
}
else {
Transferable trans = null;
final Collection<NodeModel> selecteds = mapController.getSelectedNodes();
if (DnDConstants.ACTION_MOVE == dropAction) {
NodeModel actualNode = targetNode;
do {
if (selecteds.contains(actualNode)) {
final String message = TextUtils.getText("cannot_move_to_child");
JOptionPane.showMessageDialog(controller.getViewController().getContentPane(), message,
"Freeplane", JOptionPane.WARNING_MESSAGE);
dtde.dropComplete(true);
return;
}
actualNode = (actualNode.isRoot()) ? null : actualNode.getParentNode();
} while (actualNode != null);
final NodeModel[] array = selecteds.toArray(new NodeModel[selecteds.size()]);
final List<NodeModel> sortedSelection = controller.getSelection().getSortedSelection(true);
for (final NodeModel node : sortedSelection) {
boolean changeSide = isLeft != node.isLeft();
if (dropAsSibling) {
mapController.moveNodeBefore(node, targetNode, isLeft, changeSide);
}
else {
mapController.moveNodeAsChild(node, targetNode, isLeft, changeSide);
}
}
if(dropAsSibling || ! targetNode.isFolded())
controller.getSelection().replaceSelection(array);
else
controller.getSelection().selectAsTheOnlyOneSelected(targetNode);
}
else {
trans = ClipboardController.getController().copy(controller.getSelection());
((MClipboardController) ClipboardController.getController()).paste(trans, targetNode, dropAsSibling, isLeft);
controller.getSelection().selectAsTheOnlyOneSelected(targetNode);
}
}
}
catch (final Exception e) {
LogUtils.severe("Drop exception:", e);
dtde.dropComplete(false);
return;
}
dtde.dropComplete(true);
}
public void dropActionChanged(final DropTargetDragEvent e) {
}
public boolean isDragAcceptable(final DropTargetDragEvent ev) {
if (ev.isDataFlavorSupported(DataFlavor.stringFlavor)) {
return true;
}
if (ev.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor)) {
return true;
}
return false;
}
protected boolean isDropAcceptable(final DropTargetDropEvent event) {
final NodeModel node = ((MainView) event.getDropTargetContext().getComponent()).getNodeView().getModel();
final ModeController modeController = Controller.getCurrentController().getModeController();
final NodeModel selected = modeController.getMapController().getSelectedNode();
return ((node != selected) && !node.isDescendantOf(selected));
}
}