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