summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/RepaintManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/RepaintManager.java')
-rw-r--r--libjava/classpath/javax/swing/RepaintManager.java860
1 files changed, 860 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java
new file mode 100644
index 000000000..23c05a262
--- /dev/null
+++ b/libjava/classpath/javax/swing/RepaintManager.java
@@ -0,0 +1,860 @@
+/* RepaintManager.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing;
+
+import gnu.classpath.SystemProperties;
+import gnu.java.awt.LowPriorityEvent;
+
+import java.applet.Applet;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.InvocationEvent;
+import java.awt.image.VolatileImage;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * <p>The repaint manager holds a set of dirty regions, invalid components,
+ * and a double buffer surface. The dirty regions and invalid components
+ * are used to coalesce multiple revalidate() and repaint() calls in the
+ * component tree into larger groups to be refreshed "all at once"; the
+ * double buffer surface is used by root components to paint
+ * themselves.</p>
+ *
+ * <p>See <a
+ * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this
+ * document</a> for more details.</p>
+ * document</a> for more details.</p>
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+public class RepaintManager
+{
+ /**
+ * An InvocationEvent subclass that implements LowPriorityEvent. This is used
+ * to defer the execution of RepaintManager requests as long as possible on
+ * the event queue. This way we make sure that all available input is
+ * processed before getting active with the RepaintManager. This allows
+ * for better optimization (more validate and repaint requests can be
+ * coalesced) and thus has a positive effect on performance for GUI
+ * applications under heavy load.
+ */
+ private static class RepaintWorkerEvent
+ extends InvocationEvent
+ implements LowPriorityEvent
+ {
+
+ /**
+ * Creates a new RepaintManager event.
+ *
+ * @param source the source
+ * @param runnable the runnable to execute
+ */
+ public RepaintWorkerEvent(Object source, Runnable runnable,
+ Object notifier, boolean catchEx)
+ {
+ super(source, runnable, notifier, catchEx);
+ }
+
+ /**
+ * An application that I met implements its own event dispatching and
+ * calls dispatch() via reflection, and only checks declared methods,
+ * that is, it expects this method to be in the event's class, not
+ * in a superclass. So I put this in here... sigh.
+ */
+ public void dispatch()
+ {
+ super.dispatch();
+ }
+ }
+
+ /**
+ * The current repaint managers, indexed by their ThreadGroups.
+ */
+ static WeakHashMap currentRepaintManagers;
+
+ /**
+ * A rectangle object to be reused in damaged regions calculation.
+ */
+ private static Rectangle rectCache = new Rectangle();
+
+ /**
+ * <p>A helper class which is placed into the system event queue at
+ * various times in order to facilitate repainting and layout. There is
+ * typically only one of these objects active at any time. When the
+ * {@link RepaintManager} is told to queue a repaint, it checks to see if
+ * a {@link RepaintWorker} is "live" in the system event queue, and if
+ * not it inserts one using {@link SwingUtilities#invokeLater}.</p>
+ *
+ * <p>When the {@link RepaintWorker} comes to the head of the system
+ * event queue, its {@link RepaintWorker#run} method is executed by the
+ * swing paint thread, which revalidates all invalid components and
+ * repaints any damage in the swing scene.</p>
+ */
+ private class RepaintWorker
+ implements Runnable
+ {
+
+ boolean live;
+
+ public RepaintWorker()
+ {
+ live = false;
+ }
+
+ public synchronized void setLive(boolean b)
+ {
+ live = b;
+ }
+
+ public synchronized boolean isLive()
+ {
+ return live;
+ }
+
+ public void run()
+ {
+ try
+ {
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ RepaintManager rm =
+ (RepaintManager) currentRepaintManagers.get(threadGroup);
+ rm.validateInvalidComponents();
+ rm.paintDirtyRegions();
+ }
+ finally
+ {
+ setLive(false);
+ }
+ }
+
+ }
+
+ /**
+ * A table storing the dirty regions of components. The keys of this
+ * table are components, the values are rectangles. Each component maps
+ * to exactly one rectangle. When more regions are marked as dirty on a
+ * component, they are union'ed with the existing rectangle.
+ *
+ * This is package private to avoid a synthetic accessor method in inner
+ * class.
+ *
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ private HashMap dirtyComponents;
+
+ /**
+ * The dirtyComponents which is used in paintDiryRegions to avoid unnecessary
+ * locking.
+ */
+ private HashMap dirtyComponentsWork;
+
+ /**
+ * A single, shared instance of the helper class. Any methods which mark
+ * components as invalid or dirty eventually activate this instance. It
+ * is added to the event queue if it is not already active, otherwise
+ * reused.
+ *
+ * @see #addDirtyRegion
+ * @see #addInvalidComponent
+ */
+ private RepaintWorker repaintWorker;
+
+ /**
+ * The set of components which need revalidation, in the "layout" sense.
+ * There is no additional information about "what kind of layout" they
+ * need (as there is with dirty regions), so it is just a vector rather
+ * than a table.
+ *
+ * @see #addInvalidComponent
+ * @see #removeInvalidComponent
+ * @see #validateInvalidComponents
+ */
+ private ArrayList invalidComponents;
+
+ /**
+ * Whether or not double buffering is enabled on this repaint
+ * manager. This is merely a hint to clients; the RepaintManager will
+ * always return an offscreen buffer when one is requested.
+ *
+ * @see #isDoubleBufferingEnabled
+ * @see #setDoubleBufferingEnabled
+ */
+ private boolean doubleBufferingEnabled;
+
+ /**
+ * The offscreen buffers. This map holds one offscreen buffer per
+ * Window/Applet and releases them as soon as the Window/Applet gets garbage
+ * collected.
+ */
+ private WeakHashMap offscreenBuffers;
+
+ /**
+ * The maximum width and height to allocate as a double buffer. Requests
+ * beyond this size are ignored.
+ *
+ * @see #paintDirtyRegions
+ * @see #getDoubleBufferMaximumSize
+ * @see #setDoubleBufferMaximumSize
+ */
+ private Dimension doubleBufferMaximumSize;
+
+
+ /**
+ * Create a new RepaintManager object.
+ */
+ public RepaintManager()
+ {
+ dirtyComponents = new HashMap();
+ dirtyComponentsWork = new HashMap();
+ invalidComponents = new ArrayList();
+ repaintWorker = new RepaintWorker();
+ doubleBufferMaximumSize = new Dimension(2000,2000);
+ doubleBufferingEnabled =
+ SystemProperties.getProperty("gnu.swing.doublebuffering", "true")
+ .equals("true");
+ offscreenBuffers = new WeakHashMap();
+ }
+
+ /**
+ * Returns the <code>RepaintManager</code> for the current thread's
+ * thread group. The default implementation ignores the
+ * <code>component</code> parameter and returns the same repaint manager
+ * for all components.
+ *
+ * @param component a component to look up the manager of
+ *
+ * @return the current repaint manager for the calling thread's thread group
+ * and the specified component
+ *
+ * @see #setCurrentManager
+ */
+ public static RepaintManager currentManager(Component component)
+ {
+ if (currentRepaintManagers == null)
+ currentRepaintManagers = new WeakHashMap();
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ RepaintManager currentManager =
+ (RepaintManager) currentRepaintManagers.get(threadGroup);
+ if (currentManager == null)
+ {
+ currentManager = new RepaintManager();
+ currentRepaintManagers.put(threadGroup, currentManager);
+ }
+ return currentManager;
+ }
+
+ /**
+ * Returns the <code>RepaintManager</code> for the current thread's
+ * thread group. The default implementation ignores the
+ * <code>component</code> parameter and returns the same repaint manager
+ * for all components.
+ *
+ * This method is only here for backwards compatibility with older versions
+ * of Swing and simply forwards to {@link #currentManager(Component)}.
+ *
+ * @param component a component to look up the manager of
+ *
+ * @return the current repaint manager for the calling thread's thread group
+ * and the specified component
+ *
+ * @see #setCurrentManager
+ */
+ public static RepaintManager currentManager(JComponent component)
+ {
+ return currentManager((Component)component);
+ }
+
+ /**
+ * Sets the repaint manager for the calling thread's thread group.
+ *
+ * @param manager the repaint manager to set for the current thread's thread
+ * group
+ *
+ * @see #currentManager(Component)
+ */
+ public static void setCurrentManager(RepaintManager manager)
+ {
+ if (currentRepaintManagers == null)
+ currentRepaintManagers = new WeakHashMap();
+
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ currentRepaintManagers.put(threadGroup, manager);
+ }
+
+ /**
+ * Add a component to the {@link #invalidComponents} vector. If the
+ * {@link #repaintWorker} class is not active, insert it in the system
+ * event queue.
+ *
+ * @param component The component to add
+ *
+ * @see #removeInvalidComponent
+ */
+ public void addInvalidComponent(JComponent component)
+ {
+ Component validateRoot = null;
+ Component c = component;
+ while (c != null)
+ {
+ // Special cases we don't bother validating are when the invalidated
+ // component (or any of it's ancestors) is inside a CellRendererPane
+ // or if it doesn't have a peer yet (== not displayable).
+ if (c instanceof CellRendererPane || ! c.isDisplayable())
+ return;
+ if (c instanceof JComponent && ((JComponent) c).isValidateRoot())
+ {
+ validateRoot = c;
+ break;
+ }
+
+ c = c.getParent();
+ }
+
+ // If we didn't find a validate root, then we don't validate.
+ if (validateRoot == null)
+ return;
+
+ // Make sure the validate root and all of it's ancestors are visible.
+ c = validateRoot;
+ while (c != null)
+ {
+ if (! c.isVisible() || ! c.isDisplayable())
+ return;
+ c = c.getParent();
+ }
+
+ if (invalidComponents.contains(validateRoot))
+ return;
+
+ //synchronized (invalidComponents)
+ // {
+ invalidComponents.add(validateRoot);
+ // }
+
+ if (! repaintWorker.isLive())
+ {
+ repaintWorker.setLive(true);
+ invokeLater(repaintWorker);
+ }
+ }
+
+ /**
+ * Remove a component from the {@link #invalidComponents} vector.
+ *
+ * @param component The component to remove
+ *
+ * @see #addInvalidComponent
+ */
+ public void removeInvalidComponent(JComponent component)
+ {
+ synchronized (invalidComponents)
+ {
+ invalidComponents.remove(component);
+ }
+ }
+
+ /**
+ * Add a region to the set of dirty regions for a specified component.
+ * This involves union'ing the new region with any existing dirty region
+ * associated with the component. If the {@link #repaintWorker} class
+ * is not active, insert it in the system event queue.
+ *
+ * @param component The component to add a dirty region for
+ * @param x The left x coordinate of the new dirty region
+ * @param y The top y coordinate of the new dirty region
+ * @param w The width of the new dirty region
+ * @param h The height of the new dirty region
+ *
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ public void addDirtyRegion(JComponent component, int x, int y,
+ int w, int h)
+ {
+ if (w <= 0 || h <= 0 || !component.isShowing())
+ return;
+ component.computeVisibleRect(rectCache);
+ SwingUtilities.computeIntersection(x, y, w, h, rectCache);
+
+ if (! rectCache.isEmpty())
+ {
+ synchronized (dirtyComponents)
+ {
+ Rectangle dirtyRect = (Rectangle)dirtyComponents.get(component);
+ if (dirtyRect != null)
+ {
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y,
+ rectCache.width, rectCache.height,
+ dirtyRect);
+ }
+ else
+ {
+ dirtyComponents.put(component, rectCache.getBounds());
+ }
+ }
+
+ if (! repaintWorker.isLive())
+ {
+ repaintWorker.setLive(true);
+ invokeLater(repaintWorker);
+ }
+ }
+ }
+
+ /**
+ * Get the dirty region associated with a component, or <code>null</code>
+ * if the component has no dirty region.
+ *
+ * @param component The component to get the dirty region of
+ *
+ * @return The dirty region of the component
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ public Rectangle getDirtyRegion(JComponent component)
+ {
+ Rectangle dirty = (Rectangle) dirtyComponents.get(component);
+ if (dirty == null)
+ dirty = new Rectangle();
+ return dirty;
+ }
+
+ /**
+ * Mark a component as dirty over its entire bounds.
+ *
+ * @param component The component to mark as dirty
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ */
+ public void markCompletelyDirty(JComponent component)
+ {
+ addDirtyRegion(component, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Remove all dirty regions for a specified component
+ *
+ * @param component The component to mark as clean
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyDirty
+ */
+ public void markCompletelyClean(JComponent component)
+ {
+ synchronized (dirtyComponents)
+ {
+ dirtyComponents.remove(component);
+ }
+ }
+
+ /**
+ * Return <code>true</code> if the specified component is completely
+ * contained within its dirty region, otherwise <code>false</code>
+ *
+ * @param component The component to check for complete dirtyness
+ *
+ * @return Whether the component is completely dirty
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ */
+ public boolean isCompletelyDirty(JComponent component)
+ {
+ boolean dirty = false;
+ Rectangle r = getDirtyRegion(component);
+ if(r.width == Integer.MAX_VALUE && r.height == Integer.MAX_VALUE)
+ dirty = true;
+ return dirty;
+ }
+
+ /**
+ * Validate all components which have been marked invalid in the {@link
+ * #invalidComponents} vector.
+ */
+ public void validateInvalidComponents()
+ {
+ // We don't use an iterator here because that would fail when there are
+ // components invalidated during the validation of others, which happens
+ // quite frequently. Instead we synchronize the access a little more.
+ while (invalidComponents.size() > 0)
+ {
+ Component comp;
+ synchronized (invalidComponents)
+ {
+ comp = (Component) invalidComponents.remove(0);
+ }
+ // Validate the validate component.
+ if (! (comp.isVisible() && comp.isShowing()))
+ continue;
+ comp.validate();
+ }
+ }
+
+ /**
+ * Repaint all regions of all components which have been marked dirty in the
+ * {@link #dirtyComponents} table.
+ */
+ public void paintDirtyRegions()
+ {
+ // Short circuit if there is nothing to paint.
+ if (dirtyComponents.size() == 0)
+ return;
+
+ // Swap dirtyRegions with dirtyRegionsWork to avoid locking.
+ synchronized (dirtyComponents)
+ {
+ HashMap swap = dirtyComponents;
+ dirtyComponents = dirtyComponentsWork;
+ dirtyComponentsWork = swap;
+ }
+
+ // Compile a set of repaint roots.
+ HashSet repaintRoots = new HashSet();
+ Set components = dirtyComponentsWork.keySet();
+ for (Iterator i = components.iterator(); i.hasNext();)
+ {
+ JComponent dirty = (JComponent) i.next();
+ compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots);
+ }
+
+ for (Iterator i = repaintRoots.iterator(); i.hasNext();)
+ {
+ JComponent comp = (JComponent) i.next();
+ Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp);
+ if (damaged == null || damaged.isEmpty())
+ continue;
+ comp.paintImmediately(damaged);
+ }
+ dirtyComponentsWork.clear();
+ }
+
+ /**
+ * Compiles a list of components that really get repainted. This is called
+ * once for each component in the dirtyRegions HashMap, each time with
+ * another <code>dirty</code> parameter. This searches up the component
+ * hierarchy of <code>dirty</code> to find the highest parent that is also
+ * marked dirty and merges the dirty regions.
+ *
+ * @param dirtyRegions the dirty regions
+ * @param dirty the component for which to find the repaint root
+ * @param roots the list to which new repaint roots get appended
+ */
+ private void compileRepaintRoots(HashMap dirtyRegions, JComponent dirty,
+ HashSet roots)
+ {
+ Component current = dirty;
+ Component root = dirty;
+
+ // This will contain the dirty region in the root coordinate system,
+ // possibly clipped by ancestor's bounds.
+ Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty);
+ rectCache.setBounds(originalDirtyRect);
+
+ // The bounds of the current component.
+ int x = dirty.getX();
+ int y = dirty.getY();
+ int w = dirty.getWidth();
+ int h = dirty.getHeight();
+
+ // Do nothing if dirty region is clipped away by the component's bounds.
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+ if (rectCache.isEmpty())
+ return;
+
+ // The cumulated offsets.
+ int dx = 0;
+ int dy = 0;
+ // The actual offset for the found root.
+ int rootDx = 0;
+ int rootDy = 0;
+
+ // Search the highest component that is also marked dirty.
+ Component parent;
+ while (true)
+ {
+ parent = current.getParent();
+ if (parent == null || !(parent instanceof JComponent))
+ break;
+
+ current = parent;
+ // Update the offset.
+ dx += x;
+ dy += y;
+ rectCache.x += x;
+ rectCache.y += y;
+
+ x = current.getX();
+ y = current.getY();
+ w = current.getWidth();
+ h = current.getHeight();
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+
+ // Don't paint if the dirty regions is clipped away by any of
+ // its ancestors.
+ if (rectCache.isEmpty())
+ return;
+
+ // We can skip to the next up when this parent is not dirty.
+ if (dirtyRegions.containsKey(parent))
+ {
+ root = current;
+ rootDx = dx;
+ rootDy = dy;
+ }
+ }
+
+ // Merge the rectangles of the root and the requested component if
+ // the are different.
+ if (root != dirty)
+ {
+ rectCache.x += rootDx - dx;
+ rectCache.y += rootDy - dy;
+ Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root);
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width,
+ rectCache.height, dirtyRect);
+ }
+
+ // Adds the root to the roots set.
+ if (! roots.contains(root))
+ roots.add(root);
+ }
+
+ /**
+ * Get an offscreen buffer for painting a component's image. This image
+ * may be smaller than the proposed dimensions, depending on the value of
+ * the {@link #doubleBufferMaximumSize} property.
+ *
+ * @param component The component to return an offscreen buffer for
+ * @param proposedWidth The proposed width of the offscreen buffer
+ * @param proposedHeight The proposed height of the offscreen buffer
+ *
+ * @return A shared offscreen buffer for painting
+ */
+ public Image getOffscreenBuffer(Component component, int proposedWidth,
+ int proposedHeight)
+ {
+ Component root = SwingUtilities.getWindowAncestor(component);
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer == null
+ || buffer.getWidth(null) < proposedWidth
+ || buffer.getHeight(null) < proposedHeight)
+ {
+ int width = Math.max(proposedWidth, root.getWidth());
+ width = Math.min(doubleBufferMaximumSize.width, width);
+ int height = Math.max(proposedHeight, root.getHeight());
+ height = Math.min(doubleBufferMaximumSize.height, height);
+ buffer = component.createImage(width, height);
+ offscreenBuffers.put(root, buffer);
+ }
+ return buffer;
+ }
+
+ /**
+ * Blits the back buffer of the specified root component to the screen.
+ * This is package private because it must get called by JComponent.
+ *
+ * @param comp the component to be painted
+ * @param x the area to paint on screen, in comp coordinates
+ * @param y the area to paint on screen, in comp coordinates
+ * @param w the area to paint on screen, in comp coordinates
+ * @param h the area to paint on screen, in comp coordinates
+ */
+ void commitBuffer(Component comp, int x, int y, int w, int h)
+ {
+ Component root = comp;
+ while (root != null
+ && ! (root instanceof Window || root instanceof Applet))
+ {
+ x += root.getX();
+ y += root.getY();
+ root = root.getParent();
+ }
+
+ if (root != null)
+ {
+ Graphics g = root.getGraphics();
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer != null)
+ {
+ // Make sure we have a sane clip at this point.
+ g.clipRect(x, y, w, h);
+ g.drawImage(buffer, 0, 0, root);
+ g.dispose();
+ }
+ }
+ }
+
+ /**
+ * Creates and returns a volatile offscreen buffer for the specified
+ * component that can be used as a double buffer. The returned image
+ * is a {@link VolatileImage}. Its size will be <code>(proposedWidth,
+ * proposedHeight)</code> except when the maximum double buffer size
+ * has been set in this RepaintManager.
+ *
+ * @param comp the Component for which to create a volatile buffer
+ * @param proposedWidth the proposed width of the buffer
+ * @param proposedHeight the proposed height of the buffer
+ *
+ * @since 1.4
+ *
+ * @see VolatileImage
+ */
+ public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth,
+ int proposedHeight)
+ {
+ Component root = SwingUtilities.getWindowAncestor(comp);
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer == null
+ || buffer.getWidth(null) < proposedWidth
+ || buffer.getHeight(null) < proposedHeight
+ || !(buffer instanceof VolatileImage))
+ {
+ int width = Math.max(proposedWidth, root.getWidth());
+ width = Math.min(doubleBufferMaximumSize.width, width);
+ int height = Math.max(proposedHeight, root.getHeight());
+ height = Math.min(doubleBufferMaximumSize.height, height);
+ buffer = root.createVolatileImage(width, height);
+ if (buffer != null)
+ offscreenBuffers.put(root, buffer);
+ }
+ return buffer;
+ }
+
+
+ /**
+ * Get the value of the {@link #doubleBufferMaximumSize} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setDoubleBufferMaximumSize
+ */
+ public Dimension getDoubleBufferMaximumSize()
+ {
+ return doubleBufferMaximumSize;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBufferMaximumSize} property.
+ *
+ * @param size The new value of the property
+ *
+ * @see #getDoubleBufferMaximumSize
+ */
+ public void setDoubleBufferMaximumSize(Dimension size)
+ {
+ doubleBufferMaximumSize = size;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBufferingEnabled} property.
+ *
+ * @param buffer The new value of the property
+ *
+ * @see #isDoubleBufferingEnabled
+ */
+ public void setDoubleBufferingEnabled(boolean buffer)
+ {
+ doubleBufferingEnabled = buffer;
+ }
+
+ /**
+ * Get the value of the {@link #doubleBufferingEnabled} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setDoubleBufferingEnabled
+ */
+ public boolean isDoubleBufferingEnabled()
+ {
+ return doubleBufferingEnabled;
+ }
+
+ public String toString()
+ {
+ return "RepaintManager";
+ }
+
+ /**
+ * Sends an RepaintManagerEvent to the event queue with the specified
+ * runnable. This is similar to SwingUtilities.invokeLater(), only that the
+ * event is a low priority event in order to defer the execution a little
+ * more.
+ */
+ private void invokeLater(Runnable runnable)
+ {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ EventQueue evQueue = tk.getSystemEventQueue();
+ InvocationEvent ev = new RepaintWorkerEvent(evQueue, runnable, null, false);
+ evQueue.postEvent(ev);
+ }
+}