Gridarta Editor
TreeDropTarget.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2023 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.gui.mapmenu;
21 
22 import java.awt.Point;
23 import java.awt.Rectangle;
24 import java.awt.datatransfer.DataFlavor;
25 import java.awt.datatransfer.Transferable;
26 import java.awt.datatransfer.UnsupportedFlavorException;
27 import java.awt.dnd.DnDConstants;
28 import java.awt.dnd.DropTarget;
29 import java.awt.dnd.DropTargetContext;
30 import java.awt.dnd.DropTargetDragEvent;
31 import java.awt.dnd.DropTargetDropEvent;
32 import java.awt.dnd.DropTargetEvent;
33 import java.awt.dnd.DropTargetListener;
34 import java.io.IOException;
35 import javax.swing.JTree;
36 import javax.swing.tree.DefaultTreeModel;
37 import javax.swing.tree.MutableTreeNode;
38 import javax.swing.tree.TreePath;
39 import org.jetbrains.annotations.NotNull;
40 
46 public class TreeDropTarget {
47 
52  @NotNull
53  private final DropTargetListener dropTargetListener = new DropTargetListener() {
54 
55  @Override
56  public void dragEnter(@NotNull final DropTargetDragEvent dtde) {
57  checkDragEvent(dtde);
58  }
59 
60  @Override
61  public void dragOver(@NotNull final DropTargetDragEvent dtde) {
62  checkDragEvent(dtde);
63  }
64 
65  @Override
66  public void dropActionChanged(@NotNull final DropTargetDragEvent dtde) {
67  checkDragEvent(dtde);
68  }
69 
70  @Override
71  public void dragExit(@NotNull final DropTargetEvent dte) {
72  // ignore
73  }
74 
75  @Override
76  public void drop(@NotNull final DropTargetDropEvent dtde) {
77  final Point location = dtde.getLocation();
78  final DropTargetContext dropTargetContext = dtde.getDropTargetContext();
79  final JTree tree = (JTree) dropTargetContext.getComponent();
80  final TreePath parentTreePath = tree.getClosestPathForLocation(location.x, location.y);
81  final MutableTreeNode parentTreeNode = (MutableTreeNode) parentTreePath.getLastPathComponent();
82 
83  final int dropAction = dtde.getDropAction();
84  if (dropAction != DnDConstants.ACTION_COPY && dropAction != DnDConstants.ACTION_MOVE) {
85  dtde.rejectDrop();
86  return;
87  }
88 
89  try {
90  final Transferable transferable = dtde.getTransferable();
91  final DataFlavor[] flavors = transferable.getTransferDataFlavors();
92  for (final DataFlavor flavor : flavors) {
93  if (transferable.isDataFlavorSupported(flavor)) {
94  dtde.acceptDrop(dropAction);
95  final Rectangle parentRectangle = tree.getPathBounds(parentTreePath);
96  final int relativeY = location.y - parentRectangle.y;
97  final TreePath treePath = (TreePath) transferable.getTransferData(flavor);
98  final MutableTreeNode draggedTreeNode = (MutableTreeNode) treePath.getLastPathComponent();
99  final DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel();
100  final MutableTreeNode parent;
101  final int index;
102  if (parentTreeNode == treeModel.getRoot()) {
103  // drop into root => insert into root
104  // insert at front of parentTreeNode
105  parent = parentTreeNode;
106  index = 0;
107  } else if (!parentTreeNode.getAllowsChildren()) {
108  // drop into leaf => insert before or after leaf
109  parent = (MutableTreeNode) parentTreeNode.getParent();
110  assert parent != null;
111  if (relativeY < parentRectangle.height / 2) {
112  // insert before parentTreeNode
113  index = treeModel.getIndexOfChild(parent, parentTreeNode);
114  } else {
115  // insert after parentTreeNode
116  index = treeModel.getIndexOfChild(parent, parentTreeNode) + 1;
117  }
118  } else if (tree.isExpanded(parentTreePath)) {
119  // drop into expanded branch => insert before branch or into branch
120  if (relativeY < parentRectangle.height / 2) {
121  // insert before parentTreeNode
122  parent = (MutableTreeNode) parentTreeNode.getParent();
123  assert parent != null;
124  index = treeModel.getIndexOfChild(parent, parentTreeNode);
125  } else {
126  // insert at front of parentTreeNode
127  parent = parentTreeNode;
128  index = 0;
129  }
130  } else {
131  // drop into collapsed branch => insert before, into, or after branch
132  if (relativeY < parentRectangle.height / 4) {
133  // insert before parentTreeNode
134  parent = (MutableTreeNode) parentTreeNode.getParent();
135  assert parent != null;
136  index = treeModel.getIndexOfChild(parent, parentTreeNode);
137  } else if (relativeY >= parentRectangle.height * 3 / 4) {
138  // insert after parentTreeNode
139  parent = (MutableTreeNode) parentTreeNode.getParent();
140  assert parent != null;
141  index = treeModel.getIndexOfChild(parent, parentTreeNode) + 1;
142  } else {
143  // insert at end of parentTreeNode
144  parent = parentTreeNode;
145  index = parent.getChildCount();
146  }
147  }
148  treeModel.insertNodeInto(draggedTreeNode, parent, index);
149  tree.setSelectionPath(new TreePath(treeModel.getPathToRoot(draggedTreeNode)));
150  dtde.dropComplete(true);
151  return;
152  }
153  }
154  } catch (final IOException | UnsupportedFlavorException ignored) {
155  // ignore
156  }
157  dtde.rejectDrop();
158  }
159 
160  };
161 
166  public TreeDropTarget(@NotNull final JTree tree) {
167  //noinspection ResultOfObjectAllocationIgnored
168  new DropTarget(tree, dropTargetListener);
169  }
170 
175  private static void checkDragEvent(@NotNull final DropTargetDragEvent dtde) {
176  final int dragOperation = dtde.getDropAction();
177  switch (dragOperation) {
178  case DnDConstants.ACTION_COPY:
179  case DnDConstants.ACTION_MOVE:
180  dtde.acceptDrag(dragOperation);
181  return;
182  }
183 
184  dtde.rejectDrag();
185  }
186 
187 }
net.sf.gridarta.gui.mapmenu.TreeDropTarget.dropTargetListener
final DropTargetListener dropTargetListener
The DropTargetListener attached to the tree.
Definition: TreeDropTarget.java:53
net.sf.gridarta.gui.mapmenu.TreeDropTarget.checkDragEvent
static void checkDragEvent(@NotNull final DropTargetDragEvent dtde)
Called when a drag operation is ongoing.
Definition: TreeDropTarget.java:175
net.sf.gridarta.gui.mapmenu.TreeDropTarget.TreeDropTarget
TreeDropTarget(@NotNull final JTree tree)
Creates a new instance.
Definition: TreeDropTarget.java:166
net.sf.gridarta.gui.mapmenu.TreeDropTarget
Tracks JTree instances for drop targets.
Definition: TreeDropTarget.java:46