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     */
  72     private EventDispatchThread() {
  73         throw new UnsupportedOperationException("Must erase locals");
  74     }
  75 
  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      */
  84     public void stopDispatching() {
  85         doDispatch = false;
  86     }
  87 
  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 
 100     void pumpEvents(Conditional cond) {
 101         pumpEvents(ANY_EVENT, cond);
 102     }
 103 
 104     void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
 105         pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
 106     }
 107 
 108     void pumpEvents(int id, Conditional cond) {
 109         pumpEventsForHierarchy(id, cond, null);
 110     }
 111 
 112     void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) {
 113         pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
 114     }
 115 
 116     void pumpEventsForFilter(Conditional cond, EventFilter filter) {
 117         pumpEventsForFilter(ANY_EVENT, cond, filter);
 118     }
 119 
 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 
 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 
 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 
 164     void pumpOneEventForFilters(int id) {
 165         AWTEvent event = null;
 166         boolean eventOK = false;
 167         try {
 168             EventQueue eq = null;
 169             do {
 170                 // EventQueue may change during the dispatching
 171                 eq = getEventQueue();
 172 
 173                 event = (id == ANY_EVENT) ? eq.getNextEvent() : eq.getNextEvent(id);
 174 
 175                 eventOK = true;
 176                 synchronized (eventFilters) {
 177                     for (int i = eventFilters.size() - 1; i >= 0; i--) {
 178                         EventFilter f = eventFilters.get(i);
 179                         EventFilter.FilterAction accept = f.acceptEvent(event);
 180                         if (accept == EventFilter.FilterAction.REJECT) {
 181                             eventOK = false;
 182                             break;
 183                         } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
 184                             break;
 185                         }
 186                     }
 187                 }
 188                 eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
 189                 if (!eventOK) {
 190                     event.consume();
 191                 }
 192             }
 193             while (eventOK == false);
 194 
 195             if (eventLog.isLoggable(PlatformLogger.Level.FINEST)) {
 196                 eventLog.finest("Dispatching: " + event);
 197             }
 198 
 199             eq.dispatchEvent(event);
 200         }
 201         catch (ThreadDeath death) {
 202             doDispatch = false;
 203             throw death;
 204         }
 205         catch (InterruptedException interruptedException) {
 206             doDispatch = false; // AppContext.dispose() interrupts all
 207                                 // Threads in the AppContext
 208         }
 209         catch (Throwable e) {
 210             processException(e);
 211         }
 212     }
 213 
 214     private void processException(Throwable e) {
 215         if (eventLog.isLoggable(PlatformLogger.Level.FINE)) {
 216             eventLog.fine("Processing exception: " + e);
 217         }
 218         getUncaughtExceptionHandler().uncaughtException(this, e);
 219     }
 220 
 221     public synchronized EventQueue getEventQueue() {
 222         return theQueue;
 223     }
 224     public synchronized void setEventQueue(EventQueue eq) {
 225         theQueue = eq;
 226     }
 227 
 228     private static class HierarchyEventFilter implements EventFilter {
 229         private Component modalComponent;
 230         public HierarchyEventFilter(Component modalComponent) {
 231             this.modalComponent = modalComponent;
 232         }
 233         public FilterAction acceptEvent(AWTEvent event) {
 234             if (modalComponent != null) {
 235                 int eventID = event.getID();
 236                 boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
 237                                      (eventID <= MouseEvent.MOUSE_LAST);
 238                 boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
 239                                       (eventID <= ActionEvent.ACTION_LAST);
 240                 boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
 241                 /*
 242                  * filter out MouseEvent and ActionEvent that's outside
 243                  * the modalComponent hierarchy.
 244                  * KeyEvent is handled by using enqueueKeyEvent
 245                  * in Dialog.show
 246                  */
 247                 if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
 248                     /*
 249                      * Modal internal frames are handled separately. If event is
 250                      * for some component from another heavyweight than modalComp,
 251                      * it is accepted. If heavyweight is the same - we still accept
 252                      * event and perform further filtering in LightweightDispatcher
 253                      */
 254                     return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
 255                 }
 256                 if (mouseEvent || actionEvent || windowClosingEvent) {
 257                     Object o = event.getSource();
 258                     if (o instanceof sun.awt.ModalExclude) {
 259                         // Exclude this object from modality and
 260                         // continue to pump it's events.
 261                         return FilterAction.ACCEPT;
 262                     } else if (o instanceof Component) {
 263                         Component c = (Component) o;
 264                         // 5.0u3 modal exclusion
 265                         boolean modalExcluded = false;
 266                         if (modalComponent instanceof Container) {
 267                             while (c != modalComponent && c != null) {
 268                                 if ((c instanceof Window) &&
 269                                     (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
 270                                     // Exclude this window and all its children from
 271                                     //  modality and continue to pump it's events.
 272                                     modalExcluded = true;
 273                                     break;
 274                                 }
 275                                 c = c.getParent();
 276                             }
 277                         }
 278                         if (!modalExcluded && (c != modalComponent)) {
 279                             return FilterAction.REJECT;
 280                         }
 281                     }
 282                 }
 283             }
 284             return FilterAction.ACCEPT;
 285         }
 286     }
 287 }