- /*
- * @(#)SystemEventQueueUtilities.java 1.39 04/02/18
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- package javax.swing;
-
- import java.awt.*;
- import java.awt.event.*;
- import java.awt.image.*;
-
- import java.util.*;
-
- import java.lang.reflect.InvocationTargetException;
-
- import sun.awt.AppContext;
-
- /**
- * Swing internal utilities for dealing with the AWT system event
- * queue. Four methods are exported, see the individual method javadoc
- * for more information: addRunnableCanvas(), removeRunnableCanvas(),
- * postRunnable(), queueComponentWorkRequest().
- *
- * Note: most of the code in this class is no longer needed since
- * we're no longer supporting Swing in 1.1.x VM's and in 1.2 we're
- * guaranteed access to the AWT event queue. However all of the entry
- * points, save postRunnable(), are still used.
- *
- * @see RepaintManager
- * @see JRootPane
- */
- class SystemEventQueueUtilities
- {
- private static final Object classLock = new Object();
-
-
- private static final Object rootTableKey =
- new StringBuffer("SystemEventQueueUtilties.rootTableKey");
-
- private static Map getRootTable() {
- Map rt = (Map)AppContext.getAppContext().get(rootTableKey);
- if (rt == null) {
- synchronized (rootTableKey) {
- rt = (Map)AppContext.getAppContext().get(rootTableKey);
- if (rt == null) {
- rt = Collections.synchronizedMap(new WeakHashMap(4));
- AppContext.getAppContext().put(rootTableKey, rt);
- }
- }
- }
- return rt;
- }
-
-
- /**
- * SystemEventQueue class. This private class just exists to
- * encapsulate the details of getting at the System Event queue
- * in the Java 2 platform. The rest of the SystemEventQueueUtilities
- * class just uses SystemEventQueue.get() to access the event queue.
- */
-
- private static class SystemEventQueue
- {
- // If the AWT system event queue is accessible then return it.
- // otherwise return null.
-
- static EventQueue get() {
- EventQueue retValue;
- try {
- retValue = Toolkit.getDefaultToolkit().getSystemEventQueue();
- }
- catch (SecurityException se) {
- // Should never happen.
- retValue = null;
- }
- return retValue;
- }
-
- // If the AWT system event queue is accessible then return it.
- // otherwise return null.
- static EventQueue get(JRootPane rootPane) {
- return get();
- }
- }
-
- /**
- * A Runnable with a component. If we need to post this
- * runnable to the AWT system event queue, we'll find it's
- * JRootPane ancestor and use that as the key to the table
- * of RunnableCanvas's.
- *
- * @see RunnableCanvas
- */
- private static class ComponentWorkRequest implements Runnable
- {
- boolean isPending;
- Component component;
-
- ComponentWorkRequest(Component c) {
- /* As of 1.2, the component field is no longer used. It was
- * used by the RunnableCanvas class to find the JRootPane
- * associated with a ComponentWorkRequest for JDK1.1.x.
- */
- // component = c;
- }
- public void run() {
- RepaintManager rm;
- synchronized (this) {
- rm = RepaintManager.currentManager(component /*null*/);
- isPending = false;
- }
- rm.validateInvalidComponents();
- rm.paintDirtyRegions();
- }
- }
-
-
- /**
- * This method is used by RepaintManager to queue a ComponentWorkRequest
- * with invokeLater(). It assumes that the root argument is either
- * and Applet or a Window, the root passed in obtained in a
- * slightly different manner than see SwingUtilities.getRoot(). If this
- * called with the root obtained in a different way than RepaintManager
- * currently uses, be sure to also tweak removeRunnableCanvas.
- */
- static void queueComponentWorkRequest(Component root)
- {
- ComponentWorkRequest req = (ComponentWorkRequest)(getRootTable().get(root));
- boolean newWorkRequest = (req == null);
- if (newWorkRequest) {
- req = new ComponentWorkRequest(root);
- }
-
- /* At this point the ComponentWorkRequest may be accessible from
- * an event dispatching thread so before updating it further
- * we synchronize access to it.
- */
- synchronized(req) {
- if (newWorkRequest) {
- getRootTable().put(root, req);
- }
- if (!req.isPending) {
- SwingUtilities.invokeLater(req);
- req.isPending = true;
- }
- }
- }
-
-
- /**
- * Associate a RunnableCanvas and a JRootPane to enable queuing
- * events for the root pane's parent window's event dispatching thread.
- * Adds a 1x1 RunnableCanvas to the root pane's layered pane.
- * <p>
- * Called by JRootPane.addNotify() to set up the RunnableCanvas.
- *
- * @see RunnableCanvas
- * @see JRootPane#addNotify
- */
- static void addRunnableCanvas(JRootPane rootPane)
- {
- synchronized (classLock) {
- /* If we have access to the system event queue, we don't bother
- * with a RunnableCanvas
- */
- if (SystemEventQueue.get(rootPane) != null) {
- return;
- }
-
- JLayeredPane layeredPane = rootPane.getLayeredPane();
- if (layeredPane != null) {
- RunnableCanvas rc = new RunnableCanvas(rootPane);
- layeredPane.add(rc);
- }
- }
- }
-
-
- /**
- * Remove the RunnableCanvas from the JRootPane and clear the
- * internal bookeeping associated with it.
- * <p>
- * Called by JRootPane.removeNotify()
- *
- * @see RunnableCanvas
- */
- static void removeRunnableCanvas(JRootPane rootPane) {
- synchronized (classLock) {
- // We don't use SwingUtilities.getRoot, as it has different
- // behavior then the RepaintManager call to add the initial root.
- Component root = null;
- for (Component c = rootPane; c != null; c = c.getParent()) {
- if ((c instanceof Window) ||
- (c instanceof java.applet.Applet)) {
- root = c;
- break;
- }
- }
- if (root != null) {
- getRootTable().remove(root);
- }
- RunnableCanvas.remove(rootPane);
- }
- }
-
-
- /**
- * Post an event to the AWT System event queue that, when dispatched,
- * will invoke the specified Runnable. If lock is non-null this call
- * blocks (by waiting on the lock) until the doRun() method returns,
- * otherwise we return as soon as the event has been enqueued. An
- * exception is only returned if lock is non-null, i.e. if we're
- * being called from invokeAndWait().
- * <p>
- * This method is only intended to support SwingUtilities.invokeLater()
- * and SwingUtilities.invokeAndWait().
- */
- static Exception postRunnable(Runnable doRun, Object lock)
- {
- EventQueue systemEventQueue = SystemEventQueue.get();
-
- RunnableEvent event = new RunnableEvent(doRun, lock);
- if (systemEventQueue != null) {
- systemEventQueue.postEvent(event);
- }
- else {
- postRunnableCanvasEvent(event);
- }
- return event.exception;
- }
-
-
- /**
- * Adds a RunnableEvent to all the remaining RunnableCanvases to restart
- * the TimerQueues thread.
- *
- * @see RunnableCanvas#postRunnableEventToAll
- */
- static void restartTimerQueueThread() {
- synchronized (classLock) {
- if (SystemEventQueue.get() == null) {
- Runnable restarter = new TimerQueueRestart();
- RunnableEvent event = new RunnableEvent(restarter, null);
- RunnableCanvas.postRunnableEventToAll(event);
- }
- }
- }
-
-
- /**
- * Runnable that will message the shared instance of the Timer Queue
- * to restart.
- *
- * @see #restartTimerQueueThread
- */
- private static class TimerQueueRestart implements Runnable {
- boolean attemptedStart;
-
- public synchronized void run() {
- // Only try and restart the q once.
- if(!attemptedStart) {
- TimerQueue q = TimerQueue.sharedInstance();
-
- synchronized(q) {
- if(!q.running)
- q.start();
- }
- attemptedStart = true;
- }
- }
- }
-
- /**
- * Event type used for dispatching runnable objects for
- * SwingUtilities.invokeLater() and SwingUtilities.invokeAndWait().
- *
- * @see #postRunnable
- */
- private static class RunnableEvent extends AWTEvent {
- static final int EVENT_ID = AWTEvent.RESERVED_ID_MAX + 1000;
- static final Component target = new RunnableTarget();
- final Runnable doRun;
- final Object lock;
- Exception exception;
-
- RunnableEvent(Runnable doRun, Object lock) {
- super(target, EVENT_ID);
- this.doRun = doRun;
- this.lock = lock;
- }
- }
-
-
- /**
- * Calls RunnableEvent.doRun.run(). If RunnableEvent.lock is non
- * null then we synchronize the run() call and save the exception
- * (if any) in the RunnableEvent.exception field.
- */
- private static void processRunnableEvent(RunnableEvent runnableEvent)
- {
- Object lock = runnableEvent.lock;
- if (lock == null) {
- runnableEvent.doRun.run();
- }
- else {
- synchronized(lock) {
- try {
- runnableEvent.doRun.run();
- }
- catch (Exception e) {
- runnableEvent.exception = e;
- }
- finally {
- if (runnableEvent.lock != null) {
- runnableEvent.lock.notify();
- }
- }
- }
- }
- }
-
-
- /**
- * A dummy Component subclass that (only) handles RunnableEvents. If the
- * AWT System event queue is accessible (i.e. we're running as
- * an application or as trusted code), RunnableEvents are dispatched
- * to this component.
- *
- * @see #processRunnableEvent
- */
- private static class RunnableTarget extends Component
- {
- RunnableTarget() {
- super();
- enableEvents(RunnableEvent.EVENT_ID);
- }
-
- protected void processEvent(AWTEvent event) {
- if (event instanceof RunnableEvent) {
- processRunnableEvent((RunnableEvent)event);
- }
- }
- }
-
-
- /**
- * Synchronized entry point to the applet support for AWT System
- * event queue access. This method adds the event to the appropriate
- * runnable canvas's queue and then has the canvas repaint(). Note
- * that by the time the event dispatching thread gets to handling
- * the repaint() (by calling runnableCanvas.update()), many runnable
- * events may have been queued up.
- *
- * @see RunnableCanvas#addRunnableEvent
- * @see RunnableCanvas#update
- */
- private static void postRunnableCanvasEvent(RunnableEvent e) {
- synchronized (classLock) {
- RunnableCanvas runnableCanvas = RunnableCanvas.lookup(e);
-
- if (runnableCanvas == null) {
-
- /* If this is a ComponentWorkRequest and we were unable to
- * queue it, then clear the pending flag.
- */
- if (e.doRun instanceof ComponentWorkRequest) {
- ComponentWorkRequest req = (ComponentWorkRequest)e.doRun;
- synchronized(req) {
- req.isPending = false;
- }
- }
-
- /* If this is a Timer event let it know that it didn't fire.
- */
- if(e.doRun instanceof Timer.DoPostEvent) {
- ((Timer.DoPostEvent)e.doRun).getTimer().cancelEvent();
- }
-
- /* We are unable to queue this event on a system event queue. Make
- * sure that any code that's waiting for the runnable to finish
- * doesn't hang.
- */
- if (e.lock != null) {
- e.lock.notify();
- }
- return;
- }
-
- runnableCanvas.addRunnableEvent(e);
- runnableCanvas.repaint();
- }
- }
-
-
- /**
- * Return the current threads ThreadGroup, even on IE4.0.
- * IE4.0 throws a SecurityException if you apply getThreadGroup()
- * to the event dispatching thread. However a child of the
- * event dispatching thread (same thread group) is OK.
- */
- private static ThreadGroup getThreadGroupSafely() {
- return new Thread().getThreadGroup();
- }
-
-
- /**
- * Applets don't have direct access to the AWT SystemEvent queue. To
- * work around this we call RunnableCanvas.repaint() on a per applet
- * instance of this class. The AWT deals with this by queuing a
- * java.awt.PaintEvent for the event dispatching thread which
- * is dispatched (Component.dispatchEvent()) the usual way.
- * Component.dispatchEvent() handles PaintEvents by calling our update()
- * method (on the event dispatching thread) which processes
- * the RunnableEvents stashed in the runnableEvents vector.
- */
- private static class RunnableCanvas extends Canvas
- {
- private static final Graphics nullGraphics = new RunnableCanvasGraphics();
- private static Hashtable runnableCanvasTable = new Hashtable(1);
- private Vector runnableEvents = new Vector(2);
- private boolean isRegistered = false;
-
- RunnableCanvas(JRootPane rootPane) {
- super();
- setBounds(0, 0, 1, 1);
-
- /* Remember the mapping from the current thread (and the current
- * thread group) to this RunnableCanvas. Note that if a mapping
- * has already been defined, e.g. this rootPane belongs to an
- * existing applet, then leave the table alone. We're assuming that
- * an applets addNotify method will always run before the addNotify
- * method in any subsidiary windows the applet creates can run.
- */
- if (runnableCanvasTable.get(Thread.currentThread()) == null) {
- try {
- runnableCanvasTable.put(Thread.currentThread(), this);
- runnableCanvasTable.put(getThreadGroupSafely(), this);
- if (SwingUtilities.isEventDispatchThread()) {
- isRegistered = true;
- }
- }
- catch(Exception e) {
- System.err.println("Can't register RunnableCanvas");
- e.printStackTrace();
- }
- }
- runnableCanvasTable.put(rootPane, this);
- maybeRegisterEventDispatchThread();
- }
-
-
- /**
- * If called on an event dispatching thread that we haven't seen
- * before then make two hashtable entries in the runnableCanvasTable:
- * <pre>
- * current thread => this RunnableCanvas
- * current thread group => this RunnableCanvas
- * </pre>
- * @see #lookup
- */
- private void maybeRegisterEventDispatchThread() {
- /* Avoid the cost of a synchronized block (or method) in the
- * common case, since this method is called each time paint is called.
- */
- if (!isRegistered) {
- synchronized(this) {
- if (!isRegistered && SwingUtilities.isEventDispatchThread()) {
- Thread currentThread = Thread.currentThread();
-
- /* If this event dispatching thread is already mapped to
- * a runnableCanvas then don't replace the mapping (which
- * we expect to be generated by the applet).
- */
- if (runnableCanvasTable.get(currentThread) != null) {
- isRegistered = true;
- }
- else {
- runnableCanvasTable.put(currentThread, this);
- runnableCanvasTable.put(getThreadGroupSafely(), this);
- isRegistered = true;
- }
- }
- }
- }
- }
-
-
- /**
- * If we're running on the event dispatching thread then lookup
- * the canvas with the current thread itself, otherwise use the
- * current threads thread group. If there is no match for the
- * ThreadGroup, the first visible RunnableCanvas is returned.
- */
- static RunnableCanvas lookup(RunnableEvent e)
- {
- /* If this is a ComponentWorkRequest, find the components
- * JRootPane ancestor and use that as the index into the
- * runnableCanvasTable. This case occurs when any thread,
- * other than the event dispatching thead, calls repaint
- */
- if (e.doRun instanceof ComponentWorkRequest) {
- ComponentWorkRequest req = (ComponentWorkRequest)e.doRun;
- synchronized(req) {
- JRootPane rootPane = SwingUtilities.getRootPane(req.component);
- if(rootPane != null) {
- return (RunnableCanvas)(runnableCanvasTable.get(rootPane));
- }
- /* Failure. There doesn't appear to be a RunnableCanvas to use
- * so indicate that a new request will need to be queued, see
- * RepaintManager.queueWorkRequest().
- */
- req.isPending = false;
- return null;
- }
- }
-
- /* If the current thread is in the runnableCanvasTable
- * (e.g. we're on the event dispatching thread) we're done.
- */
- Object rv = runnableCanvasTable.get(Thread.currentThread());
- if (rv != null) {
- return (RunnableCanvas)rv;
- }
-
- /* At this point we're assuming that the calling thread isn't
- * a system thread (like an image observer thread), so it's safe
- * to lookup via the current threads ThreadGroup.
- */
- Object threadGroup;
- try {
- threadGroup = Thread.currentThread().getThreadGroup();
- }
- catch (SecurityException exc) {
- return null;
- }
- RunnableCanvas rc = (RunnableCanvas)runnableCanvasTable.get(threadGroup);
-
- /* There's no RunnableCanvas associated with this thread group
- * (so punt). Return the first visible RunnableCanvas.
- */
- if(rc == null) {
- Enumeration keys = runnableCanvasTable.keys();
- if(keys == null) {
- return null;
- }
- while(keys.hasMoreElements()) {
- Object key = keys.nextElement();
- if ((key instanceof JRootPane) && ((JRootPane)key).isShowing()) {
- return (RunnableCanvas)runnableCanvasTable.get(key);
- }
- }
- }
-
- return rc;
- }
-
-
- /**
- * Adds the event to all the RunnableCanvases.
- *
- * @see #restartTimerQueueThread
- */
- static void postRunnableEventToAll(RunnableEvent e) {
- // Determine the RunnableCanvas for the current thread. It
- // may be null.
- RunnableCanvas currentThreadCanvas;
- ThreadGroup tg;
- try {
- tg = new Thread().getThreadGroup();
- }
- catch (SecurityException se) {
- tg = null;
- }
- if(tg != null) {
- currentThreadCanvas = (RunnableCanvas)runnableCanvasTable.
- get(tg);
- }
- else
- currentThreadCanvas = null;
-
- // Add the event to all canvases, except the current one.
- // Presumably the current one is no longer valid and will be
- // going away shortly.
- Enumeration keys = runnableCanvasTable.keys();
- while(keys.hasMoreElements()) {
- Object key = keys.nextElement();
- if(key instanceof JRootPane) {
- Object canvas = runnableCanvasTable.get(key);
- if(canvas != currentThreadCanvas) {
- RunnableCanvas rc = (RunnableCanvas)canvas;
- rc.addRunnableEvent(e);
- rc.repaint();
- }
- }
- }
- }
-
-
- /**
- * Remove the RunnableCanvas associated with this applet from the
- * applets Layered pane and clear all of the runnableCanvasTable
- * entries that point at it.
- */
- static void remove(JRootPane rootPane) {
- RunnableCanvas rc = (RunnableCanvas)(runnableCanvasTable.get(rootPane));
- if (rc != null) {
- RunnableCanvas nextCanvas = null;
- JLayeredPane layeredPane = rootPane.getLayeredPane();
- layeredPane.remove((Component)rc);
-
- Enumeration keys = runnableCanvasTable.keys();
- while(keys.hasMoreElements()) {
- Object key = keys.nextElement();
- Object next = runnableCanvasTable.get(key);
- if (rc == next) {
- runnableCanvasTable.remove(key);
- }
- else if(nextCanvas == null) {
- nextCanvas = (RunnableCanvas)next;
- }
- }
-
- // If there are still events, either move them to another
- // canvas, or mark the Timer type events as not having
- // fired.
- RunnableEvent[] events = rc.getRunnableCanvasEvents();
- int numEvents = (events == null) ? 0 : events.length;
- if(numEvents > 0) {
- if(nextCanvas != null) {
- for(int counter = 0; counter < numEvents; counter++) {
- RunnableEvent e = events[counter];
- if(e.doRun instanceof Timer.DoPostEvent)
- nextCanvas.addRunnableEvent(e);
- }
- nextCanvas.repaint();
- }
- else {
- // Mark all Timer type event as not having fired.
- for(int counter = 0; counter < numEvents; counter++) {
- RunnableEvent event = events[counter];
- if(event.doRun instanceof Timer.DoPostEvent) {
- ((Timer.DoPostEvent)event.doRun).getTimer().
- cancelEvent();
- }
- }
- }
- }
- }
- }
-
-
- /**
- * If there are events to be processed then we're showing. Note
- * that the AWT code that dispatches paint events short circuits
- * (does nothing) if isShowing() returns false.
- */
- public boolean isShowing() {
- return runnableEvents.size() > 0;
- }
-
-
- /**
- * Reduce the cost of repainting (since we're not going to draw
- * anything) by returning a constant no-op graphics object.
- */
- public Graphics getGraphics() {
- return nullGraphics;
- }
-
-
- /**
- * Testing purposes only. This method shouldn't be called;
- * the parent of this component should have a null layout
- * manager.
- */
- public Dimension getPreferredSize() {
- return new Dimension(1, 1);
- }
-
-
- /**
- * Add a RunnableEvent to the queue that will be dispatched
- * when this component is repainted.
- * @see #update
- */
- synchronized void addRunnableEvent(RunnableEvent e) {
- runnableEvents.addElement(e);
- }
-
-
- /**
- * Return an (array) copy of the runnableEvents vector or
- * null if the vector is empty. The update method processes
- * a copy of the vector so that we don't have to hold
- * the synchronized lock while calling processRunnableEvent().
- * @see #update
- */
- private synchronized RunnableEvent[] getRunnableCanvasEvents() {
- int n = runnableEvents.size();
- if (n == 0) {
- return null;
- }
- else {
- RunnableEvent[] rv = new RunnableEvent[n];
- for(int i = 0; i < n; i++) {
- rv[i] = (RunnableEvent)(runnableEvents.elementAt(i));
- }
- runnableEvents.removeAllElements();
- return rv;
- }
- }
-
-
- public void paint(Graphics g) {
- maybeRegisterEventDispatchThread();
- }
-
-
- /**
- * Process all of the RunnableEvents that have accumulated
- * since RunnableCanvas.repaint() was called.
- */
- public void update(Graphics g) {
- RunnableEvent[] events = getRunnableCanvasEvents();
- if (events != null) {
- for(int i = 0; i < events.length; i++) {
- processRunnableEvent(events[i]);
- }
- }
- }
- }
-
-
- /**
- * A no-op Graphics object for the RunnableCanvas component.
- * Most AWT Component implementations handle update events
- * like this:
- * <pre>
- * Graphics g = getGraphics();
- * Rectangle r = ((PaintEvent)e).getUpdateRect();
- * g.clipRect(r.x, r.y, r.width, r.height);
- * update(g)
- * g.dispose();
- * </pre>
- * Since the RunnableCanvas component isn't really going to do
- * any painting we don't bother with creating/disposing real
- * graphics objects, or setting any of the properties.
- *
- */
- private static class RunnableCanvasGraphics extends Graphics
- {
- public Graphics create() {
- return this;
- }
-
- /* We don't expect any of the following methods to be called but
- * we still return marginally valid values for the get methods
- * just in case.
- */
-
- public Rectangle getClipBounds() {
- return new Rectangle(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
- }
-
- public Shape getClip() {
- return (Shape)(getClipBounds());
- }
-
- public void dispose() {}
- public void translate(int x, int y) {}
- public Color getColor() { return Color.black; }
- public void setColor(Color c) {}
- public void setPaintMode() {}
- public void setXORMode(Color c) {}
- public Font getFont() { return null; }
- public void setFont(Font font) {}
- public FontMetrics getFontMetrics(Font f) { return null; }
- public void clipRect(int x, int y, int width, int height) {}
- public void setClip(int x, int y, int width, int height) {}
- public void setClip(Shape clip) {}
- public void copyArea(int x, int y, int w, int h, int dx, int dy) {}
- public void drawLine(int x1, int y1, int x2, int y2) {}
- public void fillRect(int x, int y, int width, int height) {}
- public void clearRect(int x, int y, int width, int height) {}
- public void drawRoundRect(int x, int y, int w, int h, int aw, int ah) {}
- public void fillRoundRect(int x, int y, int w, int h, int aw, int ah) {}
- public void drawOval(int x, int y, int w, int h) {}
- public void fillOval(int x, int y, int w, int h) {}
- public void drawArc(int x, int y, int w, int h, int sa, int aa) {}
- public void fillArc(int x, int y, int w, int h, int sa, int aa) {}
- public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {}
- public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {}
- public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {}
- public void drawString(String str, int x, int y) {}
- public void drawString(java.text.AttributedCharacterIterator iterator, int x, int y) {}
- public boolean drawImage(Image i, int x, int y, ImageObserver o) { return false; }
- public boolean drawImage(Image i, int x, int y, int w, int h, ImageObserver o) { return false; }
- public boolean drawImage(Image i, int x, int y, Color bgcolor, ImageObserver o) { return false; }
- public boolean drawImage(Image i, int x, int y, int w, int h, Color c, ImageObserver o) { return false; }
- public boolean drawImage(Image i,
- int dx1, int dy1, int dx2, int dy2,
- int sx1, int sy1, int sx2, int sy2, ImageObserver o)
- { return false; }
- public boolean drawImage(Image i,
- int dx1, int dy1, int dx2, int dy2,
- int sx1, int sy1, int sx2, int sy2, Color c, ImageObserver o)
- { return false; }
- }
- }