1 /*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.awt;
27
28 import java.awt.event.InputEvent;
29 import java.awt.event.MouseEvent;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.WindowEvent;
32 import java.lang.reflect.Method;
33 import java.security.AccessController;
34 import sun.security.action.GetPropertyAction;
35 import sun.awt.AWTAutoShutdown;
36 import sun.awt.SunToolkit;
37
38 import java.util.Vector;
39 import sun.util.logging.PlatformLogger;
40
41 import sun.awt.dnd.SunDragSourceContextPeer;
42 import sun.awt.EventQueueDelegate;
43
44 /**
45 * EventDispatchThread is a package-private AWT class which takes
46 * events off the EventQueue and dispatches them to the appropriate
47 * AWT components.
48 *
49 * The Thread starts a "permanent" event pump with a call to
50 * pumpEvents(Conditional) in its run() method. Event handlers can choose to
51 * block this event pump at any time, but should start a new pump (<b>not</b>
52 * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
53 * secondary event pump will exit automatically as soon as the Condtional
54 * evaluate()s to false and an additional Event is pumped and dispatched.
55 *
56 * @author Tom Ball
57 * @author Amy Fowler
58 * @author Fred Ecks
59 * @author David Mendenhall
60 *
61 * @since 1.1
62 */
63 class EventDispatchThread extends Thread {
64
65 private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventDispatchThread");
66
67 private EventQueue theQueue;
68 private boolean doDispatch = true;
69 private boolean threadDeathCaught = false;
70
71 private static final int ANY_EVENT = -1;
72
73 private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
74
75 EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
76 super(group, name);
77 setEventQueue(queue);
78 }
79
80 /*
81 * Must be called on EDT only, that's why no synchronization
82 */
83 public void stopDispatching() {
84 doDispatch = false;
85 }
86
87 public void run() {
88 while (true) {
89 try {
90 pumpEvents(new Conditional() {
91 public boolean evaluate() {
92 return true;
93 }
94 });
95 } finally {
96 EventQueue eq = getEventQueue();
97 if (eq.detachDispatchThread(this) || threadDeathCaught) {
98 break;
99 }
100 }
101 }
102 }
103
104 void pumpEvents(Conditional cond) {
105 pumpEvents(ANY_EVENT, cond);
106 }
107
108 void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
109 pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
110 }
111
112 void pumpEvents(int id, Conditional cond) {
113 pumpEventsForHierarchy(id, cond, null);
114 }
115
116 void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) {
117 pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
118 }
119
120 void pumpEventsForFilter(Conditional cond, EventFilter filter) {
121 pumpEventsForFilter(ANY_EVENT, cond, filter);
122 }
123
124 void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
125 addEventFilter(filter);
126 doDispatch = true;
127 while (doDispatch && cond.evaluate()) {
128 if (isInterrupted() || !pumpOneEventForFilters(id)) {
129 doDispatch = false;
130 }
131 }
132 removeEventFilter(filter);
133 }
134
135 void addEventFilter(EventFilter filter) {
136 eventLog.finest("adding the event filter: " + filter);
137 synchronized (eventFilters) {
138 if (!eventFilters.contains(filter)) {
139 if (filter instanceof ModalEventFilter) {
140 ModalEventFilter newFilter = (ModalEventFilter)filter;
141 int k = 0;
142 for (k = 0; k < eventFilters.size(); k++) {
143 EventFilter f = eventFilters.get(k);
144 if (f instanceof ModalEventFilter) {
145 ModalEventFilter cf = (ModalEventFilter)f;
146 if (cf.compareTo(newFilter) > 0) {
147 break;
148 }
149 }
150 }
151 eventFilters.add(k, filter);
152 } else {
153 eventFilters.add(filter);
154 }
155 }
156 }
157 }
158
159 void removeEventFilter(EventFilter filter) {
160 eventLog.finest("removing the event filter: " + filter);
161 synchronized (eventFilters) {
162 eventFilters.remove(filter);
163 }
164 }
165
166 boolean pumpOneEventForFilters(int id) {
167 AWTEvent event = null;
168 boolean eventOK = false;
169 try {
170 EventQueue eq = null;
171 EventQueueDelegate.Delegate delegate = null;
172 do {
173 // EventQueue may change during the dispatching
174 eq = getEventQueue();
175 delegate = EventQueueDelegate.getDelegate();
176
177 if (delegate != null && id == ANY_EVENT) {
178 event = delegate.getNextEvent(eq);
179 } else {
180 event = (id == ANY_EVENT) ? eq.getNextEvent() : eq.getNextEvent(id);
181 }
182
183 eventOK = true;
184 synchronized (eventFilters) {
185 for (int i = eventFilters.size() - 1; i >= 0; i--) {
186 EventFilter f = eventFilters.get(i);
187 EventFilter.FilterAction accept = f.acceptEvent(event);
188 if (accept == EventFilter.FilterAction.REJECT) {
189 eventOK = false;
190 break;
191 } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
192 break;
193 }
194 }
195 }
196 eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
197 if (!eventOK) {
198 event.consume();
199 }
200 }
201 while (eventOK == false);
202
203 if (eventLog.isLoggable(PlatformLogger.FINEST)) {
204 eventLog.finest("Dispatching: " + event);
205 }
206
207 Object handle = null;
208 if (delegate != null) {
209 handle = delegate.beforeDispatch(event);
210 }
211 eq.dispatchEvent(event);
212 if (delegate != null) {
213 delegate.afterDispatch(event, handle);
214 }
215
216 return true;
217 }
218 catch (ThreadDeath death) {
219 threadDeathCaught = true;
220 return false;
221
222 }
223 catch (InterruptedException interruptedException) {
224 return false; // AppContext.dispose() interrupts all
225 // Threads in the AppContext
226
227 }
228 catch (Throwable e) {
229 processException(e);
230 }
231
232 return true;
233 }
234
235 private void processException(Throwable e) {
236 if (eventLog.isLoggable(PlatformLogger.FINE)) {
237 eventLog.fine("Processing exception: " + e);
238 }
239 getUncaughtExceptionHandler().uncaughtException(this, e);
240 }
241
242 public synchronized EventQueue getEventQueue() {
243 return theQueue;
244 }
245 public synchronized void setEventQueue(EventQueue eq) {
246 theQueue = eq;
247 }
248
249 private static class HierarchyEventFilter implements EventFilter {
250 private Component modalComponent;
251 public HierarchyEventFilter(Component modalComponent) {
252 this.modalComponent = modalComponent;
253 }
254 public FilterAction acceptEvent(AWTEvent event) {
255 if (modalComponent != null) {
256 int eventID = event.getID();
257 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
258 (eventID <= MouseEvent.MOUSE_LAST);
259 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
260 (eventID <= ActionEvent.ACTION_LAST);
261 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
262 /*
263 * filter out MouseEvent and ActionEvent that's outside
264 * the modalComponent hierarchy.
265 * KeyEvent is handled by using enqueueKeyEvent
266 * in Dialog.show
267 */
268 if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
269 /*
270 * Modal internal frames are handled separately. If event is
271 * for some component from another heavyweight than modalComp,
272 * it is accepted. If heavyweight is the same - we still accept
273 * event and perform further filtering in LightweightDispatcher
274 */
275 return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
276 }
277 if (mouseEvent || actionEvent || windowClosingEvent) {
278 Object o = event.getSource();
279 if (o instanceof sun.awt.ModalExclude) {
280 // Exclude this object from modality and
281 // continue to pump it's events.
282 return FilterAction.ACCEPT;
283 } else if (o instanceof Component) {
284 Component c = (Component) o;
285 // 5.0u3 modal exclusion
286 boolean modalExcluded = false;
287 if (modalComponent instanceof Container) {
288 while (c != modalComponent && c != null) {
289 if ((c instanceof Window) &&
290 (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
291 // Exclude this window and all its children from
292 // modality and continue to pump it's events.
293 modalExcluded = true;
294 break;
295 }
296 c = c.getParent();
297 }
298 }
299 if (!modalExcluded && (c != modalComponent)) {
300 return FilterAction.REJECT;
301 }
302 }
303 }
304 }
305 return FilterAction.ACCEPT;
306 }
307 }
308 }