| /* |
| * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| package java.awt; |
| |
| import java.awt.event.InputEvent; |
| import java.awt.event.MouseEvent; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.WindowEvent; |
| import java.lang.reflect.Method; |
| import java.security.AccessController; |
| import sun.security.action.GetPropertyAction; |
| import sun.awt.AWTAutoShutdown; |
| import sun.awt.SunToolkit; |
| |
| import java.util.Vector; |
| import java.util.logging.*; |
| |
| import sun.awt.dnd.SunDragSourceContextPeer; |
| |
| /** |
| * EventDispatchThread is a package-private AWT class which takes |
| * events off the EventQueue and dispatches them to the appropriate |
| * AWT components. |
| * |
| * The Thread starts a "permanent" event pump with a call to |
| * pumpEvents(Conditional) in its run() method. Event handlers can choose to |
| * block this event pump at any time, but should start a new pump (<b>not</b> |
| * a new EventDispatchThread) by again calling pumpEvents(Conditional). This |
| * secondary event pump will exit automatically as soon as the Condtional |
| * evaluate()s to false and an additional Event is pumped and dispatched. |
| * |
| * @author Tom Ball |
| * @author Amy Fowler |
| * @author Fred Ecks |
| * @author David Mendenhall |
| * |
| * @since 1.1 |
| */ |
| class EventDispatchThread extends Thread { |
| private static final Logger eventLog = Logger.getLogger("java.awt.event.EventDispatchThread"); |
| |
| private EventQueue theQueue; |
| private boolean doDispatch = true; |
| private static final int ANY_EVENT = -1; |
| |
| private Vector<EventFilter> eventFilters = new Vector<EventFilter>(); |
| // used in handleException |
| private int modalFiltersCount = 0; |
| |
| EventDispatchThread(ThreadGroup group, String name, EventQueue queue) { |
| super(group, name); |
| theQueue = queue; |
| } |
| |
| void stopDispatchingImpl(boolean wait) { |
| // Note: We stop dispatching via a flag rather than using |
| // Thread.interrupt() because we can't guarantee that the wait() |
| // we interrupt will be EventQueue.getNextEvent()'s. -fredx 8-11-98 |
| |
| StopDispatchEvent stopEvent = new StopDispatchEvent(); |
| |
| // wait for the dispatcher to complete |
| if (Thread.currentThread() != this) { |
| |
| // fix 4122683, 4128923 |
| // Post an empty event to ensure getNextEvent is unblocked |
| // |
| // We have to use postEventPrivate instead of postEvent because |
| // EventQueue.pop calls EventDispatchThread.stopDispatching. |
| // Calling SunToolkit.flushPendingEvents in this case could |
| // lead to deadlock. |
| theQueue.postEventPrivate(stopEvent); |
| |
| if (wait) { |
| try { |
| join(); |
| } catch(InterruptedException e) { |
| } |
| } |
| } else { |
| stopEvent.dispatch(); |
| } |
| synchronized (theQueue) { |
| if (theQueue.getDispatchThread() == this) { |
| theQueue.detachDispatchThread(); |
| } |
| } |
| } |
| |
| public void stopDispatching() { |
| stopDispatchingImpl(true); |
| } |
| |
| public void stopDispatchingLater() { |
| stopDispatchingImpl(false); |
| } |
| |
| class StopDispatchEvent extends AWTEvent implements ActiveEvent { |
| /* |
| * serialVersionUID |
| */ |
| static final long serialVersionUID = -3692158172100730735L; |
| |
| public StopDispatchEvent() { |
| super(EventDispatchThread.this,0); |
| } |
| |
| public void dispatch() { |
| doDispatch = false; |
| } |
| } |
| |
| public void run() { |
| try { |
| pumpEvents(new Conditional() { |
| public boolean evaluate() { |
| return true; |
| } |
| }); |
| } finally { |
| /* |
| * This synchronized block is to secure that the event dispatch |
| * thread won't die in the middle of posting a new event to the |
| * associated event queue. It is important because we notify |
| * that the event dispatch thread is busy after posting a new event |
| * to its queue, so the EventQueue.dispatchThread reference must |
| * be valid at that point. |
| */ |
| synchronized (theQueue) { |
| if (theQueue.getDispatchThread() == this) { |
| theQueue.detachDispatchThread(); |
| } |
| /* |
| * Event dispatch thread dies in case of an uncaught exception. |
| * A new event dispatch thread for this queue will be started |
| * only if a new event is posted to it. In case if no more |
| * events are posted after this thread died all events that |
| * currently are in the queue will never be dispatched. |
| */ |
| /* |
| * Fix for 4648733. Check both the associated java event |
| * queue and the PostEventQueue. |
| */ |
| if (theQueue.peekEvent() != null || |
| !SunToolkit.isPostEventQueueEmpty()) { |
| theQueue.initDispatchThread(); |
| } |
| AWTAutoShutdown.getInstance().notifyThreadFree(this); |
| } |
| } |
| } |
| |
| void pumpEvents(Conditional cond) { |
| pumpEvents(ANY_EVENT, cond); |
| } |
| |
| void pumpEventsForHierarchy(Conditional cond, Component modalComponent) { |
| pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent); |
| } |
| |
| void pumpEvents(int id, Conditional cond) { |
| pumpEventsForHierarchy(id, cond, null); |
| } |
| |
| void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) |
| { |
| pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent)); |
| } |
| |
| void pumpEventsForFilter(Conditional cond, EventFilter filter) { |
| pumpEventsForFilter(ANY_EVENT, cond, filter); |
| } |
| |
| void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) { |
| addEventFilter(filter); |
| while (doDispatch && cond.evaluate()) { |
| if (isInterrupted() || !pumpOneEventForFilters(id)) { |
| doDispatch = false; |
| } |
| } |
| removeEventFilter(filter); |
| } |
| |
| void addEventFilter(EventFilter filter) { |
| synchronized (eventFilters) { |
| if (!eventFilters.contains(filter)) { |
| if (filter instanceof ModalEventFilter) { |
| ModalEventFilter newFilter = (ModalEventFilter)filter; |
| int k = 0; |
| for (k = 0; k < eventFilters.size(); k++) { |
| EventFilter f = eventFilters.get(k); |
| if (f instanceof ModalEventFilter) { |
| ModalEventFilter cf = (ModalEventFilter)f; |
| if (cf.compareTo(newFilter) > 0) { |
| break; |
| } |
| } |
| } |
| eventFilters.add(k, filter); |
| modalFiltersCount++; |
| } else { |
| eventFilters.add(filter); |
| } |
| } |
| } |
| } |
| |
| void removeEventFilter(EventFilter filter) { |
| synchronized (eventFilters) { |
| if (eventFilters.contains(filter)) { |
| if (filter instanceof ModalEventFilter) { |
| modalFiltersCount--; |
| } |
| eventFilters.remove(filter); |
| } |
| } |
| } |
| |
| boolean pumpOneEventForFilters(int id) { |
| try { |
| AWTEvent event; |
| boolean eventOK; |
| do { |
| event = (id == ANY_EVENT) |
| ? theQueue.getNextEvent() |
| : theQueue.getNextEvent(id); |
| |
| eventOK = true; |
| synchronized (eventFilters) { |
| for (int i = eventFilters.size() - 1; i >= 0; i--) { |
| EventFilter f = eventFilters.get(i); |
| EventFilter.FilterAction accept = f.acceptEvent(event); |
| if (accept == EventFilter.FilterAction.REJECT) { |
| eventOK = false; |
| break; |
| } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) { |
| break; |
| } |
| } |
| } |
| eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event); |
| if (!eventOK) { |
| event.consume(); |
| } |
| } |
| while (eventOK == false); |
| |
| if (eventLog.isLoggable(Level.FINEST)) { |
| eventLog.log(Level.FINEST, "Dispatching: " + event); |
| } |
| |
| theQueue.dispatchEvent(event); |
| return true; |
| } |
| catch (ThreadDeath death) { |
| return false; |
| |
| } |
| catch (InterruptedException interruptedException) { |
| return false; // AppContext.dispose() interrupts all |
| // Threads in the AppContext |
| |
| } |
| // Can get and throw only unchecked exceptions |
| catch (RuntimeException e) { |
| processException(e, modalFiltersCount > 0); |
| } catch (Error e) { |
| processException(e, modalFiltersCount > 0); |
| } |
| return true; |
| } |
| |
| private void processException(Throwable e, boolean isModal) { |
| if (eventLog.isLoggable(Level.FINE)) { |
| eventLog.log(Level.FINE, "Processing exception: " + e + |
| ", isModal = " + isModal); |
| } |
| if (!handleException(e)) { |
| // See bug ID 4499199. |
| // If we are in a modal dialog, we cannot throw |
| // an exception for the ThreadGroup to handle (as added |
| // in RFE 4063022). If we did, the message pump of |
| // the modal dialog would be interrupted. |
| // We instead choose to handle the exception ourselves. |
| // It may be useful to add either a runtime flag or API |
| // later if someone would like to instead dispose the |
| // dialog and allow the thread group to handle it. |
| if (isModal) { |
| System.err.println( |
| "Exception occurred during event dispatching:"); |
| e.printStackTrace(); |
| } else if (e instanceof RuntimeException) { |
| throw (RuntimeException)e; |
| } else if (e instanceof Error) { |
| throw (Error)e; |
| } |
| } |
| } |
| |
| private static final String handlerPropName = "sun.awt.exception.handler"; |
| private static String handlerClassName = null; |
| private static String NO_HANDLER = new String(); |
| |
| /** |
| * Handles an exception thrown in the event-dispatch thread. |
| * |
| * <p> If the system property "sun.awt.exception.handler" is defined, then |
| * when this method is invoked it will attempt to do the following: |
| * |
| * <ol> |
| * <li> Load the class named by the value of that property, using the |
| * current thread's context class loader, |
| * <li> Instantiate that class using its zero-argument constructor, |
| * <li> Find the resulting handler object's <tt>public void handle</tt> |
| * method, which should take a single argument of type |
| * <tt>Throwable</tt>, and |
| * <li> Invoke the handler's <tt>handle</tt> method, passing it the |
| * <tt>thrown</tt> argument that was passed to this method. |
| * </ol> |
| * |
| * If any of the first three steps fail then this method will return |
| * <tt>false</tt> and all following invocations of this method will return |
| * <tt>false</tt> immediately. An exception thrown by the handler object's |
| * <tt>handle</tt> will be caught, and will cause this method to return |
| * <tt>false</tt>. If the handler's <tt>handle</tt> method is successfully |
| * invoked, then this method will return <tt>true</tt>. This method will |
| * never throw any sort of exception. |
| * |
| * <p> <i>Note:</i> This method is a temporary hack to work around the |
| * absence of a real API that provides the ability to replace the |
| * event-dispatch thread. The magic "sun.awt.exception.handler" property |
| * <i>will be removed</i> in a future release. |
| * |
| * @param thrown The Throwable that was thrown in the event-dispatch |
| * thread |
| * |
| * @return <tt>false</tt> if any of the above steps failed, otherwise |
| * <tt>true</tt> |
| */ |
| private boolean handleException(Throwable thrown) { |
| |
| try { |
| |
| if (handlerClassName == NO_HANDLER) { |
| return false; /* Already tried, and failed */ |
| } |
| |
| /* Look up the class name */ |
| if (handlerClassName == null) { |
| handlerClassName = ((String) AccessController.doPrivileged( |
| new GetPropertyAction(handlerPropName))); |
| if (handlerClassName == null) { |
| handlerClassName = NO_HANDLER; /* Do not try this again */ |
| return false; |
| } |
| } |
| |
| /* Load the class, instantiate it, and find its handle method */ |
| Method m; |
| Object h; |
| try { |
| ClassLoader cl = Thread.currentThread().getContextClassLoader(); |
| Class c = Class.forName(handlerClassName, true, cl); |
| m = c.getMethod("handle", new Class[] { Throwable.class }); |
| h = c.newInstance(); |
| } catch (Throwable x) { |
| handlerClassName = NO_HANDLER; /* Do not try this again */ |
| return false; |
| } |
| |
| /* Finally, invoke the handler */ |
| m.invoke(h, new Object[] { thrown }); |
| |
| } catch (Throwable x) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| boolean isDispatching(EventQueue eq) { |
| return theQueue.equals(eq); |
| } |
| |
| EventQueue getEventQueue() { return theQueue; } |
| |
| private static class HierarchyEventFilter implements EventFilter { |
| private Component modalComponent; |
| public HierarchyEventFilter(Component modalComponent) { |
| this.modalComponent = modalComponent; |
| } |
| public FilterAction acceptEvent(AWTEvent event) { |
| if (modalComponent != null) { |
| int eventID = event.getID(); |
| boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) && |
| (eventID <= MouseEvent.MOUSE_LAST); |
| boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) && |
| (eventID <= ActionEvent.ACTION_LAST); |
| boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING); |
| /* |
| * filter out MouseEvent and ActionEvent that's outside |
| * the modalComponent hierarchy. |
| * KeyEvent is handled by using enqueueKeyEvent |
| * in Dialog.show |
| */ |
| if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) { |
| /* |
| * Modal internal frames are handled separately. If event is |
| * for some component from another heavyweight than modalComp, |
| * it is accepted. If heavyweight is the same - we still accept |
| * event and perform further filtering in LightweightDispatcher |
| */ |
| return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT; |
| } |
| if (mouseEvent || actionEvent || windowClosingEvent) { |
| Object o = event.getSource(); |
| if (o instanceof sun.awt.ModalExclude) { |
| // Exclude this object from modality and |
| // continue to pump it's events. |
| return FilterAction.ACCEPT; |
| } else if (o instanceof Component) { |
| Component c = (Component) o; |
| // 5.0u3 modal exclusion |
| boolean modalExcluded = false; |
| if (modalComponent instanceof Container) { |
| while (c != modalComponent && c != null) { |
| if ((c instanceof Window) && |
| (sun.awt.SunToolkit.isModalExcluded((Window)c))) { |
| // Exclude this window and all its children from |
| // modality and continue to pump it's events. |
| modalExcluded = true; |
| break; |
| } |
| c = c.getParent(); |
| } |
| } |
| if (!modalExcluded && (c != modalComponent)) { |
| return FilterAction.REJECT; |
| } |
| } |
| } |
| } |
| return FilterAction.ACCEPT; |
| } |
| } |
| } |