FileDocCategorySizeDatePackage
BasicDropTargetListener.javaAPI DocJava SE 5 API9385Fri Aug 26 14:58:04 BST 2005javax.swing.plaf.basic

BasicDropTargetListener

public class BasicDropTargetListener extends Object implements DropTargetListener, ActionListener, UIResource
The Swing DropTarget implementation supports multicast notification to listeners, so this implementation is used as an additional listener that extends the primary drop target functionality (i.e. linkage to the TransferHandler) to include autoscroll and establish an insertion point for the drop. This is used by the ComponentUI of components supporting a selection mechanism, which have a way of indicating a location within their model.

The autoscroll functionality is based upon the Swing scrolling mechanism of the Scrollable interface. The unit scroll increment is used to as the scroll amount, and the scrolling is based upon JComponent.getVisibleRect and JComponent.scrollRectToVisible. The band of area around the visible rectangle used to invoke autoscroll is based upon the unit scroll increment as that is assumed to represent the last possible item in the visible region.

The subclasses are expected to implement the following methods to manage the insertion location via the components selection mechanism.

  • saveComponentState
  • restoreComponentState
  • restoreComponentStateForDrop
  • updateInsertionLocation
author
Timothy Prinzing
version
1.10 12/19/03

Fields Summary
private static final int
AUTOSCROLL_INSET
private Timer
timer
private Point
lastPosition
private Rectangle
outer
private Rectangle
inner
private int
hysteresis
private boolean
canImport
private JComponent
component
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.
Constructors Summary
protected BasicDropTargetListener()
construct a DropTargetAutoScroller

param
c the Component
param
p the Point

    
Methods Summary
public synchronized voidactionPerformed(java.awt.event.ActionEvent e)
The timer fired, perform autoscroll if the pointer is within the autoscroll region.

param
e the ActionEvent

	updateAutoscrollRegion(component);
	if (outer.contains(lastPosition) && !inner.contains(lastPosition)) {
	    autoscroll(component, lastPosition);
	}
    
voidautoscroll(javax.swing.JComponent c, java.awt.Point pos)
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.

	if (c instanceof Scrollable) {
	    Scrollable s = (Scrollable) c;
	    if (pos.y < inner.y) {
		// scroll upward
		int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, -1);
		Rectangle r = new Rectangle(inner.x, outer.y - dy, inner.width, dy);
		c.scrollRectToVisible(r);
	    } else if (pos.y > (inner.y + inner.height)) {
		// scroll downard
		int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, 1);
		Rectangle r = new Rectangle(inner.x, outer.y + outer.height, inner.width, dy);
		c.scrollRectToVisible(r);
	    }

	    if (pos.x < inner.x) {
		// scroll left
		int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, -1);
		Rectangle r = new Rectangle(outer.x - dx, inner.y, dx, inner.height);
		c.scrollRectToVisible(r);
	    } else if (pos.x > (inner.x + inner.width)) {
		// scroll right
		int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, 1);
		Rectangle r = new Rectangle(outer.x + outer.width, inner.y, dx, inner.height);
		c.scrollRectToVisible(r);
	    }
	}
    
private voidcleanup()
Cleans up internal state after the drop has finished (either succeeded or failed).

        if (timer != null) {
            timer.stop();
        }
	component = null;
	lastPosition = null;
    
public voiddragEnter(java.awt.dnd.DropTargetDragEvent e)

	component = getComponent(e);
	TransferHandler th = component.getTransferHandler();
	canImport = th.canImport(component, e.getCurrentDataFlavors());
	if (canImport) {
	    saveComponentState(component);
	    lastPosition = e.getLocation();
	    updateAutoscrollRegion(component);
            initPropertiesIfNecessary();
	}
    
public voiddragExit(java.awt.dnd.DropTargetEvent e)

        if (canImport) {
            restoreComponentState(component);
        }
        cleanup();
    
public voiddragOver(java.awt.dnd.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 voiddrop(java.awt.dnd.DropTargetDropEvent e)

        if (canImport) {
            restoreComponentStateForDrop(component);
        }
        cleanup();
    
public voiddropActionChanged(java.awt.dnd.DropTargetDragEvent e)

    
static javax.swing.JComponentgetComponent(java.awt.dnd.DropTargetEvent e)

	DropTargetContext context = e.getDropTargetContext();
	return (JComponent) context.getComponent();
    
private voidinitPropertiesIfNecessary()
Initializes the internal properties if they haven't been already inited. This is done lazily to avoid loading of desktop properties.

        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
            }
        }
    
protected voidrestoreComponentState(javax.swing.JComponent c)
called to restore the state of a component in case a drop is not performed.

    
protected voidrestoreComponentStateForDrop(javax.swing.JComponent c)
called to restore the state of a component in case a drop is performed.

    
protected voidsaveComponentState(javax.swing.JComponent c)
called to save the state of a component in case it needs to be restored because a drop is not performed.

    
voidupdateAutoscrollRegion(javax.swing.JComponent c)
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.

This is implemented to use the visible area of the component as the outer rectangle, and the insets are fixed at 10. Should the component be smaller than a total of 20 in any direction, autoscroll will not occur in that direction.


                                                                                                        
       
	// compute the outer
	Rectangle visible = c.getVisibleRect();
	outer.reshape(visible.x, visible.y, visible.width, visible.height);

	// compute the insets
	Insets i = new Insets(0, 0, 0, 0);
	if (c instanceof Scrollable) {
            int minSize = 2 * AUTOSCROLL_INSET;

            if (visible.width >= minSize) {
                i.left = i.right = AUTOSCROLL_INSET;
            }
            
            if (visible.height >= minSize) {
                i.top = i.bottom = AUTOSCROLL_INSET;
            }
	}

	// set the inner from the insets
	inner.reshape(visible.x + i.left, 
		      visible.y + i.top,
		      visible.width - (i.left + i.right),
		      visible.height - (i.top  + i.bottom));
    
protected voidupdateInsertionLocation(javax.swing.JComponent c, java.awt.Point p)
called to set the insertion location to match the current mouse pointer coordinates.