/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.gui.swing.contexttree;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.image.BufferedImage;
import javax.swing.tree.TreePath;
import org.geotools.map.MapContext;
import org.jdesktop.swingx.JXTreeTable;
/**
* Drop Class used for drag and drop purpose
*
* @author Johann Sorel
*/
final class DADContextTreeDrop extends DropTarget {
/* ----------------- class fields ------------------ */
/** bounding rectangle of the last row a dragOver was recorded for */
private Rectangle lastRowBounds;
/** height of the gap between any two node rows to treat as an area for inserts */
private int insertAreaHeight = 8;
/** insets for autoscroll */
private Insets autoscrollInsets = new Insets(20, 20, 20, 20);
/** rectangle to clear (where the last image was drawn) */
private Rectangle rect2D = new Rectangle();
/** the transfer handler that provides the image for the currently dragged node */
private DADContextTreeTransferHandler handler;
private Point mostRecentLocation;
/**
* constructor
* @param h handler for drop purpose.
*/
DADContextTreeDrop(DADContextTreeTransferHandler h) {
this.handler = h;
}
/* -------------- DropTargetListener start ----------------- */
/**
* use method dragOver to constantly update the drag mark and darg image as
* well as to support automatic scrolling durng a drag operation
* @param dtde drop target event
*/
@Override
public void dragOver(DropTargetDragEvent dtde) {
Point loc = dtde.getLocation();
JXTreeTable tree = (JXTreeTable) dtde.getDropTargetContext().getComponent();
updateDragMark(tree, loc);
paintImage(tree, loc);
autoscroll(tree, loc);
super.dragOver(dtde);
}
/**
* clear the drawings on exit
* @param dtde drage event
*/
public void dragExit(DropTargetDragEvent dtde) {
clearImage((JXTreeTable) dtde.getDropTargetContext().getComponent());
super.dragExit(dtde);
}
/**
* clear the drawings on drop
* @param dtde drag event
*/
@Override
public void drop(DropTargetDropEvent dtde) {
clearImage((JXTreeTable) dtde.getDropTargetContext().getComponent());
super.drop(dtde);
}
/* ----------------- DropTartgetListener end ------------------ */
/* ----------------- drag image painting start ------------------ */
/**
* paint the dragged node
* @param tree
* @param pt
*/
private final void paintImage(JXTreeTable tree, Point pt) {
BufferedImage image = handler.getDragImage(tree);
if (image != null) {
tree.paintImmediately(rect2D.getBounds());
rect2D.setRect((int) pt.getX() - 15, (int) pt.getY() - 15, image.getWidth(), image.getHeight());
tree.getGraphics().drawImage(image, (int) pt.getX() - 15, (int) pt.getY() - 15, tree);
}
}
/**
* clear drawings
* @param tree
*/
private final void clearImage(JXTreeTable tree) {
tree.paintImmediately(rect2D.getBounds());
}
/* ----------------- drag image painting end ------------------ */
/* ----------------- autoscroll implementation start ------------------ */
private Insets getAutoscrollInsets() {
return autoscrollInsets;
}
/**
* scroll visible tree parts when user drags outside an 'inner part' of
* the visible region
* @param tree
* @param cursorLocation
*/
private void autoscroll(JXTreeTable tree, Point cursorLocation) {
Insets insets = getAutoscrollInsets();
Rectangle outer = tree.getVisibleRect();
Rectangle inner = new Rectangle(outer.x + insets.left, outer.y + insets.top, outer.width - (insets.left + insets.right), outer.height - (insets.top + insets.bottom));
if (!inner.contains(cursorLocation)) {
Rectangle scrollRect = new Rectangle(cursorLocation.x - insets.left, cursorLocation.y - insets.top, insets.left + insets.right, insets.top + insets.bottom);
tree.scrollRectToVisible(scrollRect);
}
}
/* ----------------- autoscroll implementation end ------------------ */
/* ----------------- insertion mark painting start ------------------ */
/**
* manage display of a drag mark either highlighting a node or drawing an
* insertion mark
* @param tree associate jxtreetable
* @param location drop position
*/
public void updateDragMark(JXTreeTable tree, Point location) {
mostRecentLocation = location;
int row = tree.getRowForPath(tree.getPathForLocation(location.x, location.y));
TreePath path = tree.getPathForRow(row);
if (path != null) {
Rectangle rowBounds = tree.getCellRect(row, 0, false); // tree.getPathBounds(path);
/*
* find out if we have to mark a tree node or if we
* have to draw an insertion marker
*/
int rby = rowBounds.y;
int topBottomDist = insertAreaHeight / 2;
// x = top, y = bottom of insert area
Point topBottom = new Point(rby - topBottomDist, rby + topBottomDist);
if (topBottom.x <= location.y && topBottom.y >= location.y) {
// we are inside an insertArea
paintInsertMarker(tree, location);
} else {
// we are inside a node
markNode(tree, location);
}
}
}
/**
* get the most recent mouse location, i.e. the drop location when called upon drop
* @return the mouse location recorded most recently during a drag operation
*/
public Point getMostRecentDragLocation() {
return mostRecentLocation;
}
/**
* mark the node that is closest to the current mouse location
* @param tree
* @param location
*/
private void markNode(JXTreeTable tree, Point location) {
TreePath path = tree.getPathForLocation(location.x, location.y);
int row = tree.getRowForPath(path);
if (path != null) {
if (lastRowBounds != null) {
Graphics g = tree.getGraphics();
g.setColor(Color.white);
g.drawLine(lastRowBounds.x, lastRowBounds.y, lastRowBounds.x + lastRowBounds.width, lastRowBounds.y);
}
tree.setRowSelectionInterval(row, row);
//tree.setSelectionPath(path);
//only expand subnode if its a mapcontext node
Object node = path.getLastPathComponent();
if(node instanceof ContextTreeNode){
Object obj = ((ContextTreeNode)node).getUserObject();
if(obj instanceof MapContext){
tree.expandPath(path);
}
}
}
}
/**
* paint an insert marker between the nodes closest to the current mouse location
* @param tree
* @param location
*/
private void paintInsertMarker(JXTreeTable tree, Point location) {
Graphics g = tree.getGraphics();
tree.clearSelection();
int row = tree.getRowForPath(tree.getPathForLocation(location.x, location.y));
TreePath path = tree.getPathForRow(row);
if (path != null) {
Rectangle rowBounds = tree.getCellRect(row, 0, false); //tree.getPathBounds(path);
if (lastRowBounds != null) {
g.setColor(Color.white);
g.drawLine(lastRowBounds.x, lastRowBounds.y, lastRowBounds.x + lastRowBounds.width, lastRowBounds.y);
}
if (rowBounds != null) {
g.setColor(Color.black);
g.drawLine(rowBounds.x, rowBounds.y, rowBounds.x + rowBounds.width, rowBounds.y);
}
lastRowBounds = rowBounds;
}
}
/* ----------------- insertion mark painting end ------------------ */
}