/* Copyright (c) 2006-2007 Timothy Wall, All Rights Reserved
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
* <p/>
* 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 furbelow;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.dnd.Autoscroll;
import java.awt.dnd.DropTarget;
import javax.swing.JComponent;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
/** Provides consistent drag/drop autoscrolling behavior for components
* which implement {@link Autoscroll}. The component's implementation
* of {@link Autoscroll#getAutoscrollInsets} and
* {@link Autoscroll#autoscroll(Point)} should call through to this object's
* methods.<p>
* The default behavior is to scroll by the unit/line increment when near
* the edge of the viewport or the block/page increment when <em>very</em>
* near the edge.
*/
public class Autoscroller implements Autoscroll {
private static final int DEFAULT_MARGIN = 32;
public static int MARGIN = DEFAULT_MARGIN;
private static final int ASK = -1;
private JComponent component;
private int unitIncrement;
private int blockIncrement;
private Insets margins;
private Insets fastMargins;
static {
try {
MARGIN = Integer.getInteger("Autoscroller.margin", DEFAULT_MARGIN).intValue();
}
catch(SecurityException e) { }
}
public Autoscroller(JComponent scrolled) {
this(scrolled, MARGIN, MARGIN/2, ASK, ASK);
}
public Autoscroller(JComponent scrolled,
int normal, int fast, int unit, int block) {
this.component = scrolled;
margins = new Insets(normal, normal, normal, normal);
fastMargins = new Insets(fast, fast, fast, fast);
unitIncrement = unit;
blockIncrement = block;
}
protected Insets fastScrollInsets() {
return fastMargins;
}
protected Insets scrollInsets() {
return margins;
}
/** Return the insets of the component's rectangle that should be
* active. Note that the actual inset values represent pixels in the
* component coordinate space which appear in the viewport.
*/
public Insets getAutoscrollInsets() {
Insets insets = scrollInsets();
Rectangle rect = component.getVisibleRect();
return new Insets(rect.y + insets.top, rect.x + insets.left,
component.getHeight() - rect.y - rect.height
+ insets.bottom - 1,
component.getWidth() - rect.x - rect.width
+ insets.right - 1);
}
private int unit(Rectangle visible, int orientation, int direction) {
if (unitIncrement == ASK) {
if (component instanceof Scrollable) {
return ((Scrollable)component).getScrollableUnitIncrement(visible, orientation, direction);
}
return 1;
}
return unitIncrement;
}
private int block(Rectangle visible, int orientation, int direction) {
if (blockIncrement == ASK) {
if (component instanceof Scrollable) {
return ((Scrollable)component).getScrollableBlockIncrement(visible, orientation, direction);
}
return 1;
}
return blockIncrement;
}
/** Invoke from {@link Autoscroll#autoscroll(Point)}.
* @param where The current cursor location in the target component
* coordinate space
*/
public void autoscroll(Point where) {
Insets insets = scrollInsets();
Insets fast = fastScrollInsets();
Rectangle visible = component.getVisibleRect();
if (where.x <= visible.x + insets.left) {
int delta = where.x < visible.x + fast.left
? block(visible, SwingConstants.HORIZONTAL, -1)
: unit(visible, SwingConstants.HORIZONTAL, -1);
visible.x = Math.max(visible.x - delta, 0);
}
else if (where.x >= visible.x + visible.width - insets.right - 1) {
int delta = where.x >= visible.x + visible.width - fast.right - 1
? block(visible, SwingConstants.HORIZONTAL, 1)
: unit(visible, SwingConstants.HORIZONTAL, 1);
visible.x = Math.min(visible.x + delta,
visible.x + component.getWidth() - visible.width - 1);
}
if (where.y <= visible.y + insets.top) {
int delta = where.y < visible.y + fast.top
? block(visible, SwingConstants.VERTICAL, -1)
: unit(visible, SwingConstants.VERTICAL, -1);
visible.y = Math.max(visible.y - delta, 0);
}
else if (where.y >= visible.y + visible.height - insets.bottom - 1) {
int delta = where.y >= visible.y + visible.height - fast.bottom - 1
? block(visible, SwingConstants.VERTICAL, 1)
: unit(visible, SwingConstants.VERTICAL, 1);
visible.y = Math.min(visible.y + delta,
visible.y + component.getHeight() - visible.height - 1);
}
component.scrollRectToVisible(visible);
}
}