home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / RepaintManager.java < prev    next >
Text File  |  1998-02-26  |  14KB  |  456 lines

  1. /*
  2.  * @(#)RepaintManager.java    1.19 98/01/30
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing;
  21.  
  22. /**
  23.  * @version 1.19 01/30/98
  24.  * @author Arnaud Weber
  25.  */
  26.  
  27. import java.awt.*;
  28. import java.awt.event.*;
  29. import java.util.*;
  30. import java.applet.*;
  31.  
  32. public class RepaintManager {
  33.     Hashtable dirtyComponents;
  34.     boolean   doubleBufferingEnabled = true;
  35.     Image     doubleBuffer;
  36.     Dimension doubleBufferMaxSize;
  37.     Vector    invalidComponents;
  38.     Timer     flushTimer;
  39.     boolean   actionPending;
  40.     RepaintListener repaintListener = new RepaintListener(this);
  41.  
  42.     private static final Object repaintManagerKey = RepaintManager.class;
  43.  
  44.     /** Return the RepaintManager for the calling thread.
  45.      *  This method will maintain one RepaintManager per 
  46.      *  thread group.
  47.      */
  48.     public static RepaintManager currentManager(JComponent comp) {
  49.         RepaintManager result = (RepaintManager)
  50.             SwingUtilities.appContextGet(repaintManagerKey);
  51.         if(result == null) {
  52.             result = new RepaintManager();
  53.             SwingUtilities.appContextPut(repaintManagerKey, result);
  54.         }
  55.     return result;
  56.     }
  57.  
  58.     /** Set the RepaintManager that should be used for the calling 
  59.      *  thread. <b>aRepaintManager</b> will become the current RepaintManager
  60.      *  for the calling thread's thread group.
  61.      */
  62.     public static void setCurrentManager(RepaintManager aRepaintManager) {
  63.         if (aRepaintManager != null) {
  64.             SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
  65.         } else {
  66.             SwingUtilities.appContextRemove(repaintManagerKey);
  67.         }
  68.     }
  69.  
  70.     /** Create a new RepaintManager instance. You rarely call this constructor.
  71.      *  directly. To get the default RepaintManager, use 
  72.      *  RepaintManager.currentManager(JComponent) (normally "this").
  73.      */
  74.     public RepaintManager() {
  75.     dirtyComponents = new Hashtable();
  76.         doubleBufferMaxSize = Toolkit.getDefaultToolkit().getScreenSize();
  77.     }
  78.  
  79.     void setupTimer() {
  80.         if(flushTimer == null) {
  81.             flushTimer = new Timer(0,repaintListener);
  82.             flushTimer.setRepeats(false);
  83.             flushTimer.setCoalesce(true);
  84.         }
  85.     if(flushTimer.isRunning()) 
  86.       flushTimer.stop();
  87.         flushTimer.start();
  88.     //System.out.println("**** timer started *****" + TimerQueue.sharedInstance());
  89.     }
  90.  
  91.     /* Add a component in the list of component with an invalid layout
  92.      */
  93.     public synchronized void addInvalidComponent(JComponent component) {
  94.         Component validateRoot = null;
  95.  
  96.         for(Component c = component; c != null; c = c.getParent()) {
  97.         if (c instanceof CellRendererPane) {
  98.         return;
  99.         }
  100.         if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {
  101.         validateRoot = c;
  102.         }
  103.     }
  104.         
  105.         if(validateRoot != null) {
  106.         Component ic;
  107.             int i,c;
  108.  
  109.             if(invalidComponents == null) {
  110.                 invalidComponents = new Vector();
  111.         }
  112.  
  113.             for(i=0,c=invalidComponents.size();i<c;i++) {
  114.                 ic = (Component) invalidComponents.elementAt(i);
  115.                 if(ic == validateRoot)
  116.                     return;
  117.             }
  118.  
  119.             invalidComponents.addElement(validateRoot);
  120.             if(!actionPending) {
  121.                 actionPending = true; 
  122.                 setupTimer();
  123.             }
  124.         }
  125.     }
  126.  
  127.     /** Remove an invalid component **/
  128.     public synchronized void removeInvalidComponent(JComponent component) {
  129.         if(invalidComponents != null) {
  130.             int index = invalidComponents.indexOf(component);
  131.             if(index != -1) {
  132.                 invalidComponents.removeElementAt(index);
  133.             }
  134.         }
  135.     }
  136.     /** Add a component in the list of component that should be refreshed.
  137.      *  If aComponent already has some dirty region, the rectangle <b>(x,y,w,h)</b> will be 
  138.      *  added to the region that should be redrawn. 
  139.      */
  140.     public synchronized void addDirtyRegion(JComponent aComponent,int x,int y,int w,int h) {
  141.     Rectangle r = null;
  142.         
  143.         if(w <= 0 || h <= 0)
  144.             return;
  145.  
  146.         if(!aComponent.isShowing()) {
  147.             return;
  148.         }
  149.  
  150.     if(dirtyComponents == null)
  151.         dirtyComponents = new Hashtable();
  152.     else
  153.         r = (Rectangle) dirtyComponents.get(aComponent);
  154.  
  155.     if(r == null) {
  156.         r = new Rectangle(x,y,w,h);
  157.             dirtyComponents.put(aComponent,r);
  158.     } else 
  159.         SwingUtilities.computeUnion(x,y,w,h,r);
  160.         
  161.         if(!actionPending) {
  162.             actionPending = true; 
  163.             setupTimer();
  164.         }
  165.     }
  166.     
  167.     /** Return the current dirty region for a component.
  168.      *  Return an empty rectangle if the component is not
  169.      *  dirty.
  170.      */
  171.     public Rectangle getDirtyRegion(JComponent aComponent) {
  172.     Rectangle r = null;
  173.     synchronized(this) {
  174.         if(dirtyComponents != null)
  175.         r = (Rectangle)dirtyComponents.get(aComponent);
  176.     }
  177.     if(r == null)
  178.         return new Rectangle(0,0,0,0);
  179.     else
  180.         return new Rectangle(r);
  181.     }
  182.  
  183.     /** Mark a component completely dirty. <b>aComponent</b> will be
  184.      *  completely painted during the next paintDirtyRegions() call.
  185.      */
  186.     public void markCompletelyDirty(JComponent aComponent) {
  187.     addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
  188.     }
  189.         
  190.     /** Mark a component completely clean. <b>aComponent</b> will not
  191.      *  get painted during the next paintDirtyRegions() call
  192.      */
  193.     public void markCompletelyClean(JComponent aComponent) {
  194.     synchronized(this) {
  195.         if(dirtyComponents != null)
  196.         dirtyComponents.remove(aComponent);
  197.     }
  198.     }
  199.  
  200.     /** Convenience that returns true if <b>aComponent</b> will be completely
  201.      *  painted during the next paintDirtyRegions(). If computing dirty regions is
  202.      *  expensive for your component, use this method and avoid computing dirty region
  203.      *  if it return true.
  204.      */
  205.     public boolean isCompletelyDirty(JComponent aComponent) {
  206.     Rectangle r;
  207.     
  208.     r = getDirtyRegion(aComponent);
  209.     if(r.width == Integer.MAX_VALUE &&
  210.        r.height == Integer.MAX_VALUE)
  211.         return true;
  212.     else
  213.         return false;
  214.     }
  215.  
  216.     public void repaintDirtyRegions() {
  217.     // System.out.println("**** action performed ***** " + invalidComponents);
  218.         synchronized(this) {
  219.             actionPending = false;
  220.             if(flushTimer != null)
  221.                 flushTimer.stop();    
  222.         }    
  223.         validateInvalidComponents();
  224.         paintDirtyRegions();
  225.     }
  226.  
  227.     /** Cause all invalide component to get validated **/
  228.     public void validateInvalidComponents() {
  229.         Vector ic;
  230.         int i,c;
  231.         
  232.         synchronized(this) {
  233.             if(invalidComponents == null)
  234.                 return;
  235.             ic = invalidComponents;
  236.             invalidComponents = null;
  237.         }
  238.         
  239.         for(i=0,c=ic.size() ; i < c ; i++) {
  240.             ((Component)ic.elementAt(i)).validate();
  241.  
  242.         }
  243.     }
  244.     
  245.     /** Cause all the known dirty regions to be painted 
  246.      */
  247.     public void paintDirtyRegions() {
  248.         int i, count;
  249.         Hashtable tmpDirtyComponents;
  250.     Vector roots;
  251.         JComponent dirtyComponent;
  252.  
  253.     /** Get all the dirty components at once.
  254.      */
  255.     synchronized(this) {
  256.         if(dirtyComponents == null) {
  257.         return;
  258.             }
  259.         tmpDirtyComponents = dirtyComponents;
  260.         dirtyComponents = null;
  261.     }
  262.  
  263.         count = tmpDirtyComponents.size();
  264.  
  265.         if (count == 0) {
  266.       //      System.out.println("No dirty component");
  267.             return;
  268.         } 
  269.  
  270.         Rectangle rect;
  271.         Rectangle localBounds;
  272.         Enumeration keys;
  273.  
  274.         roots = new Vector(count);
  275.         keys = tmpDirtyComponents.keys();
  276.  
  277.         while(keys.hasMoreElements()) {
  278.             dirtyComponent = (JComponent) keys.nextElement();
  279.             collectDirtyComponents(tmpDirtyComponents,dirtyComponent, roots);
  280.         }
  281.  
  282.         count = roots.size();
  283.         //        System.out.println("roots size is " + count);
  284.         for(i=0 ; i < count ; i++) {
  285.             dirtyComponent = (JComponent) roots.elementAt(i);
  286.             rect = (Rectangle) tmpDirtyComponents.get(dirtyComponent);
  287.             //            System.out.println("Should refresh :" + rect);
  288.             localBounds = dirtyComponent.getBounds();
  289.             localBounds.x = localBounds.y = 0;
  290.             rect = rect.intersection(localBounds);
  291.             //System.out.println("** paint of " + dirtyComponent + rect);
  292.             dirtyComponent.paintImmediately(rect.x,rect.y,rect.width,rect.height);
  293.         }
  294.     }
  295.  
  296.     void collectDirtyComponents(Hashtable dirtyComponents,
  297.                 JComponent dirtyComponent,
  298.                 Vector roots) {
  299.         int dx, dy, rootDx, rootDy;
  300.         Component component, rootDirtyComponent,parent;
  301.     Rectangle tmp;
  302.         Rectangle cBounds;
  303.         boolean opaqueAncestorFound = false;
  304.  
  305.         // Find the highest parent which is dirty.  When we get out of this
  306.         // rootDx and rootDy will contain the translation from the
  307.         // rootDirtyComponent's coordinate system to the coordinates of the
  308.         // original dirty component.  The tmp Rect is also used to compute the
  309.         // visible portion of the dirtyRect.
  310.  
  311.         component = rootDirtyComponent = dirtyComponent;
  312.  
  313.         cBounds = dirtyComponent._bounds;
  314.  
  315.         dx = rootDx = 0;
  316.         dy = rootDy = 0;
  317.         tmp = new Rectangle((Rectangle) dirtyComponents.get(dirtyComponent));
  318.  
  319.         //        System.out.println("Collect dirty component for bound " + tmp + 
  320.         //                                   "component bounds is " + cBounds);;
  321.         SwingUtilities.computeIntersection(0,0,cBounds.width,cBounds.height,tmp);
  322.  
  323.         if (tmp.isEmpty()) {
  324.             //            System.out.println("Empty 1");
  325.             return;
  326.         } 
  327.  
  328.         if(dirtyComponent.isOpaque())
  329.             opaqueAncestorFound = true;
  330.  
  331.         for(;;) {
  332.             parent = component.getParent();
  333.             if(parent == null) 
  334.                 break;
  335.  
  336.             if(!(parent instanceof JComponent))
  337.                 break;
  338.  
  339.             component = parent;
  340.  
  341.             if(((JComponent)component).isOpaque())
  342.                 opaqueAncestorFound = true;
  343.  
  344.             dx += cBounds.x;
  345.             dy += cBounds.y;
  346.             tmp.setLocation(tmp.x + cBounds.x,
  347.                             tmp.y + cBounds.y);
  348.  
  349.             cBounds = ((JComponent)component)._bounds;
  350.             tmp = SwingUtilities.computeIntersection(0,0,cBounds.width,cBounds.height,tmp);
  351.  
  352.             if (tmp.isEmpty()) {
  353.                 //                    System.out.println("Empty 2");
  354.                 return;
  355.             }
  356.  
  357.             if (dirtyComponents.get(component) != null) {
  358.                 rootDirtyComponent = component;
  359.                 rootDx = dx;
  360.                 rootDy = dy;
  361.             }
  362.         } 
  363.  
  364.         if (dirtyComponent != rootDirtyComponent) {
  365.         Rectangle r;
  366.             tmp.setLocation(tmp.x + rootDx - dx,
  367.                 tmp.y + rootDy - dy);
  368.         r = (Rectangle)dirtyComponents.get(rootDirtyComponent);
  369.         SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
  370.         }
  371.  
  372.         // If we haven't seen this root before, then we need to add it to the
  373.         // list of root dirty Views.
  374.  
  375.         if (!roots.contains(rootDirtyComponent)) 
  376.             roots.addElement(rootDirtyComponent);    
  377.     }
  378.  
  379.     public synchronized String toString() {
  380.     StringBuffer sb = new StringBuffer();
  381.     if(dirtyComponents != null) 
  382.         sb.append("" + dirtyComponents);
  383.         return sb.toString();
  384.     }
  385.  
  386.     /** Return the offscreen buffer that should be used as a double buffer with the component <code>c</code>
  387.      *  By default there is a double buffer per RepaintManager.
  388.      *  The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
  389.      *  This happens when the maximum double buffer size as been set for the receiving
  390.      *  repaint manager.
  391.      */
  392.     public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
  393.         Image result;
  394.         int width,height;
  395.  
  396.         if(proposedWidth > doubleBufferMaxSize.width)
  397.             width = doubleBufferMaxSize.width;
  398.         else
  399.             width = proposedWidth;
  400.  
  401.         if(proposedHeight > doubleBufferMaxSize.height) 
  402.             height =doubleBufferMaxSize.height;
  403.         else
  404.             height = proposedHeight;
  405.  
  406.         if(doubleBuffer != null) {
  407.             if(doubleBuffer.getWidth(null) < width ||
  408.                doubleBuffer.getHeight(null) < height)
  409.                 doubleBuffer = null;
  410.         }
  411.  
  412.         if(doubleBuffer == null) {
  413.             doubleBuffer = c.createImage(width,height);
  414.             //            System.out.println("Creating offscreen buffer " + new Dimension(width,height));
  415.         }
  416.         return doubleBuffer;
  417.     }
  418.  
  419.     /** Set the maximum double buffer size. **/
  420.     public void setDoubleBufferMaximumSize(Dimension d) {
  421.         doubleBufferMaxSize = d;
  422.         if(doubleBuffer != null) {
  423.             if(doubleBuffer.getWidth(null) > d.width ||
  424.                doubleBuffer.getHeight(null) > d.height)
  425.                 doubleBuffer = null;
  426.         }
  427.     }
  428.  
  429.     public Dimension getDoubleBufferMaximumSize() {
  430.         return doubleBufferMaxSize;
  431.     }
  432.  
  433.     public void setDoubleBufferingEnabled(boolean aFlag) {
  434.         doubleBufferingEnabled = aFlag;
  435.     if(!doubleBufferingEnabled) {
  436.       doubleBuffer = null;
  437.     }
  438.     }
  439.  
  440.     public boolean isDoubleBufferingEnabled() {
  441.       return doubleBufferingEnabled;
  442.     }
  443.  
  444.     static class RepaintListener implements ActionListener {
  445.         RepaintManager manager;
  446.  
  447.         public RepaintListener(RepaintManager rp) {
  448.             manager = rp;
  449.         }
  450.         
  451.         public void actionPerformed(ActionEvent e) {
  452.             manager.repaintDirtyRegions();
  453.         }
  454.     }
  455. }
  456.