/*
* @(#)BasicGraphDropTargetListener 1.0 03-JUL-04
*
* Copyright (c) 2001-2004 Gaudenz Alder
*
*/
package org.jgraph.plaf.basic;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.plaf.UIResource;
public class BasicGraphDropTargetListener
implements DropTargetListener, UIResource, ActionListener {
/**
* construct a DropTargetAutoScroller
*/
public BasicGraphDropTargetListener() {
}
/**
* called to save the state of a component in case it needs to
* be restored because a drop is not performed.
*/
protected void saveComponentState(JComponent c) {
}
/**
* called to restore the state of a component in case a drop
* is not performed.
*/
protected void restoreComponentState(JComponent c) {
}
/**
* called to restore the state of a component in case a drop
* is performed.
*/
protected void restoreComponentStateForDrop(JComponent c) {
}
/**
* called to set the insertion location to match the current
* mouse pointer coordinates.
*/
protected void updateInsertionLocation(JComponent c, Point p) {
}
/**
* Update the geometry of the autoscroll region. The geometry is
* maintained as a pair of rectangles. The region can cause
* a scroll if the pointer sits inside it for the duration of the
* timer. The region that causes the timer countdown is the area
* between the two rectangles.
* <p>
* This is implemented to use the visible area of the component
* as the outer rectangle and the insets are based upon the
* Scrollable information (if any). If the Scrollable is
* scrollable along an axis, the step increment is used as
* the autoscroll inset. If the component is not scrollable,
* the insets will be zero (i.e. autoscroll will not happen).
*/
void updateAutoscrollRegion(JComponent c) {
// compute the outer
Rectangle visible = c.getVisibleRect();
outer.setBounds(visible.x, visible.y, visible.width, visible.height);
// compute the insets
// TBD - the thing with the scrollable
Insets i = new Insets(0, 0, 0, 0);
if (c instanceof Scrollable) {
Scrollable s = (Scrollable) c;
i.left =
s.getScrollableUnitIncrement(
visible,
SwingConstants.HORIZONTAL,
1);
i.top =
s.getScrollableUnitIncrement(
visible,
SwingConstants.VERTICAL,
1);
i.right =
s.getScrollableUnitIncrement(
visible,
SwingConstants.HORIZONTAL,
-1);
i.bottom =
s.getScrollableUnitIncrement(
visible,
SwingConstants.VERTICAL,
-1);
}
// set the inner from the insets
inner.setBounds(
visible.x + i.left,
visible.y + i.top,
visible.width - (i.left + i.right),
visible.height - (i.top + i.bottom));
}
/**
* Perform an autoscroll operation. This is implemented to scroll by the
* unit increment of the Scrollable using scrollRectToVisible. If the
* cursor is in a corner of the autoscroll region, more than one axis will
* scroll.
*/
void autoscroll(JComponent c, Point pos) {
if (c instanceof org.jgraph.JGraph)
BasicGraphUI.autoscroll((org.jgraph.JGraph) c, pos);
}
/**
* Initializes the internal properties if they haven't been already
* inited. This is done lazily to avoid loading of desktop properties.
*/
private void initPropertiesIfNecessary() {
if (timer == null) {
Toolkit t = Toolkit.getDefaultToolkit();
Integer initial = new Integer(100);
Integer interval = new Integer(100);
try {
initial =
(Integer) t.getDesktopProperty(
"DnD.Autoscroll.initialDelay");
} catch (Exception e) {
// ignore
}
try {
interval =
(Integer) t.getDesktopProperty("DnD.Autoscroll.interval");
} catch (Exception e) {
// ignore
}
timer = new Timer(interval.intValue(), this);
timer.setCoalesce(true);
timer.setInitialDelay(initial.intValue());
try {
hysteresis =
((Integer) t
.getDesktopProperty("DnD.Autoscroll.cursorHysteresis"))
.intValue();
} catch (Exception e) {
// ignore
}
}
}
static JComponent getComponent(DropTargetEvent e) {
DropTargetContext context = e.getDropTargetContext();
return (JComponent) context.getComponent();
}
// --- ActionListener methods --------------------------------------
/**
* The timer fired, perform autoscroll if the pointer is within the
* autoscroll region.
* <P>
* @param e the <code>ActionEvent</code>
*/
public synchronized void actionPerformed(ActionEvent e) {
updateAutoscrollRegion(component);
if (outer.contains(lastPosition) && !inner.contains(lastPosition)) {
autoscroll(component, lastPosition);
}
}
// --- DropTargetListener methods -----------------------------------
public void dragEnter(DropTargetDragEvent e) {
component = getComponent(e);
// DO NOT REMOVE OR MODIFY THIS LINE!
javax.swing.TransferHandler // JAVA13: org.jgraph.plaf.basic.TransferHandler
th = (
// DO NOT REMOVE OR MODIFY THIS LINE!
(JComponent) // JAVA13: (org.jgraph.plaf.basic.TransferHandler.JDNDAdapter)
component).getTransferHandler();
canImport = th.canImport(component, e.getCurrentDataFlavors());
if (canImport) {
saveComponentState(component);
lastPosition = e.getLocation();
updateAutoscrollRegion(component);
initPropertiesIfNecessary();
}
}
public void dragOver(DropTargetDragEvent e) {
if (canImport) {
Point p = e.getLocation();
updateInsertionLocation(component, p);
// check autoscroll
synchronized (this) {
if (Math.abs(p.x - lastPosition.x) > hysteresis
|| Math.abs(p.y - lastPosition.y) > hysteresis) {
// no autoscroll
if (timer.isRunning())
timer.stop();
} else {
if (!timer.isRunning())
timer.start();
}
lastPosition = p;
}
}
}
public void dragExit(DropTargetEvent e) {
if (canImport) {
restoreComponentState(component);
}
cleanup();
}
public void drop(DropTargetDropEvent e) {
if (canImport) {
restoreComponentStateForDrop(component);
}
cleanup();
}
public void dropActionChanged(DropTargetDragEvent e) {
}
/**
* Cleans up internal state after the drop has finished (either succeeded
* or failed).
*/
private void cleanup() {
if (timer != null) {
timer.stop();
}
component = null;
lastPosition = null;
}
// --- fields --------------------------------------------------
private Timer timer;
private Point lastPosition;
private Rectangle outer = new Rectangle();
private Rectangle inner = new Rectangle();
private int hysteresis = 10;
private boolean canImport;
/**
* The current component. The value is cached from the drop events and used
* by the timer. When a drag exits or a drop occurs, this value is cleared.
*/
private JComponent component;
}