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

    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           Enumeration keys;
  748   
  749           roots = new ArrayList<Component>(count);
  750   
  751           for (Component dirty : tmpDirtyComponents.keySet()) {
  752               collectDirtyComponents(tmpDirtyComponents, dirty, roots);
  753           }
  754   
  755           count = roots.size();
  756           painting = true;
  757           try {
  758               for(i=0 ; i < count ; i++) {
  759                   dirtyComponent = roots.get(i);
  760                   rect = tmpDirtyComponents.get(dirtyComponent);
  761                   localBoundsH = dirtyComponent.getHeight();
  762                   localBoundsW = dirtyComponent.getWidth();
  763   
  764                   SwingUtilities.computeIntersection(localBoundsX,
  765                                                      localBoundsY,
  766                                                      localBoundsW,
  767                                                      localBoundsH,
  768                                                      rect);
  769                   if (dirtyComponent instanceof JComponent) {
  770                       ((JComponent)dirtyComponent).paintImmediately(
  771                           rect.x,rect.y,rect.width, rect.height);
  772                   }
  773                   else if (dirtyComponent.isShowing()) {
  774                       Graphics g = JComponent.safelyGetGraphics(
  775                               dirtyComponent, dirtyComponent);
  776                       // If the Graphics goes away, it means someone disposed of
  777                       // the window, don't do anything.
  778                       if (g != null) {
  779                           g.setClip(rect.x, rect.y, rect.width, rect.height);
  780                           try {
  781                               dirtyComponent.paint(g);
  782                           } finally {
  783                               g.dispose();
  784                           }
  785                       }
  786                   }
  787                   // If the repaintRoot has been set, service it now and
  788                   // remove any components that are children of repaintRoot.
  789                   if (repaintRoot != null) {
  790                       adjustRoots(repaintRoot, roots, i + 1);
  791                       count = roots.size();
  792                       paintManager.isRepaintingRoot = true;
  793                       repaintRoot.paintImmediately(0, 0, repaintRoot.getWidth(),
  794                                                    repaintRoot.getHeight());
  795                       paintManager.isRepaintingRoot = false;
  796                       // Only service repaintRoot once.
  797                       repaintRoot = null;
  798                   }
  799               }
  800           } finally {
  801               painting = false;
  802           }
  803   
  804           updateWindows(tmpDirtyComponents);
  805   
  806           tmpDirtyComponents.clear();
  807       }
  808   
  809   
  810       /**
  811        * Removes any components from roots that are children of
  812        * root.
  813        */
  814       private void adjustRoots(JComponent root,
  815                                java.util.List<Component> roots, int index) {
  816           for (int i = roots.size() - 1; i >= index; i--) {
  817               Component c = roots.get(i);
  818               for(;;) {
  819                   if (c == root || c == null || !(c instanceof JComponent)) {
  820                       break;
  821                   }
  822                   c = c.getParent();
  823               }
  824               if (c == root) {
  825                   roots.remove(i);
  826               }
  827           }
  828       }
  829   
  830       Rectangle tmp = new Rectangle();
  831   
  832       void collectDirtyComponents(Map<Component,Rectangle> dirtyComponents,
  833                                   Component dirtyComponent,
  834                                   java.util.List<Component> roots) {
  835           int dx, dy, rootDx, rootDy;
  836           Component component, rootDirtyComponent,parent;
  837           Rectangle cBounds;
  838   
  839           // Find the highest parent which is dirty.  When we get out of this
  840           // rootDx and rootDy will contain the translation from the
  841           // rootDirtyComponent's coordinate system to the coordinates of the
  842           // original dirty component.  The tmp Rect is also used to compute the
  843           // visible portion of the dirtyRect.
  844   
  845           component = rootDirtyComponent = dirtyComponent;
  846   
  847           int x = dirtyComponent.getX();
  848           int y = dirtyComponent.getY();
  849           int w = dirtyComponent.getWidth();
  850           int h = dirtyComponent.getHeight();
  851   
  852           dx = rootDx = 0;
  853           dy = rootDy = 0;
  854           tmp.setBounds(dirtyComponents.get(dirtyComponent));
  855   
  856           // System.out.println("Collect dirty component for bound " + tmp +
  857           //                                   "component bounds is " + cBounds);;
  858           SwingUtilities.computeIntersection(0,0,w,h,tmp);
  859   
  860           if (tmp.isEmpty()) {
  861               // System.out.println("Empty 1");
  862               return;
  863           }
  864   
  865           for(;;) {
  866               if(!(component instanceof JComponent))
  867                   break;
  868   
  869               parent = component.getParent();
  870               if(parent == null)
  871                   break;
  872   
  873               component = parent;
  874   
  875               dx += x;
  876               dy += y;
  877               tmp.setLocation(tmp.x + x, tmp.y + y);
  878   
  879               x = component.getX();
  880               y = component.getY();
  881               w = component.getWidth();
  882               h = component.getHeight();
  883               tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp);
  884   
  885               if (tmp.isEmpty()) {
  886                   // System.out.println("Empty 2");
  887                   return;
  888               }
  889   
  890               if (dirtyComponents.get(component) != null) {
  891                   rootDirtyComponent = component;
  892                   rootDx = dx;
  893                   rootDy = dy;
  894               }
  895           }
  896   
  897           if (dirtyComponent != rootDirtyComponent) {
  898               Rectangle r;
  899               tmp.setLocation(tmp.x + rootDx - dx,
  900                               tmp.y + rootDy - dy);
  901               r = dirtyComponents.get(rootDirtyComponent);
  902               SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
  903           }
  904   
  905           // If we haven't seen this root before, then we need to add it to the
  906           // list of root dirty Views.
  907   
  908           if (!roots.contains(rootDirtyComponent))
  909               roots.add(rootDirtyComponent);
  910       }
  911   
  912   
  913       /**
  914        * Returns a string that displays and identifies this
  915        * object's properties.
  916        *
  917        * @return a String representation of this object
  918        */
  919       public synchronized String toString() {
  920           StringBuffer sb = new StringBuffer();
  921           if(dirtyComponents != null)
  922               sb.append("" + dirtyComponents);
  923           return sb.toString();
  924       }
  925   
  926   
  927      /**
  928        * Return the offscreen buffer that should be used as a double buffer with
  929        * the component <code>c</code>.
  930        * By default there is a double buffer per RepaintManager.
  931        * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
  932        * This happens when the maximum double buffer size as been set for the receiving
  933        * repaint manager.
  934        */
  935       public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
  936           RepaintManager delegate = getDelegate(c);
  937           if (delegate != null) {
  938               return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight);
  939           }
  940           return _getOffscreenBuffer(c, proposedWidth, proposedHeight);
  941       }
  942   
  943     /**
  944      * Return a volatile offscreen buffer that should be used as a
  945      * double buffer with the specified component <code>c</code>.
  946      * The image returned will be an instance of VolatileImage, or null
  947      * if a VolatileImage object could not be instantiated.
  948      * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>.
  949      * This happens when the maximum double buffer size has been set for this
  950      * repaint manager.
  951      *
  952      * @see java.awt.image.VolatileImage
  953      * @since 1.4
  954      */
  955       public Image getVolatileOffscreenBuffer(Component c,
  956                                               int proposedWidth,int proposedHeight) {
  957           RepaintManager delegate = getDelegate(c);
  958           if (delegate != null) {
  959               return delegate.getVolatileOffscreenBuffer(c, proposedWidth,
  960                                                           proposedHeight);
  961           }
  962   
  963           // If the window is non-opaque, it's double-buffered at peer's level
  964           Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
  965           if (!w.isOpaque()) {
  966               Toolkit tk = Toolkit.getDefaultToolkit();
  967               if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
  968                   return null;
  969               }
  970           }
  971   
  972           GraphicsConfiguration config = c.getGraphicsConfiguration();
  973           if (config == null) {
  974               config = GraphicsEnvironment.getLocalGraphicsEnvironment().
  975                               getDefaultScreenDevice().getDefaultConfiguration();
  976           }
  977           Dimension maxSize = getDoubleBufferMaximumSize();
  978           int width = proposedWidth < 1 ? 1 :
  979               (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
  980           int height = proposedHeight < 1 ? 1 :
  981               (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
  982           VolatileImage image = volatileMap.get(config);
  983           if (image == null || image.getWidth() < width ||
  984                                image.getHeight() < height) {
  985               if (image != null) {
  986                   image.flush();
  987               }
  988               image = config.createCompatibleVolatileImage(width, height);
  989               volatileMap.put(config, image);
  990           }
  991           return image;
  992       }
  993   
  994       private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
  995           Dimension maxSize = getDoubleBufferMaximumSize();
  996           DoubleBufferInfo doubleBuffer;
  997           int width, height;
  998   
  999           // If the window is non-opaque, it's double-buffered at peer's level
 1000           Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
 1001           if (!w.isOpaque()) {
 1002               Toolkit tk = Toolkit.getDefaultToolkit();
 1003               if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
 1004                   return null;
 1005               }
 1006           }
 1007   
 1008           if (standardDoubleBuffer == null) {
 1009               standardDoubleBuffer = new DoubleBufferInfo();
 1010           }
 1011           doubleBuffer = standardDoubleBuffer;
 1012   
 1013           width = proposedWidth < 1? 1 :
 1014                     (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
 1015           height = proposedHeight < 1? 1 :
 1016                     (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
 1017   
 1018           if (doubleBuffer.needsReset || (doubleBuffer.image != null &&
 1019                                           (doubleBuffer.size.width < width ||
 1020                                            doubleBuffer.size.height < height))) {
 1021               doubleBuffer.needsReset = false;
 1022               if (doubleBuffer.image != null) {
 1023                   doubleBuffer.image.flush();
 1024                   doubleBuffer.image = null;
 1025               }
 1026               width = Math.max(doubleBuffer.size.width, width);
 1027               height = Math.max(doubleBuffer.size.height, height);
 1028           }
 1029   
 1030           Image result = doubleBuffer.image;
 1031   
 1032           if (doubleBuffer.image == null) {
 1033               result = c.createImage(width , height);
 1034               doubleBuffer.size = new Dimension(width, height);
 1035               if (c instanceof JComponent) {
 1036                   ((JComponent)c).setCreatedDoubleBuffer(true);
 1037                   doubleBuffer.image = result;
 1038               }
 1039               // JComponent will inform us when it is no longer valid
 1040               // (via removeNotify) we have no such hook to other components,
 1041               // therefore we don't keep a ref to the Component
 1042               // (indirectly through the Image) by stashing the image.
 1043           }
 1044           return result;
 1045       }
 1046   
 1047   
 1048       /** Set the maximum double buffer size. **/
 1049       public void setDoubleBufferMaximumSize(Dimension d) {
 1050           doubleBufferMaxSize = d;
 1051           if (doubleBufferMaxSize == null) {
 1052               clearImages();
 1053           } else {
 1054               clearImages(d.width, d.height);
 1055           }
 1056       }
 1057   
 1058       private void clearImages() {
 1059           clearImages(0, 0);
 1060       }
 1061   
 1062       private void clearImages(int width, int height) {
 1063           if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) {
 1064               if (standardDoubleBuffer.image.getWidth(null) > width ||
 1065                   standardDoubleBuffer.image.getHeight(null) > height) {
 1066                   standardDoubleBuffer.image.flush();
 1067                   standardDoubleBuffer.image = null;
 1068               }
 1069           }
 1070           // Clear out the VolatileImages
 1071           Iterator gcs = volatileMap.keySet().iterator();
 1072           while (gcs.hasNext()) {
 1073               GraphicsConfiguration gc = (GraphicsConfiguration)gcs.next();
 1074               VolatileImage image = volatileMap.get(gc);
 1075               if (image.getWidth() > width || image.getHeight() > height) {
 1076                   image.flush();
 1077                   gcs.remove();
 1078               }
 1079           }
 1080       }
 1081   
 1082       /**
 1083        * Returns the maximum double buffer size.
 1084        *
 1085        * @return a Dimension object representing the maximum size
 1086        */
 1087       public Dimension getDoubleBufferMaximumSize() {
 1088           if (doubleBufferMaxSize == null) {
 1089               try {
 1090                   Rectangle virtualBounds = new Rectangle();
 1091                   GraphicsEnvironment ge = GraphicsEnvironment.
 1092                                                    getLocalGraphicsEnvironment();
 1093                   for (GraphicsDevice gd : ge.getScreenDevices()) {
 1094                       GraphicsConfiguration gc = gd.getDefaultConfiguration();
 1095                       virtualBounds = virtualBounds.union(gc.getBounds());
 1096                   }
 1097                   doubleBufferMaxSize = new Dimension(virtualBounds.width,
 1098                                                       virtualBounds.height);
 1099               } catch (HeadlessException e) {
 1100                   doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
 1101               }
 1102           }
 1103           return doubleBufferMaxSize;
 1104       }
 1105   
 1106       /**
 1107        * Enables or disables double buffering in this RepaintManager.
 1108        * CAUTION: The default value for this property is set for optimal
 1109        * paint performance on the given platform and it is not recommended
 1110        * that programs modify this property directly.
 1111        *
 1112        * @param aFlag  true to activate double buffering
 1113        * @see #isDoubleBufferingEnabled
 1114        */
 1115       public void setDoubleBufferingEnabled(boolean aFlag) {
 1116           doubleBufferingEnabled = aFlag;
 1117           PaintManager paintManager = getPaintManager();
 1118           if (!aFlag && paintManager.getClass() != PaintManager.class) {
 1119               setPaintManager(new PaintManager());
 1120           }
 1121       }
 1122   
 1123       /**
 1124        * Returns true if this RepaintManager is double buffered.
 1125        * The default value for this property may vary from platform
 1126        * to platform.  On platforms where native double buffering
 1127        * is supported in the AWT, the default value will be <code>false</code>
 1128        * to avoid unnecessary buffering in Swing.
 1129        * On platforms where native double buffering is not supported,
 1130        * the default value will be <code>true</code>.
 1131        *
 1132        * @return true if this object is double buffered
 1133        */
 1134       public boolean isDoubleBufferingEnabled() {
 1135           return doubleBufferingEnabled;
 1136       }
 1137   
 1138       /**
 1139        * This resets the double buffer. Actually, it marks the double buffer
 1140        * as invalid, the double buffer will then be recreated on the next
 1141        * invocation of getOffscreenBuffer.
 1142        */
 1143       void resetDoubleBuffer() {
 1144           if (standardDoubleBuffer != null) {
 1145               standardDoubleBuffer.needsReset = true;
 1146           }
 1147       }
 1148   
 1149       /**
 1150        * This resets the volatile double buffer.
 1151        */
 1152       void resetVolatileDoubleBuffer(GraphicsConfiguration gc) {
 1153           Image image = volatileMap.remove(gc);
 1154           if (image != null) {
 1155               image.flush();
 1156           }
 1157       }
 1158   
 1159       /**
 1160        * Returns true if we should use the <code>Image</code> returned
 1161        * from <code>getVolatileOffscreenBuffer</code> to do double buffering.
 1162        */
 1163       boolean useVolatileDoubleBuffer() {
 1164           return volatileImageBufferEnabled;
 1165       }
 1166   
 1167       /**
 1168        * Returns true if the current thread is the thread painting.  This
 1169        * will return false if no threads are painting.
 1170        */
 1171       private synchronized boolean isPaintingThread() {
 1172           return (Thread.currentThread() == paintThread);
 1173       }
 1174       //
 1175       // Paint methods.  You very, VERY rarely need to invoke these.
 1176       // They are invoked directly from JComponent's painting code and
 1177       // when painting happens outside the normal flow: DefaultDesktopManager
 1178       // and JViewport.  If you end up needing these methods in other places be
 1179       // careful that you don't get stuck in a paint loop.
 1180       //
 1181   
 1182       /**
 1183        * Paints a region of a component
 1184        *
 1185        * @param paintingComponent Component to paint
 1186        * @param bufferComponent Component to obtain buffer for
 1187        * @param g Graphics to paint to
 1188        * @param x X-coordinate
 1189        * @param y Y-coordinate
 1190        * @param w Width
 1191        * @param h Height
 1192        */
 1193       void paint(JComponent paintingComponent,
 1194                  JComponent bufferComponent, Graphics g,
 1195                  int x, int y, int w, int h) {
 1196           PaintManager paintManager = getPaintManager();
 1197           if (!isPaintingThread()) {
 1198               // We're painting to two threads at once.  PaintManager deals
 1199               // with this a bit better than BufferStrategyPaintManager, use
 1200               // it to avoid possible exceptions/corruption.
 1201               if (paintManager.getClass() != PaintManager.class) {
 1202                   paintManager = new PaintManager();
 1203                   paintManager.repaintManager = this;
 1204               }
 1205           }
 1206           if (!paintManager.paint(paintingComponent, bufferComponent, g,
 1207                                   x, y, w, h)) {
 1208               g.setClip(x, y, w, h);
 1209               paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h);
 1210           }
 1211       }
 1212   
 1213       /**
 1214        * Does a copy area on the specified region.
 1215        *
 1216        * @param clip Whether or not the copyArea needs to be clipped to the
 1217        *             Component's bounds.
 1218        */
 1219       void copyArea(JComponent c, Graphics g, int x, int y, int w, int h,
 1220                     int deltaX, int deltaY, boolean clip) {
 1221           getPaintManager().copyArea(c, g, x, y, w, h, deltaX, deltaY, clip);
 1222       }
 1223   
 1224       /**
 1225        * Invoked prior to any paint/copyArea method calls.  This will
 1226        * be followed by an invocation of <code>endPaint</code>.
 1227        * <b>WARNING</b>: Callers of this method need to wrap the call
 1228        * in a <code>try/finally</code>, otherwise if an exception is thrown
 1229        * during the course of painting the RepaintManager may
 1230        * be left in a state in which the screen is not updated, eg:
 1231        * <pre>
 1232        * repaintManager.beginPaint();
 1233        * try {
 1234        *   repaintManager.paint(...);
 1235        * } finally {
 1236        *   repaintManager.endPaint();
 1237        * }
 1238        * </pre>
 1239        */
 1240       void beginPaint() {
 1241           boolean multiThreadedPaint = false;
 1242           int paintDepth;
 1243           Thread currentThread = Thread.currentThread();
 1244           synchronized(this) {
 1245               paintDepth = this.paintDepth;
 1246               if (paintThread == null || currentThread == paintThread) {
 1247                   paintThread = currentThread;
 1248                   this.paintDepth++;
 1249               } else {
 1250                   multiThreadedPaint = true;
 1251               }
 1252           }
 1253           if (!multiThreadedPaint && paintDepth == 0) {
 1254               getPaintManager().beginPaint();
 1255           }
 1256       }
 1257   
 1258       /**
 1259        * Invoked after <code>beginPaint</code> has been invoked.
 1260        */
 1261       void endPaint() {
 1262           if (isPaintingThread()) {
 1263               PaintManager paintManager = null;
 1264               synchronized(this) {
 1265                   if (--paintDepth == 0) {
 1266                       paintManager = getPaintManager();
 1267                   }
 1268               }
 1269               if (paintManager != null) {
 1270                   paintManager.endPaint();
 1271                   synchronized(this) {
 1272                       paintThread = null;
 1273                   }
 1274               }
 1275           }
 1276       }
 1277   
 1278       /**
 1279        * If possible this will show a previously rendered portion of
 1280        * a Component.  If successful, this will return true, otherwise false.
 1281        * <p>
 1282        * WARNING: This method is invoked from the native toolkit thread, be
 1283        * very careful as to what methods this invokes!
 1284        */
 1285       boolean show(Container c, int x, int y, int w, int h) {
 1286           return getPaintManager().show(c, x, y, w, h);
 1287       }
 1288   
 1289       /**
 1290        * Invoked when the doubleBuffered or useTrueDoubleBuffering
 1291        * properties of a JRootPane change.  This may come in on any thread.
 1292        */
 1293       void doubleBufferingChanged(JRootPane rootPane) {
 1294           getPaintManager().doubleBufferingChanged(rootPane);
 1295       }
 1296   
 1297       /**
 1298        * Sets the <code>PaintManager</code> that is used to handle all
 1299        * double buffered painting.
 1300        *
 1301        * @param paintManager The PaintManager to use.  Passing in null indicates
 1302        *        the fallback PaintManager should be used.
 1303        */
 1304       void setPaintManager(PaintManager paintManager) {
 1305           if (paintManager == null) {
 1306               paintManager = new PaintManager();
 1307           }
 1308           PaintManager oldPaintManager;
 1309           synchronized(this) {
 1310               oldPaintManager = this.paintManager;
 1311               this.paintManager = paintManager;
 1312               paintManager.repaintManager = this;
 1313           }
 1314           if (oldPaintManager != null) {
 1315               oldPaintManager.dispose();
 1316           }
 1317       }
 1318   
 1319       private synchronized PaintManager getPaintManager() {
 1320           if (paintManager == null) {
 1321               PaintManager paintManager = null;
 1322               if (doubleBufferingEnabled && !nativeDoubleBuffering) {
 1323                   switch (bufferStrategyType) {
 1324                   case BUFFER_STRATEGY_NOT_SPECIFIED:
 1325                       Toolkit tk = Toolkit.getDefaultToolkit();
 1326                       if (tk instanceof SunToolkit) {
 1327                           SunToolkit stk = (SunToolkit) tk;
 1328                           if (stk.useBufferPerWindow()) {
 1329                               paintManager = new BufferStrategyPaintManager();
 1330                           }
 1331                       }
 1332                       break;
 1333                   case BUFFER_STRATEGY_SPECIFIED_ON:
 1334                       paintManager = new BufferStrategyPaintManager();
 1335                       break;
 1336                   default:
 1337                       break;
 1338                   }
 1339               }
 1340               // null case handled in setPaintManager
 1341               setPaintManager(paintManager);
 1342           }
 1343           return paintManager;
 1344       }
 1345   
 1346       private void scheduleProcessingRunnable() {
 1347           scheduleProcessingRunnable(AppContext.getAppContext());
 1348       }
 1349   
 1350       private void scheduleProcessingRunnable(AppContext context) {
 1351           if (processingRunnable.markPending()) {
 1352               Toolkit tk = Toolkit.getDefaultToolkit();
 1353               if (tk instanceof SunToolkit) {
 1354                   SunToolkit.getSystemEventQueueImplPP(context).
 1355                     postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
 1356                                                   processingRunnable));
 1357               } else {
 1358                   Toolkit.getDefaultToolkit().getSystemEventQueue().
 1359                         postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
 1360                                                       processingRunnable));
 1361               }
 1362           }
 1363       }
 1364   
 1365   
 1366       /**
 1367        * PaintManager is used to handle all double buffered painting for
 1368        * Swing.  Subclasses should call back into the JComponent method
 1369        * <code>paintToOffscreen</code> to handle the actual painting.
 1370        */
 1371       static class PaintManager {
 1372           /**
 1373            * RepaintManager the PaintManager has been installed on.
 1374            */
 1375           protected RepaintManager repaintManager;
 1376           boolean isRepaintingRoot;
 1377   
 1378           /**
 1379            * Paints a region of a component
 1380            *
 1381            * @param paintingComponent Component to paint
 1382            * @param bufferComponent Component to obtain buffer for
 1383            * @param g Graphics to paint to
 1384            * @param x X-coordinate
 1385            * @param y Y-coordinate
 1386            * @param w Width
 1387            * @param h Height
 1388            * @return true if painting was successful.
 1389            */
 1390           public boolean paint(JComponent paintingComponent,
 1391                                JComponent bufferComponent, Graphics g,
 1392                                int x, int y, int w, int h) {
 1393               // First attempt to use VolatileImage buffer for performance.
 1394               // If this fails (which should rarely occur), fallback to a
 1395               // standard Image buffer.
 1396               boolean paintCompleted = false;
 1397               Image offscreen;
 1398               if (repaintManager.useVolatileDoubleBuffer() &&
 1399                   (offscreen = getValidImage(repaintManager.
 1400                   getVolatileOffscreenBuffer(bufferComponent, w, h))) != null) {
 1401                   VolatileImage vImage = (java.awt.image.VolatileImage)offscreen;
 1402                   GraphicsConfiguration gc = bufferComponent.
 1403                                               getGraphicsConfiguration();
 1404                   for (int i = 0; !paintCompleted &&
 1405                            i < RepaintManager.VOLATILE_LOOP_MAX; i++) {
 1406                       if (vImage.validate(gc) ==
 1407                                      VolatileImage.IMAGE_INCOMPATIBLE) {
 1408                           repaintManager.resetVolatileDoubleBuffer(gc);
 1409                           offscreen = repaintManager.getVolatileOffscreenBuffer(
 1410                               bufferComponent,w, h);
 1411                           vImage = (java.awt.image.VolatileImage)offscreen;
 1412                       }
 1413                       paintDoubleBuffered(paintingComponent, vImage, g, x, y,
 1414                                           w, h);
 1415                       paintCompleted = !vImage.contentsLost();
 1416                   }
 1417               }
 1418               // VolatileImage painting loop failed, fallback to regular
 1419               // offscreen buffer
 1420               if (!paintCompleted && (offscreen = getValidImage(
 1421                         repaintManager.getOffscreenBuffer(
 1422                         bufferComponent, w, h))) != null) {
 1423                   paintDoubleBuffered(paintingComponent, offscreen, g, x, y, w,
 1424                                       h);
 1425                   paintCompleted = true;
 1426               }
 1427               return paintCompleted;
 1428           }
 1429   
 1430           /**
 1431            * Does a copy area on the specified region.
 1432            */
 1433           public void copyArea(JComponent c, Graphics g, int x, int y, int w,
 1434                                int h, int deltaX, int deltaY, boolean clip) {
 1435               g.copyArea(x, y, w, h, deltaX, deltaY);
 1436           }
 1437   
 1438           /**
 1439            * Invoked prior to any calls to paint or copyArea.
 1440            */
 1441           public void beginPaint() {
 1442           }
 1443   
 1444           /**
 1445            * Invoked to indicate painting has been completed.
 1446            */
 1447           public void endPaint() {
 1448           }
 1449   
 1450           /**
 1451            * Shows a region of a previously rendered component.  This
 1452            * will return true if successful, false otherwise.  The default
 1453            * implementation returns false.
 1454            */
 1455           public boolean show(Container c, int x, int y, int w, int h) {
 1456               return false;
 1457           }
 1458   
 1459           /**
 1460            * Invoked when the doubleBuffered or useTrueDoubleBuffering
 1461            * properties of a JRootPane change.  This may come in on any thread.
 1462            */
 1463           public void doubleBufferingChanged(JRootPane rootPane) {
 1464           }
 1465   
 1466           /**
 1467            * Paints a portion of a component to an offscreen buffer.
 1468            */
 1469           protected void paintDoubleBuffered(JComponent c, Image image,
 1470                               Graphics g, int clipX, int clipY,
 1471                               int clipW, int clipH) {
 1472               Graphics osg = image.getGraphics();
 1473               int bw = Math.min(clipW, image.getWidth(null));
 1474               int bh = Math.min(clipH, image.getHeight(null));
 1475               int x,y,maxx,maxy;
 1476   
 1477               try {
 1478                   for(x = clipX, maxx = clipX+clipW; x < maxx ;  x += bw ) {
 1479                       for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) {
 1480                           osg.translate(-x, -y);
 1481                           osg.setClip(x,y,bw,bh);
 1482                           c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy);
 1483                           g.setClip(x, y, bw, bh);
 1484                           g.drawImage(image, x, y, c);
 1485                           osg.translate(x, y);
 1486                       }
 1487                   }
 1488               } finally {
 1489                   osg.dispose();
 1490               }
 1491           }
 1492   
 1493           /**
 1494            * If <code>image</code> is non-null with a positive size it
 1495            * is returned, otherwise null is returned.
 1496            */
 1497           private Image getValidImage(Image image) {
 1498               if (image != null && image.getWidth(null) > 0 &&
 1499                                    image.getHeight(null) > 0) {
 1500                   return image;
 1501               }
 1502               return null;
 1503           }
 1504   
 1505           /**
 1506            * Schedules a repaint for the specified component.  This differs
 1507            * from <code>root.repaint</code> in that if the RepaintManager is
 1508            * currently processing paint requests it'll process this request
 1509            * with the current set of requests.
 1510            */
 1511           protected void repaintRoot(JComponent root) {
 1512               assert (repaintManager.repaintRoot == null);
 1513               if (repaintManager.painting) {
 1514                   repaintManager.repaintRoot = root;
 1515               }
 1516               else {
 1517                   root.repaint();
 1518               }
 1519           }
 1520   
 1521           /**
 1522            * Returns true if the component being painted is the root component
 1523            * that was previously passed to <code>repaintRoot</code>.
 1524            */
 1525           protected boolean isRepaintingRoot() {
 1526               return isRepaintingRoot;
 1527           }
 1528   
 1529           /**
 1530            * Cleans up any state.  After invoked the PaintManager will no
 1531            * longer be used anymore.
 1532            */
 1533           protected void dispose() {
 1534           }
 1535       }
 1536   
 1537   
 1538       private class DoubleBufferInfo {
 1539           public Image image;
 1540           public Dimension size;
 1541           public boolean needsReset = false;
 1542       }
 1543   
 1544   
 1545       /**
 1546        * Listener installed to detect display changes. When display changes,
 1547        * schedules a callback to notify all RepaintManagers of the display
 1548        * changes. Only one DisplayChangedHandler is ever installed. The
 1549        * singleton instance will schedule notification for all AppContexts.
 1550        */
 1551       private static final class DisplayChangedHandler implements
 1552                                                DisplayChangedListener {
 1553           public void displayChanged() {
 1554               scheduleDisplayChanges();
 1555           }
 1556   
 1557           public void paletteChanged() {
 1558           }
 1559   
 1560           private void scheduleDisplayChanges() {
 1561               // To avoid threading problems, we notify each RepaintManager
 1562               // on the thread it was created on.
 1563               for (Object c : AppContext.getAppContexts()) {
 1564                   AppContext context = (AppContext) c;
 1565                   synchronized(context) {
 1566                       if (!context.isDisposed()) {
 1567                           EventQueue eventQueue = (EventQueue)context.get(
 1568                               AppContext.EVENT_QUEUE_KEY);
 1569                           if (eventQueue != null) {
 1570                               eventQueue.postEvent(new InvocationEvent(
 1571                                   Toolkit.getDefaultToolkit(),
 1572                                   new DisplayChangedRunnable()));
 1573                           }
 1574                       }
 1575                   }
 1576               }
 1577           }
 1578       }
 1579   
 1580   
 1581       private static final class DisplayChangedRunnable implements Runnable {
 1582           public void run() {
 1583               RepaintManager.currentManager((JComponent)null).displayChanged();
 1584           }
 1585       }
 1586   
 1587   
 1588       /**
 1589        * Runnable used to process all repaint/revalidate requests.
 1590        */
 1591       private final class ProcessingRunnable implements Runnable {
 1592           // If true, we're wainting on the EventQueue.
 1593           private boolean pending;
 1594   
 1595           /**
 1596            * Marks this processing runnable as pending. If this was not
 1597            * already marked as pending, true is returned.
 1598            */
 1599           public synchronized boolean markPending() {
 1600               if (!pending) {
 1601                   pending = true;
 1602                   return true;
 1603               }
 1604               return false;
 1605           }
 1606   
 1607           public void run() {
 1608               synchronized (this) {
 1609                   pending = false;
 1610               }
 1611               // First pass, flush any heavy paint events into real paint
 1612               // events.  If there are pending heavy weight requests this will
 1613               // result in q'ing this request up one more time.  As
 1614               // long as no other requests come in between now and the time
 1615               // the second one is processed nothing will happen.  This is not
 1616               // ideal, but the logic needed to suppress the second request is
 1617               // more headache than it's worth.
 1618               scheduleHeavyWeightPaints();
 1619               // Do the actual validation and painting.
 1620               validateInvalidComponents();
 1621               prePaintDirtyRegions();
 1622           }
 1623       }
 1624       private RepaintManager getDelegate(Component c) {
 1625           RepaintManager delegate = SwingUtilities3.getDelegateRepaintManager(c);
 1626           if (this == delegate) {
 1627               delegate = null;
 1628           }
 1629           return delegate;
 1630       }
 1631   }

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