1 /*
   2  * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.awt.peer.ComponentPeer;
  31 import java.awt.peer.ContainerPeer;
  32 import java.awt.image.VolatileImage;
  33 import java.security.AccessController;
  34 import java.util.*;
  35 import java.applet.*;
  36 
  37 import sun.awt.AWTAccessor;
  38 import sun.awt.AppContext;
  39 import sun.awt.DisplayChangedListener;
  40 import sun.awt.SunToolkit;
  41 import sun.java2d.SunGraphicsEnvironment;
  42 import sun.security.action.GetPropertyAction;
  43 
  44 import com.sun.java.swing.SwingUtilities3;
  45 
  46 /**
  47  * This class manages repaint requests, allowing the number
  48  * of repaints to be minimized, for example by collapsing multiple
  49  * requests into a single repaint for members of a component tree.
  50  * <p>
  51  * As of 1.6 <code>RepaintManager</code> handles repaint requests
  52  * for Swing's top level components (<code>JApplet</code>,
  53  * <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>).
  54  * Any calls to <code>repaint</code> on one of these will call into the
  55  * appropriate <code>addDirtyRegion</code> method.
  56  *
  57  * @author Arnaud Weber
  58  */
  59 public class RepaintManager
  60 {
  61     /**
  62      * Whether or not the RepaintManager should handle paint requests
  63      * for top levels.
  64      */
  65     static final boolean HANDLE_TOP_LEVEL_PAINT;
  66 
  67     private static final short BUFFER_STRATEGY_NOT_SPECIFIED = 0;
  68     private static final short BUFFER_STRATEGY_SPECIFIED_ON = 1;
  69     private static final short BUFFER_STRATEGY_SPECIFIED_OFF = 2;
  70 
  71     private static final short BUFFER_STRATEGY_TYPE;
  72 
  73     /**
  74      * Maps from GraphicsConfiguration to VolatileImage.
  75      */
  76     private Map<GraphicsConfiguration,VolatileImage> volatileMap = new
  77                         HashMap<GraphicsConfiguration,VolatileImage>(1);
  78 
  79     //
  80     // As of 1.6 Swing handles scheduling of paint events from native code.
  81     // That is, SwingPaintEventDispatcher is invoked on the toolkit thread,
  82     // which in turn invokes nativeAddDirtyRegion.  Because this is invoked
  83     // from the native thread we can not invoke any public methods and so
  84     // we introduce these added maps.  So, any time nativeAddDirtyRegion is
  85     // invoked the region is added to hwDirtyComponents and a work request
  86     // is scheduled.  When the work request is processed all entries in
  87     // this map are pushed to the real map (dirtyComponents) and then
  88     // painted with the rest of the components.
  89     //
  90     private Map<Container,Rectangle> hwDirtyComponents;
  91 
  92     private Map<Component,Rectangle> dirtyComponents;
  93     private Map<Component,Rectangle> tmpDirtyComponents;
  94     private java.util.List<Component> invalidComponents;
  95 
  96     // List of Runnables that need to be processed before painting from AWT.
  97     private java.util.List<Runnable> runnableList;
  98 
  99     boolean   doubleBufferingEnabled = true;
 100 
 101     private Dimension doubleBufferMaxSize;
 102 
 103     // Support for both the standard and volatile offscreen buffers exists to
 104     // provide backwards compatibility for the [rare] programs which may be
 105     // calling getOffScreenBuffer() and not expecting to get a VolatileImage.
 106     // Swing internally is migrating to use *only* the volatile image buffer.
 107 
 108     // Support for standard offscreen buffer
 109     //
 110     DoubleBufferInfo standardDoubleBuffer;
 111 
 112     /**
 113      * Object responsible for hanlding core paint functionality.
 114      */
 115     private PaintManager paintManager;
 116 
 117     private static final Object repaintManagerKey = RepaintManager.class;
 118 
 119     // Whether or not a VolatileImage should be used for double-buffered painting
 120     static boolean volatileImageBufferEnabled = true;
 121     /**
 122      * Type of VolatileImage which should be used for double-buffered
 123      * painting.
 124      */
 125     private static final int doubleBufferType;
 126     /**
 127      * Value of the system property awt.nativeDoubleBuffering.
 128      */
 129     private static boolean nativeDoubleBuffering;
 130 
 131     // The maximum number of times Swing will attempt to use the VolatileImage
 132     // buffer during a paint operation.
 133     private static final int VOLATILE_LOOP_MAX = 2;
 134 
 135     /**
 136      * Number of <code>beginPaint</code> that have been invoked.
 137      */
 138     private int paintDepth = 0;
 139 
 140     /**
 141      * Type of buffer strategy to use.  Will be one of the BUFFER_STRATEGY_
 142      * constants.
 143      */
 144     private short bufferStrategyType;
 145 
 146     //
 147     // BufferStrategyPaintManager has the unique characteristic that it
 148     // must deal with the buffer being lost while painting to it.  For
 149     // example, if we paint a component and show it and the buffer has
 150     // become lost we must repaint the whole window.  To deal with that
 151     // the PaintManager calls into repaintRoot, and if we're still in
 152     // the process of painting the repaintRoot field is set to the JRootPane
 153     // and after the current JComponent.paintImmediately call finishes
 154     // paintImmediately will be invoked on the repaintRoot.  In this
 155     // way we don't try to show garbage to the screen.
 156     //
 157     /**
 158      * True if we're in the process of painting the dirty regions.  This is
 159      * set to true in <code>paintDirtyRegions</code>.
 160      */
 161     private boolean painting;
 162     /**
 163      * If the PaintManager calls into repaintRoot during painting this field
 164      * will be set to the root.
 165      */
 166     private JComponent repaintRoot;
 167 
 168     /**
 169      * The Thread that has initiated painting.  If null it
 170      * indicates painting is not currently in progress.
 171      */
 172     private Thread paintThread;
 173 
 174     /**
 175      * Runnable used to process all repaint/revalidate requests.
 176      */
 177     private final ProcessingRunnable processingRunnable;
 178 
 179 
 180     static {
 181         volatileImageBufferEnabled = "true".equals(AccessController.
 182                 doPrivileged(new GetPropertyAction(
 183                 "swing.volatileImageBufferEnabled", "true")));
 184         boolean headless = GraphicsEnvironment.isHeadless();
 185         if (volatileImageBufferEnabled && headless) {
 186             volatileImageBufferEnabled = false;
 187         }
 188         nativeDoubleBuffering = "true".equals(AccessController.doPrivileged(
 189                     new GetPropertyAction("awt.nativeDoubleBuffering")));
 190         String bs = AccessController.doPrivileged(
 191                           new GetPropertyAction("swing.bufferPerWindow"));
 192         if (headless) {
 193             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
 194         }
 195         else if (bs == null) {
 196             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED;
 197         }
 198         else if ("true".equals(bs)) {
 199             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON;
 200         }
 201         else {
 202             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
 203         }
 204         HANDLE_TOP_LEVEL_PAINT = "true".equals(AccessController.doPrivileged(
 205                new GetPropertyAction("swing.handleTopLevelPaint", "true")));
 206         GraphicsEnvironment ge = GraphicsEnvironment.
 207                 getLocalGraphicsEnvironment();
 208         if (ge instanceof SunGraphicsEnvironment) {
 209             ((SunGraphicsEnvironment)ge).addDisplayChangedListener(
 210                     new DisplayChangedHandler());
 211         }
 212         Toolkit tk = Toolkit.getDefaultToolkit();
 213         if ((tk instanceof SunToolkit)
 214                 && ((SunToolkit) tk).isSwingBackbufferTranslucencySupported()) {
 215             doubleBufferType = Transparency.TRANSLUCENT;
 216         } else {
 217             doubleBufferType = Transparency.OPAQUE;
 218         }
 219     }
 220 
 221     /**
 222      * Return the RepaintManager for the calling thread given a Component.
 223      *
 224      * @param c a Component -- unused in the default implementation, but could
 225      *          be used by an overridden version to return a different RepaintManager
 226      *          depending on the Component
 227      * @return the RepaintManager object
 228      */
 229     public static RepaintManager currentManager(Component c) {
 230         // Note: DisplayChangedRunnable passes in null as the component, so if
 231         // component is ever used to determine the current
 232         // RepaintManager, DisplayChangedRunnable will need to be modified
 233         // accordingly.
 234         return currentManager(AppContext.getAppContext());
 235     }
 236 
 237     /**
 238      * Returns the RepaintManager for the specified AppContext.  If
 239      * a RepaintManager has not been created for the specified
 240      * AppContext this will return null.
 241      */
 242     static RepaintManager currentManager(AppContext appContext) {
 243         RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey);
 244         if (rm == null) {
 245             rm = new RepaintManager(BUFFER_STRATEGY_TYPE);
 246             appContext.put(repaintManagerKey, rm);
 247         }
 248         return rm;
 249     }
 250 
 251     /**
 252      * Return the RepaintManager for the calling thread given a JComponent.
 253      * <p>
 254     * Note: This method exists for backward binary compatibility with earlier
 255      * versions of the Swing library. It simply returns the result returned by
 256      * {@link #currentManager(Component)}.
 257      *
 258      * @param c a JComponent -- unused
 259      * @return the RepaintManager object
 260      */
 261     public static RepaintManager currentManager(JComponent c) {
 262         return currentManager((Component)c);
 263     }
 264 
 265 
 266     /**
 267      * Set the RepaintManager that should be used for the calling
 268      * thread. <b>aRepaintManager</b> will become the current RepaintManager
 269      * for the calling thread's thread group.
 270      * @param aRepaintManager  the RepaintManager object to use
 271      */
 272     public static void setCurrentManager(RepaintManager aRepaintManager) {
 273         if (aRepaintManager != null) {
 274             SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
 275         } else {
 276             SwingUtilities.appContextRemove(repaintManagerKey);
 277         }
 278     }
 279 
 280     /**
 281      * Create a new RepaintManager instance. You rarely call this constructor.
 282      * directly. To get the default RepaintManager, use
 283      * RepaintManager.currentManager(JComponent) (normally "this").
 284      */
 285     public RepaintManager() {
 286         // Because we can't know what a subclass is doing with the
 287         // volatile image we immediately punt in subclasses.  If this
 288         // poses a problem we'll need a more sophisticated detection algorithm,
 289         // or API.
 290         this(BUFFER_STRATEGY_SPECIFIED_OFF);
 291     }
 292 
 293     private RepaintManager(short bufferStrategyType) {
 294         // If native doublebuffering is being used, do NOT use
 295         // Swing doublebuffering.
 296         doubleBufferingEnabled = !nativeDoubleBuffering;
 297         synchronized(this) {
 298             dirtyComponents = new IdentityHashMap<Component,Rectangle>();
 299             tmpDirtyComponents = new IdentityHashMap<Component,Rectangle>();
 300             this.bufferStrategyType = bufferStrategyType;
 301             hwDirtyComponents = new IdentityHashMap<Container,Rectangle>();
 302         }
 303         processingRunnable = new ProcessingRunnable();
 304     }
 305 
 306     private void displayChanged() {
 307         clearImages();
 308     }
 309 
 310     /**
 311      * Mark the component as in need of layout and queue a runnable
 312      * for the event dispatching thread that will validate the components
 313      * first isValidateRoot() ancestor.
 314      *
 315      * @see JComponent#isValidateRoot
 316      * @see #removeInvalidComponent
 317      */
 318     public synchronized void addInvalidComponent(JComponent invalidComponent)
 319     {
 320         RepaintManager delegate = getDelegate(invalidComponent);
 321         if (delegate != null) {
 322             delegate.addInvalidComponent(invalidComponent);
 323             return;
 324         }
 325         Component validateRoot =
 326             SwingUtilities.getValidateRoot(invalidComponent, true);
 327 
 328         if (validateRoot == null) {
 329             return;
 330         }
 331 
 332         /* Lazily create the invalidateComponents vector and add the
 333          * validateRoot if it's not there already.  If this validateRoot
 334          * is already in the vector, we're done.
 335          */
 336         if (invalidComponents == null) {
 337             invalidComponents = new ArrayList<Component>();
 338         }
 339         else {
 340             int n = invalidComponents.size();
 341             for(int i = 0; i < n; i++) {
 342                 if(validateRoot == invalidComponents.get(i)) {
 343                     return;
 344                 }
 345             }
 346         }
 347         invalidComponents.add(validateRoot);
 348 
 349         // Queue a Runnable to invoke paintDirtyRegions and
 350         // validateInvalidComponents.
 351         scheduleProcessingRunnable();
 352     }
 353 
 354 
 355     /**
 356      * Remove a component from the list of invalid components.
 357      *
 358      * @see #addInvalidComponent
 359      */
 360     public synchronized void removeInvalidComponent(JComponent component) {
 361         RepaintManager delegate = getDelegate(component);
 362         if (delegate != null) {
 363             delegate.removeInvalidComponent(component);
 364             return;
 365         }
 366         if(invalidComponents != null) {
 367             int index = invalidComponents.indexOf(component);
 368             if(index != -1) {
 369                 invalidComponents.remove(index);
 370             }
 371         }
 372     }
 373 
 374 
 375     /**
 376      * Add a component in the list of components that should be refreshed.
 377      * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
 378      * will be unioned with the region that should be redrawn.
 379      *
 380      * @see JComponent#repaint
 381      */
 382     private void addDirtyRegion0(Container c, int x, int y, int w, int h) {
 383         /* Special cases we don't have to bother with.
 384          */
 385         if ((w <= 0) || (h <= 0) || (c == null)) {
 386             return;
 387         }
 388 
 389         if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
 390             return;
 391         }
 392 
 393         if (extendDirtyRegion(c, x, y, w, h)) {
 394             // Component was already marked as dirty, region has been
 395             // extended, no need to continue.
 396             return;
 397         }
 398 
 399         /* Make sure that c and all it ancestors (up to an Applet or
 400          * Window) are visible.  This loop has the same effect as
 401          * checking c.isShowing() (and note that it's still possible
 402          * that c is completely obscured by an opaque ancestor in
 403          * the specified rectangle).
 404          */
 405         Component root = null;
 406 
 407         // Note: We can't synchronize around this, Frame.getExtendedState
 408         // is synchronized so that if we were to synchronize around this
 409         // it could lead to the possibility of getting locks out
 410         // of order and deadlocking.
 411         for (Container p = c; p != null; p = p.getParent()) {
 412             if (!p.isVisible() || (p.getPeer() == null)) {
 413                 return;
 414             }
 415             if ((p instanceof Window) || (p instanceof Applet)) {
 416                 // Iconified frames are still visible!
 417                 if (p instanceof Frame &&
 418                         (((Frame)p).getExtendedState() & Frame.ICONIFIED) ==
 419                                     Frame.ICONIFIED) {
 420                     return;
 421                 }
 422                 root = p;
 423                 break;
 424             }
 425         }
 426 
 427         if (root == null) return;
 428 
 429         synchronized(this) {
 430             if (extendDirtyRegion(c, x, y, w, h)) {
 431                 // In between last check and this check another thread
 432                 // queued up runnable, can bail here.
 433                 return;
 434             }
 435             dirtyComponents.put(c, new Rectangle(x, y, w, h));
 436         }
 437 
 438         // Queue a Runnable to invoke paintDirtyRegions and
 439         // validateInvalidComponents.
 440         scheduleProcessingRunnable();
 441     }
 442 
 443     /**
 444      * Add a component in the list of components that should be refreshed.
 445      * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
 446      * will be unioned with the region that should be redrawn.
 447      *
 448      * @param c Component to repaint, null results in nothing happening.
 449      * @param x X coordinate of the region to repaint
 450      * @param y Y coordinate of the region to repaint
 451      * @param w Width of the region to repaint
 452      * @param h Height of the region to repaint
 453      * @see JComponent#repaint
 454      */
 455     public void addDirtyRegion(JComponent c, int x, int y, int w, int h)
 456     {
 457         RepaintManager delegate = getDelegate(c);
 458         if (delegate != null) {
 459             delegate.addDirtyRegion(c, x, y, w, h);
 460             return;
 461         }
 462         addDirtyRegion0(c, x, y, w, h);
 463     }
 464 
 465     /**
 466      * Adds <code>window</code> to the list of <code>Component</code>s that
 467      * need to be repainted.
 468      *
 469      * @param window Window to repaint, null results in nothing happening.
 470      * @param x X coordinate of the region to repaint
 471      * @param y Y coordinate of the region to repaint
 472      * @param w Width of the region to repaint
 473      * @param h Height of the region to repaint
 474      * @see JFrame#repaint
 475      * @see JWindow#repaint
 476      * @see JDialog#repaint
 477      * @since 1.6
 478      */
 479     public void addDirtyRegion(Window window, int x, int y, int w, int h) {
 480         addDirtyRegion0(window, x, y, w, h);
 481     }
 482 
 483     /**
 484      * Adds <code>applet</code> to the list of <code>Component</code>s that
 485      * need to be repainted.
 486      *
 487      * @param applet Applet to repaint, null results in nothing happening.
 488      * @param x X coordinate of the region to repaint
 489      * @param y Y coordinate of the region to repaint
 490      * @param w Width of the region to repaint
 491      * @param h Height of the region to repaint
 492      * @see JApplet#repaint
 493      * @since 1.6
 494      */
 495     public void addDirtyRegion(Applet applet, int x, int y, int w, int h) {
 496         addDirtyRegion0(applet, x, y, w, h);
 497     }
 498 
 499     void scheduleHeavyWeightPaints() {
 500         Map<Container,Rectangle> hws;
 501 
 502         synchronized(this) {
 503             if (hwDirtyComponents.size() == 0) {
 504                 return;
 505             }
 506             hws = hwDirtyComponents;
 507             hwDirtyComponents =  new IdentityHashMap<Container,Rectangle>();
 508         }
 509         for (Container hw : hws.keySet()) {
 510             Rectangle dirty = hws.get(hw);
 511             if (hw instanceof Window) {
 512                 addDirtyRegion((Window)hw, dirty.x, dirty.y,
 513                                dirty.width, dirty.height);
 514             }
 515             else if (hw instanceof Applet) {
 516                 addDirtyRegion((Applet)hw, dirty.x, dirty.y,
 517                                dirty.width, dirty.height);
 518             }
 519             else { // SwingHeavyWeight
 520                 addDirtyRegion0(hw, dirty.x, dirty.y,
 521                                 dirty.width, dirty.height);
 522             }
 523         }
 524     }
 525 
 526     //
 527     // This is called from the toolkit thread when a native expose is
 528     // received.
 529     //
 530     void nativeAddDirtyRegion(AppContext appContext, Container c,
 531                               int x, int y, int w, int h) {
 532         if (w > 0 && h > 0) {
 533             synchronized(this) {
 534                 Rectangle dirty = hwDirtyComponents.get(c);
 535                 if (dirty == null) {
 536                     hwDirtyComponents.put(c, new Rectangle(x, y, w, h));
 537                 }
 538                 else {
 539                     hwDirtyComponents.put(c, SwingUtilities.computeUnion(
 540                                               x, y, w, h, dirty));
 541                 }
 542             }
 543             scheduleProcessingRunnable(appContext);
 544         }
 545     }
 546 
 547     //
 548     // This is called from the toolkit thread when awt needs to run a
 549     // Runnable before we paint.
 550     //
 551     void nativeQueueSurfaceDataRunnable(AppContext appContext, Component c,
 552                                         Runnable r) {
 553         synchronized(this) {
 554             if (runnableList == null) {
 555                 runnableList = new LinkedList<Runnable>();
 556             }
 557             runnableList.add(r);
 558         }
 559         scheduleProcessingRunnable(appContext);
 560     }
 561 
 562     /**
 563      * Extends the dirty region for the specified component to include
 564      * the new region.
 565      *
 566      * @return false if <code>c</code> is not yet marked dirty.
 567      */
 568     private synchronized boolean extendDirtyRegion(
 569         Component c, int x, int y, int w, int h) {
 570         Rectangle r = dirtyComponents.get(c);
 571         if (r != null) {
 572             // A non-null r implies c is already marked as dirty,
 573             // and that the parent is valid. Therefore we can
 574             // just union the rect and bail.
 575             SwingUtilities.computeUnion(x, y, w, h, r);
 576             return true;
 577         }
 578         return false;
 579     }
 580 
 581     /** Return the current dirty region for a component.
 582      *  Return an empty rectangle if the component is not
 583      *  dirty.
 584      */
 585     public Rectangle getDirtyRegion(JComponent aComponent) {
 586         RepaintManager delegate = getDelegate(aComponent);
 587         if (delegate != null) {
 588             return delegate.getDirtyRegion(aComponent);
 589         }
 590         Rectangle r;
 591         synchronized(this) {
 592             r = dirtyComponents.get(aComponent);
 593         }
 594         if(r == null)
 595             return new Rectangle(0,0,0,0);
 596         else
 597             return new Rectangle(r);
 598     }
 599 
 600     /**
 601      * Mark a component completely dirty. <b>aComponent</b> will be
 602      * completely painted during the next paintDirtyRegions() call.
 603      */
 604     public void markCompletelyDirty(JComponent aComponent) {
 605         RepaintManager delegate = getDelegate(aComponent);
 606         if (delegate != null) {
 607             delegate.markCompletelyDirty(aComponent);
 608             return;
 609         }
 610         addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
 611     }
 612 
 613     /**
 614      * Mark a component completely clean. <b>aComponent</b> will not
 615      * get painted during the next paintDirtyRegions() call.
 616      */
 617     public void markCompletelyClean(JComponent aComponent) {
 618         RepaintManager delegate = getDelegate(aComponent);
 619         if (delegate != null) {
 620             delegate.markCompletelyClean(aComponent);
 621             return;
 622         }
 623         synchronized(this) {
 624                 dirtyComponents.remove(aComponent);
 625         }
 626     }
 627 
 628     /**
 629      * Convenience method that returns true if <b>aComponent</b> will be completely
 630      * painted during the next paintDirtyRegions(). If computing dirty regions is
 631      * expensive for your component, use this method and avoid computing dirty region
 632      * if it return true.
 633      */
 634     public boolean isCompletelyDirty(JComponent aComponent) {
 635         RepaintManager delegate = getDelegate(aComponent);
 636         if (delegate != null) {
 637             return delegate.isCompletelyDirty(aComponent);
 638         }
 639         Rectangle r;
 640 
 641         r = getDirtyRegion(aComponent);
 642         if(r.width == Integer.MAX_VALUE &&
 643            r.height == Integer.MAX_VALUE)
 644             return true;
 645         else
 646             return false;
 647     }
 648 
 649 
 650     /**
 651      * Validate all of the components that have been marked invalid.
 652      * @see #addInvalidComponent
 653      */
 654     public void validateInvalidComponents() {
 655         java.util.List<Component> ic;
 656         synchronized(this) {
 657             if(invalidComponents == null) {
 658                 return;
 659             }
 660             ic = invalidComponents;
 661             invalidComponents = null;
 662         }
 663         int n = ic.size();
 664         for(int i = 0; i < n; i++) {
 665             ic.get(i).validate();
 666         }
 667     }
 668 
 669 
 670     /**
 671      * This is invoked to process paint requests.  It's needed
 672      * for backward compatability in so far as RepaintManager would previously
 673      * not see paint requests for top levels, so, we have to make sure
 674      * a subclass correctly paints any dirty top levels.
 675      */
 676     private void prePaintDirtyRegions() {
 677         Map<Component,Rectangle> dirtyComponents;
 678         java.util.List<Runnable> runnableList;
 679         synchronized(this) {
 680             dirtyComponents = this.dirtyComponents;
 681             runnableList = this.runnableList;
 682             this.runnableList = null;
 683         }
 684         if (runnableList != null) {
 685             for (Runnable runnable : runnableList) {
 686                 runnable.run();
 687             }
 688         }
 689         paintDirtyRegions();
 690         if (dirtyComponents.size() > 0) {
 691             // This'll only happen if a subclass isn't correctly dealing
 692             // with toplevels.
 693             paintDirtyRegions(dirtyComponents);
 694         }
 695     }
 696 
 697     private void updateWindows(Map<Component,Rectangle> dirtyComponents) {
 698         Toolkit toolkit = Toolkit.getDefaultToolkit();
 699         if (!(toolkit instanceof SunToolkit &&
 700               ((SunToolkit)toolkit).needUpdateWindow()))
 701         {
 702             return;
 703         }
 704 
 705         Set<Window> windows = new HashSet<Window>();
 706         Set<Component> dirtyComps = dirtyComponents.keySet();
 707         for (Iterator<Component> it = dirtyComps.iterator(); it.hasNext();) {
 708             Component dirty = it.next();
 709             Window window = dirty instanceof Window ?
 710                 (Window)dirty :
 711                 SwingUtilities.getWindowAncestor(dirty);
 712             if (window != null &&
 713                 !window.isOpaque())
 714             {
 715                 windows.add(window);
 716             }
 717         }
 718 
 719         for (Window window : windows) {
 720             AWTAccessor.getWindowAccessor().updateWindow(window);
 721         }
 722     }
 723 
 724     boolean isPainting() {
 725         return painting;
 726     }
 727 
 728     /**
 729      * Paint all of the components that have been marked dirty.
 730      *
 731      * @see #addDirtyRegion
 732      */
 733     public void paintDirtyRegions() {
 734         synchronized(this) {  // swap for thread safety
 735             Map<Component,Rectangle> tmp = tmpDirtyComponents;
 736             tmpDirtyComponents = dirtyComponents;
 737             dirtyComponents = tmp;
 738             dirtyComponents.clear();
 739         }
 740         paintDirtyRegions(tmpDirtyComponents);
 741     }
 742 
 743     private void paintDirtyRegions(Map<Component,Rectangle>
 744                                    tmpDirtyComponents){
 745         int i, count;
 746         java.util.List<Component> roots;
 747         Component dirtyComponent;
 748 
 749         count = tmpDirtyComponents.size();
 750         if (count == 0) {
 751             return;
 752         }
 753 
 754         Rectangle rect;
 755         int localBoundsX = 0;
 756         int localBoundsY = 0;
 757         int localBoundsH;
 758         int localBoundsW;
 759 
 760         roots = new ArrayList<Component>(count);
 761 
 762         for (Component dirty : tmpDirtyComponents.keySet()) {
 763             collectDirtyComponents(tmpDirtyComponents, dirty, roots);
 764         }
 765 
 766         count = roots.size();
 767         painting = true;
 768         try {
 769             for(i=0 ; i < count ; i++) {
 770                 dirtyComponent = roots.get(i);
 771                 rect = tmpDirtyComponents.get(dirtyComponent);
 772                 // Sometimes when RepaintManager is changed during the painting
 773                 // we may get null here, see #6995769 for details
 774                 if (rect == null) {
 775                     continue;
 776                 }
 777                 localBoundsH = dirtyComponent.getHeight();
 778                 localBoundsW = dirtyComponent.getWidth();
 779 
 780                 SwingUtilities.computeIntersection(localBoundsX,
 781                                                    localBoundsY,
 782                                                    localBoundsW,
 783                                                    localBoundsH,
 784                                                    rect);
 785                 if (dirtyComponent instanceof JComponent) {
 786                     ((JComponent)dirtyComponent).paintImmediately(
 787                         rect.x,rect.y,rect.width, rect.height);
 788                 }
 789                 else if (dirtyComponent.isShowing()) {
 790                     Graphics g = JComponent.safelyGetGraphics(
 791                             dirtyComponent, dirtyComponent);
 792                     // If the Graphics goes away, it means someone disposed of
 793                     // the window, don't do anything.
 794                     if (g != null) {
 795                         g.setClip(rect.x, rect.y, rect.width, rect.height);
 796                         try {
 797                             dirtyComponent.paint(g);
 798                         } finally {
 799                             g.dispose();
 800                         }
 801                     }
 802                 }
 803                 // If the repaintRoot has been set, service it now and
 804                 // remove any components that are children of repaintRoot.
 805                 if (repaintRoot != null) {
 806                     adjustRoots(repaintRoot, roots, i + 1);
 807                     count = roots.size();
 808                     paintManager.isRepaintingRoot = true;
 809                     repaintRoot.paintImmediately(0, 0, repaintRoot.getWidth(),
 810                                                  repaintRoot.getHeight());
 811                     paintManager.isRepaintingRoot = false;
 812                     // Only service repaintRoot once.
 813                     repaintRoot = null;
 814                 }
 815             }
 816         } finally {
 817             painting = false;
 818         }
 819 
 820         updateWindows(tmpDirtyComponents);
 821 
 822         tmpDirtyComponents.clear();
 823     }
 824 
 825 
 826     /**
 827      * Removes any components from roots that are children of
 828      * root.
 829      */
 830     private void adjustRoots(JComponent root,
 831                              java.util.List<Component> roots, int index) {
 832         for (int i = roots.size() - 1; i >= index; i--) {
 833             Component c = roots.get(i);
 834             for(;;) {
 835                 if (c == root || c == null || !(c instanceof JComponent)) {
 836                     break;
 837                 }
 838                 c = c.getParent();
 839             }
 840             if (c == root) {
 841                 roots.remove(i);
 842             }
 843         }
 844     }
 845 
 846     Rectangle tmp = new Rectangle();
 847 
 848     void collectDirtyComponents(Map<Component,Rectangle> dirtyComponents,
 849                                 Component dirtyComponent,
 850                                 java.util.List<Component> roots) {
 851         int dx, dy, rootDx, rootDy;
 852         Component component, rootDirtyComponent,parent;
 853         Rectangle cBounds;
 854 
 855         // Find the highest parent which is dirty.  When we get out of this
 856         // rootDx and rootDy will contain the translation from the
 857         // rootDirtyComponent's coordinate system to the coordinates of the
 858         // original dirty component.  The tmp Rect is also used to compute the
 859         // visible portion of the dirtyRect.
 860 
 861         component = rootDirtyComponent = dirtyComponent;
 862 
 863         int x = dirtyComponent.getX();
 864         int y = dirtyComponent.getY();
 865         int w = dirtyComponent.getWidth();
 866         int h = dirtyComponent.getHeight();
 867 
 868         dx = rootDx = 0;
 869         dy = rootDy = 0;
 870         tmp.setBounds(dirtyComponents.get(dirtyComponent));
 871 
 872         // System.out.println("Collect dirty component for bound " + tmp +
 873         //                                   "component bounds is " + cBounds);;
 874         SwingUtilities.computeIntersection(0,0,w,h,tmp);
 875 
 876         if (tmp.isEmpty()) {
 877             // System.out.println("Empty 1");
 878             return;
 879         }
 880 
 881         for(;;) {
 882             if(!(component instanceof JComponent))
 883                 break;
 884 
 885             parent = component.getParent();
 886             if(parent == null)
 887                 break;
 888 
 889             component = parent;
 890 
 891             dx += x;
 892             dy += y;
 893             tmp.setLocation(tmp.x + x, tmp.y + y);
 894 
 895             x = component.getX();
 896             y = component.getY();
 897             w = component.getWidth();
 898             h = component.getHeight();
 899             tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp);
 900 
 901             if (tmp.isEmpty()) {
 902                 // System.out.println("Empty 2");
 903                 return;
 904             }
 905 
 906             if (dirtyComponents.get(component) != null) {
 907                 rootDirtyComponent = component;
 908                 rootDx = dx;
 909                 rootDy = dy;
 910             }
 911         }
 912 
 913         if (dirtyComponent != rootDirtyComponent) {
 914             Rectangle r;
 915             tmp.setLocation(tmp.x + rootDx - dx,
 916                             tmp.y + rootDy - dy);
 917             r = dirtyComponents.get(rootDirtyComponent);
 918             SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
 919         }
 920 
 921         // If we haven't seen this root before, then we need to add it to the
 922         // list of root dirty Views.
 923 
 924         if (!roots.contains(rootDirtyComponent))
 925             roots.add(rootDirtyComponent);
 926     }
 927 
 928 
 929     /**
 930      * Returns a string that displays and identifies this
 931      * object's properties.
 932      *
 933      * @return a String representation of this object
 934      */
 935     public synchronized String toString() {
 936         StringBuffer sb = new StringBuffer();
 937         if(dirtyComponents != null)
 938             sb.append("" + dirtyComponents);
 939         return sb.toString();
 940     }
 941 
 942 
 943    /**
 944      * Return the offscreen buffer that should be used as a double buffer with
 945      * the component <code>c</code>.
 946      * By default there is a double buffer per RepaintManager.
 947      * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
 948      * This happens when the maximum double buffer size as been set for the receiving
 949      * repaint manager.
 950      */
 951     public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
 952         RepaintManager delegate = getDelegate(c);
 953         if (delegate != null) {
 954             return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight);
 955         }
 956         return _getOffscreenBuffer(c, proposedWidth, proposedHeight);
 957     }
 958 
 959   /**
 960    * Return a volatile offscreen buffer that should be used as a
 961    * double buffer with the specified component <code>c</code>.
 962    * The image returned will be an instance of VolatileImage, or null
 963    * if a VolatileImage object could not be instantiated.
 964    * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>.
 965    * This happens when the maximum double buffer size has been set for this
 966    * repaint manager.
 967    *
 968    * @see java.awt.image.VolatileImage
 969    * @since 1.4
 970    */
 971     public Image getVolatileOffscreenBuffer(Component c,
 972                                             int proposedWidth,int proposedHeight) {
 973         RepaintManager delegate = getDelegate(c);
 974         if (delegate != null) {
 975             return delegate.getVolatileOffscreenBuffer(c, proposedWidth,
 976                                                         proposedHeight);
 977         }
 978 
 979         // If the window is non-opaque, it's double-buffered at peer's level
 980         Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
 981         if (!w.isOpaque()) {
 982             Toolkit tk = Toolkit.getDefaultToolkit();
 983             if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
 984                 return null;
 985             }
 986         }
 987 
 988         GraphicsConfiguration config = c.getGraphicsConfiguration();
 989         if (config == null) {
 990             config = GraphicsEnvironment.getLocalGraphicsEnvironment().
 991                             getDefaultScreenDevice().getDefaultConfiguration();
 992         }
 993         Dimension maxSize = getDoubleBufferMaximumSize();
 994         int width = proposedWidth < 1 ? 1 :
 995             (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
 996         int height = proposedHeight < 1 ? 1 :
 997             (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
 998         VolatileImage image = volatileMap.get(config);
 999         if (image == null || image.getWidth() < width ||
1000                              image.getHeight() < height) {
1001             if (image != null) {
1002                 image.flush();
1003             }
1004             image = config.createCompatibleVolatileImage(width, height,
1005                                                          doubleBufferType);
1006             volatileMap.put(config, image);
1007         }
1008         return image;
1009     }
1010 
1011     private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
1012         Dimension maxSize = getDoubleBufferMaximumSize();
1013         DoubleBufferInfo doubleBuffer;
1014         int width, height;
1015 
1016         // If the window is non-opaque, it's double-buffered at peer's level
1017         Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
1018         if (!w.isOpaque()) {
1019             Toolkit tk = Toolkit.getDefaultToolkit();
1020             if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
1021                 return null;
1022             }
1023         }
1024 
1025         if (standardDoubleBuffer == null) {
1026             standardDoubleBuffer = new DoubleBufferInfo();
1027         }
1028         doubleBuffer = standardDoubleBuffer;
1029 
1030         width = proposedWidth < 1? 1 :
1031                   (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
1032         height = proposedHeight < 1? 1 :
1033                   (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
1034 
1035         if (doubleBuffer.needsReset || (doubleBuffer.image != null &&
1036                                         (doubleBuffer.size.width < width ||
1037                                          doubleBuffer.size.height < height))) {
1038             doubleBuffer.needsReset = false;
1039             if (doubleBuffer.image != null) {
1040                 doubleBuffer.image.flush();
1041                 doubleBuffer.image = null;
1042             }
1043             width = Math.max(doubleBuffer.size.width, width);
1044             height = Math.max(doubleBuffer.size.height, height);
1045         }
1046 
1047         Image result = doubleBuffer.image;
1048 
1049         if (doubleBuffer.image == null) {
1050             result = c.createImage(width , height);
1051             doubleBuffer.size = new Dimension(width, height);
1052             if (c instanceof JComponent) {
1053                 ((JComponent)c).setCreatedDoubleBuffer(true);
1054                 doubleBuffer.image = result;
1055             }
1056             // JComponent will inform us when it is no longer valid
1057             // (via removeNotify) we have no such hook to other components,
1058             // therefore we don't keep a ref to the Component
1059             // (indirectly through the Image) by stashing the image.
1060         }
1061         return result;
1062     }
1063 
1064 
1065     /** Set the maximum double buffer size. **/
1066     public void setDoubleBufferMaximumSize(Dimension d) {
1067         doubleBufferMaxSize = d;
1068         if (doubleBufferMaxSize == null) {
1069             clearImages();
1070         } else {
1071             clearImages(d.width, d.height);
1072         }
1073     }
1074 
1075     private void clearImages() {
1076         clearImages(0, 0);
1077     }
1078 
1079     private void clearImages(int width, int height) {
1080         if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) {
1081             if (standardDoubleBuffer.image.getWidth(null) > width ||
1082                 standardDoubleBuffer.image.getHeight(null) > height) {
1083                 standardDoubleBuffer.image.flush();
1084                 standardDoubleBuffer.image = null;
1085             }
1086         }
1087         // Clear out the VolatileImages
1088         Iterator<GraphicsConfiguration> gcs = volatileMap.keySet().iterator();
1089         while (gcs.hasNext()) {
1090             GraphicsConfiguration gc = gcs.next();
1091             VolatileImage image = volatileMap.get(gc);
1092             if (image.getWidth() > width || image.getHeight() > height) {
1093                 image.flush();
1094                 gcs.remove();
1095             }
1096         }
1097     }
1098 
1099     /**
1100      * Returns the maximum double buffer size.
1101      *
1102      * @return a Dimension object representing the maximum size
1103      */
1104     public Dimension getDoubleBufferMaximumSize() {
1105         if (doubleBufferMaxSize == null) {
1106             try {
1107                 Rectangle virtualBounds = new Rectangle();
1108                 GraphicsEnvironment ge = GraphicsEnvironment.
1109                                                  getLocalGraphicsEnvironment();
1110                 for (GraphicsDevice gd : ge.getScreenDevices()) {
1111                     GraphicsConfiguration gc = gd.getDefaultConfiguration();
1112                     virtualBounds = virtualBounds.union(gc.getBounds());
1113                 }
1114                 doubleBufferMaxSize = new Dimension(virtualBounds.width,
1115                                                     virtualBounds.height);
1116             } catch (HeadlessException e) {
1117                 doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1118             }
1119         }
1120         return doubleBufferMaxSize;
1121     }
1122 
1123     /**
1124      * Enables or disables double buffering in this RepaintManager.
1125      * CAUTION: The default value for this property is set for optimal
1126      * paint performance on the given platform and it is not recommended
1127      * that programs modify this property directly.
1128      *
1129      * @param aFlag  true to activate double buffering
1130      * @see #isDoubleBufferingEnabled
1131      */
1132     public void setDoubleBufferingEnabled(boolean aFlag) {
1133         doubleBufferingEnabled = aFlag;
1134         PaintManager paintManager = getPaintManager();
1135         if (!aFlag && paintManager.getClass() != PaintManager.class) {
1136             setPaintManager(new PaintManager());
1137         }
1138     }
1139 
1140     /**
1141      * Returns true if this RepaintManager is double buffered.
1142      * The default value for this property may vary from platform
1143      * to platform.  On platforms where native double buffering
1144      * is supported in the AWT, the default value will be <code>false</code>
1145      * to avoid unnecessary buffering in Swing.
1146      * On platforms where native double buffering is not supported,
1147      * the default value will be <code>true</code>.
1148      *
1149      * @return true if this object is double buffered
1150      */
1151     public boolean isDoubleBufferingEnabled() {
1152         return doubleBufferingEnabled;
1153     }
1154 
1155     /**
1156      * This resets the double buffer. Actually, it marks the double buffer
1157      * as invalid, the double buffer will then be recreated on the next
1158      * invocation of getOffscreenBuffer.
1159      */
1160     void resetDoubleBuffer() {
1161         if (standardDoubleBuffer != null) {
1162             standardDoubleBuffer.needsReset = true;
1163         }
1164     }
1165 
1166     /**
1167      * This resets the volatile double buffer.
1168      */
1169     void resetVolatileDoubleBuffer(GraphicsConfiguration gc) {
1170         Image image = volatileMap.remove(gc);
1171         if (image != null) {
1172             image.flush();
1173         }
1174     }
1175 
1176     /**
1177      * Returns true if we should use the <code>Image</code> returned
1178      * from <code>getVolatileOffscreenBuffer</code> to do double buffering.
1179      */
1180     boolean useVolatileDoubleBuffer() {
1181         return volatileImageBufferEnabled;
1182     }
1183 
1184     /**
1185      * Returns true if the current thread is the thread painting.  This
1186      * will return false if no threads are painting.
1187      */
1188     private synchronized boolean isPaintingThread() {
1189         return (Thread.currentThread() == paintThread);
1190     }
1191     //
1192     // Paint methods.  You very, VERY rarely need to invoke these.
1193     // They are invoked directly from JComponent's painting code and
1194     // when painting happens outside the normal flow: DefaultDesktopManager
1195     // and JViewport.  If you end up needing these methods in other places be
1196     // careful that you don't get stuck in a paint loop.
1197     //
1198 
1199     /**
1200      * Paints a region of a component
1201      *
1202      * @param paintingComponent Component to paint
1203      * @param bufferComponent Component to obtain buffer for
1204      * @param g Graphics to paint to
1205      * @param x X-coordinate
1206      * @param y Y-coordinate
1207      * @param w Width
1208      * @param h Height
1209      */
1210     void paint(JComponent paintingComponent,
1211                JComponent bufferComponent, Graphics g,
1212                int x, int y, int w, int h) {
1213         PaintManager paintManager = getPaintManager();
1214         if (!isPaintingThread()) {
1215             // We're painting to two threads at once.  PaintManager deals
1216             // with this a bit better than BufferStrategyPaintManager, use
1217             // it to avoid possible exceptions/corruption.
1218             if (paintManager.getClass() != PaintManager.class) {
1219                 paintManager = new PaintManager();
1220                 paintManager.repaintManager = this;
1221             }
1222         }
1223         if (!paintManager.paint(paintingComponent, bufferComponent, g,
1224                                 x, y, w, h)) {
1225             g.setClip(x, y, w, h);
1226             paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h);
1227         }
1228     }
1229 
1230     /**
1231      * Does a copy area on the specified region.
1232      *
1233      * @param clip Whether or not the copyArea needs to be clipped to the
1234      *             Component's bounds.
1235      */
1236     void copyArea(JComponent c, Graphics g, int x, int y, int w, int h,
1237                   int deltaX, int deltaY, boolean clip) {
1238         getPaintManager().copyArea(c, g, x, y, w, h, deltaX, deltaY, clip);
1239     }
1240 
1241     /**
1242      * Invoked prior to any paint/copyArea method calls.  This will
1243      * be followed by an invocation of <code>endPaint</code>.
1244      * <b>WARNING</b>: Callers of this method need to wrap the call
1245      * in a <code>try/finally</code>, otherwise if an exception is thrown
1246      * during the course of painting the RepaintManager may
1247      * be left in a state in which the screen is not updated, eg:
1248      * <pre>
1249      * repaintManager.beginPaint();
1250      * try {
1251      *   repaintManager.paint(...);
1252      * } finally {
1253      *   repaintManager.endPaint();
1254      * }
1255      * </pre>
1256      */
1257     void beginPaint() {
1258         boolean multiThreadedPaint = false;
1259         int paintDepth;
1260         Thread currentThread = Thread.currentThread();
1261         synchronized(this) {
1262             paintDepth = this.paintDepth;
1263             if (paintThread == null || currentThread == paintThread) {
1264                 paintThread = currentThread;
1265                 this.paintDepth++;
1266             } else {
1267                 multiThreadedPaint = true;
1268             }
1269         }
1270         if (!multiThreadedPaint && paintDepth == 0) {
1271             getPaintManager().beginPaint();
1272         }
1273     }
1274 
1275     /**
1276      * Invoked after <code>beginPaint</code> has been invoked.
1277      */
1278     void endPaint() {
1279         if (isPaintingThread()) {
1280             PaintManager paintManager = null;
1281             synchronized(this) {
1282                 if (--paintDepth == 0) {
1283                     paintManager = getPaintManager();
1284                 }
1285             }
1286             if (paintManager != null) {
1287                 paintManager.endPaint();
1288                 synchronized(this) {
1289                     paintThread = null;
1290                 }
1291             }
1292         }
1293     }
1294 
1295     /**
1296      * If possible this will show a previously rendered portion of
1297      * a Component.  If successful, this will return true, otherwise false.
1298      * <p>
1299      * WARNING: This method is invoked from the native toolkit thread, be
1300      * very careful as to what methods this invokes!
1301      */
1302     boolean show(Container c, int x, int y, int w, int h) {
1303         return getPaintManager().show(c, x, y, w, h);
1304     }
1305 
1306     /**
1307      * Invoked when the doubleBuffered or useTrueDoubleBuffering
1308      * properties of a JRootPane change.  This may come in on any thread.
1309      */
1310     void doubleBufferingChanged(JRootPane rootPane) {
1311         getPaintManager().doubleBufferingChanged(rootPane);
1312     }
1313 
1314     /**
1315      * Sets the <code>PaintManager</code> that is used to handle all
1316      * double buffered painting.
1317      *
1318      * @param paintManager The PaintManager to use.  Passing in null indicates
1319      *        the fallback PaintManager should be used.
1320      */
1321     void setPaintManager(PaintManager paintManager) {
1322         if (paintManager == null) {
1323             paintManager = new PaintManager();
1324         }
1325         PaintManager oldPaintManager;
1326         synchronized(this) {
1327             oldPaintManager = this.paintManager;
1328             this.paintManager = paintManager;
1329             paintManager.repaintManager = this;
1330         }
1331         if (oldPaintManager != null) {
1332             oldPaintManager.dispose();
1333         }
1334     }
1335 
1336     private synchronized PaintManager getPaintManager() {
1337         if (paintManager == null) {
1338             PaintManager paintManager = null;
1339             if (doubleBufferingEnabled && !nativeDoubleBuffering) {
1340                 switch (bufferStrategyType) {
1341                 case BUFFER_STRATEGY_NOT_SPECIFIED:
1342                     Toolkit tk = Toolkit.getDefaultToolkit();
1343                     if (tk instanceof SunToolkit) {
1344                         SunToolkit stk = (SunToolkit) tk;
1345                         if (stk.useBufferPerWindow()) {
1346                             paintManager = new BufferStrategyPaintManager();
1347                         }
1348                     }
1349                     break;
1350                 case BUFFER_STRATEGY_SPECIFIED_ON:
1351                     paintManager = new BufferStrategyPaintManager();
1352                     break;
1353                 default:
1354                     break;
1355                 }
1356             }
1357             // null case handled in setPaintManager
1358             setPaintManager(paintManager);
1359         }
1360         return paintManager;
1361     }
1362 
1363     private void scheduleProcessingRunnable() {
1364         scheduleProcessingRunnable(AppContext.getAppContext());
1365     }
1366 
1367     private void scheduleProcessingRunnable(AppContext context) {
1368         if (processingRunnable.markPending()) {
1369             Toolkit tk = Toolkit.getDefaultToolkit();
1370             if (tk instanceof SunToolkit) {
1371                 SunToolkit.getSystemEventQueueImplPP(context).
1372                   postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
1373                                                 processingRunnable));
1374             } else {
1375                 Toolkit.getDefaultToolkit().getSystemEventQueue().
1376                       postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
1377                                                     processingRunnable));
1378             }
1379         }
1380     }
1381 
1382 
1383     /**
1384      * PaintManager is used to handle all double buffered painting for
1385      * Swing.  Subclasses should call back into the JComponent method
1386      * <code>paintToOffscreen</code> to handle the actual painting.
1387      */
1388     static class PaintManager {
1389         /**
1390          * RepaintManager the PaintManager has been installed on.
1391          */
1392         protected RepaintManager repaintManager;
1393         boolean isRepaintingRoot;
1394 
1395         /**
1396          * Paints a region of a component
1397          *
1398          * @param paintingComponent Component to paint
1399          * @param bufferComponent Component to obtain buffer for
1400          * @param g Graphics to paint to
1401          * @param x X-coordinate
1402          * @param y Y-coordinate
1403          * @param w Width
1404          * @param h Height
1405          * @return true if painting was successful.
1406          */
1407         public boolean paint(JComponent paintingComponent,
1408                              JComponent bufferComponent, Graphics g,
1409                              int x, int y, int w, int h) {
1410             // First attempt to use VolatileImage buffer for performance.
1411             // If this fails (which should rarely occur), fallback to a
1412             // standard Image buffer.
1413             boolean paintCompleted = false;
1414             Image offscreen;
1415             if (repaintManager.useVolatileDoubleBuffer() &&
1416                 (offscreen = getValidImage(repaintManager.
1417                 getVolatileOffscreenBuffer(bufferComponent, w, h))) != null) {
1418                 VolatileImage vImage = (java.awt.image.VolatileImage)offscreen;
1419                 GraphicsConfiguration gc = bufferComponent.
1420                                             getGraphicsConfiguration();
1421                 for (int i = 0; !paintCompleted &&
1422                          i < RepaintManager.VOLATILE_LOOP_MAX; i++) {
1423                     if (vImage.validate(gc) ==
1424                                    VolatileImage.IMAGE_INCOMPATIBLE) {
1425                         repaintManager.resetVolatileDoubleBuffer(gc);
1426                         offscreen = repaintManager.getVolatileOffscreenBuffer(
1427                             bufferComponent,w, h);
1428                         vImage = (java.awt.image.VolatileImage)offscreen;
1429                     }
1430                     paintDoubleBuffered(paintingComponent, vImage, g, x, y,
1431                                         w, h);
1432                     paintCompleted = !vImage.contentsLost();
1433                 }
1434             }
1435             // VolatileImage painting loop failed, fallback to regular
1436             // offscreen buffer
1437             if (!paintCompleted && (offscreen = getValidImage(
1438                       repaintManager.getOffscreenBuffer(
1439                       bufferComponent, w, h))) != null) {
1440                 paintDoubleBuffered(paintingComponent, offscreen, g, x, y, w,
1441                                     h);
1442                 paintCompleted = true;
1443             }
1444             return paintCompleted;
1445         }
1446 
1447         /**
1448          * Does a copy area on the specified region.
1449          */
1450         public void copyArea(JComponent c, Graphics g, int x, int y, int w,
1451                              int h, int deltaX, int deltaY, boolean clip) {
1452             g.copyArea(x, y, w, h, deltaX, deltaY);
1453         }
1454 
1455         /**
1456          * Invoked prior to any calls to paint or copyArea.
1457          */
1458         public void beginPaint() {
1459         }
1460 
1461         /**
1462          * Invoked to indicate painting has been completed.
1463          */
1464         public void endPaint() {
1465         }
1466 
1467         /**
1468          * Shows a region of a previously rendered component.  This
1469          * will return true if successful, false otherwise.  The default
1470          * implementation returns false.
1471          */
1472         public boolean show(Container c, int x, int y, int w, int h) {
1473             return false;
1474         }
1475 
1476         /**
1477          * Invoked when the doubleBuffered or useTrueDoubleBuffering
1478          * properties of a JRootPane change.  This may come in on any thread.
1479          */
1480         public void doubleBufferingChanged(JRootPane rootPane) {
1481         }
1482 
1483         /**
1484          * Paints a portion of a component to an offscreen buffer.
1485          */
1486         protected void paintDoubleBuffered(JComponent c, Image image,
1487                             Graphics g, int clipX, int clipY,
1488                             int clipW, int clipH) {
1489             Graphics osg = image.getGraphics();
1490             int bw = Math.min(clipW, image.getWidth(null));
1491             int bh = Math.min(clipH, image.getHeight(null));
1492             int x,y,maxx,maxy;
1493 
1494             try {
1495                 for(x = clipX, maxx = clipX+clipW; x < maxx ;  x += bw ) {
1496                     for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) {
1497                         osg.translate(-x, -y);
1498                         osg.setClip(x,y,bw,bh);
1499                         if (doubleBufferType != Transparency.OPAQUE
1500                                 && osg instanceof Graphics2D) {
1501                             Graphics2D g2d = (Graphics2D) osg.create();
1502                             g2d.setBackground(c.getBackground());
1503                             g2d.clearRect(x, y, bw, bh);
1504                             g2d.dispose();
1505                         }
1506                         c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy);
1507                         g.setClip(x, y, bw, bh);
1508                         if (doubleBufferType != Transparency.OPAQUE
1509                                 && osg instanceof Graphics2D) {
1510                             Graphics2D g2d = (Graphics2D) g.create();
1511                             g2d.setComposite(AlphaComposite.Src);
1512                             g2d.drawImage(image, x, y, c);
1513                             g2d.dispose();
1514                         } else {
1515                             g.drawImage(image, x, y, c);
1516                         }
1517                         osg.translate(x, y);
1518                     }
1519                 }
1520             } finally {
1521                 osg.dispose();
1522             }
1523         }
1524 
1525         /**
1526          * If <code>image</code> is non-null with a positive size it
1527          * is returned, otherwise null is returned.
1528          */
1529         private Image getValidImage(Image image) {
1530             if (image != null && image.getWidth(null) > 0 &&
1531                                  image.getHeight(null) > 0) {
1532                 return image;
1533             }
1534             return null;
1535         }
1536 
1537         /**
1538          * Schedules a repaint for the specified component.  This differs
1539          * from <code>root.repaint</code> in that if the RepaintManager is
1540          * currently processing paint requests it'll process this request
1541          * with the current set of requests.
1542          */
1543         protected void repaintRoot(JComponent root) {
1544             assert (repaintManager.repaintRoot == null);
1545             if (repaintManager.painting) {
1546                 repaintManager.repaintRoot = root;
1547             }
1548             else {
1549                 root.repaint();
1550             }
1551         }
1552 
1553         /**
1554          * Returns true if the component being painted is the root component
1555          * that was previously passed to <code>repaintRoot</code>.
1556          */
1557         protected boolean isRepaintingRoot() {
1558             return isRepaintingRoot;
1559         }
1560 
1561         /**
1562          * Cleans up any state.  After invoked the PaintManager will no
1563          * longer be used anymore.
1564          */
1565         protected void dispose() {
1566         }
1567     }
1568 
1569 
1570     private class DoubleBufferInfo {
1571         public Image image;
1572         public Dimension size;
1573         public boolean needsReset = false;
1574     }
1575 
1576 
1577     /**
1578      * Listener installed to detect display changes. When display changes,
1579      * schedules a callback to notify all RepaintManagers of the display
1580      * changes. Only one DisplayChangedHandler is ever installed. The
1581      * singleton instance will schedule notification for all AppContexts.
1582      */
1583     private static final class DisplayChangedHandler implements
1584                                              DisplayChangedListener {
1585         public void displayChanged() {
1586             scheduleDisplayChanges();
1587         }
1588 
1589         public void paletteChanged() {
1590         }
1591 
1592         private void scheduleDisplayChanges() {
1593             // To avoid threading problems, we notify each RepaintManager
1594             // on the thread it was created on.
1595             for (Object c : AppContext.getAppContexts()) {
1596                 AppContext context = (AppContext) c;
1597                 synchronized(context) {
1598                     if (!context.isDisposed()) {
1599                         EventQueue eventQueue = (EventQueue)context.get(
1600                             AppContext.EVENT_QUEUE_KEY);
1601                         if (eventQueue != null) {
1602                             eventQueue.postEvent(new InvocationEvent(
1603                                 Toolkit.getDefaultToolkit(),
1604                                 new DisplayChangedRunnable()));
1605                         }
1606                     }
1607                 }
1608             }
1609         }
1610     }
1611 
1612 
1613     private static final class DisplayChangedRunnable implements Runnable {
1614         public void run() {
1615             RepaintManager.currentManager((JComponent)null).displayChanged();
1616         }
1617     }
1618 
1619 
1620     /**
1621      * Runnable used to process all repaint/revalidate requests.
1622      */
1623     private final class ProcessingRunnable implements Runnable {
1624         // If true, we're wainting on the EventQueue.
1625         private boolean pending;
1626 
1627         /**
1628          * Marks this processing runnable as pending. If this was not
1629          * already marked as pending, true is returned.
1630          */
1631         public synchronized boolean markPending() {
1632             if (!pending) {
1633                 pending = true;
1634                 return true;
1635             }
1636             return false;
1637         }
1638 
1639         public void run() {
1640             synchronized (this) {
1641                 pending = false;
1642             }
1643             // First pass, flush any heavy paint events into real paint
1644             // events.  If there are pending heavy weight requests this will
1645             // result in q'ing this request up one more time.  As
1646             // long as no other requests come in between now and the time
1647             // the second one is processed nothing will happen.  This is not
1648             // ideal, but the logic needed to suppress the second request is
1649             // more headache than it's worth.
1650             scheduleHeavyWeightPaints();
1651             // Do the actual validation and painting.
1652             validateInvalidComponents();
1653             prePaintDirtyRegions();
1654         }
1655     }
1656     private RepaintManager getDelegate(Component c) {
1657         RepaintManager delegate = SwingUtilities3.getDelegateRepaintManager(c);
1658         if (this == delegate) {
1659             delegate = null;
1660         }
1661         return delegate;
1662     }
1663 }