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)
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
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
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. */
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
;
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
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();
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
;
72 * The current mouseEventTarget.
74 private Component mouseEventTarget
;
77 * Returns an instance of LightweightDispatcher for the current thread's
80 * @return an instance of LightweightDispatcher for the current thread's
83 static LightweightDispatcher
getInstance()
85 Thread t
= Thread
.currentThread();
86 ThreadGroup tg
= t
.getThreadGroup();
87 LightweightDispatcher instance
= (LightweightDispatcher
) instances
.get(tg
);
90 instance
= new LightweightDispatcher();
91 instances
.put(tg
, instance
);
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
);
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
);
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)
144 case MouseEvent
.MOUSE_ENTERED
:
145 case MouseEvent
.MOUSE_EXITED
:
146 // This is already handled in trackEnterExit().
148 case MouseEvent
.MOUSE_PRESSED
:
149 case MouseEvent
.MOUSE_RELEASED
:
150 case MouseEvent
.MOUSE_MOVED
:
151 redispatch(ev
, mouseEventTarget
, id
);
153 case MouseEvent
.MOUSE_CLICKED
:
154 // MOUSE_CLICKED must be dispatched to the original target of
156 if (target
== mouseEventTarget
)
157 redispatch(ev
, mouseEventTarget
, id
);
159 case MouseEvent
.MOUSE_DRAGGED
:
161 redispatch(ev
, mouseEventTarget
, id
);
163 case MouseEvent
.MOUSE_WHEEL
:
164 redispatch(ev
, mouseEventTarget
, id
);
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
,
215 // Check if the child itself is interested in mouse events.
216 else if (isMouseListening(child
))
221 // Check the container itself, if we didn't find a target yet.
222 if (target
== null && c
.contains(x
, y
) && isMouseListening(c
))
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
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
)
263 if (target
!= lastTarget
)
265 if (lastTarget
!= null)
266 redispatch(ev
, lastTarget
, MouseEvent
.MOUSE_EXITED
);
267 if (id
== MouseEvent
.MOUSE_EXITED
)
270 redispatch(ev
, target
, MouseEvent
.MOUSE_ENTERED
);
271 if (id
== MouseEvent
.MOUSE_ENTERED
)
279 * Redispatches the specified mouse event to the specified target with the
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.
295 for (Component c
= target
; c
!= null && c
!= source
; c
= c
.getParent())
302 MouseEvent retargeted
;
303 if (id
== MouseEvent
.MOUSE_WHEEL
)
305 MouseWheelEvent mwe
= (MouseWheelEvent
) ev
;
306 retargeted
= new MouseWheelEvent(target
, id
, ev
.getWhen(),
308 | ev
.getModifiersEx(), x
, y
,
312 mwe
.getScrollAmount(),
313 mwe
.getWheelRotation());
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
);
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();
343 if (id
== MouseEvent
.MOUSE_PRESSED
|| id
== MouseEvent
.MOUSE_RELEASED
)
345 switch (ev
.getButton())
347 case MouseEvent
.BUTTON1
:
348 mods ^
= InputEvent
.BUTTON1_DOWN_MASK
;
350 case MouseEvent
.BUTTON2
:
351 mods ^
= InputEvent
.BUTTON2_DOWN_MASK
;
353 case MouseEvent
.BUTTON3
:
354 mods ^
= InputEvent
.BUTTON3_DOWN_MASK
;
358 return (mods
& (InputEvent
.BUTTON1_DOWN_MASK
359 | InputEvent
.BUTTON2_DOWN_MASK
360 | InputEvent
.BUTTON3_DOWN_MASK
)) != 0;