1631 lines · 58.2 KB · Java
RawDownload
/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing;  import java.awt.*;import java.awt.event.*;import java.awt.peer.ComponentPeer;import java.awt.peer.ContainerPeer;import java.awt.image.VolatileImage;import java.security.AccessController;import java.util.*;import java.applet.*; import sun.awt.AWTAccessor;import sun.awt.AppContext;import sun.awt.DisplayChangedListener;import sun.awt.SunToolkit;import sun.java2d.SunGraphicsEnvironment;import sun.security.action.GetPropertyAction; import com.sun.java.swing.SwingUtilities3; /** * This class manages repaint requests, allowing the number * of repaints to be minimized, for example by collapsing multiple * requests into a single repaint for members of a component tree. * <p> * As of 1.6 <code>RepaintManager</code> handles repaint requests * for Swing's top level components (<code>JApplet</code>, * <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>). * Any calls to <code>repaint</code> on one of these will call into the * appropriate <code>addDirtyRegion</code> method. * * @author Arnaud Weber */public class RepaintManager{    /**     * Whether or not the RepaintManager should handle paint requests     * for top levels.     */    static final boolean HANDLE_TOP_LEVEL_PAINT;     private static final short BUFFER_STRATEGY_NOT_SPECIFIED = 0;    private static final short BUFFER_STRATEGY_SPECIFIED_ON = 1;    private static final short BUFFER_STRATEGY_SPECIFIED_OFF = 2;     private static final short BUFFER_STRATEGY_TYPE;     /**     * Maps from GraphicsConfiguration to VolatileImage.     */    private Map<GraphicsConfiguration,VolatileImage> volatileMap = new                        HashMap<GraphicsConfiguration,VolatileImage>(1);     //    // As of 1.6 Swing handles scheduling of paint events from native code.    // That is, SwingPaintEventDispatcher is invoked on the toolkit thread,    // which in turn invokes nativeAddDirtyRegion.  Because this is invoked    // from the native thread we can not invoke any public methods and so    // we introduce these added maps.  So, any time nativeAddDirtyRegion is    // invoked the region is added to hwDirtyComponents and a work request    // is scheduled.  When the work request is processed all entries in    // this map are pushed to the real map (dirtyComponents) and then    // painted with the rest of the components.    //    private Map<Container,Rectangle> hwDirtyComponents;     private Map<Component,Rectangle> dirtyComponents;    private Map<Component,Rectangle> tmpDirtyComponents;    private java.util.List<Component> invalidComponents;     // List of Runnables that need to be processed before painting from AWT.    private java.util.List<Runnable> runnableList;     boolean   doubleBufferingEnabled = true;     private Dimension doubleBufferMaxSize;     // Support for both the standard and volatile offscreen buffers exists to    // provide backwards compatibility for the [rare] programs which may be    // calling getOffScreenBuffer() and not expecting to get a VolatileImage.    // Swing internally is migrating to use *only* the volatile image buffer.     // Support for standard offscreen buffer    //    DoubleBufferInfo standardDoubleBuffer;     /**     * Object responsible for hanlding core paint functionality.     */    private PaintManager paintManager;     private static final Object repaintManagerKey = RepaintManager.class;     // Whether or not a VolatileImage should be used for double-buffered painting    static boolean volatileImageBufferEnabled = true;    /**     * Value of the system property awt.nativeDoubleBuffering.     */    private static boolean nativeDoubleBuffering;     // The maximum number of times Swing will attempt to use the VolatileImage    // buffer during a paint operation.    private static final int VOLATILE_LOOP_MAX = 2;     /**     * Number of <code>beginPaint</code> that have been invoked.     */    private int paintDepth = 0;     /**     * Type of buffer strategy to use.  Will be one of the BUFFER_STRATEGY_     * constants.     */    private short bufferStrategyType;     //    // BufferStrategyPaintManager has the unique characteristic that it    // must deal with the buffer being lost while painting to it.  For    // example, if we paint a component and show it and the buffer has    // become lost we must repaint the whole window.  To deal with that    // the PaintManager calls into repaintRoot, and if we're still in    // the process of painting the repaintRoot field is set to the JRootPane    // and after the current JComponent.paintImmediately call finishes    // paintImmediately will be invoked on the repaintRoot.  In this    // way we don't try to show garbage to the screen.    //    /**     * True if we're in the process of painting the dirty regions.  This is     * set to true in <code>paintDirtyRegions</code>.     */    private boolean painting;    /**     * If the PaintManager calls into repaintRoot during painting this field     * will be set to the root.     */    private JComponent repaintRoot;     /**     * The Thread that has initiated painting.  If null it     * indicates painting is not currently in progress.     */    private Thread paintThread;     /**     * Runnable used to process all repaint/revalidate requests.     */    private final ProcessingRunnable processingRunnable;      static {        volatileImageBufferEnabled = "true".equals(AccessController.                doPrivileged(new GetPropertyAction(                "swing.volatileImageBufferEnabled", "true")));        boolean headless = GraphicsEnvironment.isHeadless();        if (volatileImageBufferEnabled && headless) {            volatileImageBufferEnabled = false;        }        nativeDoubleBuffering = "true".equals(AccessController.doPrivileged(                    new GetPropertyAction("awt.nativeDoubleBuffering")));        String bs = AccessController.doPrivileged(                          new GetPropertyAction("swing.bufferPerWindow"));        if (headless) {            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;        }        else if (bs == null) {            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED;        }        else if ("true".equals(bs)) {            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON;        }        else {            BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;        }        HANDLE_TOP_LEVEL_PAINT = "true".equals(AccessController.doPrivileged(               new GetPropertyAction("swing.handleTopLevelPaint", "true")));        GraphicsEnvironment ge = GraphicsEnvironment.                getLocalGraphicsEnvironment();        if (ge instanceof SunGraphicsEnvironment) {            ((SunGraphicsEnvironment)ge).addDisplayChangedListener(                    new DisplayChangedHandler());        }    }     /**     * Return the RepaintManager for the calling thread given a Component.     *     * @param c a Component -- unused in the default implementation, but could     *          be used by an overridden version to return a different RepaintManager     *          depending on the Component     * @return the RepaintManager object     */    public static RepaintManager currentManager(Component c) {        // Note: DisplayChangedRunnable passes in null as the component, so if        // component is ever used to determine the current        // RepaintManager, DisplayChangedRunnable will need to be modified        // accordingly.        return currentManager(AppContext.getAppContext());    }     /**     * Returns the RepaintManager for the specified AppContext.  If     * a RepaintManager has not been created for the specified     * AppContext this will return null.     */    static RepaintManager currentManager(AppContext appContext) {        RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey);        if (rm == null) {            rm = new RepaintManager(BUFFER_STRATEGY_TYPE);            appContext.put(repaintManagerKey, rm);        }        return rm;    }     /**     * Return the RepaintManager for the calling thread given a JComponent.     * <p>    * Note: This method exists for backward binary compatibility with earlier     * versions of the Swing library. It simply returns the result returned by     * {@link #currentManager(Component)}.     *     * @param c a JComponent -- unused     * @return the RepaintManager object     */    public static RepaintManager currentManager(JComponent c) {        return currentManager((Component)c);    }      /**     * Set the RepaintManager that should be used for the calling     * thread. <b>aRepaintManager</b> will become the current RepaintManager     * for the calling thread's thread group.     * @param aRepaintManager  the RepaintManager object to use     */    public static void setCurrentManager(RepaintManager aRepaintManager) {        if (aRepaintManager != null) {            SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);        } else {            SwingUtilities.appContextRemove(repaintManagerKey);        }    }     /**     * Create a new RepaintManager instance. You rarely call this constructor.     * directly. To get the default RepaintManager, use     * RepaintManager.currentManager(JComponent) (normally "this").     */    public RepaintManager() {        // Because we can't know what a subclass is doing with the        // volatile image we immediately punt in subclasses.  If this        // poses a problem we'll need a more sophisticated detection algorithm,        // or API.        this(BUFFER_STRATEGY_SPECIFIED_OFF);    }     private RepaintManager(short bufferStrategyType) {        // If native doublebuffering is being used, do NOT use        // Swing doublebuffering.        doubleBufferingEnabled = !nativeDoubleBuffering;        synchronized(this) {            dirtyComponents = new IdentityHashMap<Component,Rectangle>();            tmpDirtyComponents = new IdentityHashMap<Component,Rectangle>();            this.bufferStrategyType = bufferStrategyType;            hwDirtyComponents = new IdentityHashMap<Container,Rectangle>();        }        processingRunnable = new ProcessingRunnable();    }     private void displayChanged() {        clearImages();    }     /**     * Mark the component as in need of layout and queue a runnable     * for the event dispatching thread that will validate the components     * first isValidateRoot() ancestor.     *     * @see JComponent#isValidateRoot     * @see #removeInvalidComponent     */    public synchronized void addInvalidComponent(JComponent invalidComponent)    {        RepaintManager delegate = getDelegate(invalidComponent);        if (delegate != null) {            delegate.addInvalidComponent(invalidComponent);            return;        }        Component validateRoot =            SwingUtilities.getValidateRoot(invalidComponent, true);         if (validateRoot == null) {            return;        }         /* Lazily create the invalidateComponents vector and add the         * validateRoot if it's not there already.  If this validateRoot         * is already in the vector, we're done.         */        if (invalidComponents == null) {            invalidComponents = new ArrayList<Component>();        }        else {            int n = invalidComponents.size();            for(int i = 0; i < n; i++) {                if(validateRoot == invalidComponents.get(i)) {                    return;                }            }        }        invalidComponents.add(validateRoot);         // Queue a Runnable to invoke paintDirtyRegions and        // validateInvalidComponents.        scheduleProcessingRunnable();    }      /**     * Remove a component from the list of invalid components.     *     * @see #addInvalidComponent     */    public synchronized void removeInvalidComponent(JComponent component) {        RepaintManager delegate = getDelegate(component);        if (delegate != null) {            delegate.removeInvalidComponent(component);            return;        }        if(invalidComponents != null) {            int index = invalidComponents.indexOf(component);            if(index != -1) {                invalidComponents.remove(index);            }        }    }      /**     * Add a component in the list of components that should be refreshed.     * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>     * will be unioned with the region that should be redrawn.     *     * @see JComponent#repaint     */    private void addDirtyRegion0(Container c, int x, int y, int w, int h) {        /* Special cases we don't have to bother with.         */        if ((w <= 0) || (h <= 0) || (c == null)) {            return;        }         if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {            return;        }         if (extendDirtyRegion(c, x, y, w, h)) {            // Component was already marked as dirty, region has been            // extended, no need to continue.            return;        }         /* Make sure that c and all it ancestors (up to an Applet or         * Window) are visible.  This loop has the same effect as         * checking c.isShowing() (and note that it's still possible         * that c is completely obscured by an opaque ancestor in         * the specified rectangle).         */        Component root = null;         // Note: We can't synchronize around this, Frame.getExtendedState        // is synchronized so that if we were to synchronize around this        // it could lead to the possibility of getting locks out        // of order and deadlocking.        for (Container p = c; p != null; p = p.getParent()) {            if (!p.isVisible() || (p.getPeer() == null)) {                return;            }            if ((p instanceof Window) || (p instanceof Applet)) {                // Iconified frames are still visible!                if (p instanceof Frame &&                        (((Frame)p).getExtendedState() & Frame.ICONIFIED) ==                                    Frame.ICONIFIED) {                    return;                }                root = p;                break;            }        }         if (root == null) return;         synchronized(this) {            if (extendDirtyRegion(c, x, y, w, h)) {                // In between last check and this check another thread                // queued up runnable, can bail here.                return;            }            dirtyComponents.put(c, new Rectangle(x, y, w, h));        }         // Queue a Runnable to invoke paintDirtyRegions and        // validateInvalidComponents.        scheduleProcessingRunnable();    }     /**     * Add a component in the list of components that should be refreshed.     * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>     * will be unioned with the region that should be redrawn.     *     * @param c Component to repaint, null results in nothing happening.     * @param x X coordinate of the region to repaint     * @param y Y coordinate of the region to repaint     * @param w Width of the region to repaint     * @param h Height of the region to repaint     * @see JComponent#repaint     */    public void addDirtyRegion(JComponent c, int x, int y, int w, int h)    {        RepaintManager delegate = getDelegate(c);        if (delegate != null) {            delegate.addDirtyRegion(c, x, y, w, h);            return;        }        addDirtyRegion0(c, x, y, w, h);    }     /**     * Adds <code>window</code> to the list of <code>Component</code>s that     * need to be repainted.     *     * @param window Window to repaint, null results in nothing happening.     * @param x X coordinate of the region to repaint     * @param y Y coordinate of the region to repaint     * @param w Width of the region to repaint     * @param h Height of the region to repaint     * @see JFrame#repaint     * @see JWindow#repaint     * @see JDialog#repaint     * @since 1.6     */    public void addDirtyRegion(Window window, int x, int y, int w, int h) {        addDirtyRegion0(window, x, y, w, h);    }     /**     * Adds <code>applet</code> to the list of <code>Component</code>s that     * need to be repainted.     *     * @param applet Applet to repaint, null results in nothing happening.     * @param x X coordinate of the region to repaint     * @param y Y coordinate of the region to repaint     * @param w Width of the region to repaint     * @param h Height of the region to repaint     * @see JApplet#repaint     * @since 1.6     */    public void addDirtyRegion(Applet applet, int x, int y, int w, int h) {        addDirtyRegion0(applet, x, y, w, h);    }     void scheduleHeavyWeightPaints() {        Map<Container,Rectangle> hws;         synchronized(this) {            if (hwDirtyComponents.size() == 0) {                return;            }            hws = hwDirtyComponents;            hwDirtyComponents =  new IdentityHashMap<Container,Rectangle>();        }        for (Container hw : hws.keySet()) {            Rectangle dirty = hws.get(hw);            if (hw instanceof Window) {                addDirtyRegion((Window)hw, dirty.x, dirty.y,

Lines 1–500 of 1631.