View Javadoc
1   /*
2    * Copyright (c) 1996, 2013, 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  import sun.util.logging.PlatformLogger;
34  
35  import sun.awt.dnd.SunDragSourceContextPeer;
36  import sun.awt.EventQueueDelegate;
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 Condtional
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      EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
69          super(group, name);
70          setEventQueue(queue);
71      }
72  
73      /*
74       * Must be called on EDT only, that's why no synchronization
75       */
76      public void stopDispatching() {
77          doDispatch = false;
78      }
79  
80      public void run() {
81          try {
82              pumpEvents(new Conditional() {
83                  public boolean evaluate() {
84                      return true;
85                  }
86              });
87          } finally {
88              getEventQueue().detachDispatchThread(this);
89          }
90      }
91  
92      void pumpEvents(Conditional cond) {
93          pumpEvents(ANY_EVENT, cond);
94      }
95  
96      void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
97          pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
98      }
99  
100     void pumpEvents(int id, Conditional cond) {
101         pumpEventsForHierarchy(id, cond, null);
102     }
103 
104     void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) {
105         pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
106     }
107 
108     void pumpEventsForFilter(Conditional cond, EventFilter filter) {
109         pumpEventsForFilter(ANY_EVENT, cond, filter);
110     }
111 
112     void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
113         addEventFilter(filter);
114         doDispatch = true;
115         while (doDispatch && !isInterrupted() && cond.evaluate()) {
116             pumpOneEventForFilters(id);
117         }
118         removeEventFilter(filter);
119     }
120 
121     void addEventFilter(EventFilter filter) {
122         if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
123             eventLog.finest("adding the event filter: " + filter);
124         }
125         synchronized (eventFilters) {
126             if (!eventFilters.contains(filter)) {
127                 if (filter instanceof ModalEventFilter) {
128                     ModalEventFilter newFilter = (ModalEventFilter)filter;
129                     int k = 0;
130                     for (k = 0; k < eventFilters.size(); k++) {
131                         EventFilter f = eventFilters.get(k);
132                         if (f instanceof ModalEventFilter) {
133                             ModalEventFilter cf = (ModalEventFilter)f;
134                             if (cf.compareTo(newFilter) > 0) {
135                                 break;
136                             }
137                         }
138                     }
139                     eventFilters.add(k, filter);
140                 } else {
141                     eventFilters.add(filter);
142                 }
143             }
144         }
145     }
146 
147     void removeEventFilter(EventFilter filter) {
148         if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
149             eventLog.finest("removing the event filter: " + filter);
150         }
151         synchronized (eventFilters) {
152             eventFilters.remove(filter);
153         }
154     }
155 
156     void pumpOneEventForFilters(int id) {
157         AWTEvent event = null;
158         boolean eventOK = false;
159         try {
160             EventQueue eq = null;
161             EventQueueDelegate.Delegate delegate = null;
162             do {
163                 // EventQueue may change during the dispatching
164                 eq = getEventQueue();
165                 delegate = EventQueueDelegate.getDelegate();
166 
167                 if (delegate != null && id == ANY_EVENT) {
168                     event = delegate.getNextEvent(eq);
169                 } else {
170                     event = (id == ANY_EVENT) ? eq.getNextEvent() : eq.getNextEvent(id);
171                 }
172 
173                 eventOK = true;
174                 synchronized (eventFilters) {
175                     for (int i = eventFilters.size() - 1; i >= 0; i--) {
176                         EventFilter f = eventFilters.get(i);
177                         EventFilter.FilterAction accept = f.acceptEvent(event);
178                         if (accept == EventFilter.FilterAction.REJECT) {
179                             eventOK = false;
180                             break;
181                         } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
182                             break;
183                         }
184                     }
185                 }
186                 eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
187                 if (!eventOK) {
188                     event.consume();
189                 }
190             }
191             while (eventOK == false);
192 
193             if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
194                 eventLog.finest("Dispatching: " + event);
195             }
196 
197             Object handle = null;
198             if (delegate != null) {
199                 handle = delegate.beforeDispatch(event);
200             }
201             eq.dispatchEvent(event);
202             if (delegate != null) {
203                 delegate.afterDispatch(event, handle);
204             }
205         }
206         catch (ThreadDeath death) {
207             doDispatch = false;
208             throw death;
209         }
210         catch (InterruptedException interruptedException) {
211             doDispatch = false; // AppContext.dispose() interrupts all
212                                 // Threads in the AppContext
213         }
214         catch (Throwable e) {
215             processException(e);
216         }
217     }
218 
219     private void processException(Throwable e) {
220         if (eventLog.isLoggable(PlatformLogger.Level.FINE)) {
221             eventLog.fine("Processing exception: " + e);
222         }
223         getUncaughtExceptionHandler().uncaughtException(this, e);
224     }
225 
226     public synchronized EventQueue getEventQueue() {
227         return theQueue;
228     }
229     public synchronized void setEventQueue(EventQueue eq) {
230         theQueue = eq;
231     }
232 
233     private static class HierarchyEventFilter implements EventFilter {
234         private Component modalComponent;
235         public HierarchyEventFilter(Component modalComponent) {
236             this.modalComponent = modalComponent;
237         }
238         public FilterAction acceptEvent(AWTEvent event) {
239             if (modalComponent != null) {
240                 int eventID = event.getID();
241                 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
242                                      (eventID <= MouseEvent.MOUSE_LAST);
243                 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
244                                       (eventID <= ActionEvent.ACTION_LAST);
245                 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
246                 /*
247                  * filter out MouseEvent and ActionEvent that's outside
248                  * the modalComponent hierarchy.
249                  * KeyEvent is handled by using enqueueKeyEvent
250                  * in Dialog.show
251                  */
252                 if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
253                     /*
254                      * Modal internal frames are handled separately. If event is
255                      * for some component from another heavyweight than modalComp,
256                      * it is accepted. If heavyweight is the same - we still accept
257                      * event and perform further filtering in LightweightDispatcher
258                      */
259                     return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
260                 }
261                 if (mouseEvent || actionEvent || windowClosingEvent) {
262                     Object o = event.getSource();
263                     if (o instanceof sun.awt.ModalExclude) {
264                         // Exclude this object from modality and
265                         // continue to pump it's events.
266                         return FilterAction.ACCEPT;
267                     } else if (o instanceof Component) {
268                         Component c = (Component) o;
269                         // 5.0u3 modal exclusion
270                         boolean modalExcluded = false;
271                         if (modalComponent instanceof Container) {
272                             while (c != modalComponent && c != null) {
273                                 if ((c instanceof Window) &&
274                                     (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
275                                     // Exclude this window and all its children from
276                                     //  modality and continue to pump it's events.
277                                     modalExcluded = true;
278                                     break;
279                                 }
280                                 c = c.getParent();
281                             }
282                         }
283                         if (!modalExcluded && (c != modalComponent)) {
284                             return FilterAction.REJECT;
285                         }
286                     }
287                 }
288             }
289             return FilterAction.ACCEPT;
290         }
291     }
292 }