GNU Classpath (0.95) | |
Frames | No Frames |
1: /* RepaintManager.java -- 2: Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import gnu.classpath.SystemProperties; 42: import gnu.java.awt.LowPriorityEvent; 43: 44: import java.applet.Applet; 45: import java.awt.Component; 46: import java.awt.Dimension; 47: import java.awt.EventQueue; 48: import java.awt.Graphics; 49: import java.awt.Image; 50: import java.awt.Rectangle; 51: import java.awt.Toolkit; 52: import java.awt.Window; 53: import java.awt.event.InvocationEvent; 54: import java.awt.image.VolatileImage; 55: import java.util.ArrayList; 56: import java.util.HashMap; 57: import java.util.HashSet; 58: import java.util.Iterator; 59: import java.util.Set; 60: import java.util.WeakHashMap; 61: 62: import javax.swing.text.JTextComponent; 63: 64: /** 65: * <p>The repaint manager holds a set of dirty regions, invalid components, 66: * and a double buffer surface. The dirty regions and invalid components 67: * are used to coalesce multiple revalidate() and repaint() calls in the 68: * component tree into larger groups to be refreshed "all at once"; the 69: * double buffer surface is used by root components to paint 70: * themselves.</p> 71: * 72: * <p>See <a 73: * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this 74: * document</a> for more details.</p> 75: * document</a> for more details.</p> 76: * 77: * @author Roman Kennke (kennke@aicas.com) 78: * @author Graydon Hoare (graydon@redhat.com) 79: * @author Audrius Meskauskas (audriusa@bioinformatics.org) 80: */ 81: public class RepaintManager 82: { 83: /** 84: * An InvocationEvent subclass that implements LowPriorityEvent. This is used 85: * to defer the execution of RepaintManager requests as long as possible on 86: * the event queue. This way we make sure that all available input is 87: * processed before getting active with the RepaintManager. This allows 88: * for better optimization (more validate and repaint requests can be 89: * coalesced) and thus has a positive effect on performance for GUI 90: * applications under heavy load. 91: */ 92: private static class RepaintWorkerEvent 93: extends InvocationEvent 94: implements LowPriorityEvent 95: { 96: 97: /** 98: * Creates a new RepaintManager event. 99: * 100: * @param source the source 101: * @param runnable the runnable to execute 102: */ 103: public RepaintWorkerEvent(Object source, Runnable runnable, 104: Object notifier, boolean catchEx) 105: { 106: super(source, runnable, notifier, catchEx); 107: } 108: 109: /** 110: * An application that I met implements its own event dispatching and 111: * calls dispatch() via reflection, and only checks declared methods, 112: * that is, it expects this method to be in the event's class, not 113: * in a superclass. So I put this in here... sigh. 114: */ 115: public void dispatch() 116: { 117: super.dispatch(); 118: } 119: } 120: 121: /** 122: * The current repaint managers, indexed by their ThreadGroups. 123: */ 124: static WeakHashMap currentRepaintManagers; 125: 126: /** 127: * A rectangle object to be reused in damaged regions calculation. 128: */ 129: private static Rectangle rectCache = new Rectangle(); 130: 131: /** 132: * <p>A helper class which is placed into the system event queue at 133: * various times in order to facilitate repainting and layout. There is 134: * typically only one of these objects active at any time. When the 135: * {@link RepaintManager} is told to queue a repaint, it checks to see if 136: * a {@link RepaintWorker} is "live" in the system event queue, and if 137: * not it inserts one using {@link SwingUtilities#invokeLater}.</p> 138: * 139: * <p>When the {@link RepaintWorker} comes to the head of the system 140: * event queue, its {@link RepaintWorker#run} method is executed by the 141: * swing paint thread, which revalidates all invalid components and 142: * repaints any damage in the swing scene.</p> 143: */ 144: private class RepaintWorker 145: implements Runnable 146: { 147: 148: boolean live; 149: 150: public RepaintWorker() 151: { 152: live = false; 153: } 154: 155: public synchronized void setLive(boolean b) 156: { 157: live = b; 158: } 159: 160: public synchronized boolean isLive() 161: { 162: return live; 163: } 164: 165: public void run() 166: { 167: try 168: { 169: ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 170: RepaintManager rm = 171: (RepaintManager) currentRepaintManagers.get(threadGroup); 172: rm.validateInvalidComponents(); 173: rm.paintDirtyRegions(); 174: } 175: finally 176: { 177: setLive(false); 178: } 179: } 180: 181: } 182: 183: /** 184: * A table storing the dirty regions of components. The keys of this 185: * table are components, the values are rectangles. Each component maps 186: * to exactly one rectangle. When more regions are marked as dirty on a 187: * component, they are union'ed with the existing rectangle. 188: * 189: * This is package private to avoid a synthetic accessor method in inner 190: * class. 191: * 192: * @see #addDirtyRegion 193: * @see #getDirtyRegion 194: * @see #isCompletelyDirty 195: * @see #markCompletelyClean 196: * @see #markCompletelyDirty 197: */ 198: private HashMap dirtyComponents; 199: 200: /** 201: * The dirtyComponents which is used in paintDiryRegions to avoid unnecessary 202: * locking. 203: */ 204: private HashMap dirtyComponentsWork; 205: 206: /** 207: * A single, shared instance of the helper class. Any methods which mark 208: * components as invalid or dirty eventually activate this instance. It 209: * is added to the event queue if it is not already active, otherwise 210: * reused. 211: * 212: * @see #addDirtyRegion 213: * @see #addInvalidComponent 214: */ 215: private RepaintWorker repaintWorker; 216: 217: /** 218: * The set of components which need revalidation, in the "layout" sense. 219: * There is no additional information about "what kind of layout" they 220: * need (as there is with dirty regions), so it is just a vector rather 221: * than a table. 222: * 223: * @see #addInvalidComponent 224: * @see #removeInvalidComponent 225: * @see #validateInvalidComponents 226: */ 227: private ArrayList invalidComponents; 228: 229: /** 230: * Whether or not double buffering is enabled on this repaint 231: * manager. This is merely a hint to clients; the RepaintManager will 232: * always return an offscreen buffer when one is requested. 233: * 234: * @see #isDoubleBufferingEnabled 235: * @see #setDoubleBufferingEnabled 236: */ 237: private boolean doubleBufferingEnabled; 238: 239: /** 240: * The offscreen buffers. This map holds one offscreen buffer per 241: * Window/Applet and releases them as soon as the Window/Applet gets garbage 242: * collected. 243: */ 244: private WeakHashMap offscreenBuffers; 245: 246: /** 247: * The maximum width and height to allocate as a double buffer. Requests 248: * beyond this size are ignored. 249: * 250: * @see #paintDirtyRegions 251: * @see #getDoubleBufferMaximumSize 252: * @see #setDoubleBufferMaximumSize 253: */ 254: private Dimension doubleBufferMaximumSize; 255: 256: 257: /** 258: * Create a new RepaintManager object. 259: */ 260: public RepaintManager() 261: { 262: dirtyComponents = new HashMap(); 263: dirtyComponentsWork = new HashMap(); 264: invalidComponents = new ArrayList(); 265: repaintWorker = new RepaintWorker(); 266: doubleBufferMaximumSize = new Dimension(2000,2000); 267: doubleBufferingEnabled = 268: SystemProperties.getProperty("gnu.swing.doublebuffering", "true") 269: .equals("true"); 270: offscreenBuffers = new WeakHashMap(); 271: } 272: 273: /** 274: * Returns the <code>RepaintManager</code> for the current thread's 275: * thread group. The default implementation ignores the 276: * <code>component</code> parameter and returns the same repaint manager 277: * for all components. 278: * 279: * @param component a component to look up the manager of 280: * 281: * @return the current repaint manager for the calling thread's thread group 282: * and the specified component 283: * 284: * @see #setCurrentManager 285: */ 286: public static RepaintManager currentManager(Component component) 287: { 288: if (currentRepaintManagers == null) 289: currentRepaintManagers = new WeakHashMap(); 290: ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 291: RepaintManager currentManager = 292: (RepaintManager) currentRepaintManagers.get(threadGroup); 293: if (currentManager == null) 294: { 295: currentManager = new RepaintManager(); 296: currentRepaintManagers.put(threadGroup, currentManager); 297: } 298: return currentManager; 299: } 300: 301: /** 302: * Returns the <code>RepaintManager</code> for the current thread's 303: * thread group. The default implementation ignores the 304: * <code>component</code> parameter and returns the same repaint manager 305: * for all components. 306: * 307: * This method is only here for backwards compatibility with older versions 308: * of Swing and simply forwards to {@link #currentManager(Component)}. 309: * 310: * @param component a component to look up the manager of 311: * 312: * @return the current repaint manager for the calling thread's thread group 313: * and the specified component 314: * 315: * @see #setCurrentManager 316: */ 317: public static RepaintManager currentManager(JComponent component) 318: { 319: return currentManager((Component)component); 320: } 321: 322: /** 323: * Sets the repaint manager for the calling thread's thread group. 324: * 325: * @param manager the repaint manager to set for the current thread's thread 326: * group 327: * 328: * @see #currentManager(Component) 329: */ 330: public static void setCurrentManager(RepaintManager manager) 331: { 332: if (currentRepaintManagers == null) 333: currentRepaintManagers = new WeakHashMap(); 334: 335: ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 336: currentRepaintManagers.put(threadGroup, manager); 337: } 338: 339: /** 340: * Add a component to the {@link #invalidComponents} vector. If the 341: * {@link #repaintWorker} class is not active, insert it in the system 342: * event queue. 343: * 344: * @param component The component to add 345: * 346: * @see #removeInvalidComponent 347: */ 348: public void addInvalidComponent(JComponent component) 349: { 350: Component validateRoot = null; 351: Component c = component; 352: while (c != null) 353: { 354: // Special cases we don't bother validating are when the invalidated 355: // component (or any of it's ancestors) is inside a CellRendererPane 356: // or if it doesn't have a peer yet (== not displayable). 357: if (c instanceof CellRendererPane || ! c.isDisplayable()) 358: return; 359: if (c instanceof JComponent && ((JComponent) c).isValidateRoot()) 360: { 361: validateRoot = c; 362: break; 363: } 364: 365: c = c.getParent(); 366: } 367: 368: // If we didn't find a validate root, then we don't validate. 369: if (validateRoot == null) 370: return; 371: 372: // Make sure the validate root and all of it's ancestors are visible. 373: c = validateRoot; 374: while (c != null) 375: { 376: if (! c.isVisible() || ! c.isDisplayable()) 377: return; 378: c = c.getParent(); 379: } 380: 381: if (invalidComponents.contains(validateRoot)) 382: return; 383: 384: //synchronized (invalidComponents) 385: // { 386: invalidComponents.add(validateRoot); 387: // } 388: 389: if (! repaintWorker.isLive()) 390: { 391: repaintWorker.setLive(true); 392: invokeLater(repaintWorker); 393: } 394: } 395: 396: /** 397: * Remove a component from the {@link #invalidComponents} vector. 398: * 399: * @param component The component to remove 400: * 401: * @see #addInvalidComponent 402: */ 403: public void removeInvalidComponent(JComponent component) 404: { 405: synchronized (invalidComponents) 406: { 407: invalidComponents.remove(component); 408: } 409: } 410: 411: /** 412: * Add a region to the set of dirty regions for a specified component. 413: * This involves union'ing the new region with any existing dirty region 414: * associated with the component. If the {@link #repaintWorker} class 415: * is not active, insert it in the system event queue. 416: * 417: * @param component The component to add a dirty region for 418: * @param x The left x coordinate of the new dirty region 419: * @param y The top y coordinate of the new dirty region 420: * @param w The width of the new dirty region 421: * @param h The height of the new dirty region 422: * 423: * @see #addDirtyRegion 424: * @see #getDirtyRegion 425: * @see #isCompletelyDirty 426: * @see #markCompletelyClean 427: * @see #markCompletelyDirty 428: */ 429: public void addDirtyRegion(JComponent component, int x, int y, 430: int w, int h) 431: { 432: if (w <= 0 || h <= 0 || !component.isShowing()) 433: return; 434: component.computeVisibleRect(rectCache); 435: SwingUtilities.computeIntersection(x, y, w, h, rectCache); 436: 437: if (! rectCache.isEmpty()) 438: { 439: synchronized (dirtyComponents) 440: { 441: Rectangle dirtyRect = (Rectangle)dirtyComponents.get(component); 442: if (dirtyRect != null) 443: { 444: SwingUtilities.computeUnion(rectCache.x, rectCache.y, 445: rectCache.width, rectCache.height, 446: dirtyRect); 447: } 448: else 449: { 450: dirtyComponents.put(component, rectCache.getBounds()); 451: } 452: } 453: 454: if (! repaintWorker.isLive()) 455: { 456: repaintWorker.setLive(true); 457: invokeLater(repaintWorker); 458: } 459: } 460: } 461: 462: /** 463: * Get the dirty region associated with a component, or <code>null</code> 464: * if the component has no dirty region. 465: * 466: * @param component The component to get the dirty region of 467: * 468: * @return The dirty region of the component 469: * 470: * @see #dirtyComponents 471: * @see #addDirtyRegion 472: * @see #isCompletelyDirty 473: * @see #markCompletelyClean 474: * @see #markCompletelyDirty 475: */ 476: public Rectangle getDirtyRegion(JComponent component) 477: { 478: Rectangle dirty = (Rectangle) dirtyComponents.get(component); 479: if (dirty == null) 480: dirty = new Rectangle(); 481: return dirty; 482: } 483: 484: /** 485: * Mark a component as dirty over its entire bounds. 486: * 487: * @param component The component to mark as dirty 488: * 489: * @see #dirtyComponents 490: * @see #addDirtyRegion 491: * @see #getDirtyRegion 492: * @see #isCompletelyDirty 493: * @see #markCompletelyClean 494: */ 495: public void markCompletelyDirty(JComponent component) 496: { 497: addDirtyRegion(component, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); 498: } 499: 500: /** 501: * Remove all dirty regions for a specified component 502: * 503: * @param component The component to mark as clean 504: * 505: * @see #dirtyComponents 506: * @see #addDirtyRegion 507: * @see #getDirtyRegion 508: * @see #isCompletelyDirty 509: * @see #markCompletelyDirty 510: */ 511: public void markCompletelyClean(JComponent component) 512: { 513: synchronized (dirtyComponents) 514: { 515: dirtyComponents.remove(component); 516: } 517: } 518: 519: /** 520: * Return <code>true</code> if the specified component is completely 521: * contained within its dirty region, otherwise <code>false</code> 522: * 523: * @param component The component to check for complete dirtyness 524: * 525: * @return Whether the component is completely dirty 526: * 527: * @see #dirtyComponents 528: * @see #addDirtyRegion 529: * @see #getDirtyRegion 530: * @see #isCompletelyDirty 531: * @see #markCompletelyClean 532: */ 533: public boolean isCompletelyDirty(JComponent component) 534: { 535: boolean dirty = false; 536: Rectangle r = getDirtyRegion(component); 537: if(r.width == Integer.MAX_VALUE && r.height == Integer.MAX_VALUE) 538: dirty = true; 539: return dirty; 540: } 541: 542: /** 543: * Validate all components which have been marked invalid in the {@link 544: * #invalidComponents} vector. 545: */ 546: public void validateInvalidComponents() 547: { 548: // We don't use an iterator here because that would fail when there are 549: // components invalidated during the validation of others, which happens 550: // quite frequently. Instead we synchronize the access a little more. 551: while (invalidComponents.size() > 0) 552: { 553: Component comp; 554: synchronized (invalidComponents) 555: { 556: comp = (Component) invalidComponents.remove(0); 557: } 558: // Validate the validate component. 559: if (! (comp.isVisible() && comp.isShowing())) 560: continue; 561: comp.validate(); 562: } 563: } 564: 565: /** 566: * Repaint all regions of all components which have been marked dirty in the 567: * {@link #dirtyComponents} table. 568: */ 569: public void paintDirtyRegions() 570: { 571: // Short circuit if there is nothing to paint. 572: if (dirtyComponents.size() == 0) 573: return; 574: 575: // Swap dirtyRegions with dirtyRegionsWork to avoid locking. 576: synchronized (dirtyComponents) 577: { 578: HashMap swap = dirtyComponents; 579: dirtyComponents = dirtyComponentsWork; 580: dirtyComponentsWork = swap; 581: } 582: 583: // Compile a set of repaint roots. 584: HashSet repaintRoots = new HashSet(); 585: Set components = dirtyComponentsWork.keySet(); 586: for (Iterator i = components.iterator(); i.hasNext();) 587: { 588: JComponent dirty = (JComponent) i.next(); 589: compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots); 590: } 591: 592: for (Iterator i = repaintRoots.iterator(); i.hasNext();) 593: { 594: JComponent comp = (JComponent) i.next(); 595: Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp); 596: if (damaged == null || damaged.isEmpty()) 597: continue; 598: comp.paintImmediately(damaged); 599: } 600: dirtyComponentsWork.clear(); 601: } 602: 603: /** 604: * Compiles a list of components that really get repainted. This is called 605: * once for each component in the dirtyRegions HashMap, each time with 606: * another <code>dirty</code> parameter. This searches up the component 607: * hierarchy of <code>dirty</code> to find the highest parent that is also 608: * marked dirty and merges the dirty regions. 609: * 610: * @param dirtyRegions the dirty regions 611: * @param dirty the component for which to find the repaint root 612: * @param roots the list to which new repaint roots get appended 613: */ 614: private void compileRepaintRoots(HashMap dirtyRegions, JComponent dirty, 615: HashSet roots) 616: { 617: Component current = dirty; 618: Component root = dirty; 619: 620: // This will contain the dirty region in the root coordinate system, 621: // possibly clipped by ancestor's bounds. 622: Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty); 623: rectCache.setBounds(originalDirtyRect); 624: 625: // The bounds of the current component. 626: int x = dirty.getX(); 627: int y = dirty.getY(); 628: int w = dirty.getWidth(); 629: int h = dirty.getHeight(); 630: 631: // Do nothing if dirty region is clipped away by the component's bounds. 632: rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache); 633: if (rectCache.isEmpty()) 634: return; 635: 636: // The cumulated offsets. 637: int dx = 0; 638: int dy = 0; 639: // The actual offset for the found root. 640: int rootDx = 0; 641: int rootDy = 0; 642: 643: // Search the highest component that is also marked dirty. 644: Component parent; 645: while (true) 646: { 647: parent = current.getParent(); 648: if (parent == null || !(parent instanceof JComponent)) 649: break; 650: 651: current = parent; 652: // Update the offset. 653: dx += x; 654: dy += y; 655: rectCache.x += x; 656: rectCache.y += y; 657: 658: x = current.getX(); 659: y = current.getY(); 660: w = current.getWidth(); 661: h = current.getHeight(); 662: rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache); 663: 664: // Don't paint if the dirty regions is clipped away by any of 665: // its ancestors. 666: if (rectCache.isEmpty()) 667: return; 668: 669: // We can skip to the next up when this parent is not dirty. 670: if (dirtyRegions.containsKey(parent)) 671: { 672: root = current; 673: rootDx = dx; 674: rootDy = dy; 675: } 676: } 677: 678: // Merge the rectangles of the root and the requested component if 679: // the are different. 680: if (root != dirty) 681: { 682: rectCache.x += rootDx - dx; 683: rectCache.y += rootDy - dy; 684: Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root); 685: SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width, 686: rectCache.height, dirtyRect); 687: } 688: 689: // Adds the root to the roots set. 690: if (! roots.contains(root)) 691: roots.add(root); 692: } 693: 694: /** 695: * Get an offscreen buffer for painting a component's image. This image 696: * may be smaller than the proposed dimensions, depending on the value of 697: * the {@link #doubleBufferMaximumSize} property. 698: * 699: * @param component The component to return an offscreen buffer for 700: * @param proposedWidth The proposed width of the offscreen buffer 701: * @param proposedHeight The proposed height of the offscreen buffer 702: * 703: * @return A shared offscreen buffer for painting 704: */ 705: public Image getOffscreenBuffer(Component component, int proposedWidth, 706: int proposedHeight) 707: { 708: Component root = SwingUtilities.getWindowAncestor(component); 709: Image buffer = (Image) offscreenBuffers.get(root); 710: if (buffer == null 711: || buffer.getWidth(null) < proposedWidth 712: || buffer.getHeight(null) < proposedHeight) 713: { 714: int width = Math.max(proposedWidth, root.getWidth()); 715: width = Math.min(doubleBufferMaximumSize.width, width); 716: int height = Math.max(proposedHeight, root.getHeight()); 717: height = Math.min(doubleBufferMaximumSize.height, height); 718: buffer = component.createImage(width, height); 719: offscreenBuffers.put(root, buffer); 720: } 721: return buffer; 722: } 723: 724: /** 725: * Blits the back buffer of the specified root component to the screen. 726: * This is package private because it must get called by JComponent. 727: * 728: * @param comp the component to be painted 729: * @param x the area to paint on screen, in comp coordinates 730: * @param y the area to paint on screen, in comp coordinates 731: * @param w the area to paint on screen, in comp coordinates 732: * @param h the area to paint on screen, in comp coordinates 733: */ 734: void commitBuffer(Component comp, int x, int y, int w, int h) 735: { 736: Component root = comp; 737: while (root != null 738: && ! (root instanceof Window || root instanceof Applet)) 739: { 740: x += root.getX(); 741: y += root.getY(); 742: root = root.getParent(); 743: } 744: 745: if (root != null) 746: { 747: Graphics g = root.getGraphics(); 748: Image buffer = (Image) offscreenBuffers.get(root); 749: if (buffer != null) 750: { 751: // Make sure we have a sane clip at this point. 752: g.clipRect(x, y, w, h); 753: g.drawImage(buffer, 0, 0, root); 754: g.dispose(); 755: } 756: } 757: } 758: 759: /** 760: * Creates and returns a volatile offscreen buffer for the specified 761: * component that can be used as a double buffer. The returned image 762: * is a {@link VolatileImage}. Its size will be <code>(proposedWidth, 763: * proposedHeight)</code> except when the maximum double buffer size 764: * has been set in this RepaintManager. 765: * 766: * @param comp the Component for which to create a volatile buffer 767: * @param proposedWidth the proposed width of the buffer 768: * @param proposedHeight the proposed height of the buffer 769: * 770: * @since 1.4 771: * 772: * @see VolatileImage 773: */ 774: public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth, 775: int proposedHeight) 776: { 777: Component root = SwingUtilities.getWindowAncestor(comp); 778: Image buffer = (Image) offscreenBuffers.get(root); 779: if (buffer == null 780: || buffer.getWidth(null) < proposedWidth 781: || buffer.getHeight(null) < proposedHeight 782: || !(buffer instanceof VolatileImage)) 783: { 784: int width = Math.max(proposedWidth, root.getWidth()); 785: width = Math.min(doubleBufferMaximumSize.width, width); 786: int height = Math.max(proposedHeight, root.getHeight()); 787: height = Math.min(doubleBufferMaximumSize.height, height); 788: buffer = root.createVolatileImage(width, height); 789: if (buffer != null) 790: offscreenBuffers.put(root, buffer); 791: } 792: return buffer; 793: } 794: 795: 796: /** 797: * Get the value of the {@link #doubleBufferMaximumSize} property. 798: * 799: * @return The current value of the property 800: * 801: * @see #setDoubleBufferMaximumSize 802: */ 803: public Dimension getDoubleBufferMaximumSize() 804: { 805: return doubleBufferMaximumSize; 806: } 807: 808: /** 809: * Set the value of the {@link #doubleBufferMaximumSize} property. 810: * 811: * @param size The new value of the property 812: * 813: * @see #getDoubleBufferMaximumSize 814: */ 815: public void setDoubleBufferMaximumSize(Dimension size) 816: { 817: doubleBufferMaximumSize = size; 818: } 819: 820: /** 821: * Set the value of the {@link #doubleBufferingEnabled} property. 822: * 823: * @param buffer The new value of the property 824: * 825: * @see #isDoubleBufferingEnabled 826: */ 827: public void setDoubleBufferingEnabled(boolean buffer) 828: { 829: doubleBufferingEnabled = buffer; 830: } 831: 832: /** 833: * Get the value of the {@link #doubleBufferingEnabled} property. 834: * 835: * @return The current value of the property 836: * 837: * @see #setDoubleBufferingEnabled 838: */ 839: public boolean isDoubleBufferingEnabled() 840: { 841: return doubleBufferingEnabled; 842: } 843: 844: public String toString() 845: { 846: return "RepaintManager"; 847: } 848: 849: /** 850: * Sends an RepaintManagerEvent to the event queue with the specified 851: * runnable. This is similar to SwingUtilities.invokeLater(), only that the 852: * event is a low priority event in order to defer the execution a little 853: * more. 854: */ 855: private void invokeLater(Runnable runnable) 856: { 857: Toolkit tk = Toolkit.getDefaultToolkit(); 858: EventQueue evQueue = tk.getSystemEventQueue(); 859: InvocationEvent ev = new RepaintWorkerEvent(evQueue, runnable, null, false); 860: evQueue.postEvent(ev); 861: } 862: }
GNU Classpath (0.95) |