OSDN Git Service

Imported Classpath 0.18.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / RepaintManager.java
1 /* RepaintManager.java --
2    Copyright (C) 2002, 2004  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 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;
50 import java.util.Map;
51 import java.util.Vector;
52
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>
60  *
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>
64  *
65  * @author Graydon Hoare (graydon@redhat.com)
66  */
67 public class RepaintManager
68 {
69
70   /**
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>
77    *
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>
82    */
83
84   protected class RepaintWorker
85     implements Runnable
86   {
87     boolean live;
88     public RepaintWorker()
89     {
90       live = false;
91     }
92     public synchronized void setLive(boolean b) 
93     {
94       live = b;
95     }
96     public synchronized boolean isLive()
97     {
98       return live;
99     }
100     public void run()
101     {
102       RepaintManager rm = RepaintManager.globalManager;
103       setLive(false);
104       rm.validateInvalidComponents();
105       rm.paintDirtyRegions();
106     }
107   }
108
109   
110   /** 
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.
115    *
116    * @see #addDirtyRegion
117    * @see #getDirtyRegion
118    * @see #isCompletelyDirty
119    * @see #markCompletelyClean
120    * @see #markCompletelyDirty
121    */
122   Hashtable dirtyComponents;
123
124   /**
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
128    * reused.
129    *
130    * @see #addDirtyRegion
131    * @see #addInvalidComponent
132    */
133   RepaintWorker repaintWorker;
134
135   /** 
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
139    * than a table.
140    *
141    * @see #addInvalidComponent
142    * @see #removeInvalidComponent
143    * @see #validateInvalidComponents
144    */
145   Vector invalidComponents;
146
147   /** 
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.
151    * 
152    * @see #getDoubleBufferingEnabled
153    * @see #setDoubleBufferingEnabled
154    */
155   boolean doubleBufferingEnabled;
156
157   /** 
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.
161    *
162    * @see #getOffscreenBuffer
163    * @see #doubleBufferMaximumSize
164    */
165   Image doubleBuffer;
166
167   /**
168    * The maximum width and height to allocate as a double buffer. Requests
169    * beyond this size are ignored.
170    *
171    * @see #paintDirtyRegions
172    * @see #getDoubleBufferMaximumSize
173    * @see #setDoubleBufferMaximumSize
174    */
175   Dimension doubleBufferMaximumSize;
176
177
178   /**
179    * The global, shared RepaintManager instance. This is reused for all
180    * components in all windows.  This is package-private to avoid an accessor
181    * method.
182    *
183    * @see #currentManager
184    * @see #setCurrentManager
185    */
186   static RepaintManager globalManager;
187
188   /**
189    * Create a new RepaintManager object.
190    */
191   public RepaintManager()
192   {
193     dirtyComponents = new Hashtable();
194     invalidComponents = new Vector();
195     repaintWorker = new RepaintWorker();
196     doubleBufferMaximumSize = new Dimension(2000,2000);
197     doubleBufferingEnabled = true;
198   }
199
200   /**
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.
204    *
205    * @param component A component to look up the manager of
206    *
207    * @return The current repaint manager
208    *
209    * @see #setCurrentManager
210    */
211   public static RepaintManager currentManager(Component component)
212   {
213     if (globalManager == null)
214       globalManager = new RepaintManager();
215     return globalManager;
216   }
217
218   /**
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.
222    *
223    * @param component A component to look up the manager of
224    *
225    * @return The current repaint manager
226    *
227    * @see #setCurrentManager
228    */
229   public static RepaintManager currentManager(JComponent component)
230   {
231     return currentManager((Component)component);
232   }
233
234   /**
235    * Set the value of the shared {@link #globalManager} instance.
236    *
237    * @param manager The new value of the shared instance
238    *
239    * @see #currentManager(JComponent)
240    */
241   public static void setCurrentManager(RepaintManager manager)
242   {
243     globalManager = manager;
244   }
245
246   /**
247    * Add a component to the {@link #invalidComponents} vector. If the
248    * {@link #repaintWorker} class is not active, insert it in the system
249    * event queue.
250    *
251    * @param component The component to add
252    *
253    * @see #removeInvalidComponent
254    */
255   public synchronized void addInvalidComponent(JComponent component)
256   {
257     Component ancestor = component.getParent();
258
259     while (ancestor != null
260            && (! (ancestor instanceof JComponent)
261                || ! ((JComponent) ancestor).isValidateRoot() ))
262       ancestor = ancestor.getParent();
263
264     if (ancestor != null
265         && ancestor instanceof JComponent
266         && ((JComponent) ancestor).isValidateRoot())
267       component = (JComponent) ancestor;
268
269     if (invalidComponents.contains(component))
270       return;
271
272     invalidComponents.add(component);
273     
274     if (! repaintWorker.isLive())
275       {
276         repaintWorker.setLive(true);
277         SwingUtilities.invokeLater(repaintWorker);
278       }
279   }
280
281   /**
282    * Remove a component from the {@link #invalidComponents} vector.
283    *
284    * @param component The component to remove
285    *
286    * @see #addInvalidComponent
287    */
288   public synchronized void removeInvalidComponent(JComponent component)
289   {
290     invalidComponents.removeElement(component);
291   }
292
293   /**
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.
298    *
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
304    *
305    * @see #addDirtyRegion
306    * @see #getDirtyRegion
307    * @see #isCompletelyDirty
308    * @see #markCompletelyClean
309    * @see #markCompletelyDirty
310    */
311   public synchronized void addDirtyRegion(JComponent component, int x, int y,
312                                           int w, int h)
313   {
314     if (w == 0 || h == 0)
315       return;
316
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())
322       {
323         repaintWorker.setLive(true);
324         SwingUtilities.invokeLater(repaintWorker);
325       }
326   }
327   
328   /**
329    * Get the dirty region associated with a component, or <code>null</code>
330    * if the component has no dirty region.
331    *
332    * @param component The component to get the dirty region of
333    *
334    * @return The dirty region of the component
335    *
336    * @see #dirtyComponents
337    * @see #addDirtyRegion
338    * @see #isCompletelyDirty
339    * @see #markCompletelyClean
340    * @see #markCompletelyDirty
341    */
342   public Rectangle getDirtyRegion(JComponent component)
343   {
344     return (Rectangle) dirtyComponents.get(component);
345   }
346   
347   /**
348    * Mark a component as dirty over its entire bounds.
349    *
350    * @param component The component to mark as dirty
351    *
352    * @see #dirtyComponents
353    * @see #addDirtyRegion
354    * @see #getDirtyRegion
355    * @see #isCompletelyDirty
356    * @see #markCompletelyClean
357    */
358   public void markCompletelyDirty(JComponent component)
359   {
360     Rectangle r = component.getBounds();
361     addDirtyRegion(component, r.x, r.y, r.width, r.height);
362   }
363
364   /**
365    * Remove all dirty regions for a specified component
366    *
367    * @param component The component to mark as clean
368    *
369    * @see #dirtyComponents
370    * @see #addDirtyRegion
371    * @see #getDirtyRegion
372    * @see #isCompletelyDirty
373    * @see #markCompletelyDirty
374    */
375   public void markCompletelyClean(JComponent component)
376   {
377     dirtyComponents.remove(component);
378   }
379
380   /**
381    * Return <code>true</code> if the specified component is completely
382    * contained within its dirty region, otherwise <code>false</code>
383    *
384    * @param component The component to check for complete dirtyness
385    *
386    * @return Whether the component is completely dirty
387    *
388    * @see #dirtyComponents
389    * @see #addDirtyRegion
390    * @see #getDirtyRegion
391    * @see #isCompletelyDirty
392    * @see #markCompletelyClean
393    */
394   public boolean isCompletelyDirty(JComponent component)
395   {
396     Rectangle dirty = (Rectangle) dirtyComponents.get(component);
397     if (dirty == null)
398       return false;
399     Rectangle r = component.getBounds();
400     if (r == null)
401       return true;
402     return dirty.contains(r);
403   }
404
405   /**
406    * Validate all components which have been marked invalid in the {@link
407    * #invalidComponents} vector.
408    */
409   public void validateInvalidComponents()
410   {
411     for (Enumeration e = invalidComponents.elements(); e.hasMoreElements(); )
412       {
413         JComponent comp = (JComponent) e.nextElement();
414         if (! (comp.isVisible() && comp.isShowing()))
415           continue;
416         comp.validate();
417       }
418     invalidComponents.clear();
419   }
420
421   /**
422    * Repaint all regions of all components which have been marked dirty in
423    * the {@link #dirtyComponents} table.
424    */
425   public void paintDirtyRegions()
426   {
427     // step 1: pull out roots and calculate spanning damage
428
429     HashMap roots = new HashMap();
430     for (Enumeration e = dirtyComponents.keys(); e.hasMoreElements(); )
431       {
432         JComponent comp = (JComponent) e.nextElement();
433         if (! (comp.isVisible() && comp.isShowing()))
434           continue;
435         Rectangle damaged = getDirtyRegion(comp);
436         if (damaged.width == 0 || damaged.height == 0)
437           continue;
438         JRootPane root = comp.getRootPane();
439         // If the component has no root, no repainting will occur.
440         if (root == null)
441           continue;
442         Rectangle rootDamage = SwingUtilities.convertRectangle(comp, damaged, root);
443         if (! roots.containsKey(root))
444           {
445             roots.put(root, rootDamage);
446           }
447         else
448           {
449             roots.put(root, ((Rectangle)roots.get(root)).union(rootDamage));
450           }
451       }
452     dirtyComponents.clear();
453
454     // step 2: paint those roots
455     Iterator i = roots.entrySet().iterator();
456     while(i.hasNext())
457       {
458         Map.Entry ent = (Map.Entry) i.next();
459         JRootPane root = (JRootPane) ent.getKey();
460         Rectangle rect = (Rectangle) ent.getValue();
461         root.paintImmediately(rect);                    
462       }
463   }
464
465   /**
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.
469    *
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
473    *
474    * @return A shared offscreen buffer for painting
475    *
476    * @see #doubleBuffer
477    */
478   public Image getOffscreenBuffer(Component component, int proposedWidth,
479                                   int proposedHeight)
480   {
481     if (doubleBuffer == null 
482         || (((doubleBuffer.getWidth(null) < proposedWidth) 
483              || (doubleBuffer.getHeight(null) < proposedHeight))
484             && (proposedWidth < doubleBufferMaximumSize.width)
485             && (proposedHeight < doubleBufferMaximumSize.height)))
486       {
487         doubleBuffer = component.createImage(proposedWidth, proposedHeight);
488       }
489     return doubleBuffer;
490   }
491
492   /**
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.
498    *
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
502    *
503    * @since 1.4
504    *
505    * @see VolatileImage
506    */
507   public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth,
508                                           int proposedHeight)
509   {
510     int maxWidth = doubleBufferMaximumSize.width;
511     int maxHeight = doubleBufferMaximumSize.height;
512     return comp.createVolatileImage(Math.min(maxWidth, proposedWidth),
513                                     Math.min(maxHeight, proposedHeight));
514   }
515   
516
517   /**
518    * Get the value of the {@link #doubleBufferMaximumSize} property.
519    *
520    * @return The current value of the property
521    *
522    * @see #setDoubleBufferMaximumSize
523    */
524   public Dimension getDoubleBufferMaximumSize()
525   {
526     return doubleBufferMaximumSize;
527   }
528
529   /**
530    * Set the value of the {@link #doubleBufferMaximumSize} property.
531    *
532    * @param size The new value of the property
533    *
534    * @see #getDoubleBufferMaximumSize
535    */
536   public void setDoubleBufferMaximumSize(Dimension size)
537   {
538     doubleBufferMaximumSize = size;
539   }
540
541   /**
542    * Set the value of the {@link #doubleBufferingEnabled} property.
543    *
544    * @param buffer The new value of the property
545    *
546    * @see #isDoubleBufferingEnabled
547    */
548   public void setDoubleBufferingEnabled(boolean buffer)
549   {
550     doubleBufferingEnabled = buffer;
551   }
552
553   /**
554    * Get the value of the {@link #doubleBufferingEnabled} property.
555    *
556    * @return The current value of the property
557    *
558    * @see #setDoubleBufferingEnabled
559    */
560   public boolean isDoubleBufferingEnabled()
561   {
562     return doubleBufferingEnabled;
563   }
564   
565   public String toString()
566   {
567     return "RepaintManager";
568   }
569 }