/*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2001 Joerg Mueller <joergmueller@bigfoot.com> *See COPYING for Details * *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, write to the Free Software *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package freemind.modes.mindmapmode.listeners; 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.List; import java.util.ListIterator; import javax.swing.JOptionPane; import freemind.controller.MindMapNodesSelection; import freemind.modes.MindMapNode; import freemind.modes.mindmapmode.MindMapController; import freemind.modes.mindmapmode.MindMapMapModel; import freemind.modes.mindmapmode.MindMapNodeModel; import freemind.view.mindmapview.MainView; import freemind.view.mindmapview.NodeView; // import ublic class MindMapNodesSelection implements Transferable, // ClipboardOwner { // public static DataFlavor fileListFlavor = null; public class MindMapNodeDropListener implements DropTargetListener { private final MindMapController mMindMapController; public MindMapNodeDropListener(MindMapController controller) { mMindMapController = controller; } // See DropTargetListener for the meaning. private boolean isDragAcceptable(DropTargetDragEvent ev) { if (ev.isDataFlavorSupported(DataFlavor.stringFlavor)) { return true; } if (ev.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor)) { return true; } return false; } private boolean isDropAcceptable(DropTargetDropEvent event) { MindMapNode node = ((MainView) event.getDropTargetContext() .getComponent()).getNodeView().getModel(); MindMapNode selected = mMindMapController.getSelected(); if (!node.isWriteable()) return false; return ((node != selected) && !node.isDescendantOf(selected)); // I think (node!=selected) is a hack for windows } public void drop(DropTargetDropEvent dtde) { try { int dropAction = dtde.getDropAction(); Transferable t = dtde.getTransferable(); final MainView mainView = (MainView) dtde.getDropTargetContext() .getComponent(); NodeView targetNodeView = mainView.getNodeView(); MindMapNode targetNode = targetNodeView.getModel(); MindMapNodeModel targetNodeModel = (MindMapNodeModel) targetNode; // Intra application DnD // For some reason, getting sourceAction is only possible for local // transfer. When I try to remove clause dtde.isLocalTransfer, I get // an answer // like "no drop current". One hypothesis is that with nonlocal // transfers, I // have to accept drop action before I can get transfer data. // However, this is // not what I want in this particular situation. A part of the // problem lies in // the hackery of sending source action using data flavour too. if (dtde.isLocalTransfer() && t.isDataFlavorSupported(MindMapNodesSelection.dropActionFlavor)) { 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; } dtde.acceptDrop(dtde.getDropAction()); if (!dtde.isLocalTransfer()) { // if // (dtde.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor)) // { // System.err.println("filelist"); mMindMapController.paste(t, targetNode, mainView.dropAsSibling(dtde.getLocation().getX()), mainView.dropPosition(dtde.getLocation().getX())); dtde.dropComplete(true); return; } // This all is specific to MindMap model. Needs rewrite to work for // other modes. // We ignore data transfer in dtde. We take selected nodes as drag // sources. // <problem> // The behaviour is not so fine, when some of selected nodes is an // ancestor of other selected nodes. // Ideally, we would first unselect all nodes, which have an // ancestor among selected nodes. // I don't have time/lust to do this. This is just a minor problem. // </problem> // By transferable object we only transfer source action. This will // be a problem, when we want // to implement extra application dnd or dnd between different Java // Virtual Machines. if (dropAction == DnDConstants.ACTION_LINK) { // ACTION_LINK means for us change the color, style and font. // This is not very clean. This all should probably be // implemented on the mindMapMapModel level. On the other // hand, one would have to downcast to MindMapMapModel anyway. // MindMapNode selectedNode = // c.getView().getSelected().getModel(); MindMapMapModel mindMapMapModel = (MindMapMapModel) mMindMapController .getModel(); // link feature continues here. fc, 01.11.2003: // if there are more than 4 nodes, then ask the user: int yesorno = JOptionPane.YES_OPTION; if (mMindMapController.getView().getSelecteds().size() >= 5) { yesorno = JOptionPane .showConfirmDialog( mMindMapController.getFrame() .getContentPane(), mMindMapController .getText("lots_of_links_warning"), Integer.toString(mMindMapController .getView().getSelecteds().size()) + " links to the same node", JOptionPane.YES_NO_OPTION); } if (yesorno == JOptionPane.YES_OPTION) { for (ListIterator it = mMindMapController.getView() .getSelecteds().listIterator(); it.hasNext();) { MindMapNodeModel selectedNodeModel = (MindMapNodeModel) ((NodeView) it .next()).getModel(); // mindMapMapModel.setNodeColor(selectedNodeModel,targetNode.getColor()); // mindMapMapModel.setNodeFont(selectedNodeModel,targetNode.getFont()); mMindMapController.addLink(selectedNodeModel, targetNodeModel); } } } else { if (!targetNode.isWriteable()) { String message = mMindMapController .getText("node_is_write_protected"); JOptionPane.showMessageDialog(mMindMapController.getFrame() .getContentPane(), message, "Freemind", JOptionPane.ERROR_MESSAGE); return; } Transferable trans = null; // if move, verify, that the target is not a son of the sources. List selecteds = mMindMapController.getSelecteds(); if (DnDConstants.ACTION_MOVE == dropAction) { MindMapNode actualNode = targetNode; do { if (selecteds.contains(actualNode)) { String message = mMindMapController .getText("cannot_move_to_child"); JOptionPane.showMessageDialog(mMindMapController .getFrame().getContentPane(), message, "Freemind", JOptionPane.WARNING_MESSAGE); dtde.dropComplete(true); return; } actualNode = (actualNode.isRoot()) ? null : actualNode .getParentNode(); } while (actualNode != null); trans = mMindMapController.cut(); } else { trans = mMindMapController.copy(); } mMindMapController.getView().selectAsTheOnlyOneSelected( targetNodeView); boolean result = mMindMapController.paste(trans, targetNode, mainView.dropAsSibling(dtde.getLocation().getX()), mainView.dropPosition(dtde.getLocation().getX())); if (!result && DnDConstants.ACTION_MOVE == dropAction) { // an error occured. how to react? } } } catch (Exception e) { System.err.println("Drop exception:" + e); freemind.main.Resources.getInstance().logException(e); dtde.dropComplete(false); return; } dtde.dropComplete(true); } /** * 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(DropTargetDragEvent dtde) { // TODO: Accepting the action ACTION_MOVE is false, because we cannot // know if the action is really ACTION_MOVE. if (isDragAcceptable(dtde)) { dtde.acceptDrag(DnDConstants.ACTION_MOVE); } else { dtde.rejectDrag(); } } public void dragOver(DropTargetDragEvent e) { MainView draggedNode = (MainView) e.getDropTargetContext() .getComponent(); int oldDraggedOver = draggedNode.getDraggedOver(); // let the node decide, which dragged over type it is: draggedNode.setDraggedOver(e.getLocation()); int newDraggedOver = draggedNode.getDraggedOver(); boolean repaint = newDraggedOver != oldDraggedOver; if (repaint) { draggedNode.repaint(); } } public void dragExit(DropTargetEvent e) { MainView draggedNode = (MainView) e.getDropTargetContext() .getComponent(); draggedNode.setDraggedOver(NodeView.DRAGGED_OVER_NO); draggedNode.repaint(); } public void dragScroll(DropTargetDragEvent e) { } public void dropActionChanged(DropTargetDragEvent e) { } }