0001 /*
0002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 package javax.swing;
0026
0027 import java.awt.*;
0028 import java.awt.event.*;
0029 import java.awt.peer.ComponentPeer;
0030 import java.awt.peer.ContainerPeer;
0031 import java.awt.image.VolatileImage;
0032 import java.security.AccessController;
0033 import java.util.*;
0034 import java.applet.*;
0035
0036 import sun.awt.AppContext;
0037 import sun.awt.DisplayChangedListener;
0038 import sun.awt.SunToolkit;
0039 import sun.java2d.SunGraphicsEnvironment;
0040 import sun.security.action.GetPropertyAction;
0041
0042 /**
0043 * This class manages repaint requests, allowing the number
0044 * of repaints to be minimized, for example by collapsing multiple
0045 * requests into a single repaint for members of a component tree.
0046 * <p>
0047 * As of 1.6 <code>RepaintManager</code> handles repaint requests
0048 * for Swing's top level components (<code>JApplet</code>,
0049 * <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>).
0050 * Any calls to <code>repaint</code> on one of these will call into the
0051 * appropriate <code>addDirtyRegion</code> method.
0052 *
0053 * @version 1.76 05/09/07
0054 * @author Arnaud Weber
0055 */
0056 public class RepaintManager {
0057 /**
0058 * Whether or not the RepaintManager should handle paint requests
0059 * for top levels.
0060 */
0061 static final boolean HANDLE_TOP_LEVEL_PAINT;
0062
0063 private static final short BUFFER_STRATEGY_NOT_SPECIFIED = 0;
0064 private static final short BUFFER_STRATEGY_SPECIFIED_ON = 1;
0065 private static final short BUFFER_STRATEGY_SPECIFIED_OFF = 2;
0066
0067 private static final short BUFFER_STRATEGY_TYPE;
0068
0069 /**
0070 * Maps from GraphicsConfiguration to VolatileImage.
0071 */
0072 private Map<GraphicsConfiguration, VolatileImage> volatileMap = new HashMap<GraphicsConfiguration, VolatileImage>(
0073 1);
0074
0075 //
0076 // As of 1.6 Swing handles scheduling of paint events from native code.
0077 // That is, SwingPaintEventDispatcher is invoked on the toolkit thread,
0078 // which in turn invokes nativeAddDirtyRegion. Because this is invoked
0079 // from the native thread we can not invoke any public methods and so
0080 // we introduce these added maps. So, any time nativeAddDirtyRegion is
0081 // invoked the region is added to hwDirtyComponents and a work request
0082 // is scheduled. When the work request is processed all entries in
0083 // this map are pushed to the real map (dirtyComponents) and then
0084 // painted with the rest of the components.
0085 //
0086 private Map<Container, Rectangle> hwDirtyComponents;
0087
0088 private Map<Component, Rectangle> dirtyComponents;
0089 private Map<Component, Rectangle> tmpDirtyComponents;
0090 private java.util.List<Component> invalidComponents;
0091
0092 // List of Runnables that need to be processed before painting from AWT.
0093 private java.util.List<Runnable> runnableList;
0094
0095 boolean doubleBufferingEnabled = true;
0096
0097 private Dimension doubleBufferMaxSize;
0098
0099 // Support for both the standard and volatile offscreen buffers exists to
0100 // provide backwards compatibility for the [rare] programs which may be
0101 // calling getOffScreenBuffer() and not expecting to get a VolatileImage.
0102 // Swing internally is migrating to use *only* the volatile image buffer.
0103
0104 // Support for standard offscreen buffer
0105 //
0106 DoubleBufferInfo standardDoubleBuffer;
0107
0108 /**
0109 * Object responsible for hanlding core paint functionality.
0110 */
0111 private PaintManager paintManager;
0112
0113 private static final Object repaintManagerKey = RepaintManager.class;
0114
0115 // Whether or not a VolatileImage should be used for double-buffered painting
0116 static boolean volatileImageBufferEnabled = true;
0117 /**
0118 * Value of the system property awt.nativeDoubleBuffering.
0119 */
0120 private static boolean nativeDoubleBuffering;
0121
0122 // The maximum number of times Swing will attempt to use the VolatileImage
0123 // buffer during a paint operation.
0124 private static final int VOLATILE_LOOP_MAX = 2;
0125
0126 /**
0127 * Number of <code>beginPaint</code> that have been invoked.
0128 */
0129 private int paintDepth = 0;
0130
0131 /**
0132 * Type of buffer strategy to use. Will be one of the BUFFER_STRATEGY_
0133 * constants.
0134 */
0135 private short bufferStrategyType;
0136
0137 //
0138 // BufferStrategyPaintManager has the unique characteristic that it
0139 // must deal with the buffer being lost while painting to it. For
0140 // example, if we paint a component and show it and the buffer has
0141 // become lost we must repaint the whole window. To deal with that
0142 // the PaintManager calls into repaintRoot, and if we're still in
0143 // the process of painting the repaintRoot field is set to the JRootPane
0144 // and after the current JComponent.paintImmediately call finishes
0145 // paintImmediately will be invoked on the repaintRoot. In this
0146 // way we don't try to show garbage to the screen.
0147 //
0148 /**
0149 * True if we're in the process of painting the dirty regions. This is
0150 * set to true in <code>paintDirtyRegions</code>.
0151 */
0152 private boolean painting;
0153 /**
0154 * If the PaintManager calls into repaintRoot during painting this field
0155 * will be set to the root.
0156 */
0157 private JComponent repaintRoot;
0158
0159 /**
0160 * The Thread that has initiated painting. If null it
0161 * indicates painting is not currently in progress.
0162 */
0163 private Thread paintThread;
0164
0165 /**
0166 * Runnable used to process all repaint/revalidate requests.
0167 */
0168 private final ProcessingRunnable processingRunnable;
0169
0170 static {
0171 volatileImageBufferEnabled = "true".equals(AccessController
0172 .doPrivileged(new GetPropertyAction(
0173 "swing.volatileImageBufferEnabled", "true")));
0174 boolean headless = GraphicsEnvironment.isHeadless();
0175 if (volatileImageBufferEnabled && headless) {
0176 volatileImageBufferEnabled = false;
0177 }
0178 nativeDoubleBuffering = "true".equals(AccessController
0179 .doPrivileged(new GetPropertyAction(
0180 "awt.nativeDoubleBuffering")));
0181 String bs = AccessController
0182 .doPrivileged(new GetPropertyAction(
0183 "swing.bufferPerWindow"));
0184 if (headless) {
0185 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
0186 } else if (bs == null) {
0187 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED;
0188 } else if ("true".equals(bs)) {
0189 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON;
0190 } else {
0191 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
0192 }
0193 HANDLE_TOP_LEVEL_PAINT = "true".equals(AccessController
0194 .doPrivileged(new GetPropertyAction(
0195 "swing.handleTopLevelPaint", "true")));
0196 GraphicsEnvironment ge = GraphicsEnvironment
0197 .getLocalGraphicsEnvironment();
0198 if (ge instanceof SunGraphicsEnvironment) {
0199 ((SunGraphicsEnvironment) ge)
0200 .addDisplayChangedListener(new DisplayChangedHandler());
0201 }
0202 }
0203
0204 /**
0205 * Return the RepaintManager for the calling thread given a Component.
0206 *
0207 * @param c a Component -- unused in the default implementation, but could
0208 * be used by an overridden version to return a different RepaintManager
0209 * depending on the Component
0210 * @return the RepaintManager object
0211 */
0212 public static RepaintManager currentManager(Component c) {
0213 // Note: DisplayChangedRunnable passes in null as the component, so if
0214 // component is ever used to determine the current
0215 // RepaintManager, DisplayChangedRunnable will need to be modified
0216 // accordingly.
0217 return currentManager(AppContext.getAppContext());
0218 }
0219
0220 /**
0221 * Returns the RepaintManager for the specified AppContext. If
0222 * a RepaintManager has not been created for the specified
0223 * AppContext this will return null.
0224 */
0225 static RepaintManager currentManager(AppContext appContext) {
0226 RepaintManager rm = (RepaintManager) appContext
0227 .get(repaintManagerKey);
0228 if (rm == null) {
0229 rm = new RepaintManager(BUFFER_STRATEGY_TYPE);
0230 appContext.put(repaintManagerKey, rm);
0231 }
0232 return rm;
0233 }
0234
0235 /**
0236 * Return the RepaintManager for the calling thread given a JComponent.
0237 * <p>
0238 * Note: This method exists for backward binary compatibility with earlier
0239 * versions of the Swing library. It simply returns the result returned by
0240 * {@link #currentManager(Component)}.
0241 *
0242 * @param c a JComponent -- unused
0243 * @return the RepaintManager object
0244 */
0245 public static RepaintManager currentManager(JComponent c) {
0246 return currentManager((Component) c);
0247 }
0248
0249 /**
0250 * Set the RepaintManager that should be used for the calling
0251 * thread. <b>aRepaintManager</b> will become the current RepaintManager
0252 * for the calling thread's thread group.
0253 * @param aRepaintManager the RepaintManager object to use
0254 */
0255 public static void setCurrentManager(RepaintManager aRepaintManager) {
0256 if (aRepaintManager != null) {
0257 SwingUtilities.appContextPut(repaintManagerKey,
0258 aRepaintManager);
0259 } else {
0260 SwingUtilities.appContextRemove(repaintManagerKey);
0261 }
0262 }
0263
0264 /**
0265 * Create a new RepaintManager instance. You rarely call this constructor.
0266 * directly. To get the default RepaintManager, use
0267 * RepaintManager.currentManager(JComponent) (normally "this").
0268 */
0269 public RepaintManager() {
0270 // Because we can't know what a subclass is doing with the
0271 // volatile image we immediately punt in subclasses. If this
0272 // poses a problem we'll need a more sophisticated detection algorithm,
0273 // or API.
0274 this (BUFFER_STRATEGY_SPECIFIED_OFF);
0275 }
0276
0277 private RepaintManager(short bufferStrategyType) {
0278 // If native doublebuffering is being used, do NOT use
0279 // Swing doublebuffering.
0280 doubleBufferingEnabled = !nativeDoubleBuffering;
0281 synchronized (this ) {
0282 dirtyComponents = new IdentityHashMap<Component, Rectangle>();
0283 tmpDirtyComponents = new IdentityHashMap<Component, Rectangle>();
0284 this .bufferStrategyType = bufferStrategyType;
0285 hwDirtyComponents = new IdentityHashMap<Container, Rectangle>();
0286 }
0287 processingRunnable = new ProcessingRunnable();
0288 }
0289
0290 private void displayChanged() {
0291 clearImages();
0292 }
0293
0294 /**
0295 * Mark the component as in need of layout and queue a runnable
0296 * for the event dispatching thread that will validate the components
0297 * first isValidateRoot() ancestor.
0298 *
0299 * @see JComponent#isValidateRoot
0300 * @see #removeInvalidComponent
0301 */
0302 public synchronized void addInvalidComponent(
0303 JComponent invalidComponent) {
0304 Component validateRoot = null;
0305
0306 /* Find the first JComponent ancestor of this component whose
0307 * isValidateRoot() method returns true.
0308 */
0309 for (Component c = invalidComponent; c != null; c = c
0310 .getParent()) {
0311 if ((c instanceof CellRendererPane)
0312 || (c.getPeer() == null)) {
0313 return;
0314 }
0315 if ((c instanceof JComponent)
0316 && (((JComponent) c).isValidateRoot())) {
0317 validateRoot = c;
0318 break;
0319 }
0320 }
0321
0322 /* There's no validateRoot to apply validate to, so we're done.
0323 */
0324 if (validateRoot == null) {
0325 return;
0326 }
0327
0328 /* If the validateRoot and all of its ancestors aren't visible
0329 * then we don't do anything. While we're walking up the tree
0330 * we find the root Window or Applet.
0331 */
0332 Component root = null;
0333
0334 for (Component c = validateRoot; c != null; c = c.getParent()) {
0335 if (!c.isVisible() || (c.getPeer() == null)) {
0336 return;
0337 }
0338 if ((c instanceof Window) || (c instanceof Applet)) {
0339 root = c;
0340 break;
0341 }
0342 }
0343
0344 if (root == null) {
0345 return;
0346 }
0347
0348 /* Lazily create the invalidateComponents vector and add the
0349 * validateRoot if it's not there already. If this validateRoot
0350 * is already in the vector, we're done.
0351 */
0352 if (invalidComponents == null) {
0353 invalidComponents = new ArrayList<Component>();
0354 } else {
0355 int n = invalidComponents.size();
0356 for (int i = 0; i < n; i++) {
0357 if (validateRoot == invalidComponents.get(i)) {
0358 return;
0359 }
0360 }
0361 }
0362 invalidComponents.add(validateRoot);
0363
0364 // Queue a Runnable to invoke paintDirtyRegions and
0365 // validateInvalidComponents.
0366 scheduleProcessingRunnable();
0367 }
0368
0369 /**
0370 * Remove a component from the list of invalid components.
0371 *
0372 * @see #addInvalidComponent
0373 */
0374 public synchronized void removeInvalidComponent(JComponent component) {
0375 if (invalidComponents != null) {
0376 int index = invalidComponents.indexOf(component);
0377 if (index != -1) {
0378 invalidComponents.remove(index);
0379 }
0380 }
0381 }
0382
0383 /**
0384 * Add a component in the list of components that should be refreshed.
0385 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
0386 * will be unioned with the region that should be redrawn.
0387 *
0388 * @see JComponent#repaint
0389 */
0390 private void addDirtyRegion0(Container c, int x, int y, int w, int h) {
0391 /* Special cases we don't have to bother with.
0392 */
0393 if ((w <= 0) || (h <= 0) || (c == null)) {
0394 return;
0395 }
0396
0397 if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
0398 return;
0399 }
0400
0401 if (extendDirtyRegion(c, x, y, w, h)) {
0402 // Component was already marked as dirty, region has been
0403 // extended, no need to continue.
0404 return;
0405 }
0406
0407 /* Make sure that c and all it ancestors (up to an Applet or
0408 * Window) are visible. This loop has the same effect as
0409 * checking c.isShowing() (and note that it's still possible
0410 * that c is completely obscured by an opaque ancestor in
0411 * the specified rectangle).
0412 */
0413 Component root = null;
0414
0415 // Note: We can't synchronize around this, Frame.getExtendedState
0416 // is synchronized so that if we were to synchronize around this
0417 // it could lead to the possibility of getting locks out
0418 // of order and deadlocking.
0419 for (Container p = c; p != null; p = p.getParent()) {
0420 if (!p.isVisible() || (p.getPeer() == null)) {
0421 return;
0422 }
0423 if ((p instanceof Window) || (p instanceof Applet)) {
0424 // Iconified frames are still visible!
0425 if (p instanceof Frame
0426 && (((Frame) p).getExtendedState() & Frame.ICONIFIED) == Frame.ICONIFIED) {
0427 return;
0428 }
0429 root = p;
0430 break;
0431 }
0432 }
0433
0434 if (root == null)
0435 return;
0436
0437 synchronized (this ) {
0438 if (extendDirtyRegion(c, x, y, w, h)) {
0439 // In between last check and this check another thread
0440 // queued up runnable, can bail here.
0441 return;
0442 }
0443 dirtyComponents.put(c, new Rectangle(x, y, w, h));
0444 }
0445
0446 // Queue a Runnable to invoke paintDirtyRegions and
0447 // validateInvalidComponents.
0448 scheduleProcessingRunnable();
0449 }
0450
0451 /**
0452 * Add a component in the list of components that should be refreshed.
0453 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
0454 * will be unioned with the region that should be redrawn.
0455 *
0456 * @param c Component to repaint, null results in nothing happening.
0457 * @param x X coordinate of the region to repaint
0458 * @param y Y coordinate of the region to repaint
0459 * @param w Width of the region to repaint
0460 * @param h Height of the region to repaint
0461 * @see JComponent#repaint
0462 */
0463 public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
0464 addDirtyRegion0(c, x, y, w, h);
0465 }
0466
0467 /**
0468 * Adds <code>window</code> to the list of <code>Component</code>s that
0469 * need to be repainted.
0470 *
0471 * @param window Window to repaint, null results in nothing happening.
0472 * @param x X coordinate of the region to repaint
0473 * @param y Y coordinate of the region to repaint
0474 * @param w Width of the region to repaint
0475 * @param h Height of the region to repaint
0476 * @see JFrame#repaint
0477 * @see JWindow#repaint
0478 * @see JDialog#repaint
0479 * @since 1.6
0480 */
0481 public void addDirtyRegion(Window window, int x, int y, int w, int h) {
0482 addDirtyRegion0(window, x, y, w, h);
0483 }
0484
0485 /**
0486 * Adds <code>applet</code> to the list of <code>Component</code>s that
0487 * need to be repainted.
0488 *
0489 * @param applet Applet to repaint, null results in nothing happening.
0490 * @param x X coordinate of the region to repaint
0491 * @param y Y coordinate of the region to repaint
0492 * @param w Width of the region to repaint
0493 * @param h Height of the region to repaint
0494 * @see JApplet#repaint
0495 * @since 1.6
0496 */
0497 public void addDirtyRegion(Applet applet, int x, int y, int w, int h) {
0498 addDirtyRegion0(applet, x, y, w, h);
0499 }
0500
0501 void scheduleHeavyWeightPaints() {
0502 Map<Container, Rectangle> hws;
0503
0504 synchronized (this ) {
0505 if (hwDirtyComponents.size() == 0) {
0506 return;
0507 }
0508 hws = hwDirtyComponents;
0509 hwDirtyComponents = new IdentityHashMap<Container, Rectangle>();
0510 }
0511 for (Container hw : hws.keySet()) {
0512 Rectangle dirty = hws.get(hw);
0513 if (hw instanceof Window) {
0514 addDirtyRegion((Window) hw, dirty.x, dirty.y,
0515 dirty.width, dirty.height);
0516 } else if (hw instanceof Applet) {
0517 addDirtyRegion((Applet) hw, dirty.x, dirty.y,
0518 dirty.width, dirty.height);
0519 } else { // SwingHeavyWeight
0520 addDirtyRegion0(hw, dirty.x, dirty.y, dirty.width,
0521 dirty.height);
0522 }
0523 }
0524 }
0525
0526 //
0527 // This is called from the toolkit thread when a native expose is
0528 // received.
0529 //
0530 void nativeAddDirtyRegion(AppContext appContext, Container c,
0531 int x, int y, int w, int h) {
0532 if (w > 0 && h > 0) {
0533 synchronized (this ) {
0534 Rectangle dirty = hwDirtyComponents.get(c);
0535 if (dirty == null) {
0536 hwDirtyComponents.put(c, new Rectangle(x, y, w, h));
0537 } else {
0538 hwDirtyComponents.put(c, SwingUtilities
0539 .computeUnion(x, y, w, h, dirty));
0540 }
0541 }
0542 scheduleProcessingRunnable(appContext);
0543 }
0544 }
0545
0546 //
0547 // This is called from the toolkit thread when awt needs to run a
0548 // Runnable before we paint.
0549 //
0550 void nativeQueueSurfaceDataRunnable(AppContext appContext,
0551 Component c, Runnable r) {
0552 synchronized (this ) {
0553 if (runnableList == null) {
0554 runnableList = new LinkedList<Runnable>();
0555 }
0556 runnableList.add(r);
0557 }
0558 scheduleProcessingRunnable(appContext);
0559 }
0560
0561 /**
0562 * Extends the dirty region for the specified component to include
0563 * the new region.
0564 *
0565 * @return false if <code>c</code> is not yet marked dirty.
0566 */
0567 private synchronized boolean extendDirtyRegion(Component c, int x,
0568 int y, int w, int h) {
0569 Rectangle r = (Rectangle) dirtyComponents.get(c);
0570 if (r != null) {
0571 // A non-null r implies c is already marked as dirty,
0572 // and that the parent is valid. Therefore we can
0573 // just union the rect and bail.
0574 SwingUtilities.computeUnion(x, y, w, h, r);
0575 return true;
0576 }
0577 return false;
0578 }
0579
0580 /** Return the current dirty region for a component.
0581 * Return an empty rectangle if the component is not
0582 * dirty.
0583 */
0584 public Rectangle getDirtyRegion(JComponent aComponent) {
0585 Rectangle r = null;
0586 synchronized (this ) {
0587 r = (Rectangle) dirtyComponents.get(aComponent);
0588 }
0589 if (r == null)
0590 return new Rectangle(0, 0, 0, 0);
0591 else
0592 return new Rectangle(r);
0593 }
0594
0595 /**
0596 * Mark a component completely dirty. <b>aComponent</b> will be
0597 * completely painted during the next paintDirtyRegions() call.
0598 */
0599 public void markCompletelyDirty(JComponent aComponent) {
0600 addDirtyRegion(aComponent, 0, 0, Integer.MAX_VALUE,
0601 Integer.MAX_VALUE);
0602 }
0603
0604 /**
0605 * Mark a component completely clean. <b>aComponent</b> will not
0606 * get painted during the next paintDirtyRegions() call.
0607 */
0608 public void markCompletelyClean(JComponent aComponent) {
0609 synchronized (this ) {
0610 dirtyComponents.remove(aComponent);
0611 }
0612 }
0613
0614 /**
0615 * Convenience method that returns true if <b>aComponent</b> will be completely
0616 * painted during the next paintDirtyRegions(). If computing dirty regions is
0617 * expensive for your component, use this method and avoid computing dirty region
0618 * if it return true.
0619 */
0620 public boolean isCompletelyDirty(JComponent aComponent) {
0621 Rectangle r;
0622
0623 r = getDirtyRegion(aComponent);
0624 if (r.width == Integer.MAX_VALUE
0625 && r.height == Integer.MAX_VALUE)
0626 return true;
0627 else
0628 return false;
0629 }
0630
0631 /**
0632 * Validate all of the components that have been marked invalid.
0633 * @see #addInvalidComponent
0634 */
0635 public void validateInvalidComponents() {
0636 java.util.List<Component> ic;
0637 synchronized (this ) {
0638 if (invalidComponents == null) {
0639 return;
0640 }
0641 ic = invalidComponents;
0642 invalidComponents = null;
0643 }
0644 int n = ic.size();
0645 for (int i = 0; i < n; i++) {
0646 ic.get(i).validate();
0647 }
0648 }
0649
0650 /**
0651 * This is invoked to process paint requests. It's needed
0652 * for backward compatability in so far as RepaintManager would previously
0653 * not see paint requests for top levels, so, we have to make sure
0654 * a subclass correctly paints any dirty top levels.
0655 */
0656 private void prePaintDirtyRegions() {
0657 Map<Component, Rectangle> dirtyComponents;
0658 java.util.List<Runnable> runnableList;
0659 synchronized (this ) {
0660 dirtyComponents = this .dirtyComponents;
0661 runnableList = this .runnableList;
0662 this .runnableList = null;
0663 }
0664 if (runnableList != null) {
0665 for (Runnable runnable : runnableList) {
0666 runnable.run();
0667 }
0668 }
0669 paintDirtyRegions();
0670 if (dirtyComponents.size() > 0) {
0671 // This'll only happen if a subclass isn't correctly dealing
0672 // with toplevels.
0673 paintDirtyRegions(dirtyComponents);
0674 }
0675 }
0676
0677 /**
0678 * Paint all of the components that have been marked dirty.
0679 *
0680 * @see #addDirtyRegion
0681 */
0682 public void paintDirtyRegions() {
0683 synchronized (this ) { // swap for thread safety
0684 Map<Component, Rectangle> tmp = tmpDirtyComponents;
0685 tmpDirtyComponents = dirtyComponents;
0686 dirtyComponents = tmp;
0687 dirtyComponents.clear();
0688 }
0689 paintDirtyRegions(tmpDirtyComponents);
0690 }
0691
0692 private void paintDirtyRegions(
0693 Map<Component, Rectangle> tmpDirtyComponents) {
0694 int i, count;
0695 java.util.List<Component> roots;
0696 Component dirtyComponent;
0697
0698 count = tmpDirtyComponents.size();
0699 if (count == 0) {
0700 return;
0701 }
0702
0703 Rectangle rect;
0704 int localBoundsX = 0;
0705 int localBoundsY = 0;
0706 int localBoundsH = 0;
0707 int localBoundsW = 0;
0708 Enumeration keys;
0709
0710 roots = new ArrayList<Component>(count);
0711
0712 for (Component dirty : tmpDirtyComponents.keySet()) {
0713 collectDirtyComponents(tmpDirtyComponents, dirty, roots);
0714 }
0715
0716 count = roots.size();
0717 // System.out.println("roots size is " + count);
0718 painting = true;
0719 try {
0720 for (i = 0; i < count; i++) {
0721 dirtyComponent = roots.get(i);
0722 rect = tmpDirtyComponents.get(dirtyComponent);
0723 // System.out.println("Should refresh :" + rect);
0724 localBoundsH = dirtyComponent.getHeight();
0725 localBoundsW = dirtyComponent.getWidth();
0726
0727 SwingUtilities.computeIntersection(localBoundsX,
0728 localBoundsY, localBoundsW, localBoundsH, rect);
0729 if (dirtyComponent instanceof JComponent) {
0730 ((JComponent) dirtyComponent).paintImmediately(
0731 rect.x, rect.y, rect.width, rect.height);
0732 } else if (dirtyComponent.isShowing()) {
0733 Graphics g = JComponent.safelyGetGraphics(
0734 dirtyComponent, dirtyComponent);
0735 // If the Graphics goes away, it means someone disposed of
0736 // the window, don't do anything.
0737 if (g != null) {
0738 g.setClip(rect.x, rect.y, rect.width,
0739 rect.height);
0740 try {
0741 dirtyComponent.paint(g);
0742 } finally {
0743 g.dispose();
0744 }
0745 }
0746 }
0747 // If the repaintRoot has been set, service it now and
0748 // remove any components that are children of repaintRoot.
0749 if (repaintRoot != null) {
0750 adjustRoots(repaintRoot, roots, i + 1);
0751 count = roots.size();
0752 paintManager.isRepaintingRoot = true;
0753 repaintRoot.paintImmediately(0, 0, repaintRoot
0754 .getWidth(), repaintRoot.getHeight());
0755 paintManager.isRepaintingRoot = false;
0756 // Only service repaintRoot once.
0757 repaintRoot = null;
0758 }
0759 }
0760 } finally {
0761 painting = false;
0762 }
0763 tmpDirtyComponents.clear();
0764 }
0765
0766 /**
0767 * Removes any components from roots that are children of
0768 * root.
0769 */
0770 private void adjustRoots(JComponent root,
0771 java.util.List<Component> roots, int index) {
0772 for (int i = roots.size() - 1; i >= index; i--) {
0773 Component c = roots.get(i);
0774 for (;;) {
0775 if (c == root || c == null
0776 || !(c instanceof JComponent)) {
0777 break;
0778 }
0779 c = c.getParent();
0780 }
0781 if (c == root) {
0782 roots.remove(i);
0783 }
0784 }
0785 }
0786
0787 Rectangle tmp = new Rectangle();
0788
0789 void collectDirtyComponents(
0790 Map<Component, Rectangle> dirtyComponents,
0791 Component dirtyComponent, java.util.List<Component> roots) {
0792 int dx, dy, rootDx, rootDy;
0793 Component component, rootDirtyComponent, parent;
0794 Rectangle cBounds;
0795
0796 // Find the highest parent which is dirty. When we get out of this
0797 // rootDx and rootDy will contain the translation from the
0798 // rootDirtyComponent's coordinate system to the coordinates of the
0799 // original dirty component. The tmp Rect is also used to compute the
0800 // visible portion of the dirtyRect.
0801
0802 component = rootDirtyComponent = dirtyComponent;
0803
0804 int x = dirtyComponent.getX();
0805 int y = dirtyComponent.getY();
0806 int w = dirtyComponent.getWidth();
0807 int h = dirtyComponent.getHeight();
0808
0809 dx = rootDx = 0;
0810 dy = rootDy = 0;
0811 tmp.setBounds((Rectangle) dirtyComponents.get(dirtyComponent));
0812
0813 // System.out.println("Collect dirty component for bound " + tmp +
0814 // "component bounds is " + cBounds);;
0815 SwingUtilities.computeIntersection(0, 0, w, h, tmp);
0816
0817 if (tmp.isEmpty()) {
0818 // System.out.println("Empty 1");
0819 return;
0820 }
0821
0822 for (;;) {
0823 if (!(component instanceof JComponent))
0824 break;
0825
0826 parent = component.getParent();
0827 if (parent == null)
0828 break;
0829
0830 component = parent;
0831
0832 dx += x;
0833 dy += y;
0834 tmp.setLocation(tmp.x + x, tmp.y + y);
0835
0836 x = component.getX();
0837 y = component.getY();
0838 w = component.getWidth();
0839 h = component.getHeight();
0840 tmp = SwingUtilities.computeIntersection(0, 0, w, h, tmp);
0841
0842 if (tmp.isEmpty()) {
0843 // System.out.println("Empty 2");
0844 return;
0845 }
0846
0847 if (dirtyComponents.get(component) != null) {
0848 rootDirtyComponent = component;
0849 rootDx = dx;
0850 rootDy = dy;
0851 }
0852 }
0853
0854 if (dirtyComponent != rootDirtyComponent) {
0855 Rectangle r;
0856 tmp.setLocation(tmp.x + rootDx - dx, tmp.y + rootDy - dy);
0857 r = (Rectangle) dirtyComponents.get(rootDirtyComponent);
0858 SwingUtilities.computeUnion(tmp.x, tmp.y, tmp.width,
0859 tmp.height, r);
0860 }
0861
0862 // If we haven't seen this root before, then we need to add it to the
0863 // list of root dirty Views.
0864
0865 if (!roots.contains(rootDirtyComponent))
0866 roots.add(rootDirtyComponent);
0867 }
0868
0869 /**
0870 * Returns a string that displays and identifies this
0871 * object's properties.
0872 *
0873 * @return a String representation of this object
0874 */
0875 public synchronized String toString() {
0876 StringBuffer sb = new StringBuffer();
0877 if (dirtyComponents != null)
0878 sb.append("" + dirtyComponents);
0879 return sb.toString();
0880 }
0881
0882 /**
0883 * Return the offscreen buffer that should be used as a double buffer with
0884 * the component <code>c</code>.
0885 * By default there is a double buffer per RepaintManager.
0886 * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
0887 * This happens when the maximum double buffer size as been set for the receiving
0888 * repaint manager.
0889 */
0890 public Image getOffscreenBuffer(Component c, int proposedWidth,
0891 int proposedHeight) {
0892 return _getOffscreenBuffer(c, proposedWidth, proposedHeight);
0893 }
0894
0895 /**
0896 * Return a volatile offscreen buffer that should be used as a
0897 * double buffer with the specified component <code>c</code>.
0898 * The image returned will be an instance of VolatileImage, or null
0899 * if a VolatileImage object could not be instantiated.
0900 * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>.
0901 * This happens when the maximum double buffer size has been set for this
0902 * repaint manager.
0903 *
0904 * @see java.awt.image.VolatileImage
0905 * @since 1.4
0906 */
0907 public Image getVolatileOffscreenBuffer(Component c,
0908 int proposedWidth, int proposedHeight) {
0909 GraphicsConfiguration config = c.getGraphicsConfiguration();
0910 if (config == null) {
0911 config = GraphicsEnvironment.getLocalGraphicsEnvironment()
0912 .getDefaultScreenDevice().getDefaultConfiguration();
0913 }
0914 Dimension maxSize = getDoubleBufferMaximumSize();
0915 int width = proposedWidth < 1 ? 1
0916 : (proposedWidth > maxSize.width ? maxSize.width
0917 : proposedWidth);
0918 int height = proposedHeight < 1 ? 1
0919 : (proposedHeight > maxSize.height ? maxSize.height
0920 : proposedHeight);
0921 VolatileImage image = volatileMap.get(config);
0922 if (image == null || image.getWidth() < width
0923 || image.getHeight() < height) {
0924 if (image != null) {
0925 image.flush();
0926 }
0927 image = config.createCompatibleVolatileImage(width, height);
0928 volatileMap.put(config, image);
0929 }
0930 return image;
0931 }
0932
0933 private Image _getOffscreenBuffer(Component c, int proposedWidth,
0934 int proposedHeight) {
0935 Dimension maxSize = getDoubleBufferMaximumSize();
0936 DoubleBufferInfo doubleBuffer = null;
0937 int width, height;
0938
0939 if (standardDoubleBuffer == null) {
0940 standardDoubleBuffer = new DoubleBufferInfo();
0941 }
0942 doubleBuffer = standardDoubleBuffer;
0943
0944 width = proposedWidth < 1 ? 1
0945 : (proposedWidth > maxSize.width ? maxSize.width
0946 : proposedWidth);
0947 height = proposedHeight < 1 ? 1
0948 : (proposedHeight > maxSize.height ? maxSize.height
0949 : proposedHeight);
0950
0951 if (doubleBuffer.needsReset
0952 || (doubleBuffer.image != null && (doubleBuffer.size.width < width || doubleBuffer.size.height < height))) {
0953 doubleBuffer.needsReset = false;
0954 if (doubleBuffer.image != null) {
0955 doubleBuffer.image.flush();
0956 doubleBuffer.image = null;
0957 }
0958 width = Math.max(doubleBuffer.size.width, width);
0959 height = Math.max(doubleBuffer.size.height, height);
0960 }
0961
0962 Image result = doubleBuffer.image;
0963
0964 if (doubleBuffer.image == null) {
0965 result = c.createImage(width, height);
0966 doubleBuffer.size = new Dimension(width, height);
0967 if (c instanceof JComponent) {
0968 ((JComponent) c).setCreatedDoubleBuffer(true);
0969 doubleBuffer.image = result;
0970 }
0971 // JComponent will inform us when it is no longer valid
0972 // (via removeNotify) we have no such hook to other components,
0973 // therefore we don't keep a ref to the Component
0974 // (indirectly through the Image) by stashing the image.
0975 }
0976 return result;
0977 }
0978
0979 /** Set the maximum double buffer size. **/
0980 public void setDoubleBufferMaximumSize(Dimension d) {
0981 doubleBufferMaxSize = d;
0982 if (doubleBufferMaxSize == null) {
0983 clearImages();
0984 } else {
0985 clearImages(d.width, d.height);
0986 }
0987 }
0988
0989 private void clearImages() {
0990 clearImages(0, 0);
0991 }
0992
0993 private void clearImages(int width, int height) {
0994 if (standardDoubleBuffer != null
0995 && standardDoubleBuffer.image != null) {
0996 if (standardDoubleBuffer.image.getWidth(null) > width
0997 || standardDoubleBuffer.image.getHeight(null) > height) {
0998 standardDoubleBuffer.image.flush();
0999 standardDoubleBuffer.image = null;
1000 }
1001 }
1002 // Clear out the VolatileImages
1003 Iterator gcs = volatileMap.keySet().iterator();
1004 while (gcs.hasNext()) {
1005 GraphicsConfiguration gc = (GraphicsConfiguration) gcs
1006 .next();
1007 VolatileImage image = (VolatileImage) volatileMap.get(gc);
1008 if (image.getWidth() > width || image.getHeight() > height) {
1009 image.flush();
1010 gcs.remove();
1011 }
1012 }
1013 }
1014
1015 /**
1016 * Returns the maximum double buffer size.
1017 *
1018 * @return a Dimension object representing the maximum size
1019 */
1020 public Dimension getDoubleBufferMaximumSize() {
1021 if (doubleBufferMaxSize == null) {
1022 try {
1023 Rectangle virtualBounds = new Rectangle();
1024 GraphicsEnvironment ge = GraphicsEnvironment
1025 .getLocalGraphicsEnvironment();
1026 for (GraphicsDevice gd : ge.getScreenDevices()) {
1027 GraphicsConfiguration gc = gd
1028 .getDefaultConfiguration();
1029 virtualBounds = virtualBounds.union(gc.getBounds());
1030 }
1031 doubleBufferMaxSize = new Dimension(
1032 virtualBounds.width, virtualBounds.height);
1033 } catch (HeadlessException e) {
1034 doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE,
1035 Integer.MAX_VALUE);
1036 }
1037 }
1038 return doubleBufferMaxSize;
1039 }
1040
1041 /**
1042 * Enables or disables double buffering in this RepaintManager.
1043 * CAUTION: The default value for this property is set for optimal
1044 * paint performance on the given platform and it is not recommended
1045 * that programs modify this property directly.
1046 *
1047 * @param aFlag true to activate double buffering
1048 * @see #isDoubleBufferingEnabled
1049 */
1050 public void setDoubleBufferingEnabled(boolean aFlag) {
1051 doubleBufferingEnabled = aFlag;
1052 PaintManager paintManager = getPaintManager();
1053 if (!aFlag && paintManager.getClass() != PaintManager.class) {
1054 setPaintManager(new PaintManager());
1055 }
1056 }
1057
1058 /**
1059 * Returns true if this RepaintManager is double buffered.
1060 * The default value for this property may vary from platform
1061 * to platform. On platforms where native double buffering
1062 * is supported in the AWT, the default value will be <code>false</code>
1063 * to avoid unnecessary buffering in Swing.
1064 * On platforms where native double buffering is not supported,
1065 * the default value will be <code>true</code>.
1066 *
1067 * @return true if this object is double buffered
1068 */
1069 public boolean isDoubleBufferingEnabled() {
1070 return doubleBufferingEnabled;
1071 }
1072
1073 /**
1074 * This resets the double buffer. Actually, it marks the double buffer
1075 * as invalid, the double buffer will then be recreated on the next
1076 * invocation of getOffscreenBuffer.
1077 */
1078 void resetDoubleBuffer() {
1079 if (standardDoubleBuffer != null) {
1080 standardDoubleBuffer.needsReset = true;
1081 }
1082 }
1083
1084 /**
1085 * This resets the volatile double buffer.
1086 */
1087 void resetVolatileDoubleBuffer(GraphicsConfiguration gc) {
1088 Image image = volatileMap.remove(gc);
1089 if (image != null) {
1090 image.flush();
1091 }
1092 }
1093
1094 /**
1095 * Returns true if we should use the <code>Image</code> returned
1096 * from <code>getVolatileOffscreenBuffer</code> to do double buffering.
1097 */
1098 boolean useVolatileDoubleBuffer() {
1099 return volatileImageBufferEnabled;
1100 }
1101
1102 /**
1103 * Returns true if the current thread is the thread painting. This
1104 * will return false if no threads are painting.
1105 */
1106 private synchronized boolean isPaintingThread() {
1107 return (Thread.currentThread() == paintThread);
1108 }
1109
1110 //
1111 // Paint methods. You very, VERY rarely need to invoke these.
1112 // They are invoked directly from JComponent's painting code and
1113 // when painting happens outside the normal flow: DefaultDesktopManager
1114 // and JViewport. If you end up needing these methods in other places be
1115 // careful that you don't get stuck in a paint loop.
1116 //
1117
1118 /**
1119 * Paints a region of a component
1120 *
1121 * @param paintingComponent Component to paint
1122 * @param bufferComponent Component to obtain buffer for
1123 * @param g Graphics to paint to
1124 * @param x X-coordinate
1125 * @param y Y-coordinate
1126 * @param w Width
1127 * @param h Height
1128 */
1129 void paint(JComponent paintingComponent,
1130 JComponent bufferComponent, Graphics g, int x, int y,
1131 int w, int h) {
1132 PaintManager paintManager = getPaintManager();
1133 if (!isPaintingThread()) {
1134 // We're painting to two threads at once. PaintManager deals
1135 // with this a bit better than BufferStrategyPaintManager, use
1136 // it to avoid possible exceptions/corruption.
1137 if (paintManager.getClass() != PaintManager.class) {
1138 paintManager = new PaintManager();
1139 paintManager.repaintManager = this ;
1140 }
1141 }
1142 if (!paintManager.paint(paintingComponent, bufferComponent, g,
1143 x, y, w, h)) {
1144 g.setClip(x, y, w, h);
1145 paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y
1146 + h);
1147 }
1148 }
1149
1150 /**
1151 * Does a copy area on the specified region.
1152 *
1153 * @param clip Whether or not the copyArea needs to be clipped to the
1154 * Component's bounds.
1155 */
1156 void copyArea(JComponent c, Graphics g, int x, int y, int w, int h,
1157 int deltaX, int deltaY, boolean clip) {
1158 getPaintManager().copyArea(c, g, x, y, w, h, deltaX, deltaY,
1159 clip);
1160 }
1161
1162 /**
1163 * Invoked prior to any paint/copyArea method calls. This will
1164 * be followed by an invocation of <code>endPaint</code>.
1165 * <b>WARNING</b>: Callers of this method need to wrap the call
1166 * in a <code>try/finally</code>, otherwise if an exception is thrown
1167 * during the course of painting the RepaintManager may
1168 * be left in a state in which the screen is not updated, eg:
1169 * <pre>
1170 * repaintManager.beginPaint();
1171 * try {
1172 * repaintManager.paint(...);
1173 * } finally {
1174 * repaintManager.endPaint();
1175 * }
1176 * </pre>
1177 */
1178 void beginPaint() {
1179 boolean multiThreadedPaint = false;
1180 int paintDepth = 0;
1181 Thread currentThread = Thread.currentThread();
1182 synchronized (this ) {
1183 paintDepth = this .paintDepth;
1184 if (paintThread == null || currentThread == paintThread) {
1185 paintThread = currentThread;
1186 this .paintDepth++;
1187 } else {
1188 multiThreadedPaint = true;
1189 }
1190 }
1191 if (!multiThreadedPaint && paintDepth == 0) {
1192 getPaintManager().beginPaint();
1193 }
1194 }
1195
1196 /**
1197 * Invoked after <code>beginPaint</code> has been invoked.
1198 */
1199 void endPaint() {
1200 if (isPaintingThread()) {
1201 PaintManager paintManager = null;
1202 synchronized (this ) {
1203 if (--paintDepth == 0) {
1204 paintManager = getPaintManager();
1205 }
1206 }
1207 if (paintManager != null) {
1208 paintManager.endPaint();
1209 synchronized (this ) {
1210 paintThread = null;
1211 }
1212 }
1213 }
1214 }
1215
1216 /**
1217 * If possible this will show a previously rendered portion of
1218 * a Component. If successful, this will return true, otherwise false.
1219 * <p>
1220 * WARNING: This method is invoked from the native toolkit thread, be
1221 * very careful as to what methods this invokes!
1222 */
1223 boolean show(Container c, int x, int y, int w, int h) {
1224 return getPaintManager().show(c, x, y, w, h);
1225 }
1226
1227 /**
1228 * Invoked when the doubleBuffered or useTrueDoubleBuffering
1229 * properties of a JRootPane change. This may come in on any thread.
1230 */
1231 void doubleBufferingChanged(JRootPane rootPane) {
1232 getPaintManager().doubleBufferingChanged(rootPane);
1233 }
1234
1235 /**
1236 * Sets the <code>PaintManager</code> that is used to handle all
1237 * double buffered painting.
1238 *
1239 * @param paintManager The PaintManager to use. Passing in null indicates
1240 * the fallback PaintManager should be used.
1241 */
1242 void setPaintManager(PaintManager paintManager) {
1243 if (paintManager == null) {
1244 paintManager = new PaintManager();
1245 }
1246 PaintManager oldPaintManager;
1247 synchronized (this ) {
1248 oldPaintManager = this .paintManager;
1249 this .paintManager = paintManager;
1250 paintManager.repaintManager = this ;
1251 }
1252 if (oldPaintManager != null) {
1253 oldPaintManager.dispose();
1254 }
1255 }
1256
1257 private synchronized PaintManager getPaintManager() {
1258 if (paintManager == null) {
1259 PaintManager paintManager = null;
1260 if (doubleBufferingEnabled && !nativeDoubleBuffering) {
1261 switch (bufferStrategyType) {
1262 case BUFFER_STRATEGY_NOT_SPECIFIED:
1263 if (((SunToolkit) Toolkit.getDefaultToolkit())
1264 .useBufferPerWindow()) {
1265 paintManager = new BufferStrategyPaintManager();
1266 }
1267 break;
1268 case BUFFER_STRATEGY_SPECIFIED_ON:
1269 paintManager = new BufferStrategyPaintManager();
1270 break;
1271 default:
1272 break;
1273 }
1274 }
1275 // null case handled in setPaintManager
1276 setPaintManager(paintManager);
1277 }
1278 return paintManager;
1279 }
1280
1281 private void scheduleProcessingRunnable() {
1282 scheduleProcessingRunnable(AppContext.getAppContext());
1283 }
1284
1285 private void scheduleProcessingRunnable(AppContext context) {
1286 if (processingRunnable.markPending()) {
1287 SunToolkit.getSystemEventQueueImplPP(context).postEvent(
1288 new InvocationEvent(Toolkit.getDefaultToolkit(),
1289 processingRunnable));
1290 }
1291 }
1292
1293 /**
1294 * PaintManager is used to handle all double buffered painting for
1295 * Swing. Subclasses should call back into the JComponent method
1296 * <code>paintToOffscreen</code> to handle the actual painting.
1297 */
1298 static class PaintManager {
1299 /**
1300 * RepaintManager the PaintManager has been installed on.
1301 */
1302 protected RepaintManager repaintManager;
1303 boolean isRepaintingRoot;
1304
1305 /**
1306 * Paints a region of a component
1307 *
1308 * @param paintingComponent Component to paint
1309 * @param bufferComponent Component to obtain buffer for
1310 * @param g Graphics to paint to
1311 * @param x X-coordinate
1312 * @param y Y-coordinate
1313 * @param w Width
1314 * @param h Height
1315 * @return true if painting was successful.
1316 */
1317 public boolean paint(JComponent paintingComponent,
1318 JComponent bufferComponent, Graphics g, int x, int y,
1319 int w, int h) {
1320 // First attempt to use VolatileImage buffer for performance.
1321 // If this fails (which should rarely occur), fallback to a
1322 // standard Image buffer.
1323 boolean paintCompleted = false;
1324 Image offscreen;
1325 if (repaintManager.useVolatileDoubleBuffer()
1326 && (offscreen = getValidImage(repaintManager
1327 .getVolatileOffscreenBuffer(
1328 bufferComponent, w, h))) != null) {
1329 VolatileImage vImage = (java.awt.image.VolatileImage) offscreen;
1330 GraphicsConfiguration gc = bufferComponent
1331 .getGraphicsConfiguration();
1332 for (int i = 0; !paintCompleted
1333 && i < RepaintManager.VOLATILE_LOOP_MAX; i++) {
1334 if (vImage.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
1335 repaintManager.resetVolatileDoubleBuffer(gc);
1336 offscreen = repaintManager
1337 .getVolatileOffscreenBuffer(
1338 bufferComponent, w, h);
1339 vImage = (java.awt.image.VolatileImage) offscreen;
1340 }
1341 paintDoubleBuffered(paintingComponent, vImage, g,
1342 x, y, w, h);
1343 paintCompleted = !vImage.contentsLost();
1344 }
1345 }
1346 // VolatileImage painting loop failed, fallback to regular
1347 // offscreen buffer
1348 if (!paintCompleted
1349 && (offscreen = getValidImage(repaintManager
1350 .getOffscreenBuffer(bufferComponent, w, h))) != null) {
1351 paintDoubleBuffered(paintingComponent, offscreen, g, x,
1352 y, w, h);
1353 paintCompleted = true;
1354 }
1355 return paintCompleted;
1356 }
1357
1358 /**
1359 * Does a copy area on the specified region.
1360 */
1361 public void copyArea(JComponent c, Graphics g, int x, int y,
1362 int w, int h, int deltaX, int deltaY, boolean clip) {
1363 g.copyArea(x, y, w, h, deltaX, deltaY);
1364 }
1365
1366 /**
1367 * Invoked prior to any calls to paint or copyArea.
1368 */
1369 public void beginPaint() {
1370 }
1371
1372 /**
1373 * Invoked to indicate painting has been completed.
1374 */
1375 public void endPaint() {
1376 }
1377
1378 /**
1379 * Shows a region of a previously rendered component. This
1380 * will return true if successful, false otherwise. The default
1381 * implementation returns false.
1382 */
1383 public boolean show(Container c, int x, int y, int w, int h) {
1384 return false;
1385 }
1386
1387 /**
1388 * Invoked when the doubleBuffered or useTrueDoubleBuffering
1389 * properties of a JRootPane change. This may come in on any thread.
1390 */
1391 public void doubleBufferingChanged(JRootPane rootPane) {
1392 }
1393
1394 /**
1395 * Paints a portion of a component to an offscreen buffer.
1396 */
1397 protected void paintDoubleBuffered(JComponent c, Image image,
1398 Graphics g, int clipX, int clipY, int clipW, int clipH) {
1399 Graphics osg = image.getGraphics();
1400 int bw = Math.min(clipW, image.getWidth(null));
1401 int bh = Math.min(clipH, image.getHeight(null));
1402 int x, y, maxx, maxy;
1403
1404 try {
1405 for (x = clipX, maxx = clipX + clipW; x < maxx; x += bw) {
1406 for (y = clipY, maxy = clipY + clipH; y < maxy; y += bh) {
1407 osg.translate(-x, -y);
1408 osg.setClip(x, y, bw, bh);
1409 c.paintToOffscreen(osg, x, y, bw, bh, maxx,
1410 maxy);
1411 g.setClip(x, y, bw, bh);
1412 g.drawImage(image, x, y, c);
1413 osg.translate(x, y);
1414 }
1415 }
1416 } finally {
1417 osg.dispose();
1418 }
1419 }
1420
1421 /**
1422 * If <code>image</code> is non-null with a positive size it
1423 * is returned, otherwise null is returned.
1424 */
1425 private Image getValidImage(Image image) {
1426 if (image != null && image.getWidth(null) > 0
1427 && image.getHeight(null) > 0) {
1428 return image;
1429 }
1430 return null;
1431 }
1432
1433 /**
1434 * Schedules a repaint for the specified component. This differs
1435 * from <code>root.repaint</code> in that if the RepaintManager is
1436 * currently processing paint requests it'll process this request
1437 * with the current set of requests.
1438 */
1439 protected void repaintRoot(JComponent root) {
1440 assert (repaintManager.repaintRoot == null);
1441 if (repaintManager.painting) {
1442 repaintManager.repaintRoot = root;
1443 } else {
1444 root.repaint();
1445 }
1446 }
1447
1448 /**
1449 * Returns true if the component being painted is the root component
1450 * that was previously passed to <code>repaintRoot</code>.
1451 */
1452 protected boolean isRepaintingRoot() {
1453 return isRepaintingRoot;
1454 }
1455
1456 /**
1457 * Cleans up any state. After invoked the PaintManager will no
1458 * longer be used anymore.
1459 */
1460 protected void dispose() {
1461 }
1462 }
1463
1464 private class DoubleBufferInfo {
1465 public Image image;
1466 public Dimension size;
1467 public boolean needsReset = false;
1468 }
1469
1470 /**
1471 * Listener installed to detect display changes. When display changes,
1472 * schedules a callback to notify all RepaintManagers of the display
1473 * changes. Only one DisplayChangedHandler is ever installed. The
1474 * singleton instance will schedule notification for all AppContexts.
1475 */
1476 private static final class DisplayChangedHandler implements
1477 DisplayChangedListener {
1478 public void displayChanged() {
1479 scheduleDisplayChanges();
1480 }
1481
1482 public void paletteChanged() {
1483 }
1484
1485 private void scheduleDisplayChanges() {
1486 // To avoid threading problems, we notify each RepaintManager
1487 // on the thread it was created on.
1488 for (Object c : AppContext.getAppContexts()) {
1489 AppContext context = (AppContext) c;
1490 synchronized (context) {
1491 if (!context.isDisposed()) {
1492 EventQueue eventQueue = (EventQueue) context
1493 .get(AppContext.EVENT_QUEUE_KEY);
1494 if (eventQueue != null) {
1495 eventQueue.postEvent(new InvocationEvent(
1496 Toolkit.getDefaultToolkit(),
1497 new DisplayChangedRunnable()));
1498 }
1499 }
1500 }
1501 }
1502 }
1503 }
1504
1505 private static final class DisplayChangedRunnable implements
1506 Runnable {
1507 public void run() {
1508 RepaintManager.currentManager((JComponent) null)
1509 .displayChanged();
1510 }
1511 }
1512
1513 /**
1514 * Runnable used to process all repaint/revalidate requests.
1515 */
1516 private final class ProcessingRunnable implements Runnable {
1517 // If true, we're wainting on the EventQueue.
1518 private boolean pending;
1519
1520 /**
1521 * Marks this processing runnable as pending. If this was not
1522 * already marked as pending, true is returned.
1523 */
1524 public synchronized boolean markPending() {
1525 if (!pending) {
1526 pending = true;
1527 return true;
1528 }
1529 return false;
1530 }
1531
1532 public void run() {
1533 synchronized (this ) {
1534 pending = false;
1535 }
1536 // First pass, flush any heavy paint events into real paint
1537 // events. If there are pending heavy weight requests this will
1538 // result in q'ing this request up one more time. As
1539 // long as no other requests come in between now and the time
1540 // the second one is processed nothing will happen. This is not
1541 // ideal, but the logic needed to suppress the second request is
1542 // more headache than it's worth.
1543 scheduleHeavyWeightPaints();
1544 // Do the actual validation and painting.
1545 validateInvalidComponents();
1546 prePaintDirtyRegions();
1547 }
1548 }
1549 }
|