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