1 /* RepaintManager.java --
2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
41 import java.awt.Component;
42 import java.awt.Dimension;
43 import java.awt.Image;
44 import java.awt.Rectangle;
45 import java.awt.image.VolatileImage;
46 import java.util.Enumeration;
47 import java.util.HashMap;
48 import java.util.Hashtable;
49 import java.util.Iterator;
51 import java.util.Vector;
54 * <p>The repaint manager holds a set of dirty regions, invalid components,
55 * and a double buffer surface. The dirty regions and invalid components
56 * are used to coalesce multiple revalidate() and repaint() calls in the
57 * component tree into larger groups to be refreshed "all at once"; the
58 * double buffer surface is used by root components to paint
61 * <p>In general, painting is very confusing in swing. see <a
62 * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this
63 * document</a> for more details.</p>
65 * @author Graydon Hoare (graydon@redhat.com)
67 public class RepaintManager
71 * <p>A helper class which is placed into the system event queue at
72 * various times in order to facilitate repainting and layout. There is
73 * typically only one of these objects active at any time. When the
74 * {@link RepaintManager} is told to queue a repaint, it checks to see if
75 * a {@link RepaintWorker} is "live" in the system event queue, and if
76 * not it inserts one using {@link SwingUtilities#invokeLater}.</p>
78 * <p>When the {@link RepaintWorker} comes to the head of the system
79 * event queue, its {@link RepaintWorker#run} method is executed by the
80 * swing paint thread, which revalidates all invalid components and
81 * repaints any damage in the swing scene.</p>
84 protected class RepaintWorker
88 public RepaintWorker()
92 public synchronized void setLive(boolean b)
96 public synchronized boolean isLive()
102 RepaintManager rm = RepaintManager.globalManager;
104 rm.validateInvalidComponents();
105 rm.paintDirtyRegions();
111 * A table storing the dirty regions of components. The keys of this
112 * table are components, the values are rectangles. Each component maps
113 * to exactly one rectangle. When more regions are marked as dirty on a
114 * component, they are union'ed with the existing rectangle.
116 * @see #addDirtyRegion
117 * @see #getDirtyRegion
118 * @see #isCompletelyDirty
119 * @see #markCompletelyClean
120 * @see #markCompletelyDirty
122 Hashtable dirtyComponents;
125 * A single, shared instance of the helper class. Any methods which mark
126 * components as invalid or dirty eventually activate this instance. It
127 * is added to the event queue if it is not already active, otherwise
130 * @see #addDirtyRegion
131 * @see #addInvalidComponent
133 RepaintWorker repaintWorker;
136 * The set of components which need revalidation, in the "layout" sense.
137 * There is no additional information about "what kind of layout" they
138 * need (as there is with dirty regions), so it is just a vector rather
141 * @see #addInvalidComponent
142 * @see #removeInvalidComponent
143 * @see #validateInvalidComponents
145 Vector invalidComponents;
148 * Whether or not double buffering is enabled on this repaint
149 * manager. This is merely a hint to clients; the RepaintManager will
150 * always return an offscreen buffer when one is requested.
152 * @see #getDoubleBufferingEnabled
153 * @see #setDoubleBufferingEnabled
155 boolean doubleBufferingEnabled;
158 * The current offscreen buffer. This is reused for all requests for
159 * offscreen drawing buffers. It grows as necessary, up to {@link
160 * #doubleBufferMaximumSize}, but there is only one shared instance.
162 * @see #getOffscreenBuffer
163 * @see #doubleBufferMaximumSize
168 * The maximum width and height to allocate as a double buffer. Requests
169 * beyond this size are ignored.
171 * @see #paintDirtyRegions
172 * @see #getDoubleBufferMaximumSize
173 * @see #setDoubleBufferMaximumSize
175 Dimension doubleBufferMaximumSize;
179 * The global, shared RepaintManager instance. This is reused for all
180 * components in all windows. This is package-private to avoid an accessor
183 * @see #currentManager
184 * @see #setCurrentManager
186 static RepaintManager globalManager;
189 * Create a new RepaintManager object.
191 public RepaintManager()
193 dirtyComponents = new Hashtable();
194 invalidComponents = new Vector();
195 repaintWorker = new RepaintWorker();
196 doubleBufferMaximumSize = new Dimension(2000,2000);
197 doubleBufferingEnabled = true;
201 * Get the value of the shared {@link #globalManager} instance, possibly
202 * returning a special manager associated with the specified
203 * component. The default implementaiton ignores the component parameter.
205 * @param component A component to look up the manager of
207 * @return The current repaint manager
209 * @see #setCurrentManager
211 public static RepaintManager currentManager(Component component)
213 if (globalManager == null)
214 globalManager = new RepaintManager();
215 return globalManager;
219 * Get the value of the shared {@link #globalManager} instance, possibly
220 * returning a special manager associated with the specified
221 * component. The default implementaiton ignores the component parameter.
223 * @param component A component to look up the manager of
225 * @return The current repaint manager
227 * @see #setCurrentManager
229 public static RepaintManager currentManager(JComponent component)
231 return currentManager((Component)component);
235 * Set the value of the shared {@link #globalManager} instance.
237 * @param manager The new value of the shared instance
239 * @see #currentManager(JComponent)
241 public static void setCurrentManager(RepaintManager manager)
243 globalManager = manager;
247 * Add a component to the {@link #invalidComponents} vector. If the
248 * {@link #repaintWorker} class is not active, insert it in the system
251 * @param component The component to add
253 * @see #removeInvalidComponent
255 public synchronized void addInvalidComponent(JComponent component)
257 Component ancestor = component.getParent();
259 while (ancestor != null
260 && (! (ancestor instanceof JComponent)
261 || ! ((JComponent) ancestor).isValidateRoot() ))
262 ancestor = ancestor.getParent();
265 && ancestor instanceof JComponent
266 && ((JComponent) ancestor).isValidateRoot())
267 component = (JComponent) ancestor;
269 if (invalidComponents.contains(component))
272 invalidComponents.add(component);
274 if (! repaintWorker.isLive())
276 repaintWorker.setLive(true);
277 SwingUtilities.invokeLater(repaintWorker);
282 * Remove a component from the {@link #invalidComponents} vector.
284 * @param component The component to remove
286 * @see #addInvalidComponent
288 public synchronized void removeInvalidComponent(JComponent component)
290 invalidComponents.removeElement(component);
294 * Add a region to the set of dirty regions for a specified component.
295 * This involves union'ing the new region with any existing dirty region
296 * associated with the component. If the {@link #repaintWorker} class
297 * is not active, insert it in the system event queue.
299 * @param component The component to add a dirty region for
300 * @param x The left x coordinate of the new dirty region
301 * @param y The top y coordinate of the new dirty region
302 * @param w The width of the new dirty region
303 * @param h The height of the new dirty region
305 * @see #addDirtyRegion
306 * @see #getDirtyRegion
307 * @see #isCompletelyDirty
308 * @see #markCompletelyClean
309 * @see #markCompletelyDirty
311 public synchronized void addDirtyRegion(JComponent component, int x, int y,
314 if (w == 0 || h == 0)
317 Rectangle r = new Rectangle(x, y, w, h);
318 if (dirtyComponents.containsKey(component))
319 r = r.union((Rectangle)dirtyComponents.get(component));
320 dirtyComponents.put(component, r);
321 if (! repaintWorker.isLive())
323 repaintWorker.setLive(true);
324 SwingUtilities.invokeLater(repaintWorker);
329 * Get the dirty region associated with a component, or <code>null</code>
330 * if the component has no dirty region.
332 * @param component The component to get the dirty region of
334 * @return The dirty region of the component
336 * @see #dirtyComponents
337 * @see #addDirtyRegion
338 * @see #isCompletelyDirty
339 * @see #markCompletelyClean
340 * @see #markCompletelyDirty
342 public Rectangle getDirtyRegion(JComponent component)
344 return (Rectangle) dirtyComponents.get(component);
348 * Mark a component as dirty over its entire bounds.
350 * @param component The component to mark as dirty
352 * @see #dirtyComponents
353 * @see #addDirtyRegion
354 * @see #getDirtyRegion
355 * @see #isCompletelyDirty
356 * @see #markCompletelyClean
358 public void markCompletelyDirty(JComponent component)
360 Rectangle r = component.getBounds();
361 addDirtyRegion(component, r.x, r.y, r.width, r.height);
365 * Remove all dirty regions for a specified component
367 * @param component The component to mark as clean
369 * @see #dirtyComponents
370 * @see #addDirtyRegion
371 * @see #getDirtyRegion
372 * @see #isCompletelyDirty
373 * @see #markCompletelyDirty
375 public void markCompletelyClean(JComponent component)
377 dirtyComponents.remove(component);
381 * Return <code>true</code> if the specified component is completely
382 * contained within its dirty region, otherwise <code>false</code>
384 * @param component The component to check for complete dirtyness
386 * @return Whether the component is completely dirty
388 * @see #dirtyComponents
389 * @see #addDirtyRegion
390 * @see #getDirtyRegion
391 * @see #isCompletelyDirty
392 * @see #markCompletelyClean
394 public boolean isCompletelyDirty(JComponent component)
396 Rectangle dirty = (Rectangle) dirtyComponents.get(component);
399 Rectangle r = component.getBounds();
402 return dirty.contains(r);
406 * Validate all components which have been marked invalid in the {@link
407 * #invalidComponents} vector.
409 public void validateInvalidComponents()
411 for (Enumeration e = invalidComponents.elements(); e.hasMoreElements(); )
413 JComponent comp = (JComponent) e.nextElement();
414 if (! (comp.isVisible() && comp.isShowing()))
418 invalidComponents.clear();
422 * Repaint all regions of all components which have been marked dirty in
423 * the {@link #dirtyComponents} table.
425 public void paintDirtyRegions()
427 // step 1: pull out roots and calculate spanning damage
429 HashMap roots = new HashMap();
430 for (Enumeration e = dirtyComponents.keys(); e.hasMoreElements(); )
432 JComponent comp = (JComponent) e.nextElement();
433 if (! (comp.isVisible() && comp.isShowing()))
435 Rectangle damaged = getDirtyRegion(comp);
436 if (damaged.width == 0 || damaged.height == 0)
438 JRootPane root = comp.getRootPane();
439 // If the component has no root, no repainting will occur.
442 Rectangle rootDamage = SwingUtilities.convertRectangle(comp, damaged, root);
443 if (! roots.containsKey(root))
445 roots.put(root, rootDamage);
449 roots.put(root, ((Rectangle)roots.get(root)).union(rootDamage));
452 dirtyComponents.clear();
454 // step 2: paint those roots
455 Iterator i = roots.entrySet().iterator();
458 Map.Entry ent = (Map.Entry) i.next();
459 JRootPane root = (JRootPane) ent.getKey();
460 Rectangle rect = (Rectangle) ent.getValue();
461 root.paintImmediately(rect);
466 * Get an offscreen buffer for painting a component's image. This image
467 * may be smaller than the proposed dimensions, depending on the value of
468 * the {@link #doubleBufferMaximumSize} property.
470 * @param component The component to return an offscreen buffer for
471 * @param proposedWidth The proposed width of the offscreen buffer
472 * @param proposedHeight The proposed height of the offscreen buffer
474 * @return A shared offscreen buffer for painting
478 public Image getOffscreenBuffer(Component component, int proposedWidth,
481 if (doubleBuffer == null
482 || (((doubleBuffer.getWidth(null) < proposedWidth)
483 || (doubleBuffer.getHeight(null) < proposedHeight))
484 && (proposedWidth < doubleBufferMaximumSize.width)
485 && (proposedHeight < doubleBufferMaximumSize.height)))
487 doubleBuffer = component.createImage(proposedWidth, proposedHeight);
493 * Creates and returns a volatile offscreen buffer for the specified
494 * component that can be used as a double buffer. The returned image
495 * is a {@link VolatileImage}. Its size will be <code>(proposedWidth,
496 * proposedHeight)</code> except when the maximum double buffer size
497 * has been set in this RepaintManager.
499 * @param comp the Component for which to create a volatile buffer
500 * @param proposedWidth the proposed width of the buffer
501 * @param proposedHeight the proposed height of the buffer
507 public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth,
510 int maxWidth = doubleBufferMaximumSize.width;
511 int maxHeight = doubleBufferMaximumSize.height;
512 return comp.createVolatileImage(Math.min(maxWidth, proposedWidth),
513 Math.min(maxHeight, proposedHeight));
518 * Get the value of the {@link #doubleBufferMaximumSize} property.
520 * @return The current value of the property
522 * @see #setDoubleBufferMaximumSize
524 public Dimension getDoubleBufferMaximumSize()
526 return doubleBufferMaximumSize;
530 * Set the value of the {@link #doubleBufferMaximumSize} property.
532 * @param size The new value of the property
534 * @see #getDoubleBufferMaximumSize
536 public void setDoubleBufferMaximumSize(Dimension size)
538 doubleBufferMaximumSize = size;
542 * Set the value of the {@link #doubleBufferingEnabled} property.
544 * @param buffer The new value of the property
546 * @see #isDoubleBufferingEnabled
548 public void setDoubleBufferingEnabled(boolean buffer)
550 doubleBufferingEnabled = buffer;
554 * Get the value of the {@link #doubleBufferingEnabled} property.
556 * @return The current value of the property
558 * @see #setDoubleBufferingEnabled
560 public boolean isDoubleBufferingEnabled()
562 return doubleBufferingEnabled;
565 public String toString()
567 return "RepaintManager";