0001: /*
0002: * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package java.awt;
0027:
0028: import java.awt.event.ActionEvent;
0029: import java.awt.event.FocusEvent;
0030: import java.awt.event.InputEvent;
0031: import java.awt.event.InputMethodEvent;
0032: import java.awt.event.InvocationEvent;
0033: import java.awt.event.KeyEvent;
0034: import java.awt.event.MouseEvent;
0035: import java.awt.event.PaintEvent;
0036: import java.awt.event.WindowEvent;
0037: import java.awt.ActiveEvent;
0038: import java.awt.peer.ComponentPeer;
0039: import java.awt.TrayIcon;
0040: import java.util.EmptyStackException;
0041: import java.lang.ref.WeakReference;
0042: import java.lang.reflect.InvocationTargetException;
0043: import java.security.AccessController;
0044: import java.security.PrivilegedAction;
0045: import sun.awt.PeerEvent;
0046: import sun.awt.SunToolkit;
0047: import sun.awt.AWTAutoShutdown;
0048: import sun.awt.AppContext;
0049:
0050: /**
0051: * <code>EventQueue</code> is a platform-independent class
0052: * that queues events, both from the underlying peer classes
0053: * and from trusted application classes.
0054: * <p>
0055: * It encapsulates asynchronous event dispatch machinery which
0056: * extracts events from the queue and dispatches them by calling
0057: * {@link #dispatchEvent(AWTEvent) dispatchEvent(AWTEvent)} method
0058: * on this <code>EventQueue</code> with the event to be dispatched
0059: * as an argument. The particular behavior of this machinery is
0060: * implementation-dependent. The only requirements are that events
0061: * which were actually enqueued to this queue (note that events
0062: * being posted to the <code>EventQueue</code> can be coalesced)
0063: * are dispatched:
0064: * <dl>
0065: * <dt> Sequentially.
0066: * <dd> That is, it is not permitted that several events from
0067: * this queue are dispatched simultaneously.
0068: * <dt> In the same order as they are enqueued.
0069: * <dd> That is, if <code>AWTEvent</code> A is enqueued
0070: * to the <code>EventQueue</code> before
0071: * <code>AWTEvent</code> B then event B will not be
0072: * dispatched before event A.
0073: * </dl>
0074: * <p>
0075: * Some browsers partition applets in different code bases into
0076: * separate contexts, and establish walls between these contexts.
0077: * In such a scenario, there will be one <code>EventQueue</code>
0078: * per context. Other browsers place all applets into the same
0079: * context, implying that there will be only a single, global
0080: * <code>EventQueue</code> for all applets. This behavior is
0081: * implementation-dependent. Consult your browser's documentation
0082: * for more information.
0083: * <p>
0084: * For information on the threading issues of the event dispatch
0085: * machinery, see <a href="doc-files/AWTThreadIssues.html#Autoshutdown"
0086: * >AWT Threading Issues</a>.
0087: *
0088: * @author Thomas Ball
0089: * @author Fred Ecks
0090: * @author David Mendenhall
0091: *
0092: * @version 1.114, 06/05/07
0093: * @since 1.1
0094: */
0095: public class EventQueue {
0096:
0097: // From Thread.java
0098: private static int threadInitNumber;
0099:
0100: private static synchronized int nextThreadNum() {
0101: return threadInitNumber++;
0102: }
0103:
0104: private static final int LOW_PRIORITY = 0;
0105: private static final int NORM_PRIORITY = 1;
0106: private static final int HIGH_PRIORITY = 2;
0107: private static final int ULTIMATE_PRIORITY = 3;
0108:
0109: private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1;
0110:
0111: /*
0112: * We maintain one Queue for each priority that the EventQueue supports.
0113: * That is, the EventQueue object is actually implemented as
0114: * NUM_PRIORITIES queues and all Events on a particular internal Queue
0115: * have identical priority. Events are pulled off the EventQueue starting
0116: * with the Queue of highest priority. We progress in decreasing order
0117: * across all Queues.
0118: */
0119: private Queue[] queues = new Queue[NUM_PRIORITIES];
0120:
0121: /*
0122: * The next EventQueue on the stack, or null if this EventQueue is
0123: * on the top of the stack. If nextQueue is non-null, requests to post
0124: * an event are forwarded to nextQueue.
0125: */
0126: private EventQueue nextQueue;
0127:
0128: /*
0129: * The previous EventQueue on the stack, or null if this is the
0130: * "base" EventQueue.
0131: */
0132: private EventQueue previousQueue;
0133:
0134: private EventDispatchThread dispatchThread;
0135:
0136: private final ThreadGroup threadGroup = Thread.currentThread()
0137: .getThreadGroup();
0138: private final ClassLoader classLoader = Thread.currentThread()
0139: .getContextClassLoader();
0140:
0141: /*
0142: * Debugging flag -- set true and recompile to enable checking.
0143: */
0144: private final static boolean debug = false;
0145:
0146: /*
0147: * The time stamp of the last dispatched InputEvent or ActionEvent.
0148: */
0149: private long mostRecentEventTime = System.currentTimeMillis();
0150:
0151: /**
0152: * The modifiers field of the current event, if the current event is an
0153: * InputEvent or ActionEvent.
0154: */
0155: private WeakReference currentEvent;
0156:
0157: /*
0158: * Non-zero if a thread is waiting in getNextEvent(int) for an event of
0159: * a particular ID to be posted to the queue.
0160: */
0161: private int waitForID;
0162:
0163: private final String name = "AWT-EventQueue-" + nextThreadNum();
0164:
0165: public EventQueue() {
0166: for (int i = 0; i < NUM_PRIORITIES; i++) {
0167: queues[i] = new Queue();
0168: }
0169: /*
0170: * NOTE: if you ever have to start the associated event dispatch
0171: * thread at this point, be aware of the following problem:
0172: * If this EventQueue instance is created in
0173: * SunToolkit.createNewAppContext() the started dispatch thread
0174: * may call AppContext.getAppContext() before createNewAppContext()
0175: * completes thus causing mess in thread group to appcontext mapping.
0176: */
0177: }
0178:
0179: /**
0180: * Posts a 1.1-style event to the <code>EventQueue</code>.
0181: * If there is an existing event on the queue with the same ID
0182: * and event source, the source <code>Component</code>'s
0183: * <code>coalesceEvents</code> method will be called.
0184: *
0185: * @param theEvent an instance of <code>java.awt.AWTEvent</code>,
0186: * or a subclass of it
0187: * @throws NullPointerException if <code>theEvent</code> is <code>null</code>
0188: */
0189: public void postEvent(AWTEvent theEvent) {
0190: SunToolkit.flushPendingEvents();
0191: postEventPrivate(theEvent);
0192: }
0193:
0194: /**
0195: * Posts a 1.1-style event to the <code>EventQueue</code>.
0196: * If there is an existing event on the queue with the same ID
0197: * and event source, the source <code>Component</code>'s
0198: * <code>coalesceEvents</code> method will be called.
0199: *
0200: * @param theEvent an instance of <code>java.awt.AWTEvent</code>,
0201: * or a subclass of it
0202: */
0203: final void postEventPrivate(AWTEvent theEvent) {
0204: theEvent.isPosted = true;
0205: synchronized (this ) {
0206: if (dispatchThread == null && nextQueue == null) {
0207: if (theEvent.getSource() == AWTAutoShutdown
0208: .getInstance()) {
0209: return;
0210: } else {
0211: initDispatchThread();
0212: }
0213: }
0214: if (nextQueue != null) {
0215: // Forward event to top of EventQueue stack.
0216: nextQueue.postEventPrivate(theEvent);
0217: return;
0218: }
0219: postEvent(theEvent, getPriority(theEvent));
0220: }
0221: }
0222:
0223: private static int getPriority(AWTEvent theEvent) {
0224: if (theEvent instanceof PeerEvent
0225: && (((PeerEvent) theEvent).getFlags() & PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) {
0226: return ULTIMATE_PRIORITY;
0227: }
0228:
0229: if (theEvent instanceof PeerEvent
0230: && (((PeerEvent) theEvent).getFlags() & PeerEvent.PRIORITY_EVENT) != 0) {
0231: return HIGH_PRIORITY;
0232: }
0233:
0234: if (theEvent instanceof PeerEvent
0235: && (((PeerEvent) theEvent).getFlags() & PeerEvent.LOW_PRIORITY_EVENT) != 0) {
0236: return LOW_PRIORITY;
0237: }
0238:
0239: int id = theEvent.getID();
0240: if (id == PaintEvent.PAINT || id == PaintEvent.UPDATE) {
0241: return LOW_PRIORITY;
0242: }
0243: return NORM_PRIORITY;
0244: }
0245:
0246: /**
0247: * Posts the event to the internal Queue of specified priority,
0248: * coalescing as appropriate.
0249: *
0250: * @param theEvent an instance of <code>java.awt.AWTEvent</code>,
0251: * or a subclass of it
0252: * @param priority the desired priority of the event
0253: */
0254: private void postEvent(AWTEvent theEvent, int priority) {
0255: if (coalesceEvent(theEvent, priority)) {
0256: return;
0257: }
0258:
0259: EventQueueItem newItem = new EventQueueItem(theEvent);
0260:
0261: cacheEQItem(newItem);
0262:
0263: boolean notifyID = (theEvent.getID() == this .waitForID);
0264:
0265: if (queues[priority].head == null) {
0266: boolean shouldNotify = noEvents();
0267: queues[priority].head = queues[priority].tail = newItem;
0268:
0269: if (shouldNotify) {
0270: if (theEvent.getSource() != AWTAutoShutdown
0271: .getInstance()) {
0272: AWTAutoShutdown.getInstance().notifyThreadBusy(
0273: dispatchThread);
0274: }
0275: notifyAll();
0276: } else if (notifyID) {
0277: notifyAll();
0278: }
0279: } else {
0280: // The event was not coalesced or has non-Component source.
0281: // Insert it at the end of the appropriate Queue.
0282: queues[priority].tail.next = newItem;
0283: queues[priority].tail = newItem;
0284: if (notifyID) {
0285: notifyAll();
0286: }
0287: }
0288: }
0289:
0290: private boolean coalescePaintEvent(PaintEvent e) {
0291: ComponentPeer sourcePeer = ((Component) e.getSource()).peer;
0292: if (sourcePeer != null) {
0293: sourcePeer.coalescePaintEvent(e);
0294: }
0295: EventQueueItem[] cache = ((Component) e.getSource()).eventCache;
0296: if (cache == null) {
0297: return false;
0298: }
0299: int index = eventToCacheIndex(e);
0300:
0301: if (index != -1 && cache[index] != null) {
0302: PaintEvent merged = mergePaintEvents(e,
0303: (PaintEvent) cache[index].event);
0304: if (merged != null) {
0305: cache[index].event = merged;
0306: return true;
0307: }
0308: }
0309: return false;
0310: }
0311:
0312: private PaintEvent mergePaintEvents(PaintEvent a, PaintEvent b) {
0313: Rectangle aRect = a.getUpdateRect();
0314: Rectangle bRect = b.getUpdateRect();
0315: if (bRect.contains(aRect)) {
0316: return b;
0317: }
0318: if (aRect.contains(bRect)) {
0319: return a;
0320: }
0321: return null;
0322: }
0323:
0324: private boolean coalesceMouseEvent(MouseEvent e) {
0325: EventQueueItem[] cache = ((Component) e.getSource()).eventCache;
0326: if (cache == null) {
0327: return false;
0328: }
0329: int index = eventToCacheIndex(e);
0330: if (index != -1 && cache[index] != null) {
0331: cache[index].event = e;
0332: return true;
0333: }
0334: return false;
0335: }
0336:
0337: private boolean coalescePeerEvent(PeerEvent e) {
0338: EventQueueItem[] cache = ((Component) e.getSource()).eventCache;
0339: if (cache == null) {
0340: return false;
0341: }
0342: int index = eventToCacheIndex(e);
0343: if (index != -1 && cache[index] != null) {
0344: e = e.coalesceEvents((PeerEvent) cache[index].event);
0345: if (e != null) {
0346: cache[index].event = e;
0347: return true;
0348: } else {
0349: cache[index] = null;
0350: }
0351: }
0352: return false;
0353: }
0354:
0355: /*
0356: * Should avoid of calling this method by any means
0357: * as it's working time is dependant on EQ length.
0358: * In the wors case this method alone can slow down the entire application
0359: * 10 times by stalling the Event processing.
0360: * Only here by backward compatibility reasons.
0361: */
0362: private boolean coalesceOtherEvent(AWTEvent e, int priority) {
0363: int id = e.getID();
0364: Component source = (Component) e.getSource();
0365: for (EventQueueItem entry = queues[priority].head; entry != null; entry = entry.next) {
0366: // Give Component.coalesceEvents a chance
0367: if (entry.event.getSource() == source && entry.id == id) {
0368: AWTEvent coalescedEvent = source.coalesceEvents(
0369: entry.event, e);
0370: if (coalescedEvent != null) {
0371: entry.event = coalescedEvent;
0372: return true;
0373: }
0374: }
0375: }
0376: return false;
0377: }
0378:
0379: private boolean coalesceEvent(AWTEvent e, int priority) {
0380: if (!(e.getSource() instanceof Component)) {
0381: return false;
0382: }
0383: if (e instanceof PeerEvent) {
0384: return coalescePeerEvent((PeerEvent) e);
0385: }
0386: // The worst case
0387: if (((Component) e.getSource()).isCoalescingEnabled()
0388: && coalesceOtherEvent(e, priority)) {
0389: return true;
0390: }
0391: if (e instanceof PaintEvent) {
0392: return coalescePaintEvent((PaintEvent) e);
0393: }
0394: if (e instanceof MouseEvent) {
0395: return coalesceMouseEvent((MouseEvent) e);
0396: }
0397: return false;
0398: }
0399:
0400: private void cacheEQItem(EventQueueItem entry) {
0401: int index = eventToCacheIndex(entry.event);
0402: if (index != -1 && entry.event.getSource() instanceof Component) {
0403: Component source = (Component) entry.event.getSource();
0404: if (source.eventCache == null) {
0405: source.eventCache = new EventQueueItem[CACHE_LENGTH];
0406: }
0407: source.eventCache[index] = entry;
0408: }
0409: }
0410:
0411: private void uncacheEQItem(EventQueueItem entry) {
0412: int index = eventToCacheIndex(entry.event);
0413: if (index != -1 && entry.event.getSource() instanceof Component) {
0414: Component source = (Component) entry.event.getSource();
0415: if (source.eventCache == null) {
0416: return;
0417: }
0418: source.eventCache[index] = null;
0419: }
0420: }
0421:
0422: private static final int PAINT = 0;
0423: private static final int UPDATE = 1;
0424: private static final int MOVE = 2;
0425: private static final int DRAG = 3;
0426: private static final int PEER = 4;
0427: private static final int CACHE_LENGTH = 5;
0428:
0429: private static int eventToCacheIndex(AWTEvent e) {
0430: switch (e.getID()) {
0431: case PaintEvent.PAINT:
0432: return PAINT;
0433: case PaintEvent.UPDATE:
0434: return UPDATE;
0435: case MouseEvent.MOUSE_MOVED:
0436: return MOVE;
0437: case MouseEvent.MOUSE_DRAGGED:
0438: return DRAG;
0439: default:
0440: return e instanceof PeerEvent ? PEER : -1;
0441: }
0442: }
0443:
0444: /**
0445: * Returns whether an event is pending on any of the separate
0446: * Queues.
0447: * @return whether an event is pending on any of the separate Queues
0448: */
0449: private boolean noEvents() {
0450: for (int i = 0; i < NUM_PRIORITIES; i++) {
0451: if (queues[i].head != null) {
0452: return false;
0453: }
0454: }
0455:
0456: return true;
0457: }
0458:
0459: /**
0460: * Removes an event from the <code>EventQueue</code> and
0461: * returns it. This method will block until an event has
0462: * been posted by another thread.
0463: * @return the next <code>AWTEvent</code>
0464: * @exception InterruptedException
0465: * if any thread has interrupted this thread
0466: */
0467: public AWTEvent getNextEvent() throws InterruptedException {
0468: do {
0469: /*
0470: * SunToolkit.flushPendingEvents must be called outside
0471: * of the synchronized block to avoid deadlock when
0472: * event queues are nested with push()/pop().
0473: */
0474: SunToolkit.flushPendingEvents();
0475: synchronized (this ) {
0476: for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
0477: if (queues[i].head != null) {
0478: EventQueueItem entry = queues[i].head;
0479: queues[i].head = entry.next;
0480: if (entry.next == null) {
0481: queues[i].tail = null;
0482: }
0483: uncacheEQItem(entry);
0484: return entry.event;
0485: }
0486: }
0487: AWTAutoShutdown.getInstance().notifyThreadFree(
0488: dispatchThread);
0489: wait();
0490: }
0491: } while (true);
0492: }
0493:
0494: AWTEvent getNextEvent(int id) throws InterruptedException {
0495: do {
0496: /*
0497: * SunToolkit.flushPendingEvents must be called outside
0498: * of the synchronized block to avoid deadlock when
0499: * event queues are nested with push()/pop().
0500: */
0501: SunToolkit.flushPendingEvents();
0502: synchronized (this ) {
0503: for (int i = 0; i < NUM_PRIORITIES; i++) {
0504: for (EventQueueItem entry = queues[i].head, prev = null; entry != null; prev = entry, entry = entry.next) {
0505: if (entry.id == id) {
0506: if (prev == null) {
0507: queues[i].head = entry.next;
0508: } else {
0509: prev.next = entry.next;
0510: }
0511: if (queues[i].tail == entry) {
0512: queues[i].tail = prev;
0513: }
0514: uncacheEQItem(entry);
0515: return entry.event;
0516: }
0517: }
0518: }
0519: this .waitForID = id;
0520: wait();
0521: this .waitForID = 0;
0522: }
0523: } while (true);
0524: }
0525:
0526: /**
0527: * Returns the first event on the <code>EventQueue</code>
0528: * without removing it.
0529: * @return the first event
0530: */
0531: public synchronized AWTEvent peekEvent() {
0532: for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
0533: if (queues[i].head != null) {
0534: return queues[i].head.event;
0535: }
0536: }
0537:
0538: return null;
0539: }
0540:
0541: /**
0542: * Returns the first event with the specified id, if any.
0543: * @param id the id of the type of event desired
0544: * @return the first event of the specified id or <code>null</code>
0545: * if there is no such event
0546: */
0547: public synchronized AWTEvent peekEvent(int id) {
0548: for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
0549: EventQueueItem q = queues[i].head;
0550: for (; q != null; q = q.next) {
0551: if (q.id == id) {
0552: return q.event;
0553: }
0554: }
0555: }
0556:
0557: return null;
0558: }
0559:
0560: /**
0561: * Dispatches an event. The manner in which the event is
0562: * dispatched depends upon the type of the event and the
0563: * type of the event's source object:
0564: * <p> </p>
0565: * <table border=1 summary="Event types, source types, and dispatch methods">
0566: * <tr>
0567: * <th>Event Type</th>
0568: * <th>Source Type</th>
0569: * <th>Dispatched To</th>
0570: * </tr>
0571: * <tr>
0572: * <td>ActiveEvent</td>
0573: * <td>Any</td>
0574: * <td>event.dispatch()</td>
0575: * </tr>
0576: * <tr>
0577: * <td>Other</td>
0578: * <td>Component</td>
0579: * <td>source.dispatchEvent(AWTEvent)</td>
0580: * </tr>
0581: * <tr>
0582: * <td>Other</td>
0583: * <td>MenuComponent</td>
0584: * <td>source.dispatchEvent(AWTEvent)</td>
0585: * </tr>
0586: * <tr>
0587: * <td>Other</td>
0588: * <td>Other</td>
0589: * <td>No action (ignored)</td>
0590: * </tr>
0591: * </table>
0592: * <p> </p>
0593: * @param event an instance of <code>java.awt.AWTEvent</code>,
0594: * or a subclass of it
0595: * @throws NullPointerException if <code>event</code> is <code>null</code>
0596: * @since 1.2
0597: */
0598: protected void dispatchEvent(AWTEvent event) {
0599: event.isPosted = true;
0600: Object src = event.getSource();
0601: if (event instanceof ActiveEvent) {
0602: // This could become the sole method of dispatching in time.
0603: setCurrentEventAndMostRecentTimeImpl(event);
0604:
0605: ((ActiveEvent) event).dispatch();
0606: } else if (src instanceof Component) {
0607: ((Component) src).dispatchEvent(event);
0608: event.dispatched();
0609: } else if (src instanceof MenuComponent) {
0610: ((MenuComponent) src).dispatchEvent(event);
0611: } else if (src instanceof TrayIcon) {
0612: ((TrayIcon) src).dispatchEvent(event);
0613: } else if (src instanceof AWTAutoShutdown) {
0614: if (noEvents()) {
0615: dispatchThread.stopDispatching();
0616: }
0617: } else {
0618: System.err.println("unable to dispatch event: " + event);
0619: }
0620: }
0621:
0622: /**
0623: * Returns the timestamp of the most recent event that had a timestamp, and
0624: * that was dispatched from the <code>EventQueue</code> associated with the
0625: * calling thread. If an event with a timestamp is currently being
0626: * dispatched, its timestamp will be returned. If no events have yet
0627: * been dispatched, the EventQueue's initialization time will be
0628: * returned instead.In the current version of
0629: * the JDK, only <code>InputEvent</code>s,
0630: * <code>ActionEvent</code>s, and <code>InvocationEvent</code>s have
0631: * timestamps; however, future versions of the JDK may add timestamps to
0632: * additional event types. Note that this method should only be invoked
0633: * from an application's event dispatching thread. If this method is
0634: * invoked from another thread, the current system time (as reported by
0635: * <code>System.currentTimeMillis()</code>) will be returned instead.
0636: *
0637: * @return the timestamp of the last <code>InputEvent</code>,
0638: * <code>ActionEvent</code>, or <code>InvocationEvent</code> to be
0639: * dispatched, or <code>System.currentTimeMillis()</code> if this
0640: * method is invoked on a thread other than an event dispatching
0641: * thread
0642: * @see java.awt.event.InputEvent#getWhen
0643: * @see java.awt.event.ActionEvent#getWhen
0644: * @see java.awt.event.InvocationEvent#getWhen
0645: *
0646: * @since 1.4
0647: */
0648: public static long getMostRecentEventTime() {
0649: return Toolkit.getEventQueue().getMostRecentEventTimeImpl();
0650: }
0651:
0652: private synchronized long getMostRecentEventTimeImpl() {
0653: return (Thread.currentThread() == dispatchThread) ? mostRecentEventTime
0654: : System.currentTimeMillis();
0655: }
0656:
0657: /**
0658: * @return most recent event time on all threads.
0659: */
0660: synchronized long getMostRecentEventTimeEx() {
0661: return mostRecentEventTime;
0662: }
0663:
0664: /**
0665: * Returns the the event currently being dispatched by the
0666: * <code>EventQueue</code> associated with the calling thread. This is
0667: * useful if a method needs access to the event, but was not designed to
0668: * receive a reference to it as an argument. Note that this method should
0669: * only be invoked from an application's event dispatching thread. If this
0670: * method is invoked from another thread, null will be returned.
0671: *
0672: * @return the event currently being dispatched, or null if this method is
0673: * invoked on a thread other than an event dispatching thread
0674: * @since 1.4
0675: */
0676: public static AWTEvent getCurrentEvent() {
0677: return Toolkit.getEventQueue().getCurrentEventImpl();
0678: }
0679:
0680: private synchronized AWTEvent getCurrentEventImpl() {
0681: return (Thread.currentThread() == dispatchThread) ? ((AWTEvent) currentEvent
0682: .get())
0683: : null;
0684: }
0685:
0686: /**
0687: * Replaces the existing <code>EventQueue</code> with the specified one.
0688: * Any pending events are transferred to the new <code>EventQueue</code>
0689: * for processing by it.
0690: *
0691: * @param newEventQueue an <code>EventQueue</code>
0692: * (or subclass thereof) instance to be use
0693: * @see java.awt.EventQueue#pop
0694: * @throws NullPointerException if <code>newEventQueue</code> is <code>null</code>
0695: * @since 1.2
0696: */
0697: public synchronized void push(EventQueue newEventQueue) {
0698: if (debug) {
0699: System.out
0700: .println("EventQueue.push(" + newEventQueue + ")");
0701: }
0702:
0703: if (nextQueue != null) {
0704: nextQueue.push(newEventQueue);
0705: return;
0706: }
0707:
0708: synchronized (newEventQueue) {
0709: // Transfer all events forward to new EventQueue.
0710: while (peekEvent() != null) {
0711: try {
0712: newEventQueue.postEventPrivate(getNextEvent());
0713: } catch (InterruptedException ie) {
0714: if (debug) {
0715: System.err.println("interrupted push:");
0716: ie.printStackTrace(System.err);
0717: }
0718: }
0719: }
0720:
0721: newEventQueue.previousQueue = this ;
0722: }
0723: /*
0724: * Stop the event dispatch thread associated with the currently
0725: * active event queue, so that after the new queue is pushed
0726: * on the top this event dispatch thread won't prevent AWT from
0727: * being automatically shut down.
0728: * Use stopDispatchingLater() to avoid deadlock: stopDispatching()
0729: * waits for the dispatch thread to exit, so if the dispatch
0730: * thread attempts to synchronize on this EventQueue object
0731: * it will never exit since we already hold this lock.
0732: */
0733: if (dispatchThread != null) {
0734: dispatchThread.stopDispatchingLater();
0735: }
0736:
0737: nextQueue = newEventQueue;
0738:
0739: AppContext appContext = AppContext.getAppContext();
0740: if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this ) {
0741: appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue);
0742: }
0743: }
0744:
0745: /**
0746: * Stops dispatching events using this <code>EventQueue</code>.
0747: * Any pending events are transferred to the previous
0748: * <code>EventQueue</code> for processing.
0749: * <p>
0750: * Warning: To avoid deadlock, do not declare this method
0751: * synchronized in a subclass.
0752: *
0753: * @exception EmptyStackException if no previous push was made
0754: * on this <code>EventQueue</code>
0755: * @see java.awt.EventQueue#push
0756: * @since 1.2
0757: */
0758: protected void pop() throws EmptyStackException {
0759: if (debug) {
0760: System.out.println("EventQueue.pop(" + this + ")");
0761: }
0762:
0763: // To prevent deadlock, we lock on the previous EventQueue before
0764: // this one. This uses the same locking order as everything else
0765: // in EventQueue.java, so deadlock isn't possible.
0766: EventQueue prev = previousQueue;
0767: synchronized ((prev != null) ? prev : this ) {
0768: synchronized (this ) {
0769: if (nextQueue != null) {
0770: nextQueue.pop();
0771: return;
0772: }
0773: if (previousQueue == null) {
0774: throw new EmptyStackException();
0775: }
0776:
0777: // Transfer all events back to previous EventQueue.
0778: previousQueue.nextQueue = null;
0779: while (peekEvent() != null) {
0780: try {
0781: previousQueue.postEventPrivate(getNextEvent());
0782: } catch (InterruptedException ie) {
0783: if (debug) {
0784: System.err.println("interrupted pop:");
0785: ie.printStackTrace(System.err);
0786: }
0787: }
0788: }
0789: AppContext appContext = AppContext.getAppContext();
0790: if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this ) {
0791: appContext.put(AppContext.EVENT_QUEUE_KEY,
0792: previousQueue);
0793: }
0794:
0795: previousQueue = null;
0796: }
0797: }
0798:
0799: EventDispatchThread dt = this .dispatchThread;
0800: if (dt != null) {
0801: dt.stopDispatching(); // Must be done outside synchronized
0802: // block to avoid possible deadlock
0803: }
0804: }
0805:
0806: /**
0807: * Returns true if the calling thread is the current AWT
0808: * <code>EventQueue</code>'s dispatch thread. Use this
0809: * call the ensure that a given
0810: * task is being executed (or not being) on the current AWT
0811: * <code>EventDispatchThread</code>.
0812: *
0813: * @return true if running on the current AWT
0814: * <code>EventQueue</code>'s dispatch thread
0815: * @since 1.2
0816: */
0817: public static boolean isDispatchThread() {
0818: EventQueue eq = Toolkit.getEventQueue();
0819: EventQueue next = eq.nextQueue;
0820: while (next != null) {
0821: eq = next;
0822: next = eq.nextQueue;
0823: }
0824: return (Thread.currentThread() == eq.dispatchThread);
0825: }
0826:
0827: final void initDispatchThread() {
0828: synchronized (this ) {
0829: if (dispatchThread == null && !threadGroup.isDestroyed()) {
0830: dispatchThread = (EventDispatchThread) AccessController
0831: .doPrivileged(new PrivilegedAction() {
0832: public Object run() {
0833: EventDispatchThread t = new EventDispatchThread(
0834: threadGroup, name,
0835: EventQueue.this );
0836: t.setContextClassLoader(classLoader);
0837: t.setPriority(Thread.NORM_PRIORITY + 1);
0838: t.setDaemon(false);
0839: return t;
0840: }
0841: });
0842: AWTAutoShutdown.getInstance().notifyThreadBusy(
0843: dispatchThread);
0844: dispatchThread.start();
0845: }
0846: }
0847: }
0848:
0849: final void detachDispatchThread() {
0850: dispatchThread = null;
0851: }
0852:
0853: /*
0854: * Gets the <code>EventDispatchThread</code> for this
0855: * <code>EventQueue</code>.
0856: * @return the event dispatch thread associated with this event queue
0857: * or <code>null</code> if this event queue doesn't have a
0858: * working thread associated with it
0859: * @see java.awt.EventQueue#initDispatchThread
0860: * @see java.awt.EventQueue#detachDispatchThread
0861: */
0862: final EventDispatchThread getDispatchThread() {
0863: return dispatchThread;
0864: }
0865:
0866: /*
0867: * Removes any pending events for the specified source object.
0868: * If removeAllEvents parameter is <code>true</code> then all
0869: * events for the specified source object are removed, if it
0870: * is <code>false</code> then <code>SequencedEvent</code>, <code>SentEvent</code>,
0871: * <code>FocusEvent</code>, <code>WindowEvent</code>, <code>KeyEvent</code>,
0872: * and <code>InputMethodEvent</code> are kept in the queue, but all other
0873: * events are removed.
0874: *
0875: * This method is normally called by the source's
0876: * <code>removeNotify</code> method.
0877: */
0878: final void removeSourceEvents(Object source, boolean removeAllEvents) {
0879: SunToolkit.flushPendingEvents();
0880: synchronized (this ) {
0881: for (int i = 0; i < NUM_PRIORITIES; i++) {
0882: EventQueueItem entry = queues[i].head;
0883: EventQueueItem prev = null;
0884: while (entry != null) {
0885: if ((entry.event.getSource() == source)
0886: && (removeAllEvents || !(entry.event instanceof SequencedEvent
0887: || entry.event instanceof SentEvent
0888: || entry.event instanceof FocusEvent
0889: || entry.event instanceof WindowEvent
0890: || entry.event instanceof KeyEvent || entry.event instanceof InputMethodEvent))) {
0891: if (entry.event instanceof SequencedEvent) {
0892: ((SequencedEvent) entry.event).dispose();
0893: }
0894: if (entry.event instanceof SentEvent) {
0895: ((SentEvent) entry.event).dispose();
0896: }
0897: if (prev == null) {
0898: queues[i].head = entry.next;
0899: } else {
0900: prev.next = entry.next;
0901: }
0902: uncacheEQItem(entry);
0903: } else {
0904: prev = entry;
0905: }
0906: entry = entry.next;
0907: }
0908: queues[i].tail = prev;
0909: }
0910: }
0911: }
0912:
0913: static void setCurrentEventAndMostRecentTime(AWTEvent e) {
0914: Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e);
0915: }
0916:
0917: private synchronized void setCurrentEventAndMostRecentTimeImpl(
0918: AWTEvent e) {
0919: if (Thread.currentThread() != dispatchThread) {
0920: return;
0921: }
0922:
0923: currentEvent = new WeakReference(e);
0924:
0925: // This series of 'instanceof' checks should be replaced with a
0926: // polymorphic type (for example, an interface which declares a
0927: // getWhen() method). However, this would require us to make such
0928: // a type public, or to place it in sun.awt. Both of these approaches
0929: // have been frowned upon. So for now, we hack.
0930: //
0931: // In tiger, we will probably give timestamps to all events, so this
0932: // will no longer be an issue.
0933: long mostRecentEventTime2 = Long.MIN_VALUE;
0934: if (e instanceof InputEvent) {
0935: InputEvent ie = (InputEvent) e;
0936: mostRecentEventTime2 = ie.getWhen();
0937: } else if (e instanceof InputMethodEvent) {
0938: InputMethodEvent ime = (InputMethodEvent) e;
0939: mostRecentEventTime2 = ime.getWhen();
0940: } else if (e instanceof ActionEvent) {
0941: ActionEvent ae = (ActionEvent) e;
0942: mostRecentEventTime2 = ae.getWhen();
0943: } else if (e instanceof InvocationEvent) {
0944: InvocationEvent ie = (InvocationEvent) e;
0945: mostRecentEventTime2 = ie.getWhen();
0946: }
0947: mostRecentEventTime = Math.max(mostRecentEventTime,
0948: mostRecentEventTime2);
0949: }
0950:
0951: /**
0952: * Causes <code>runnable</code> to have its <code>run</code>
0953: * method called in the dispatch thread of
0954: * {@link Toolkit#getSystemEventQueue the system EventQueue}.
0955: * This will happen after all pending events are processed.
0956: *
0957: * @param runnable the <code>Runnable</code> whose <code>run</code>
0958: * method should be executed
0959: * synchronously on the <code>EventQueue</code>
0960: * @see #invokeAndWait
0961: * @since 1.2
0962: */
0963: public static void invokeLater(Runnable runnable) {
0964: Toolkit.getEventQueue().postEvent(
0965: new InvocationEvent(Toolkit.getDefaultToolkit(),
0966: runnable));
0967: }
0968:
0969: /**
0970: * Causes <code>runnable</code> to have its <code>run</code>
0971: * method called in the dispatch thread of
0972: * {@link Toolkit#getSystemEventQueue the system EventQueue}.
0973: * This will happen after all pending events are processed.
0974: * The call blocks until this has happened. This method
0975: * will throw an Error if called from the event dispatcher thread.
0976: *
0977: * @param runnable the <code>Runnable</code> whose <code>run</code>
0978: * method should be executed
0979: * synchronously on the <code>EventQueue</code>
0980: * @exception InterruptedException if any thread has
0981: * interrupted this thread
0982: * @exception InvocationTargetException if an throwable is thrown
0983: * when running <code>runnable</code>
0984: * @see #invokeLater
0985: * @since 1.2
0986: */
0987: public static void invokeAndWait(Runnable runnable)
0988: throws InterruptedException, InvocationTargetException {
0989:
0990: if (EventQueue.isDispatchThread()) {
0991: throw new Error(
0992: "Cannot call invokeAndWait from the event dispatcher thread");
0993: }
0994:
0995: class AWTInvocationLock {
0996: }
0997: Object lock = new AWTInvocationLock();
0998:
0999: InvocationEvent event = new InvocationEvent(Toolkit
1000: .getDefaultToolkit(), runnable, lock, true);
1001:
1002: synchronized (lock) {
1003: Toolkit.getEventQueue().postEvent(event);
1004: lock.wait();
1005: }
1006:
1007: Throwable eventThrowable = event.getThrowable();
1008: if (eventThrowable != null) {
1009: throw new InvocationTargetException(eventThrowable);
1010: }
1011: }
1012:
1013: /*
1014: * Called from PostEventQueue.postEvent to notify that a new event
1015: * appeared. First it proceeds to the EventQueue on the top of the
1016: * stack, then notifies the associated dispatch thread if it exists
1017: * or starts a new one otherwise.
1018: */
1019: private void wakeup(boolean isShutdown) {
1020: synchronized (this ) {
1021: if (nextQueue != null) {
1022: // Forward call to the top of EventQueue stack.
1023: nextQueue.wakeup(isShutdown);
1024: } else if (dispatchThread != null) {
1025: notifyAll();
1026: } else if (!isShutdown) {
1027: initDispatchThread();
1028: }
1029: }
1030: }
1031: }
1032:
1033: /**
1034: * The Queue object holds pointers to the beginning and end of one internal
1035: * queue. An EventQueue object is composed of multiple internal Queues, one
1036: * for each priority supported by the EventQueue. All Events on a particular
1037: * internal Queue have identical priority.
1038: */
1039: class Queue {
1040: EventQueueItem head;
1041: EventQueueItem tail;
1042: }
1043:
1044: class EventQueueItem {
1045: AWTEvent event;
1046: int id;
1047: EventQueueItem next;
1048:
1049: EventQueueItem(AWTEvent evt) {
1050: event = evt;
1051: id = evt.getID();
1052: }
1053: }
|