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

Save This Page
Home » openjdk-7 » javax » swing » [javadoc | source]