Dead
[official-gcc.git] / gomp-20050608-branch / libjava / classpath / javax / swing / RepaintManager.java
blob0be81053dc538a698694269ed4fe2fe2f69f3388
1 /* RepaintManager.java --
2 Copyright (C) 2002, 2004, 2005 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)
9 any later version.
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
19 02110-1301 USA.
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.
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. */
39 package javax.swing;
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.ArrayList;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.HashMap;
50 import java.util.Iterator;
51 import java.util.WeakHashMap;
53 /**
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
59 * themselves.</p>
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
69 /**
70 * The current repaint managers, indexed by their ThreadGroups.
72 static WeakHashMap currentRepaintManagers;
74 /**
75 * <p>A helper class which is placed into the system event queue at
76 * various times in order to facilitate repainting and layout. There is
77 * typically only one of these objects active at any time. When the
78 * {@link RepaintManager} is told to queue a repaint, it checks to see if
79 * a {@link RepaintWorker} is "live" in the system event queue, and if
80 * not it inserts one using {@link SwingUtilities#invokeLater}.</p>
82 * <p>When the {@link RepaintWorker} comes to the head of the system
83 * event queue, its {@link RepaintWorker#run} method is executed by the
84 * swing paint thread, which revalidates all invalid components and
85 * repaints any damage in the swing scene.</p>
87 protected class RepaintWorker
88 implements Runnable
91 boolean live;
93 public RepaintWorker()
95 live = false;
98 public synchronized void setLive(boolean b)
100 live = b;
103 public synchronized boolean isLive()
105 return live;
108 public void run()
110 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
111 RepaintManager rm =
112 (RepaintManager) currentRepaintManagers.get(threadGroup);
113 setLive(false);
114 rm.validateInvalidComponents();
115 rm.paintDirtyRegions();
121 * Compares two components using their depths in the component hierarchy.
122 * A component with a lesser depth (higher level components) are sorted
123 * before components with a deeper depth (low level components). This is used
124 * to order paint requests, so that the higher level components are painted
125 * before the low level components get painted.
127 * @author Roman Kennke (kennke@aicas.com)
129 private class ComponentComparator implements Comparator
133 * Compares two components.
135 * @param o1 the first component
136 * @param o2 the second component
138 * @return a negative integer, if <code>o1</code> is higher in the
139 * hierarchy than <code>o2</code>, zero, if both are at the same
140 * level and a positive integer, if <code>o1</code> is deeper in
141 * the hierarchy than <code>o2</code>
143 public int compare(Object o1, Object o2)
145 if (o1 instanceof JComponent && o2 instanceof JComponent)
147 JComponent c1 = (JComponent) o1;
148 JComponent c2 = (JComponent) o2;
149 return getDepth(c1) - getDepth(c2);
151 else
152 throw new ClassCastException("This comparator can only be used with "
153 + "JComponents");
157 * Computes the depth for a given JComponent.
159 * @param c the component to compute the depth for
161 * @return the depth of the component
163 private int getDepth(JComponent c)
165 Component comp = c;
166 int depth = 0;
167 while (comp != null)
169 comp = comp.getParent();
170 depth++;
172 return depth;
176 /**
177 * A table storing the dirty regions of components. The keys of this
178 * table are components, the values are rectangles. Each component maps
179 * to exactly one rectangle. When more regions are marked as dirty on a
180 * component, they are union'ed with the existing rectangle.
182 * @see #addDirtyRegion
183 * @see #getDirtyRegion
184 * @see #isCompletelyDirty
185 * @see #markCompletelyClean
186 * @see #markCompletelyDirty
188 HashMap dirtyComponents;
190 HashMap workDirtyComponents;
193 * Stores the order in which the components get repainted.
195 ArrayList repaintOrder;
196 ArrayList workRepaintOrder;
199 * The comparator used for ordered inserting into the repaintOrder list.
201 Comparator comparator;
204 * A single, shared instance of the helper class. Any methods which mark
205 * components as invalid or dirty eventually activate this instance. It
206 * is added to the event queue if it is not already active, otherwise
207 * reused.
209 * @see #addDirtyRegion
210 * @see #addInvalidComponent
212 RepaintWorker repaintWorker;
214 /**
215 * The set of components which need revalidation, in the "layout" sense.
216 * There is no additional information about "what kind of layout" they
217 * need (as there is with dirty regions), so it is just a vector rather
218 * than a table.
220 * @see #addInvalidComponent
221 * @see #removeInvalidComponent
222 * @see #validateInvalidComponents
224 ArrayList invalidComponents;
225 ArrayList workInvalidComponents;
227 /**
228 * Whether or not double buffering is enabled on this repaint
229 * manager. This is merely a hint to clients; the RepaintManager will
230 * always return an offscreen buffer when one is requested.
232 * @see #isDoubleBufferingEnabled
233 * @see #setDoubleBufferingEnabled
235 boolean doubleBufferingEnabled;
237 /**
238 * The current offscreen buffer. This is reused for all requests for
239 * offscreen drawing buffers. It grows as necessary, up to {@link
240 * #doubleBufferMaximumSize}, but there is only one shared instance.
242 * @see #getOffscreenBuffer
243 * @see #doubleBufferMaximumSize
245 Image doubleBuffer;
248 * The maximum width and height to allocate as a double buffer. Requests
249 * beyond this size are ignored.
251 * @see #paintDirtyRegions
252 * @see #getDoubleBufferMaximumSize
253 * @see #setDoubleBufferMaximumSize
255 Dimension doubleBufferMaximumSize;
259 * Create a new RepaintManager object.
261 public RepaintManager()
263 dirtyComponents = new HashMap();
264 workDirtyComponents = new HashMap();
265 repaintOrder = new ArrayList();
266 workRepaintOrder = new ArrayList();
267 invalidComponents = new ArrayList();
268 workInvalidComponents = new ArrayList();
269 repaintWorker = new RepaintWorker();
270 doubleBufferMaximumSize = new Dimension(2000,2000);
271 doubleBufferingEnabled = true;
275 * Returns the <code>RepaintManager</code> for the current thread's
276 * thread group. The default implementation ignores the
277 * <code>component</code> parameter and returns the same repaint manager
278 * for all components.
280 * @param component a component to look up the manager of
282 * @return the current repaint manager for the calling thread's thread group
283 * and the specified component
285 * @see #setCurrentManager
287 public static RepaintManager currentManager(Component component)
289 if (currentRepaintManagers == null)
290 currentRepaintManagers = new WeakHashMap();
291 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
292 RepaintManager currentManager =
293 (RepaintManager) currentRepaintManagers.get(threadGroup);
294 if (currentManager == null)
296 currentManager = new RepaintManager();
297 currentRepaintManagers.put(threadGroup, currentManager);
299 return currentManager;
303 * Returns the <code>RepaintManager</code> for the current thread's
304 * thread group. The default implementation ignores the
305 * <code>component</code> parameter and returns the same repaint manager
306 * for all components.
308 * This method is only here for backwards compatibility with older versions
309 * of Swing and simply forwards to {@link #currentManager(Component)}.
311 * @param component a component to look up the manager of
313 * @return the current repaint manager for the calling thread's thread group
314 * and the specified component
316 * @see #setCurrentManager
318 public static RepaintManager currentManager(JComponent component)
320 return currentManager((Component)component);
324 * Sets the repaint manager for the calling thread's thread group.
326 * @param manager the repaint manager to set for the current thread's thread
327 * group
329 * @see #currentManager(Component)
331 public static void setCurrentManager(RepaintManager manager)
333 if (currentRepaintManagers == null)
334 currentRepaintManagers = new WeakHashMap();
336 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
337 currentRepaintManagers.put(threadGroup, manager);
341 * Add a component to the {@link #invalidComponents} vector. If the
342 * {@link #repaintWorker} class is not active, insert it in the system
343 * event queue.
345 * @param component The component to add
347 * @see #removeInvalidComponent
349 public synchronized void addInvalidComponent(JComponent component)
351 Component ancestor = component.getParent();
353 while (ancestor != null
354 && (! (ancestor instanceof JComponent)
355 || ! ((JComponent) ancestor).isValidateRoot() ))
356 ancestor = ancestor.getParent();
358 if (ancestor != null
359 && ancestor instanceof JComponent
360 && ((JComponent) ancestor).isValidateRoot())
361 component = (JComponent) ancestor;
363 if (invalidComponents.contains(component))
364 return;
366 invalidComponents.add(component);
368 if (! repaintWorker.isLive())
370 repaintWorker.setLive(true);
371 SwingUtilities.invokeLater(repaintWorker);
376 * Remove a component from the {@link #invalidComponents} vector.
378 * @param component The component to remove
380 * @see #addInvalidComponent
382 public synchronized void removeInvalidComponent(JComponent component)
384 invalidComponents.remove(component);
388 * Add a region to the set of dirty regions for a specified component.
389 * This involves union'ing the new region with any existing dirty region
390 * associated with the component. If the {@link #repaintWorker} class
391 * is not active, insert it in the system event queue.
393 * @param component The component to add a dirty region for
394 * @param x The left x coordinate of the new dirty region
395 * @param y The top y coordinate of the new dirty region
396 * @param w The width of the new dirty region
397 * @param h The height of the new dirty region
399 * @see #addDirtyRegion
400 * @see #getDirtyRegion
401 * @see #isCompletelyDirty
402 * @see #markCompletelyClean
403 * @see #markCompletelyDirty
405 public synchronized void addDirtyRegion(JComponent component, int x, int y,
406 int w, int h)
408 if (w == 0 || h == 0 || !component.isShowing())
409 return;
410 Rectangle r = new Rectangle(x, y, w, h);
411 if (dirtyComponents.containsKey(component))
412 r = r.union((Rectangle)dirtyComponents.get(component));
413 else
414 insertInRepaintOrder(component);
415 dirtyComponents.put(component, r);
416 if (! repaintWorker.isLive())
418 repaintWorker.setLive(true);
419 SwingUtilities.invokeLater(repaintWorker);
424 * Inserts a component into the repaintOrder list in an ordered fashion,
425 * using a binary search.
427 * @param c the component to be inserted
429 private void insertInRepaintOrder(JComponent c)
431 if (comparator == null)
432 comparator = new ComponentComparator();
433 int insertIndex = Collections.binarySearch(repaintOrder, c, comparator);
434 if (insertIndex < 0)
435 insertIndex = -(insertIndex + 1);
436 repaintOrder.add(insertIndex, c);
440 * Get the dirty region associated with a component, or <code>null</code>
441 * if the component has no dirty region.
443 * @param component The component to get the dirty region of
445 * @return The dirty region of the component
447 * @see #dirtyComponents
448 * @see #addDirtyRegion
449 * @see #isCompletelyDirty
450 * @see #markCompletelyClean
451 * @see #markCompletelyDirty
453 public Rectangle getDirtyRegion(JComponent component)
455 Rectangle dirty = (Rectangle) dirtyComponents.get(component);
456 if (dirty == null)
457 dirty = new Rectangle();
458 return dirty;
462 * Mark a component as dirty over its entire bounds.
464 * @param component The component to mark as dirty
466 * @see #dirtyComponents
467 * @see #addDirtyRegion
468 * @see #getDirtyRegion
469 * @see #isCompletelyDirty
470 * @see #markCompletelyClean
472 public void markCompletelyDirty(JComponent component)
474 Rectangle r = component.getBounds();
475 addDirtyRegion(component, r.x, r.y, r.width, r.height);
476 component.isCompletelyDirty = true;
480 * Remove all dirty regions for a specified component
482 * @param component The component to mark as clean
484 * @see #dirtyComponents
485 * @see #addDirtyRegion
486 * @see #getDirtyRegion
487 * @see #isCompletelyDirty
488 * @see #markCompletelyDirty
490 public void markCompletelyClean(JComponent component)
492 synchronized (this)
494 dirtyComponents.remove(component);
496 component.isCompletelyDirty = false;
500 * Return <code>true</code> if the specified component is completely
501 * contained within its dirty region, otherwise <code>false</code>
503 * @param component The component to check for complete dirtyness
505 * @return Whether the component is completely dirty
507 * @see #dirtyComponents
508 * @see #addDirtyRegion
509 * @see #getDirtyRegion
510 * @see #isCompletelyDirty
511 * @see #markCompletelyClean
513 public boolean isCompletelyDirty(JComponent component)
515 if (! dirtyComponents.containsKey(component))
516 return false;
517 return component.isCompletelyDirty;
521 * Validate all components which have been marked invalid in the {@link
522 * #invalidComponents} vector.
524 public void validateInvalidComponents()
526 // In order to keep the blocking of application threads minimal, we switch
527 // the invalidComponents field with the workInvalidComponents field and
528 // work with the workInvalidComponents field.
529 synchronized(this)
531 ArrayList swap = invalidComponents;
532 invalidComponents = workInvalidComponents;
533 workInvalidComponents = swap;
535 for (Iterator i = workInvalidComponents.iterator(); i.hasNext(); )
537 Component comp = (Component) i.next();
538 // Find validate root.
539 while ((!(comp instanceof JComponent)
540 || !((JComponent) comp).isValidateRoot())
541 && comp.getParent() != null)
542 comp = comp.getParent();
544 // Validate the validate root.
545 if (! (comp.isVisible() && comp.isShowing()))
546 continue;
547 comp.validate();
549 workInvalidComponents.clear();
553 * Repaint all regions of all components which have been marked dirty in
554 * the {@link #dirtyComponents} table.
556 public synchronized void paintDirtyRegions()
558 // In order to keep the blocking of application threads minimal, we switch
559 // the dirtyComponents field with the workdirtyComponents field and the
560 // repaintOrder field with the workRepaintOrder field and work with the
561 // work* fields.
562 synchronized(this)
564 ArrayList swap = workRepaintOrder;
565 workRepaintOrder = repaintOrder;
566 repaintOrder = swap;
567 HashMap swap2 = workDirtyComponents;
568 workDirtyComponents = dirtyComponents;
569 dirtyComponents = swap2;
571 for (Iterator i = workRepaintOrder.iterator(); i.hasNext();)
573 JComponent comp = (JComponent) i.next();
574 // If a component is marked completely clean in the meantime, then skip
575 // it.
576 Rectangle damaged = (Rectangle) workDirtyComponents.get(comp);
577 if (damaged == null || damaged.isEmpty())
578 continue;
579 comp.paintImmediately(damaged);
581 workRepaintOrder.clear();
582 workDirtyComponents.clear();
586 * Get an offscreen buffer for painting a component's image. This image
587 * may be smaller than the proposed dimensions, depending on the value of
588 * the {@link #doubleBufferMaximumSize} property.
590 * @param component The component to return an offscreen buffer for
591 * @param proposedWidth The proposed width of the offscreen buffer
592 * @param proposedHeight The proposed height of the offscreen buffer
594 * @return A shared offscreen buffer for painting
596 * @see #doubleBuffer
598 public Image getOffscreenBuffer(Component component, int proposedWidth,
599 int proposedHeight)
601 if (doubleBuffer == null
602 || (((doubleBuffer.getWidth(null) < proposedWidth)
603 || (doubleBuffer.getHeight(null) < proposedHeight))
604 && (proposedWidth < doubleBufferMaximumSize.width)
605 && (proposedHeight < doubleBufferMaximumSize.height)))
607 doubleBuffer = component.createImage(proposedWidth, proposedHeight);
609 return doubleBuffer;
613 * Creates and returns a volatile offscreen buffer for the specified
614 * component that can be used as a double buffer. The returned image
615 * is a {@link VolatileImage}. Its size will be <code>(proposedWidth,
616 * proposedHeight)</code> except when the maximum double buffer size
617 * has been set in this RepaintManager.
619 * @param comp the Component for which to create a volatile buffer
620 * @param proposedWidth the proposed width of the buffer
621 * @param proposedHeight the proposed height of the buffer
623 * @since 1.4
625 * @see VolatileImage
627 public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth,
628 int proposedHeight)
630 int maxWidth = doubleBufferMaximumSize.width;
631 int maxHeight = doubleBufferMaximumSize.height;
632 return comp.createVolatileImage(Math.min(maxWidth, proposedWidth),
633 Math.min(maxHeight, proposedHeight));
638 * Get the value of the {@link #doubleBufferMaximumSize} property.
640 * @return The current value of the property
642 * @see #setDoubleBufferMaximumSize
644 public Dimension getDoubleBufferMaximumSize()
646 return doubleBufferMaximumSize;
650 * Set the value of the {@link #doubleBufferMaximumSize} property.
652 * @param size The new value of the property
654 * @see #getDoubleBufferMaximumSize
656 public void setDoubleBufferMaximumSize(Dimension size)
658 doubleBufferMaximumSize = size;
662 * Set the value of the {@link #doubleBufferingEnabled} property.
664 * @param buffer The new value of the property
666 * @see #isDoubleBufferingEnabled
668 public void setDoubleBufferingEnabled(boolean buffer)
670 doubleBufferingEnabled = buffer;
674 * Get the value of the {@link #doubleBufferingEnabled} property.
676 * @return The current value of the property
678 * @see #setDoubleBufferingEnabled
680 public boolean isDoubleBufferingEnabled()
682 return doubleBufferingEnabled;
685 public String toString()
687 return "RepaintManager";