OSDN Git Service

860646402cc819a666c2393c9e0c0cd5df435995
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / awt / LightweightDispatcher.java
1 /* LightweightDispatcher.java -- Dispatches mouse events to lightweights
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10
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.
15
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.
20
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.
25
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. */
37
38
39 package java.awt;
40
41 import gnu.java.awt.AWTUtilities;
42
43 import java.awt.event.MouseEvent;
44 import java.util.WeakHashMap;
45
46 /**
47  * Redispatches mouse events to lightweight components. The native peers know
48  * nothing about the lightweight components and thus mouse events are always
49  * targetted at Windows or heavyweight components. This class listenes directly
50  * on the eventqueue and dispatches mouse events to lightweight components.
51  *
52  * @author Roman Kennke (kennke@aicas.com)
53  */
54 class LightweightDispatcher
55 {
56
57   /**
58    * Maps thread groups to lightweight dispatcher instances. We need to
59    * have one instance per thread group so that 2 or more applets or otherwise
60    * separated applications (like in OSGI) do not interfer with each other.
61    */
62   private static WeakHashMap instances = new WeakHashMap();
63
64   /**
65    * The component that is the start of a mouse dragging. All MOUSE_DRAGGED
66    * events that follow the initial press must have the source set to this,
67    * as well as the MOUSE_RELEASED event following the dragging.
68    */
69   private Component dragTarget;
70
71   /**
72    * The last mouse event target. If the target changes, additional
73    * MOUSE_ENTERED and MOUSE_EXITED events must be dispatched.
74    */
75   private Component lastTarget;
76
77   /**
78    * Returns an instance of LightweightDispatcher for the current thread's
79    * thread group.
80    *
81    * @return an instance of LightweightDispatcher for the current thread's
82    *         thread group
83    */
84   static LightweightDispatcher getInstance()
85   {
86     Thread t = Thread.currentThread();
87     ThreadGroup tg = t.getThreadGroup();
88     LightweightDispatcher instance = (LightweightDispatcher) instances.get(tg);
89     if (instance == null)
90       {
91         instance = new LightweightDispatcher();
92         instances.put(tg, instance);
93       }
94     return instance;
95   }
96
97   /**
98    * Creates a new LightweightDispatcher. This is private to prevent access
99    * from outside. Use {@link #getInstance()} instead.
100    */
101   private LightweightDispatcher()
102   {
103     // Nothing to do here.
104   }
105   
106   /**
107    * Receives notification if a mouse event passes along the eventqueue.
108    *
109    * @param event the event
110    */
111   public boolean dispatchEvent(AWTEvent event)
112   {
113     if (event instanceof MouseEvent && event.getSource() instanceof Window)
114       {
115         MouseEvent mouseEvent = (MouseEvent) event;
116         return handleMouseEvent(mouseEvent);
117       }
118     return false;
119   }
120
121   /**
122    * Handles all mouse events that are targetted at toplevel containers
123    * (Window instances) and dispatches them to the correct lightweight child.
124    *
125    * @param ev the mouse event
126    * @return whether or not we found a lightweight that handled the event.
127    */
128   private boolean handleMouseEvent(MouseEvent ev)
129   {
130     Window window = (Window) ev.getSource();
131     Component target = window.findComponentAt(ev.getX(), ev.getY());
132     target = findTarget(target);
133     if (target == null || target.isLightweight())
134       {
135         // Dispatch additional MOUSE_EXITED and MOUSE_ENTERED if event target
136         // is different from the last event target.
137         if (target != lastTarget)
138           {
139             if (lastTarget != null)
140               {
141                 Point p1 = AWTUtilities.convertPoint(window, ev.getX(),
142                                                      ev.getY(), lastTarget);
143                 MouseEvent mouseExited =
144                   new MouseEvent(lastTarget, MouseEvent.MOUSE_EXITED,
145                                  ev.getWhen(), ev.getModifiers(), p1.x, p1.y,
146                                  ev.getClickCount(), ev.isPopupTrigger());
147                 lastTarget.dispatchEvent(mouseExited);
148               }
149             if (target != null)
150               {
151                 Point p = AWTUtilities.convertPoint(window, ev.getX(), ev.getY(),
152                                                     target);
153                 MouseEvent mouseEntered =
154                   new MouseEvent(target, MouseEvent.MOUSE_ENTERED, ev.getWhen(),
155                                  ev.getModifiers(), p.x, p.y, ev.getClickCount(),
156                                  ev.isPopupTrigger());
157                 target.dispatchEvent(mouseEntered);
158               }
159           }
160         
161         switch (ev.getID())
162         {
163           case MouseEvent.MOUSE_PRESSED:
164             dragTarget = target;
165             break;
166           case MouseEvent.MOUSE_RELEASED:
167             if (dragTarget != null)
168               target = dragTarget;
169             dragTarget = null;
170             break;
171           case MouseEvent.MOUSE_CLICKED:
172             // When we receive a MOUSE_CLICKED, we set the target to the
173             // previous target, which must have been a MOUSE_RELEASED event.
174             // This is necessary for the case when the MOUSE_RELEASED has
175             // caused the original target (like an internal component) go
176             // away.
177             target = lastTarget;
178             break;
179           case MouseEvent.MOUSE_DRAGGED:
180             target = dragTarget;
181             break;
182           default:
183             // Do nothing in other cases.
184             break;
185         }
186
187         lastTarget = target;
188
189         if (target != null)
190           {
191             Point targetCoordinates =
192               AWTUtilities.convertPoint(window, ev.getX(), ev.getY(), target);
193             int dx = targetCoordinates.x - ev.getX();
194             int dy = targetCoordinates.y - ev.getY();
195             ev.translatePoint(dx, dy);
196             ev.setSource(target);
197             target.dispatchEvent(ev);
198             // We reset the event, so that the normal event dispatching is not
199             // influenced by this modified event.
200             ev.setSource(window);
201             ev.translatePoint(-dx, -dy);
202           }
203
204         return true;
205       }
206     else
207       return false;
208   }
209
210   /**
211    * Finds the actual target for a mouseevent, starting at <code>c</code>.
212    * This searches upwards the component hierarchy until it finds a component
213    * that has a mouselistener attached.
214    *
215    * @param c the component to start searching from
216    *
217    * @return the actual receiver of the mouse event
218    */
219   private Component findTarget(Component c)
220   {
221     Component target = c;
222     while (target != null && target.getMouseListeners().length == 0)
223       {
224         target = target.getParent();
225       }
226     return target;
227   }
228 }