/[classpath]/classpath/javax/swing/JViewport.java
ViewVC logotype

Diff of /classpath/javax/swing/JViewport.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.10.2.11 by gnu_andrew, Tue Sep 20 18:46:32 2005 UTC revision 1.10.2.12 by gnu_andrew, Wed Nov 2 00:43:48 2005 UTC
# Line 41  package javax.swing; Line 41  package javax.swing;
41  import java.awt.Component;  import java.awt.Component;
42  import java.awt.Dimension;  import java.awt.Dimension;
43  import java.awt.Graphics;  import java.awt.Graphics;
44    import java.awt.Image;
45  import java.awt.Insets;  import java.awt.Insets;
46  import java.awt.LayoutManager;  import java.awt.LayoutManager;
47  import java.awt.Point;  import java.awt.Point;
# Line 129  public class JViewport extends JComponen Line 130  public class JViewport extends JComponen
130    
131    /**    /**
132     * A {@link java.awt.event.ComponentListener} that listens for     * A {@link java.awt.event.ComponentListener} that listens for
133     * changes of the view's size. This class forbids changes of the view     * changes of the view's size. This triggers a revalidate() call on the
134     * component's size that would exceed the viewport's size.     * viewport.
135     */     */
136    protected class ViewListener    protected class ViewListener extends ComponentAdapter implements Serializable
     extends ComponentAdapter  
     implements Serializable  
137    {    {
138      private static final long serialVersionUID = -2812489404285958070L;      private static final long serialVersionUID = -2812489404285958070L;
139    
# Line 143  public class JViewport extends JComponen Line 142  public class JViewport extends JComponen
142       */       */
143      protected ViewListener()      protected ViewListener()
144      {      {
145          // Nothing to do here.
146      }      }
147    
148      /**      /**
149       * Receives notification when a component (in this case: the view       * Receives notification when a component (in this case: the view
150       * component) changes it's size.       * component) changes it's size. This simply triggers a revalidate() on the
151         * viewport.
152       *       *
153       * @param ev the ComponentEvent describing the change       * @param ev the ComponentEvent describing the change
154       */       */
155      public void componentResized(ComponentEvent ev)      public void componentResized(ComponentEvent ev)
156      {      {
157        // According to some tests that I did with Sun's implementation        revalidate();
       // this class is supposed to make sure that the view component  
       // is not resized to a larger size than the viewport.  
       // This is not documented anywhere. What I did is: I subclassed JViewport  
       // and ViewListener and 'disabled' the componentResized method by  
       // overriding it and not calling super.componentResized().  
       // When this method is disabled I can set the size on the view component  
       // normally, when it is enabled, it gets immediatly resized back,  
       // after a resize attempt that would exceed the Viewport's size.  
       Component comp = ev.getComponent();  
       Dimension newSize = comp.getSize();  
       Dimension viewportSize = getSize();  
       boolean revert = false;  
       if (newSize.width > viewportSize.width)  
         {  
           newSize.width = viewportSize.width;  
           revert = true;  
         }  
       if (newSize.height > viewportSize.height)  
         {  
           newSize.height = viewportSize.height;  
           revert = true;  
         }  
       if (revert == true)  
         comp.setSize(newSize);  
158      }      }
159    }    }
160    
   private static final long serialVersionUID = -6925142919680527970L;  
     
161    public static final int SIMPLE_SCROLL_MODE = 0;    public static final int SIMPLE_SCROLL_MODE = 0;
162    public static final int BLIT_SCROLL_MODE = 1;    public static final int BLIT_SCROLL_MODE = 1;
163    public static final int BACKINGSTORE_SCROLL_MODE = 2;    public static final int BACKINGSTORE_SCROLL_MODE = 2;
164    
165      private static final long serialVersionUID = -6925142919680527970L;
166      
167      protected boolean scrollUnderway;
168      protected boolean isViewSizeSet;
169    
170      /**
171       * This flag indicates whether we use a backing store for drawing.
172       *
173       * @deprecated since JDK 1.3
174       */
175      protected boolean backingStore;
176    
177      /**
178       * The backingstore image used for the backingstore and blit scroll methods.
179       */
180      protected Image backingStoreImage;
181    
182      /**
183       * The position at which the view has been drawn the last time. This is used
184       * to determine the bittable area.
185       */
186      protected Point lastPaintPosition;
187    
188    ChangeEvent changeEvent = new ChangeEvent(this);    ChangeEvent changeEvent = new ChangeEvent(this);
189    
190    int scrollMode;    int scrollMode;
191    
   protected boolean scrollUnderway;  
   protected boolean isViewSizeSet;  
   
192    /**    /**
193     * The width and height of the Viewport's area in terms of view     * The width and height of the Viewport's area in terms of view
194     * coordinates.  Typically this will be the same as the width and height     * coordinates.  Typically this will be the same as the width and height
# Line 201  public class JViewport extends JComponen Line 196  public class JViewport extends JComponen
196     * width and height, which it may do, for example if it magnifies or     * width and height, which it may do, for example if it magnifies or
197     * rotates its view.     * rotates its view.
198     *     *
199     * @see #toViewCoordinates     * @see #toViewCoordinates(Dimension)
200     */     */
201    Dimension extentSize;    Dimension extentSize;
202    
203    /**    /**
204     * The width and height of the view in its own coordinate space.     * The width and height of the view in its own coordinate space.
205     */     */
   
206    Dimension viewSize;    Dimension viewSize;
207    
   Point lastPaintPosition;  
   
208    /**    /**
209     * The ViewListener instance.     * The ViewListener instance.
210     */     */
211    ViewListener viewListener;    ViewListener viewListener;
212    
213    /**    /**
214     * The accessible context of this <code>JViewport</code>.     * Stores the location from where to blit. This is a cached Point object used
215       * in blitting calculations.
216       */
217      Point cachedBlitFrom;
218    
219      /**
220       * Stores the location where to blit to. This is a cached Point object used
221       * in blitting calculations.
222       */
223      Point cachedBlitTo;
224    
225      /**
226       * Stores the width of the blitted area. This is a cached Dimension object
227       * used in blitting calculations.
228       */
229      Dimension cachedBlitSize;
230    
231      /**
232       * Stores the bounds of the area that needs to be repainted. This is a cached
233       * Rectangle object used in blitting calculations.
234     */     */
235    AccessibleContext accessibleContext;    Rectangle cachedBlitPaint;
236    
237      boolean damaged = true;
238    
239      /**
240       * A flag indicating if the size of the viewport has changed since the
241       * last repaint. This is used in double buffered painting to check if we
242       * need a new double buffer, or can reuse the old one.
243       */
244      boolean sizeChanged = true;
245    
246    public JViewport()    public JViewport()
247    {    {
248      setOpaque(true);      setOpaque(true);
249      setScrollMode(BLIT_SCROLL_MODE);      setScrollMode(BLIT_SCROLL_MODE);
     setLayout(createLayoutManager());  
250      updateUI();      updateUI();
251        setLayout(createLayoutManager());
252        lastPaintPosition = new Point();
253        cachedBlitFrom = new Point();
254        cachedBlitTo = new Point();
255        cachedBlitSize = new Dimension();
256        cachedBlitPaint = new Rectangle();
257    }    }
258    
259    public Dimension getExtentSize()    public Dimension getExtentSize()
# Line 314  public class JViewport extends JComponen Line 339  public class JViewport extends JComponen
339    
340    public void setViewPosition(Point p)    public void setViewPosition(Point p)
341    {    {
342        if (getViewPosition().equals(p))
343          return;
344      Component view = getView();      Component view = getView();
345      if (view != null)      if (view != null)
346        {        {
# Line 322  public class JViewport extends JComponen Line 349  public class JViewport extends JComponen
349          isViewSizeSet = false;          isViewSizeSet = false;
350          fireStateChanged();          fireStateChanged();
351        }        }
352        repaint();
353    }    }
354    
355    public Rectangle getViewRect()    public Rectangle getViewRect()
# Line 371  public class JViewport extends JComponen Line 399  public class JViewport extends JComponen
399    
400    public void setView(Component v)    public void setView(Component v)
401    {    {
402      while (getComponentCount() > 0)      if (viewListener != null)
403        {        getView().removeComponentListener(viewListener);
         if (viewListener != null)  
           getView().removeComponentListener(viewListener);  
         remove(0);  
       }  
404    
405      if (v != null)      if (v != null)
406        {        {
# Line 386  public class JViewport extends JComponen Line 410  public class JViewport extends JComponen
410          add(v);          add(v);
411          fireStateChanged();          fireStateChanged();
412        }        }
413    }      revalidate();
414        repaint();
   public void revalidate()  
   {  
     fireStateChanged();  
     super.revalidate();  
415    }    }
416    
417    public void reshape(int x, int y, int w, int h)    public void reshape(int x, int y, int w, int h)
418    {    {
419      boolean changed =      if (w != getWidth() || h != getHeight())
420        (x != getX())        sizeChanged = true;
       || (y != getY())  
       || (w != getWidth())  
       || (h != getHeight());  
421      super.reshape(x, y, w, h);      super.reshape(x, y, w, h);
422      if (changed)      if (sizeChanged)
423        fireStateChanged();        {
424    }          damaged = true;
425                fireStateChanged();
426    protected void addImpl(Component comp, Object constraints, int index)        }
   {  
     if (getComponentCount() > 0)  
       remove(getComponents()[0]);  
       
     super.addImpl(comp, constraints, index);  
427    }    }
428    
429    public final Insets getInsets()    public final Insets getInsets()
430    {    {
431      return new Insets(0,0,0,0);      return new Insets(0, 0, 0, 0);
432    }    }
433    
434    public final Insets getInsets(Insets insets)    public final Insets getInsets(Insets insets)
# Line 430  public class JViewport extends JComponen Line 442  public class JViewport extends JComponen
442      return insets;      return insets;
443    }    }
444            
445    
446      /**
447       * Overridden to return <code>false</code>, so the JViewport's paint method
448       * gets called instead of directly calling the children. This is necessary
449       * in order to get a useful clipping and translation on the children.
450       *
451       * @return <code>false</code>
452       */
453    public boolean isOptimizedDrawingEnabled()    public boolean isOptimizedDrawingEnabled()
454    {    {
455      return false;      return false;
# Line 437  public class JViewport extends JComponen Line 457  public class JViewport extends JComponen
457    
458    public void paint(Graphics g)    public void paint(Graphics g)
459    {    {
460      paintComponent(g);      Component view = getView();
461    
462        if (view == null)
463          return;
464    
465        Point pos = getViewPosition();
466        Rectangle viewBounds = view.getBounds();
467        Rectangle portBounds = getBounds();
468    
469        if (viewBounds.width == 0
470            || viewBounds.height == 0
471            || portBounds.width == 0
472            || portBounds.height == 0)
473          return;
474    
475        switch (getScrollMode())
476          {
477    
478          case JViewport.BACKINGSTORE_SCROLL_MODE:
479            paintBackingStore(g);
480            break;
481          case JViewport.BLIT_SCROLL_MODE:
482            paintBlit(g);
483            break;
484          case JViewport.SIMPLE_SCROLL_MODE:
485          default:
486            paintSimple(g);
487            break;
488          }
489        damaged = false;
490    }    }
491    
492    public void addChangeListener(ChangeListener listener)    public void addChangeListener(ChangeListener listener)
# Line 455  public class JViewport extends JComponen Line 504  public class JViewport extends JComponen
504      return (ChangeListener[]) getListeners(ChangeListener.class);      return (ChangeListener[]) getListeners(ChangeListener.class);
505    }    }
506    
   protected void fireStateChanged()  
   {  
     ChangeListener[] listeners = getChangeListeners();  
     for (int i = 0; i < listeners.length; ++i)  
       listeners[i].stateChanged(changeEvent);  
   }  
   
507    /**    /**
508     * This method returns the String ID of the UI class of  Separator.     * This method returns the String ID of the UI class of  Separator.
509     *     *
# Line 507  public class JViewport extends JComponen Line 549  public class JViewport extends JComponen
549    }    }
550    
551    /**    /**
552       * Scrolls the view so that contentRect becomes visible.
553       *
554       * @param contentRect the rectangle to make visible within the view
555       */
556      public void scrollRectToVisible(Rectangle contentRect)
557      {
558        Component view = getView();
559        if (view == null)
560          return;    
561          
562        Point pos = getViewPosition();
563        Rectangle viewBounds = getView().getBounds();
564        Rectangle portBounds = getBounds();
565        
566        if (isShowing())
567          getView().validate();
568    
569        // If the bottom boundary of contentRect is below the port
570        // boundaries, scroll up as necessary.
571        if (contentRect.y + contentRect.height + viewBounds.y > portBounds.height)
572          pos.y = contentRect.y + contentRect.height - portBounds.height;
573        // If contentRect.y is above the port boundaries, scroll down to
574        // contentRect.y.
575        if (contentRect.y + viewBounds.y < 0)
576          pos.y = contentRect.y;
577        // If the right boundary of contentRect is right from the port
578        // boundaries, scroll left as necessary.
579        if (contentRect.x + contentRect.width + viewBounds.x > portBounds.width)
580          pos.x = contentRect.x + contentRect.width - portBounds.width;
581        // If contentRect.x is left from the port boundaries, scroll right to
582        // contentRect.x.
583        if (contentRect.x + viewBounds.x < 0)
584          pos.x = contentRect.x;
585        setViewPosition(pos);
586      }
587    
588      /**
589       * Returns the accessible context for this <code>JViewport</code>. This
590       * will be an instance of {@link AccessibleJViewport}.
591       *
592       * @return the accessible context for this <code>JViewport</code>
593       */
594      public AccessibleContext getAccessibleContext()
595      {
596        if (accessibleContext == null)
597          accessibleContext = new AccessibleJViewport();
598        return accessibleContext;
599      }
600    
601      /**
602       * Forward repaint to parent to make sure only one paint is performed by the
603       * RepaintManager.
604       *
605       * @param tm number of milliseconds to defer the repaint request
606       * @param x the X coordinate of the upper left corner of the dirty area
607       * @param y the Y coordinate of the upper left corner of the dirty area
608       * @param w the width of the dirty area
609       * @param h the height of the dirty area
610       */
611      public void repaint(long tm, int x, int y, int w, int h)
612      {
613        Component parent = getParent();
614        if (parent != null)
615          {
616            parent.repaint(tm, x + getX(), y + getY(), w, h);
617          }
618      }
619    
620      protected void addImpl(Component comp, Object constraints, int index)
621      {
622        if (getComponentCount() > 0)
623          remove(getComponents()[0]);
624        
625        super.addImpl(comp, constraints, index);
626      }
627    
628      protected void fireStateChanged()
629      {
630        ChangeListener[] listeners = getChangeListeners();
631        for (int i = 0; i < listeners.length; ++i)
632          listeners[i].stateChanged(changeEvent);
633      }
634    
635      /**
636     * Creates a {@link ViewListener} that is supposed to listen for     * Creates a {@link ViewListener} that is supposed to listen for
637     * size changes on the view component.     * size changes on the view component.
638     *     *
# Line 529  public class JViewport extends JComponen Line 655  public class JViewport extends JComponen
655    }    }
656    
657    /**    /**
658     * Scrolls the view so that contentRect becomes visible.     * Computes the parameters for the blitting scroll method. <code>dx</code>
659       * and <code>dy</code> specifiy the X and Y offset by which the viewport
660       * is scrolled. All other arguments are output parameters and are filled by
661       * this method.
662     *     *
663     * @param contentRect the rectangle to make visible within the view     * <code>blitFrom</code> holds the position of the blit rectangle in the
664       * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea
665       * is copied to.
666       *
667       * <code>blitSize</code> holds the size of the blit area and
668       * <code>blitPaint</code> is the area of the view that needs to be painted.
669       *
670       * This method returns <code>true</code> if blitting is possible and
671       * <code>false</code> if the viewport has to be repainted completetly without
672       * blitting.
673       *
674       * @param dx the horizontal delta
675       * @param dy the vertical delta
676       * @param blitFrom the position from where to blit; set by this method
677       * @param blitTo the position where to blit area is copied to; set by this
678       *        method
679       * @param blitSize the size of the blitted area; set by this method
680       * @param blitPaint the area that needs repainting; set by this method
681       *
682       * @return <code>true</code> if blitting is possible,
683       *         <code>false</code> otherwise
684     */     */
685    public void scrollRectToVisible(Rectangle contentRect)    protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo,
686                                    Dimension blitSize, Rectangle blitPaint)
687      {
688        if ((dx != 0 && dy != 0) || damaged)
689          // We cannot blit if the viewport is scrolled in both directions at
690          // once.
691          return false;
692    
693        Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds());
694    
695        // Compute the blitFrom and blitTo parameters.
696        blitFrom.x = portBounds.x;
697        blitFrom.y = portBounds.y;
698        blitTo.x = portBounds.x;
699        blitTo.y = portBounds.y;
700    
701        if (dy > 0)
702          {
703            blitFrom.y = portBounds.y + dy;
704          }
705        else if (dy < 0)
706          {
707            blitTo.y = portBounds.y - dy;
708          }
709        else if (dx > 0)
710          {
711            blitFrom.x = portBounds.x + dx;
712          }
713        else if (dx < 0)
714          {
715            blitTo.x = portBounds.x - dx;
716          }
717    
718        // Compute size of the blit area.
719        if (dx != 0)
720          {
721            blitSize.width = portBounds.width - Math.abs(dx);
722            blitSize.height = portBounds.height;
723          }
724        else if (dy != 0)
725          {
726            blitSize.width = portBounds.width;
727            blitSize.height = portBounds.height - Math.abs(dy);
728          }
729    
730        // Compute the blitPaint parameter.
731        blitPaint.setBounds(portBounds);
732        if (dy > 0)
733          {
734            blitPaint.y = portBounds.y + portBounds.height - dy;
735            blitPaint.height = dy;
736          }
737        else if (dy < 0)
738          {
739            blitPaint.height = -dy;
740          }
741        if (dx > 0)
742          {
743            blitPaint.x = portBounds.x + portBounds.width - dx;
744            blitPaint.width = dx;
745          }
746        else if (dx < 0)
747          {
748            blitPaint.width = -dx;
749          }
750    
751        return true;
752      }
753    
754      /**
755       * Paints the viewport in case we have a scrollmode of
756       * {@link #SIMPLE_SCROLL_MODE}.
757       *
758       * This simply paints the view directly on the surface of the viewport.
759       *
760       * @param g the graphics context to use
761       */
762      void paintSimple(Graphics g)
763    {    {
764      Point pos = getViewPosition();      Point pos = getViewPosition();
765      Rectangle viewBounds = getView().getBounds();      Component view = getView();
766      Rectangle portBounds = getBounds();      boolean translated = false;
767            try
     // FIXME: should validate the view if it is not valid, however  
     // this may cause excessive validation when the containment  
     // hierarchy is being created.  
       
     // if contentRect is larger than the portBounds, center the view  
     if (contentRect.height > portBounds.height ||  
         contentRect.width > portBounds.width)  
768        {        {
769          setViewPosition(new Point(contentRect.x, contentRect.y));          g.translate(-pos.x, -pos.y);
770          return;          translated = true;
771            view.paint(g);
772          }
773        finally
774          {
775            if (translated)
776              g.translate (pos.x, pos.y);
777        }        }
       
     // Y-DIRECTION  
     if (contentRect.y < -viewBounds.y)  
       setViewPosition(new Point(pos.x, contentRect.y));  
     else if (contentRect.y + contentRect.height >  
              -viewBounds.y + portBounds.height)  
       setViewPosition (new Point(pos.x, contentRect.y -  
                                  (portBounds.height - contentRect.height)));  
       
     // X-DIRECTION  
     pos = getViewPosition();  
     if (contentRect.x < -viewBounds.x)  
       setViewPosition(new Point(contentRect.x, pos.y));  
     else if (contentRect.x + contentRect.width >  
              -viewBounds.x + portBounds.width)  
       setViewPosition (new Point(contentRect.x -  
                                  (portBounds.width - contentRect.width), pos.y));  
778    }    }
779    
780    /**    /**
781     * Returns the accessible context for this <code>JViewport</code>. This     * Paints the viewport in case we have a scroll mode of
782     * will be an instance of {@link AccessibleJViewport}.     * {@link #BACKINGSTORE_SCROLL_MODE}.
783     *     *
784     * @return the accessible context for this <code>JViewport</code>     * This method uses a backing store image to paint the view to, which is then
785       * subsequently painted on the screen. This should make scrolling more
786       * smooth.
787       *
788       * @param g the graphics context to use
789     */     */
790    public AccessibleContext getAccessibleContext()    void paintBackingStore(Graphics g)
791    {    {
792      if (accessibleContext == null)      // If we have no backing store image yet or the size of the component has
793        accessibleContext = new AccessibleJViewport();      // changed, we need to rebuild the backing store.
794      return accessibleContext;      if (backingStoreImage == null || sizeChanged)
795          {
796            backingStoreImage = createImage(getWidth(), getHeight());
797            sizeChanged = false;
798            Graphics g2 = backingStoreImage.getGraphics();
799            paintSimple(g2);
800            g2.dispose();
801          }
802        // Otherwise we can perform the blitting on the backing store image:
803        // First we move the part that remains visible after scrolling, then
804        // we only need to paint the bit that becomes newly visible.
805        else
806          {
807            Graphics g2 = backingStoreImage.getGraphics();
808            Point viewPosition = getViewPosition();
809            int dx = viewPosition.x - lastPaintPosition.x;
810            int dy = viewPosition.y - lastPaintPosition.y;
811            boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo,
812                                          cachedBlitSize, cachedBlitPaint);
813            if (canBlit)
814              {
815                // Copy the part that remains visible during scrolling.
816                g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
817                            cachedBlitSize.width, cachedBlitSize.height,
818                            cachedBlitTo.x - cachedBlitFrom.x,
819                            cachedBlitTo.y - cachedBlitFrom.y);
820                // Now paint the part that becomes newly visible.
821                g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y,
822                           cachedBlitPaint.width, cachedBlitPaint.height);
823                paintSimple(g2);
824              }
825            // If blitting is not possible for some reason, fall back to repainting
826            // everything.
827            else
828              {
829                paintSimple(g2);
830              }
831            g2.dispose();
832          }
833        // Actually draw the backingstore image to the graphics context.
834        g.drawImage(backingStoreImage, 0, 0, this);
835        // Update the lastPaintPosition so that we know what is already drawn when
836        // we paint the next time.
837        lastPaintPosition.setLocation(getViewPosition());
838      }
839    
840      /**
841       * Paints the viewport in case we have a scrollmode of
842       * {@link #BLIT_SCROLL_MODE}.
843       *
844       * This paints the viewport using a backingstore and a blitting algorithm.
845       * Only the newly exposed area of the view is painted from the view painting
846       * methods, the remainder is copied from the backing store.
847       *
848       * @param g the graphics context to use
849       */
850      void paintBlit(Graphics g)
851      {
852        // We cannot perform blitted painting as it is described in Sun's API docs.
853        // There it is suggested that this painting method should blit directly
854        // on the parent window's surface. This is not possible because when using
855        // Swing's double buffering (at least our implementation), it would
856        // immediatly be painted when the buffer is painted on the screen. For this
857        // to work we would need a kind of hole in the buffer image. And honestly
858        // I find this method not very elegant.
859        // The alternative, blitting directly on the buffer image, is also not
860        // possible because the buffer image gets cleared everytime when an opaque
861        // parent component is drawn on it.
862    
863        // What we do instead is falling back to the backing store approach which
864        // is in fact a mixed blitting/backing store approach where the blitting
865        // is performed on the backing store image and this is then drawn to the
866        // graphics context. This is very robust and works independent of the
867        // painting mechanism that is used by Swing. And it should have comparable
868        // performance characteristics as the blitting method.
869        paintBackingStore(g);
870    }    }
871  }  }

Legend:
Removed from v.1.10.2.11  
changed lines
  Added in v.1.10.2.12

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26