KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicDropTargetListener


1 /*
2  * @(#)BasicDropTargetListener.java 1.10 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.plaf.basic;
8
9 import java.awt.*;
10 import java.awt.dnd.*;
11 import javax.swing.*;
12 import javax.swing.plaf.UIResource JavaDoc;
13
14 import java.awt.event.*;
15 import javax.swing.Timer JavaDoc;
16
17 /**
18  * The Swing DropTarget implementation supports multicast notification
19  * to listeners, so this implementation is used as an additional
20  * listener that extends the primary drop target functionality
21  * (i.e. linkage to the TransferHandler) to include autoscroll and
22  * establish an insertion point for the drop. This is used by the ComponentUI
23  * of components supporting a selection mechanism, which have a
24  * way of indicating a location within their model.
25  * <p>
26  * The autoscroll functionality is based upon the Swing scrolling mechanism
27  * of the Scrollable interface. The unit scroll increment is used to as
28  * the scroll amount, and the scrolling is based upon JComponent.getVisibleRect
29  * and JComponent.scrollRectToVisible. The band of area around the visible
30  * rectangle used to invoke autoscroll is based upon the unit scroll increment
31  * as that is assumed to represent the last possible item in the visible region.
32  * <p>
33  * The subclasses are expected to implement the following methods to manage the
34  * insertion location via the components selection mechanism.
35  * <ul>
36  * <li>saveComponentState
37  * <li>restoreComponentState
38  * <li>restoreComponentStateForDrop
39  * <li>updateInsertionLocation
40  * </ul>
41  *
42  * @author Timothy Prinzing
43  * @version 1.10 12/19/03
44  */

45 class BasicDropTargetListener implements DropTargetListener, UIResource JavaDoc, ActionListener {
46
47     /**
48      * construct a DropTargetAutoScroller
49      * <P>
50      * @param c the <code>Component</code>
51      * @param p the <code>Point</code>
52      */

53     protected BasicDropTargetListener() {
54     }
55
56
57     /**
58      * called to save the state of a component in case it needs to
59      * be restored because a drop is not performed.
60      */

61     protected void saveComponentState(JComponent c) {
62     }
63
64     /**
65      * called to restore the state of a component in case a drop
66      * is not performed.
67      */

68     protected void restoreComponentState(JComponent c) {
69     }
70
71     /**
72      * called to restore the state of a component in case a drop
73      * is performed.
74      */

75     protected void restoreComponentStateForDrop(JComponent c) {
76     }
77
78     /**
79      * called to set the insertion location to match the current
80      * mouse pointer coordinates.
81      */

82     protected void updateInsertionLocation(JComponent c, Point p) {
83     }
84
85     private static final int AUTOSCROLL_INSET = 10;
86
87     /**
88      * Update the geometry of the autoscroll region. The geometry is
89      * maintained as a pair of rectangles. The region can cause
90      * a scroll if the pointer sits inside it for the duration of the
91      * timer. The region that causes the timer countdown is the area
92      * between the two rectangles.
93      * <p>
94      * This is implemented to use the visible area of the component
95      * as the outer rectangle, and the insets are fixed at 10. Should
96      * the component be smaller than a total of 20 in any direction,
97      * autoscroll will not occur in that direction.
98      */

99     void updateAutoscrollRegion(JComponent c) {
100     // compute the outer
101
Rectangle visible = c.getVisibleRect();
102     outer.reshape(visible.x, visible.y, visible.width, visible.height);
103
104     // compute the insets
105
Insets i = new Insets(0, 0, 0, 0);
106     if (c instanceof Scrollable) {
107             int minSize = 2 * AUTOSCROLL_INSET;
108
109             if (visible.width >= minSize) {
110                 i.left = i.right = AUTOSCROLL_INSET;
111             }
112             
113             if (visible.height >= minSize) {
114                 i.top = i.bottom = AUTOSCROLL_INSET;
115             }
116     }
117
118     // set the inner from the insets
119
inner.reshape(visible.x + i.left,
120               visible.y + i.top,
121               visible.width - (i.left + i.right),
122               visible.height - (i.top + i.bottom));
123     }
124
125     /**
126      * Perform an autoscroll operation. This is implemented to scroll by the
127      * unit increment of the Scrollable using scrollRectToVisible. If the
128      * cursor is in a corner of the autoscroll region, more than one axis will
129      * scroll.
130      */

131     void autoscroll(JComponent c, Point pos) {
132     if (c instanceof Scrollable) {
133         Scrollable s = (Scrollable) c;
134         if (pos.y < inner.y) {
135         // scroll upward
136
int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, -1);
137         Rectangle r = new Rectangle(inner.x, outer.y - dy, inner.width, dy);
138         c.scrollRectToVisible(r);
139         } else if (pos.y > (inner.y + inner.height)) {
140         // scroll downard
141
int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, 1);
142         Rectangle r = new Rectangle(inner.x, outer.y + outer.height, inner.width, dy);
143         c.scrollRectToVisible(r);
144         }
145
146         if (pos.x < inner.x) {
147         // scroll left
148
int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, -1);
149         Rectangle r = new Rectangle(outer.x - dx, inner.y, dx, inner.height);
150         c.scrollRectToVisible(r);
151         } else if (pos.x > (inner.x + inner.width)) {
152         // scroll right
153
int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, 1);
154         Rectangle r = new Rectangle(outer.x + outer.width, inner.y, dx, inner.height);
155         c.scrollRectToVisible(r);
156         }
157     }
158     }
159
160     /**
161      * Initializes the internal properties if they haven't been already
162      * inited. This is done lazily to avoid loading of desktop properties.
163      */

164     private void initPropertiesIfNecessary() {
165         if (timer == null) {
166             Toolkit t = Toolkit.getDefaultToolkit();
167             Integer JavaDoc initial = new Integer JavaDoc(100);
168             Integer JavaDoc interval = new Integer JavaDoc(100);
169
170             try {
171                 initial = (Integer JavaDoc)t.getDesktopProperty(
172                                      "DnD.Autoscroll.initialDelay");
173             } catch (Exception JavaDoc e) {
174                 // ignore
175
}
176             try {
177                 interval = (Integer JavaDoc)t.getDesktopProperty(
178                                       "DnD.Autoscroll.interval");
179             } catch (Exception JavaDoc e) {
180                 // ignore
181
}
182             timer = new Timer JavaDoc(interval.intValue(), this);
183
184             timer.setCoalesce(true);
185             timer.setInitialDelay(initial.intValue());
186
187             try {
188                 hysteresis = ((Integer JavaDoc)t.getDesktopProperty(
189                              "DnD.Autoscroll.cursorHysteresis")).intValue();
190             } catch (Exception JavaDoc e) {
191                 // ignore
192
}
193         }
194     }
195
196     static JComponent getComponent(DropTargetEvent e) {
197     DropTargetContext context = e.getDropTargetContext();
198     return (JComponent) context.getComponent();
199     }
200
201     // --- ActionListener methods --------------------------------------
202

203     /**
204      * The timer fired, perform autoscroll if the pointer is within the
205      * autoscroll region.
206      * <P>
207      * @param e the <code>ActionEvent</code>
208      */

209     public synchronized void actionPerformed(ActionEvent e) {
210     updateAutoscrollRegion(component);
211     if (outer.contains(lastPosition) && !inner.contains(lastPosition)) {
212         autoscroll(component, lastPosition);
213     }
214     }
215
216     // --- DropTargetListener methods -----------------------------------
217

218     public void dragEnter(DropTargetDragEvent e) {
219     component = getComponent(e);
220     TransferHandler th = component.getTransferHandler();
221     canImport = th.canImport(component, e.getCurrentDataFlavors());
222     if (canImport) {
223         saveComponentState(component);
224         lastPosition = e.getLocation();
225         updateAutoscrollRegion(component);
226             initPropertiesIfNecessary();
227     }
228     }
229
230     public void dragOver(DropTargetDragEvent e) {
231     if (canImport) {
232         Point p = e.getLocation();
233         updateInsertionLocation(component, p);
234
235
236         // check autoscroll
237
synchronized(this) {
238         if (Math.abs(p.x - lastPosition.x) > hysteresis ||
239             Math.abs(p.y - lastPosition.y) > hysteresis) {
240             // no autoscroll
241
if (timer.isRunning()) timer.stop();
242         } else {
243             if (!timer.isRunning()) timer.start();
244         }
245         lastPosition = p;
246         }
247     }
248     }
249
250     public void dragExit(DropTargetEvent e) {
251         if (canImport) {
252             restoreComponentState(component);
253         }
254         cleanup();
255     }
256
257     public void drop(DropTargetDropEvent e) {
258         if (canImport) {
259             restoreComponentStateForDrop(component);
260         }
261         cleanup();
262     }
263
264     public void dropActionChanged(DropTargetDragEvent e) {
265     }
266
267     /**
268      * Cleans up internal state after the drop has finished (either succeeded
269      * or failed).
270      */

271     private void cleanup() {
272         if (timer != null) {
273             timer.stop();
274         }
275     component = null;
276     lastPosition = null;
277     }
278
279     // --- fields --------------------------------------------------
280

281     private Timer JavaDoc timer;
282     private Point lastPosition;
283     private Rectangle outer = new Rectangle();
284     private Rectangle inner = new Rectangle();
285     private int hysteresis = 10;
286     private boolean canImport;
287
288     /**
289      * The current component. The value is cached from the drop events and used
290      * by the timer. When a drag exits or a drop occurs, this value is cleared.
291      */

292     private JComponent component;
293
294 }
295
296
Popular Tags