1 /*
2  * Copyright (c) 1996, 2015, 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.MouseEvent;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.WindowEvent;
31 
32 import java.util.ArrayList;
33 
34 import sun.util.logging.PlatformLogger;
35 
36 import sun.awt.dnd.SunDragSourceContextPeer;
37 
38 /**
39  * EventDispatchThread is a package-private AWT class which takes
40  * events off the EventQueue and dispatches them to the appropriate
41  * AWT components.
42  *
43  * The Thread starts a "permanent" event pump with a call to
44  * pumpEvents(Conditional) in its run() method. Event handlers can choose to
45  * block this event pump at any time, but should start a new pump (<b>not</b>
46  * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
47  * secondary event pump will exit automatically as soon as the Conditional
48  * evaluate()s to false and an additional Event is pumped and dispatched.
49  *
50  * @author Tom Ball
51  * @author Amy Fowler
52  * @author Fred Ecks
53  * @author David Mendenhall
54  *
55  * @since 1.1
56  */
57 class EventDispatchThread extends Thread {
58 
59     private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventDispatchThread");
60 
61     private EventQueue theQueue;
62     private volatile boolean doDispatch = true;
63 
64     private static final int ANY_EVENT = -1;
65 
66     private ArrayList<EventFilter> eventFilters = new ArrayList<EventFilter>();
67 
68    /**
69     * Must always call 5 args super-class constructor passing false
70     * to indicate not to inherit locals.
71     */
EventDispatchThread()72     private EventDispatchThread() {
73         throw new UnsupportedOperationException("Must erase locals");
74     }
75 
EventDispatchThread(ThreadGroup group, String name, EventQueue queue)76     EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
77         super(group, null, name, 0, false);
78         setEventQueue(queue);
79     }
80 
81     /*
82      * Must be called on EDT only, that's why no synchronization
83      */
stopDispatching()84     public void stopDispatching() {
85         doDispatch = false;
86     }
87 
run()88     public void run() {
89         try {
90             pumpEvents(new Conditional() {
91                 public boolean evaluate() {
92                     return true;
93                 }
94             });
95         } finally {
96             getEventQueue().detachDispatchThread(this);
97         }
98     }
99 
pumpEvents(Conditional cond)100     void pumpEvents(Conditional cond) {
101         pumpEvents(ANY_EVENT, cond);
102     }
103 
pumpEventsForHierarchy(Conditional cond, Component modalComponent)104     void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
105         pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
106     }
107 
pumpEvents(int id, Conditional cond)108     void pumpEvents(int id, Conditional cond) {
109         pumpEventsForHierarchy(id, cond, null);
110     }
111 
pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent)112     void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) {
113         pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
114     }
115 
pumpEventsForFilter(Conditional cond, EventFilter filter)116     void pumpEventsForFilter(Conditional cond, EventFilter filter) {
117         pumpEventsForFilter(ANY_EVENT, cond, filter);
118     }
119 
pumpEventsForFilter(int id, Conditional cond, EventFilter filter)120     void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
121         addEventFilter(filter);
122         doDispatch = true;
123         while (doDispatch && !isInterrupted() && cond.evaluate()) {
124             pumpOneEventForFilters(id);
125         }
126         removeEventFilter(filter);
127     }
128 
addEventFilter(EventFilter filter)129     void addEventFilter(EventFilter filter) {
130         if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
131             eventLog.finest("adding the event filter: " + filter);
132         }
133         synchronized (eventFilters) {
134             if (!eventFilters.contains(filter)) {
135                 if (filter instanceof ModalEventFilter) {
136                     ModalEventFilter newFilter = (ModalEventFilter)filter;
137                     int k = 0;
138                     for (k = 0; k < eventFilters.size(); k++) {
139                         EventFilter f = eventFilters.get(k);
140                         if (f instanceof ModalEventFilter) {
141                             ModalEventFilter cf = (ModalEventFilter)f;
142                             if (cf.compareTo(newFilter) > 0) {
143                                 break;
144                             }
145                         }
146                     }
147                     eventFilters.add(k, filter);
148                 } else {
149                     eventFilters.add(filter);
150                 }
151             }
152         }
153     }
154 
removeEventFilter(EventFilter filter)155     void removeEventFilter(EventFilter filter) {
156         if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
157             eventLog.finest("removing the event filter: " + filter);
158         }
159         synchronized (eventFilters) {
160             eventFilters.remove(filter);
161         }
162     }
163 
filterAndCheckEvent(AWTEvent event)164     boolean filterAndCheckEvent(AWTEvent event) {
165         boolean eventOK = true;
166         synchronized (eventFilters) {
167             for (int i = eventFilters.size() - 1; i >= 0; i--) {
168                 EventFilter f = eventFilters.get(i);
169                 EventFilter.FilterAction accept = f.acceptEvent(event);
170                 if (accept == EventFilter.FilterAction.REJECT) {
171                     eventOK = false;
172                     break;
173                 } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
174                     break;
175                 }
176             }
177         }
178         return eventOK && SunDragSourceContextPeer.checkEvent(event);
179     }
180 
pumpOneEventForFilters(int id)181     void pumpOneEventForFilters(int id) {
182         AWTEvent event = null;
183         boolean eventOK = false;
184         try {
185             EventQueue eq = null;
186             do {
187                 // EventQueue may change during the dispatching
188                 eq = getEventQueue();
189 
190                 event = (id == ANY_EVENT) ? eq.getNextEvent() : eq.getNextEvent(id);
191 
192                 eventOK = filterAndCheckEvent(event);
193                 if (!eventOK) {
194                     event.consume();
195                 }
196             }
197             while (eventOK == false);
198 
199             if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
200                 eventLog.finest("Dispatching: " + event);
201             }
202 
203             eq.dispatchEvent(event);
204         }
205         catch (ThreadDeath death) {
206             doDispatch = false;
207             throw death;
208         }
209         catch (InterruptedException interruptedException) {
210             doDispatch = false; // AppContext.dispose() interrupts all
211                                 // Threads in the AppContext
212         }
213         catch (Throwable e) {
214             processException(e);
215         }
216     }
217 
processException(Throwable e)218     private void processException(Throwable e) {
219         if (eventLog.isLoggable(PlatformLogger.Level.FINE)) {
220             eventLog.fine("Processing exception: " + e);
221         }
222         getUncaughtExceptionHandler().uncaughtException(this, e);
223     }
224 
getEventQueue()225     public synchronized EventQueue getEventQueue() {
226         return theQueue;
227     }
setEventQueue(EventQueue eq)228     public synchronized void setEventQueue(EventQueue eq) {
229         theQueue = eq;
230     }
231 
232     private static class HierarchyEventFilter implements EventFilter {
233         private Component modalComponent;
HierarchyEventFilter(Component modalComponent)234         public HierarchyEventFilter(Component modalComponent) {
235             this.modalComponent = modalComponent;
236         }
acceptEvent(AWTEvent event)237         public FilterAction acceptEvent(AWTEvent event) {
238             if (modalComponent != null) {
239                 int eventID = event.getID();
240                 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
241                                      (eventID <= MouseEvent.MOUSE_LAST);
242                 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
243                                       (eventID <= ActionEvent.ACTION_LAST);
244                 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
245                 /*
246                  * filter out MouseEvent and ActionEvent that's outside
247                  * the modalComponent hierarchy.
248                  * KeyEvent is handled by using enqueueKeyEvent
249                  * in Dialog.show
250                  */
251                 if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
252                     /*
253                      * Modal internal frames are handled separately. If event is
254                      * for some component from another heavyweight than modalComp,
255                      * it is accepted. If heavyweight is the same - we still accept
256                      * event and perform further filtering in LightweightDispatcher
257                      */
258                     return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
259                 }
260                 if (mouseEvent || actionEvent || windowClosingEvent) {
261                     Object o = event.getSource();
262                     if (o instanceof sun.awt.ModalExclude) {
263                         // Exclude this object from modality and
264                         // continue to pump it's events.
265                         return FilterAction.ACCEPT;
266                     } else if (o instanceof Component) {
267                         Component c = (Component) o;
268                         // 5.0u3 modal exclusion
269                         boolean modalExcluded = false;
270                         if (modalComponent instanceof Container) {
271                             while (c != modalComponent && c != null) {
272                                 if ((c instanceof Window) &&
273                                     (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
274                                     // Exclude this window and all its children from
275                                     //  modality and continue to pump it's events.
276                                     modalExcluded = true;
277                                     break;
278                                 }
279                                 c = c.getParent();
280                             }
281                         }
282                         if (!modalExcluded && (c != modalComponent)) {
283                             return FilterAction.REJECT;
284                         }
285                     }
286                 }
287             }
288             return FilterAction.ACCEPT;
289         }
290     }
291 }
292