Reset branch to trunk.
[official-gcc.git] / trunk / libjava / classpath / java / awt / LightweightDispatcher.java
blob39bdb887dc88851c0597bcfc1cefbb87622614c5
1 /* LightweightDispatcher.java -- Dispatches mouse events to lightweights
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.awt;
41 import java.awt.event.InputEvent;
42 import java.awt.event.MouseEvent;
43 import java.awt.event.MouseWheelEvent;
44 import java.awt.peer.LightweightPeer;
45 import java.util.WeakHashMap;
47 /**
48 * Redispatches mouse events to lightweight components. The native peers know
49 * nothing about the lightweight components and thus mouse events are always
50 * targetted at Windows or heavyweight components. This class listenes directly
51 * on the eventqueue and dispatches mouse events to lightweight components.
53 * @author Roman Kennke (kennke@aicas.com)
55 final class LightweightDispatcher
58 /**
59 * Maps thread groups to lightweight dispatcher instances. We need to
60 * have one instance per thread group so that 2 or more applets or otherwise
61 * separated applications (like in OSGI) do not interfer with each other.
63 private static WeakHashMap instances = new WeakHashMap();
65 /**
66 * The last mouse event target. If the target changes, additional
67 * MOUSE_ENTERED and MOUSE_EXITED events must be dispatched.
69 private Component lastTarget;
71 /**
72 * The current mouseEventTarget.
74 private Component mouseEventTarget;
76 /**
77 * Returns an instance of LightweightDispatcher for the current thread's
78 * thread group.
80 * @return an instance of LightweightDispatcher for the current thread's
81 * thread group
83 static LightweightDispatcher getInstance()
85 Thread t = Thread.currentThread();
86 ThreadGroup tg = t.getThreadGroup();
87 LightweightDispatcher instance = (LightweightDispatcher) instances.get(tg);
88 if (instance == null)
90 instance = new LightweightDispatcher();
91 instances.put(tg, instance);
93 return instance;
96 /**
97 * Creates a new LightweightDispatcher. This is private to prevent access
98 * from outside. Use {@link #getInstance()} instead.
100 private LightweightDispatcher()
102 // Nothing to do here.
106 * Receives notification if a mouse event passes along the eventqueue.
108 * @param event the event
110 public boolean dispatchEvent(final AWTEvent event)
112 if (event instanceof MouseEvent)
114 MouseEvent mouseEvent = (MouseEvent) event;
115 return handleMouseEvent(mouseEvent);
117 return false;
121 * Handles all mouse events that are targetted at toplevel containers
122 * (Window instances) and dispatches them to the correct lightweight child.
124 * @param ev the mouse event
125 * @return whether or not we found a lightweight that handled the event.
127 private boolean handleMouseEvent(final MouseEvent ev)
129 Container container = (Container) ev.getSource();
130 Component target = findTarget(container, ev.getX(), ev.getY());
131 trackEnterExit(target, ev);
132 int id = ev.getID();
134 // Dont update the mouseEventTarget when dragging. Also, MOUSE_CLICKED
135 // must be dispatched to the original target of MOUSE_PRESSED, so don't
136 // update in this case either.
137 if (! isDragging(ev) && id != MouseEvent.MOUSE_CLICKED)
138 mouseEventTarget = (target != container) ? target : null;
140 if (mouseEventTarget != null)
142 switch (id)
144 case MouseEvent.MOUSE_ENTERED:
145 case MouseEvent.MOUSE_EXITED:
146 // This is already handled in trackEnterExit().
147 break;
148 case MouseEvent.MOUSE_PRESSED:
149 case MouseEvent.MOUSE_RELEASED:
150 case MouseEvent.MOUSE_MOVED:
151 redispatch(ev, mouseEventTarget, id);
152 break;
153 case MouseEvent.MOUSE_CLICKED:
154 // MOUSE_CLICKED must be dispatched to the original target of
155 // MOUSE_PRESSED.
156 if (target == mouseEventTarget)
157 redispatch(ev, mouseEventTarget, id);
158 break;
159 case MouseEvent.MOUSE_DRAGGED:
160 if (isDragging(ev))
161 redispatch(ev, mouseEventTarget, id);
162 break;
163 case MouseEvent.MOUSE_WHEEL:
164 redispatch(ev, mouseEventTarget, id);
166 ev.consume();
169 return ev.isConsumed();
173 * Finds the actual target for a mouseevent, starting at <code>c</code>.
174 * This searches through the children of the container and finds the first
175 * one which is showing, at the location from the mouse event and has
176 * a MouseListener or MouseMotionListener attached. If no such child component
177 * is found, null is returned.
179 * @param c the container to search through
180 * @param loc the mouse event point
182 * @return the actual receiver of the mouse event, or null, if no such
183 * component has been found
185 private Component findTarget(final Container c, final int x, final int y)
187 Component target = null;
189 // First we check the children of the container.
191 // Note: It is important that we use the package private Container
192 // fields ncomponents and component here. There are applications
193 // that override getComponentCount()
194 // and getComponent() to hide internal components, which makes
195 // the LightweightDispatcher not work correctly in these cases.
196 // As a positive sideeffect this is slightly more efficient.
197 int nChildren = c.ncomponents;
198 for (int i = 0; i < nChildren && target == null; i++)
200 Component child = c.component[i];
201 int childX = x - child.x;
202 int childY = y - child.y;
203 if (child != null && child.visible
204 && child.peer instanceof LightweightPeer
205 && child.contains(childX, childY))
207 // Check if there's a deeper possible target.
208 if (child instanceof Container)
210 Component deeper = findTarget((Container) child,
211 childX, childY);
212 if (deeper != null)
213 target = deeper;
215 // Check if the child itself is interested in mouse events.
216 else if (isMouseListening(child))
217 target = child;
221 // Check the container itself, if we didn't find a target yet.
222 if (target == null && c.contains(x, y) && isMouseListening(c))
223 target = c;
225 return target;
229 * Checks if the specified component would be interested in a mouse event.
231 * @param c the component to check
233 * @return <code>true</code> if the component has mouse listeners installed,
234 * <code>false</code> otherwise
236 private boolean isMouseListening(final Component c)
238 // Note: It is important to NOT check if the component is listening
239 // for a specific event (for instance, mouse motion events). The event
240 // gets dispatched to the component if the component is listening
241 // for ANY mouse event, even when the component is not listening for the
242 // specific type of event. There are applications that depend on this
243 // (sadly).
244 return c.mouseListener != null
245 || c.mouseMotionListener != null
246 || c.mouseWheelListener != null
247 || (c.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0
248 || (c.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0
249 || (c.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0;
253 * Tracks MOUSE_ENTERED and MOUSE_EXIT as well as MOUSE_MOVED and
254 * MOUSE_DRAGGED and creates synthetic MOUSE_ENTERED and MOUSE_EXITED for
255 * lightweight component.s
257 * @param target the current mouse event target
258 * @param ev the mouse event
260 private void trackEnterExit(final Component target, final MouseEvent ev)
262 int id = ev.getID();
263 if (target != lastTarget)
265 if (lastTarget != null)
266 redispatch(ev, lastTarget, MouseEvent.MOUSE_EXITED);
267 if (id == MouseEvent.MOUSE_EXITED)
268 ev.consume();
269 if (target != null)
270 redispatch(ev, target, MouseEvent.MOUSE_ENTERED);
271 if (id == MouseEvent.MOUSE_ENTERED)
272 ev.consume();
273 lastTarget = target;
279 * Redispatches the specified mouse event to the specified target with the
280 * specified id.
282 * @param ev the mouse event
283 * @param target the new target
284 * @param id the new id
286 private void redispatch(MouseEvent ev, Component target, int id)
288 Component source = ev.getComponent();
289 assert target != null;
290 if (target.isShowing())
292 // Translate coordinates.
293 int x = ev.getX();
294 int y = ev.getY();
295 for (Component c = target; c != null && c != source; c = c.getParent())
297 x -= c.x;
298 y -= c.y;
301 // Retarget event.
302 MouseEvent retargeted;
303 if (id == MouseEvent.MOUSE_WHEEL)
305 MouseWheelEvent mwe = (MouseWheelEvent) ev;
306 retargeted = new MouseWheelEvent(target, id, ev.getWhen(),
307 ev.getModifiers()
308 | ev.getModifiersEx(), x, y,
309 ev.getClickCount(),
310 ev.isPopupTrigger(),
311 mwe.getScrollType(),
312 mwe.getScrollAmount(),
313 mwe.getWheelRotation());
315 else
317 retargeted = new MouseEvent(target, id, ev.getWhen(),
318 ev.getModifiers() | ev.getModifiersEx(),
319 x, y, ev.getClickCount(),
320 ev.isPopupTrigger(), ev.getButton());
323 if (target == source)
324 ((Container) target).dispatchNoLightweight(retargeted);
325 else
326 target.dispatchEvent(retargeted);
331 * Determines if we are in the middle of a drag operation, that is, if
332 * any of the buttons is held down.
334 * @param ev the mouse event to check
336 * @return <code>true</code> if we are in the middle of a drag operation,
337 * <code>false</code> otherwise
339 private boolean isDragging(MouseEvent ev)
341 int mods = ev.getModifiersEx();
342 int id = ev.getID();
343 if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED)
345 switch (ev.getButton())
347 case MouseEvent.BUTTON1:
348 mods ^= InputEvent.BUTTON1_DOWN_MASK;
349 break;
350 case MouseEvent.BUTTON2:
351 mods ^= InputEvent.BUTTON2_DOWN_MASK;
352 break;
353 case MouseEvent.BUTTON3:
354 mods ^= InputEvent.BUTTON3_DOWN_MASK;
355 break;
358 return (mods & (InputEvent.BUTTON1_DOWN_MASK
359 | InputEvent.BUTTON2_DOWN_MASK
360 | InputEvent.BUTTON3_DOWN_MASK)) != 0;