001: /*
002: * Copyright (C) 2004 Giuseppe MANNA
003: *
004: * This file is part of FreeReportBuilder
005: *
006: * FreeReportBuilder is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021:
022: package it.frb.tree.drag;
023:
024: import java.awt.Point;
025: import java.awt.image.BufferedImage;
026: import javax.swing.*;
027: import javax.swing.tree.TreePath;
028:
029: public class DragDropTreeView implements
030: java.awt.dnd.DragGestureListener,
031: java.awt.dnd.DragSourceListener {
032:
033: private JTree tree;
034: private JTextArea sqlEditorDatapanel;
035: private InterfacciaDragDrop gestoreDragDrop;
036: private CDropTargetListener cDropTargetListener;
037: private BufferedImage imgGhost;
038: public TreePath pathSource;
039: public Point ptOffset = new Point();
040:
041: public DragDropTreeView(javax.swing.JTree tree,
042: InterfacciaDragDrop gestoreDragDrop) {
043: this .tree = tree;
044: this .gestoreDragDrop = gestoreDragDrop;
045: this .setDnDTarget();
046: }
047:
048: /*old
049: public DragDropTreeView(JTree tree ,InterfacciaDragDrop gestoreDragDrop, JTextArea datapanel)
050: {
051: this.tree = tree;
052: this.datapanel = datapanel;
053: this.gestoreDragDrop = gestoreDragDrop;
054: this.setDnDTarget();
055: }
056: old*/
057:
058: public DragDropTreeView(javax.swing.JTree tree,
059: InterfacciaDragDrop gestoreDragDrop,
060: JTextArea sqlEditorDatapanel) {
061: this .tree = tree;
062: this .sqlEditorDatapanel = sqlEditorDatapanel;
063: this .gestoreDragDrop = gestoreDragDrop;
064: this .setDnDTarget();
065: }
066:
067: /*old
068: private void setDnDTarget()
069: {
070: DragSource dragSource = DragSource.getDefaultDragSource();
071: dragSource.createDefaultDragGestureRecognizer(tree, DnDConstants.ACTION_COPY_OR_MOVE, this);
072:
073: cDropTargetListener = new CDropTargetListener();
074:
075: DropTarget dropTarget = new DropTarget(datapanel, cDropTargetListener);
076: dropTarget.setDefaultActions(DnDConstants.ACTION_COPY_OR_MOVE);
077: }
078: old*/
079:
080: private void setDnDTarget() {
081: java.awt.dnd.DragSource dragSource = java.awt.dnd.DragSource
082: .getDefaultDragSource();
083: dragSource.createDefaultDragGestureRecognizer(tree,
084: java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE, this );
085:
086: cDropTargetListener = new CDropTargetListener();
087:
088: java.awt.dnd.DropTarget dropTarget = new java.awt.dnd.DropTarget(
089: sqlEditorDatapanel, cDropTargetListener);
090: dropTarget
091: .setDefaultActions(java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE);
092: }
093:
094: public java.awt.geom.Rectangle2D getRectangleForGhost() {
095: return this .cDropTargetListener.raGhost;
096: }
097:
098: public TreePath getLastPath() {
099: return this .cDropTargetListener.pathLast;
100: }
101:
102: public class CDropTargetListener implements
103: java.awt.dnd.DropTargetListener {
104:
105: public TreePath pathNodeSelected = null;
106: private TreePath pathLast = null;
107: private java.awt.geom.Rectangle2D raCueLine = new java.awt.geom.Rectangle2D.Float();
108: private java.awt.geom.Rectangle2D raGhost = new java.awt.geom.Rectangle2D.Float();
109: private java.awt.Color colorCueLine;
110: private Point ptLast = new Point();
111: private javax.swing.Timer timerHover;
112: private int nLeftRight = 0;
113: private BufferedImage imgRight = new CArrowImage(15, 15,
114: CArrowImage.ARROW_RIGHT);
115: private BufferedImage imgLeft = new CArrowImage(15, 15,
116: CArrowImage.ARROW_LEFT);
117: private int nShift = 0;
118:
119: /** Creates new CDropTargetListener */
120: public CDropTargetListener() {
121: colorCueLine = new java.awt.Color(
122: java.awt.SystemColor.controlShadow.getRed(),
123: java.awt.SystemColor.controlShadow.getGreen(),
124: java.awt.SystemColor.controlShadow.getBlue(), 64);
125:
126: // Set up a hover timer, so that a node will be automatically expanded or collapsed
127: // if the user lingers on it for more than a short time
128: timerHover = new javax.swing.Timer(2000,
129: new java.awt.event.ActionListener() {
130: public void actionPerformed(
131: java.awt.event.ActionEvent e) {
132: nLeftRight = 0;
133:
134: if (!gestoreDragDrop
135: .isPossibleExpandedNode(e)) {
136: return;
137: }
138:
139: if (tree.isExpanded(pathLast)) {
140: tree.collapsePath(pathLast);
141: } else {
142: tree.expandPath(pathLast);
143: }
144: }
145: });
146:
147: timerHover.setRepeats(false); // Set timer to one-shot mode
148: }
149:
150: public void dragExit(java.awt.dnd.DropTargetEvent e) {
151: if (!java.awt.dnd.DragSource.isDragImageSupported()) {
152: tree.repaint(raGhost.getBounds());
153: }
154:
155: gestoreDragDrop.dragExit(e);
156: }
157:
158: public void dragEnter(java.awt.dnd.DropTargetDragEvent e) {
159: if (!isDragAcceptable(e)) {
160: e.rejectDrag();
161: } else {
162: e.acceptDrag(e.getDropAction());
163: }
164: }
165:
166: public void drop(java.awt.dnd.DropTargetDropEvent e) {
167: timerHover.stop(); // Prevent hover timer from doing an unwanted expandPath or collapsePath
168:
169: if (!isDropAcceptable(e)) {
170: e.rejectDrop();
171: return;
172: }
173:
174: e.acceptDrop(e.getDropAction());
175:
176: java.awt.datatransfer.Transferable transferable = e
177: .getTransferable();
178:
179: java.awt.datatransfer.DataFlavor[] flavors = transferable
180: .getTransferDataFlavors();
181: for (int i = 0; i < flavors.length; i++) {
182: java.awt.datatransfer.DataFlavor flavor = flavors[i];
183: if (flavor
184: .isMimeTypeEqual(java.awt.datatransfer.DataFlavor.javaJVMLocalObjectMimeType)) {
185: try {
186: Point pt = e.getLocation();
187: TreePath pathTarget = tree
188: .getClosestPathForLocation(pt.x, pt.y);
189: TreePath pathSource = (TreePath) transferable
190: .getTransferData(flavor);
191:
192: gestoreDragDrop.drop(e, pathSource, pathTarget);
193:
194: } catch (java.awt.datatransfer.UnsupportedFlavorException ufe) {
195: System.out.println(ufe);
196: e.dropComplete(false);
197: return;
198: } catch (java.io.IOException ioe) {
199: System.out.println(ioe);
200: e.dropComplete(false);
201: return;
202: }
203: }
204: }
205: e.dropComplete(true);
206: }
207:
208: public void dragOver(java.awt.dnd.DropTargetDragEvent e) {
209: Point pt = e.getLocation();
210:
211: if (pt.equals(ptLast))
212: return;
213:
214: // Try to determine whether the user is flicking the cursor right or left
215: int nDeltaLeftRight = pt.x - ptLast.x;
216:
217: if ((nLeftRight > 0 && nDeltaLeftRight < 0)
218: || (nLeftRight < 0 && nDeltaLeftRight > 0))
219: nLeftRight = 0;
220: nLeftRight += nDeltaLeftRight;
221:
222: ptLast = pt;
223:
224: java.awt.Graphics2D g2 = (java.awt.Graphics2D) tree
225: .getGraphics();
226:
227: // If a drag image is not supported by the platform, then draw my own drag image
228: /*
229: if (!DragSource.isDragImageSupported()){
230: tree.paintImmediately(raGhost.getBounds()); // Rub out the last ghost image and cue line
231: // And remember where we are about to draw the new ghost image
232: raGhost.setRect(pt.x - ptOffset.x, pt.y - ptOffset.y, imgGhost.getWidth(), imgGhost.getHeight());
233: g2.drawImage(imgGhost, AffineTransform.getTranslateInstance(raGhost.getX(), raGhost.getY()), null);
234:
235: }else{// Just rub out the last cue line
236: tree.paintImmediately(raCueLine.getBounds());
237: }
238: */
239: TreePath path = tree.getClosestPathForLocation(pt.x, pt.y);
240:
241: if (!(path == pathLast)) {
242: nLeftRight = 0; // We've moved up or down, so reset left/right movement trend
243: pathLast = path;
244: timerHover.restart();
245: }
246:
247: /* In any case draw (over the ghost image if necessary) a cue line indicating where a drop will occur
248: Rectangle raPath = tree.getPathBounds(path);
249: raCueLine.setRect(0, raPath.y+(int)raPath.getHeight(), tree.getWidth(), 2);
250:
251: g2.setColor(colorCueLine);
252: g2.fill(raCueLine);
253: */
254: //Now superimpose the left/right movement indicator if necessary
255: if (nLeftRight > 20) {
256: //g2.drawImage(imgRight, AffineTransform.getTranslateInstance(pt.x - ptOffset.x, pt.y - ptOffset.y), null);
257: nShift = +1;
258: } else if (nLeftRight < -20) {
259: //g2.drawImage(imgLeft, AffineTransform.getTranslateInstance(pt.x - ptOffset.x, pt.y - ptOffset.y), null);
260: nShift = -1;
261: } else {
262: nShift = 0;
263: }
264:
265: // And include the cue line in the area to be rubbed out next time
266: raGhost = raGhost.createUnion(raCueLine);
267:
268: gestoreDragDrop.dragOver(e);
269: }
270:
271: public void dropActionChanged(java.awt.dnd.DropTargetDragEvent e) {
272: if (!isDragAcceptable(e))
273: e.rejectDrag();
274: else
275: e.acceptDrag(e.getDropAction());
276: }
277:
278: }
279:
280: private boolean isDragAcceptable(java.awt.dnd.DropTargetDragEvent e) {
281: // Only accept this particular flavor
282: if (!e
283: .isDataFlavorSupported(CTransferableTreePath.TREEPATH_FLAVOR))
284: return false;
285:
286: if (gestoreDragDrop.isDragAcceptable(e)) {
287: return true;
288: } else {
289: return false;
290: }
291: }
292:
293: private boolean isDropAcceptable(java.awt.dnd.DropTargetDropEvent e) {
294:
295: // Only accept COPY or MOVE gestures (ie LINK is not supported)
296: if ((e.getDropAction() & java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE) == 0)
297: return false;
298:
299: // Only accept this particular flavor
300: if (!e
301: .isDataFlavorSupported(CTransferableTreePath.TREEPATH_FLAVOR))
302: return false;
303:
304: if (gestoreDragDrop.isDropAcceptable(e)) {
305: return true;
306: } else {
307: return false;
308: }
309: }
310:
311: // Interface: DragGestureListener
312: public void dragGestureRecognized(java.awt.dnd.DragGestureEvent e) {
313:
314: Point ptDragOrigin = e.getDragOrigin();
315: TreePath path = tree.getPathForLocation(ptDragOrigin.x,
316: ptDragOrigin.y);
317:
318: if (path == null)
319: return;
320:
321: if (tree.isRootVisible() && tree.getRowForPath(path) == 0) {
322: return; // Ignore user trying to drag the root node
323: }
324:
325: // Work out the offset of the drag point from the TreePath bounding rectangle origin
326: java.awt.Rectangle raPath = tree.getPathBounds(path);
327: ptOffset.setLocation(ptDragOrigin.x - raPath.x, ptDragOrigin.y
328: - raPath.y);
329:
330: // Get the cell renderer (which is a JLabel) for the path being dragged
331: javax.swing.JLabel lbl = (javax.swing.JLabel) tree
332: .getCellRenderer().getTreeCellRendererComponent(
333: tree, // tree
334: path.getLastPathComponent(), // value
335: false, // isSelected (dont want a colored background)
336: tree.isExpanded(path), // isExpanded
337: tree.getModel().isLeaf(
338: path.getLastPathComponent()), // isLeaf
339: 0, // row (not important for rendering)
340: false // hasFocus (dont want a focus rectangle)
341: );
342:
343: lbl.setSize((int) raPath.getWidth(), (int) raPath.getHeight()); // <-- The layout manager would normally do this
344:
345: // Get a buffered image of the selection for dragging a ghost image
346: imgGhost = new BufferedImage((int) raPath.getWidth(),
347: (int) raPath.getHeight(),
348: BufferedImage.TYPE_INT_ARGB_PRE);
349: java.awt.Graphics2D g2 = imgGhost.createGraphics();
350:
351: // Ask the cell renderer to paint itself into the BufferedImage
352: g2.setComposite(java.awt.AlphaComposite.getInstance(
353: java.awt.AlphaComposite.SRC, 0.5f)); // Make the image ghostlike
354: lbl.paint(g2);
355:
356: // Now paint a gradient UNDER the ghosted JLabel text (but not under the icon if any)
357: // Note: this will need tweaking if your icon is not positioned to the left of the text
358: javax.swing.Icon icon = lbl.getIcon();
359:
360: int nStartOfText = (icon == null) ? 0 : icon.getIconWidth()
361: + lbl.getIconTextGap();
362: g2.setComposite(java.awt.AlphaComposite.getInstance(
363: java.awt.AlphaComposite.DST_OVER, 0.5f)); // Make the gradient ghostlike
364: g2.setPaint(new java.awt.GradientPaint(nStartOfText, 0,
365: java.awt.SystemColor.controlShadow, tree.getWidth(), 0,
366: new java.awt.Color(255, 255, 255, 0)));
367: g2.fillRect(nStartOfText, 0, tree.getWidth(), imgGhost
368: .getHeight());
369: g2.dispose();
370:
371: tree.setSelectionPath(path); // Select this path in the tree
372: // Wrap the path being transferred into a Transferable object
373: java.awt.datatransfer.Transferable transferable = new CTransferableTreePath(
374: path);
375: // Remember the path being dragged (because if it is being moved, we will have to delete it later)
376: pathSource = path;
377: // We pass our drag image just in case it IS supported by the platform
378: e
379: .startDrag(null, imgGhost, new Point(5, 5),
380: transferable, this );
381: }
382:
383: // Interface: DragSourceListener
384: public void dragOver(java.awt.dnd.DragSourceDragEvent e) {
385: gestoreDragDrop.dragOver(e);
386: }
387:
388: public void dragExit(java.awt.dnd.DragSourceEvent e) {
389: gestoreDragDrop.dragExit(e);
390: }
391:
392: public void dragEnter(java.awt.dnd.DragSourceDragEvent e) {
393: gestoreDragDrop.dragEnter(e);
394: }
395:
396: public void dragDropEnd(java.awt.dnd.DragSourceDropEvent e) {
397: if (e.getDropSuccess()) {
398: int nAction = e.getDropAction();
399: if (nAction == java.awt.dnd.DnDConstants.ACTION_MOVE) {
400: gestoreDragDrop.dragDropEnd(e);
401: pathSource = null;
402: }
403: }
404: }
405:
406: public void dropActionChanged(java.awt.dnd.DragSourceDragEvent e) {
407: gestoreDragDrop.dropActionChanged(e);
408: }
409:
410: public java.awt.Insets getAutoscrollInsets() {
411: return gestoreDragDrop.getAutoscrollInsets();
412: }
413:
414: public void autoscroll(Point pt) {
415: gestoreDragDrop.autoscroll(pt);
416: }
417: }
|