mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			531 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			531 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Java
		
	
	
	
/* RepaintManager.java --
 | 
						|
   Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA
 | 
						|
02111-1307 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 java.awt.Component;
 | 
						|
import java.awt.Dimension;
 | 
						|
import java.awt.Image;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.util.Enumeration;
 | 
						|
import java.util.Hashtable;
 | 
						|
import java.util.HashMap;
 | 
						|
import java.util.Iterator;
 | 
						|
import java.util.Map;
 | 
						|
import java.util.Vector;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * <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>In general, painting is very confusing in swing. see <a
 | 
						|
 * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this
 | 
						|
 * document</a> for more details.</p>
 | 
						|
 *
 | 
						|
 * @author Graydon Hoare (graydon@redhat.com)
 | 
						|
 */
 | 
						|
public class RepaintManager
 | 
						|
{
 | 
						|
 | 
						|
  /**
 | 
						|
   * <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>
 | 
						|
   */
 | 
						|
 | 
						|
  protected 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()
 | 
						|
    {
 | 
						|
      RepaintManager rm = RepaintManager.globalManager;
 | 
						|
      setLive(false);
 | 
						|
      rm.validateInvalidComponents();
 | 
						|
      rm.paintDirtyRegions();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  
 | 
						|
  /** 
 | 
						|
   * 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.
 | 
						|
   *
 | 
						|
   * @see #addDirtyRegion
 | 
						|
   * @see #getDirtyRegion
 | 
						|
   * @see #isCompletelyDirty
 | 
						|
   * @see #markCompletelyClean
 | 
						|
   * @see #markCompletelyDirty
 | 
						|
   */
 | 
						|
  Hashtable dirtyComponents;
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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
 | 
						|
   */
 | 
						|
  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
 | 
						|
   */
 | 
						|
  Vector 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 #getDoubleBufferingEnabled
 | 
						|
   * @see #setDoubleBufferingEnabled
 | 
						|
   */
 | 
						|
  boolean doubleBufferingEnabled;
 | 
						|
 | 
						|
  /** 
 | 
						|
   * The current offscreen buffer. This is reused for all requests for
 | 
						|
   * offscreen drawing buffers. It grows as necessary, up to {@link
 | 
						|
   * #doubleBufferMaximumSize}, but there is only one shared instance.
 | 
						|
   *
 | 
						|
   * @see #getOffscreenBuffer
 | 
						|
   * @see #doubleBufferMaximumSize
 | 
						|
   */
 | 
						|
  Image doubleBuffer;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The maximum width and height to allocate as a double buffer. Requests
 | 
						|
   * beyond this size are ignored.
 | 
						|
   *
 | 
						|
   * @see #paintDirtyRegions
 | 
						|
   * @see #getDoubleBufferMaximumSize
 | 
						|
   * @see #setDoubleBufferMaximumSize
 | 
						|
   */
 | 
						|
  Dimension doubleBufferMaximumSize;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * The global, shared RepaintManager instance. This is reused for all
 | 
						|
   * components in all windows.
 | 
						|
   *
 | 
						|
   * @see #currentManager
 | 
						|
   * @see #setCurrentManager
 | 
						|
   */
 | 
						|
  private static RepaintManager globalManager;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Create a new RepaintManager object.
 | 
						|
   */
 | 
						|
  public RepaintManager()
 | 
						|
  {
 | 
						|
    dirtyComponents = new Hashtable();
 | 
						|
    invalidComponents = new Vector();
 | 
						|
    repaintWorker = new RepaintWorker();
 | 
						|
    doubleBufferMaximumSize = new Dimension(2000,2000);
 | 
						|
    doubleBufferingEnabled = true;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Get the value of the shared {@link #globalManager} instance, possibly
 | 
						|
   * returning a special manager associated with the specified
 | 
						|
   * component. The default implementaiton ignores the component parameter.
 | 
						|
   *
 | 
						|
   * @param component A component to look up the manager of
 | 
						|
   *
 | 
						|
   * @return The current repaint manager
 | 
						|
   *
 | 
						|
   * @see #setCurrentManager
 | 
						|
   */
 | 
						|
  public static RepaintManager currentManager(Component component)
 | 
						|
  {
 | 
						|
    if (globalManager == null)
 | 
						|
      globalManager = new RepaintManager();
 | 
						|
    return globalManager;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Get the value of the shared {@link #globalManager} instance, possibly
 | 
						|
   * returning a special manager associated with the specified
 | 
						|
   * component. The default implementaiton ignores the component parameter.
 | 
						|
   *
 | 
						|
   * @param component A component to look up the manager of
 | 
						|
   *
 | 
						|
   * @return The current repaint manager
 | 
						|
   *
 | 
						|
   * @see #setCurrentManager
 | 
						|
   */
 | 
						|
  public static RepaintManager currentManager(JComponent component)
 | 
						|
  {
 | 
						|
    return currentManager((Component)component);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Set the value of the shared {@link #globalManager} instance.
 | 
						|
   *
 | 
						|
   * @param manager The new value of the shared instance
 | 
						|
   *
 | 
						|
   * @see #currentManager
 | 
						|
   */
 | 
						|
  public static void setCurrentManager(RepaintManager manager)
 | 
						|
  {
 | 
						|
    globalManager = 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 synchronized void addInvalidComponent(JComponent component)
 | 
						|
  {
 | 
						|
    while ((component.getParent() != null)
 | 
						|
           && (component.getParent() instanceof JComponent)
 | 
						|
           && (component.isValidateRoot()))
 | 
						|
      component = (JComponent) component.getParent();
 | 
						|
    
 | 
						|
    if (invalidComponents.contains(component))
 | 
						|
      return;
 | 
						|
 | 
						|
    invalidComponents.add(component);
 | 
						|
    component.invalidate();
 | 
						|
    
 | 
						|
    if (! repaintWorker.isLive())
 | 
						|
      {
 | 
						|
        repaintWorker.setLive(true);
 | 
						|
        SwingUtilities.invokeLater(repaintWorker);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Remove a component from the {@link #invalidComponents} vector.
 | 
						|
   *
 | 
						|
   * @param component The component to remove
 | 
						|
   *
 | 
						|
   * @see #addInvalidComponent
 | 
						|
   */
 | 
						|
  public synchronized void removeInvalidComponent(JComponent component)
 | 
						|
  {
 | 
						|
    invalidComponents.removeElement(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 synchronized void addDirtyRegion(JComponent component, int x, int y,
 | 
						|
                                          int w, int h)
 | 
						|
  {
 | 
						|
    Rectangle r = new Rectangle(x, y, w, h);
 | 
						|
    if (dirtyComponents.containsKey(component))
 | 
						|
      r = r.union((Rectangle)dirtyComponents.get(component));
 | 
						|
    dirtyComponents.put(component, r);
 | 
						|
    if (! repaintWorker.isLive())
 | 
						|
      {
 | 
						|
        repaintWorker.setLive(true);
 | 
						|
        SwingUtilities.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)
 | 
						|
  {
 | 
						|
    return (Rectangle) dirtyComponents.get(component);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * 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)
 | 
						|
  {
 | 
						|
    Rectangle r = component.getBounds();
 | 
						|
    addDirtyRegion(component, r.x, r.y, r.width, r.height);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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)
 | 
						|
  {
 | 
						|
    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)
 | 
						|
  {
 | 
						|
    Rectangle dirty = (Rectangle) dirtyComponents.get(component);
 | 
						|
    if (dirty == null)
 | 
						|
      return false;
 | 
						|
    Rectangle r = component.getBounds();
 | 
						|
    if (r == null)
 | 
						|
      return true;
 | 
						|
    return dirty.contains(r);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Validate all components which have been marked invalid in the {@link
 | 
						|
   * #invalidComponents} vector.
 | 
						|
   */
 | 
						|
  public void validateInvalidComponents()
 | 
						|
  {
 | 
						|
    for (Enumeration e = invalidComponents.elements(); e.hasMoreElements(); )
 | 
						|
      {
 | 
						|
        JComponent comp = (JComponent) e.nextElement();
 | 
						|
        if (! (comp.isVisible() && comp.isShowing()))
 | 
						|
          continue;
 | 
						|
        comp.validate();
 | 
						|
      }
 | 
						|
    invalidComponents.clear();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Repaint all regions of all components which have been marked dirty in
 | 
						|
   * the {@link #dirtyComponents} table.
 | 
						|
   */
 | 
						|
  public void paintDirtyRegions()
 | 
						|
  {
 | 
						|
    // step 1: pull out roots and calculate spanning damage
 | 
						|
 | 
						|
    HashMap roots = new HashMap();
 | 
						|
    for (Enumeration e = dirtyComponents.keys(); e.hasMoreElements(); )
 | 
						|
      {
 | 
						|
        JComponent comp = (JComponent) e.nextElement();
 | 
						|
        if (! (comp.isVisible() && comp.isShowing()))
 | 
						|
          continue;
 | 
						|
        Rectangle damaged = getDirtyRegion(comp);
 | 
						|
        if (damaged.width == 0 || damaged.height == 0)
 | 
						|
          continue;
 | 
						|
        JRootPane root = comp.getRootPane();
 | 
						|
        Rectangle rootDamage = SwingUtilities.convertRectangle(comp, damaged, root);
 | 
						|
        if (! roots.containsKey(root))
 | 
						|
          {
 | 
						|
            roots.put(root, rootDamage);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            roots.put(root, ((Rectangle)roots.get(root)).union(rootDamage));
 | 
						|
          }
 | 
						|
      }
 | 
						|
    dirtyComponents.clear();
 | 
						|
 | 
						|
    // step 2: paint those roots
 | 
						|
    Iterator i = roots.entrySet().iterator();
 | 
						|
    while(i.hasNext())
 | 
						|
      {
 | 
						|
        Map.Entry ent = (Map.Entry) i.next();
 | 
						|
        JRootPane root = (JRootPane) ent.getKey();
 | 
						|
        Rectangle rect = (Rectangle) ent.getValue();
 | 
						|
        root.paintImmediately(rect);                
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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
 | 
						|
   *
 | 
						|
   * @see #doubleBuffer
 | 
						|
   */
 | 
						|
  public Image getOffscreenBuffer(Component component, int proposedWidth,
 | 
						|
                                  int proposedHeight)
 | 
						|
  {
 | 
						|
    if (doubleBuffer == null 
 | 
						|
        || (((doubleBuffer.getWidth(null) < proposedWidth) 
 | 
						|
             || (doubleBuffer.getHeight(null) < proposedHeight))
 | 
						|
            && (proposedWidth < doubleBufferMaximumSize.width)
 | 
						|
            && (proposedHeight < doubleBufferMaximumSize.height)))
 | 
						|
      {
 | 
						|
        doubleBuffer = component.createImage(proposedWidth, proposedHeight);
 | 
						|
      }
 | 
						|
    return doubleBuffer;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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 #getDoubleBufferingEnabled
 | 
						|
   */
 | 
						|
  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";
 | 
						|
  }
 | 
						|
}
 |