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)
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
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
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. */
41 import gnu
.classpath
.SystemProperties
;
43 import java
.awt
.Component
;
44 import java
.awt
.Dimension
;
45 import java
.awt
.Graphics
;
46 import java
.awt
.Image
;
47 import java
.awt
.Insets
;
48 import java
.awt
.LayoutManager
;
49 import java
.awt
.Point
;
50 import java
.awt
.Rectangle
;
51 import java
.awt
.event
.ComponentAdapter
;
52 import java
.awt
.event
.ComponentEvent
;
53 import java
.io
.Serializable
;
55 import javax
.accessibility
.Accessible
;
56 import javax
.accessibility
.AccessibleContext
;
57 import javax
.accessibility
.AccessibleRole
;
58 import javax
.swing
.border
.Border
;
59 import javax
.swing
.event
.ChangeEvent
;
60 import javax
.swing
.event
.ChangeListener
;
61 import javax
.swing
.plaf
.ViewportUI
;
67 * +-------------------------------+ ...........Y1 \
69 * | (this component's child) | . > VY
71 * | +------------------------------+ ....Y2_/
73 * | | (this component) | | .
78 * | +------------------------------+ ....Y3
82 * +---------.---------------------+ ...........Y4
86 * X1.......X2.....................X3.......X4
92 * <p>A viewport is, like all swing components, located at some position in
93 * the swing component tree; that location is exactly the same as any other
94 * components: the viewport's "bounds".</p>
96 * <p>But in terms of drawing its child, the viewport thinks of itself as
97 * covering a particular position <em>of the view's coordinate space</em>.
98 * For example, the {@link #getViewPosition} method returns
99 * the position <code>(VX,VY)</code> shown above, which is an position in
100 * "view space", even though this is <em>implemented</em> by positioning
101 * the underlying child at position <code>(-VX,-VY)</code></p>
104 public class JViewport
extends JComponent
implements Accessible
107 * Provides accessibility support for <code>JViewport</code>.
109 * @author Roman Kennke (roman@kennke.org)
111 protected class AccessibleJViewport
extends AccessibleJComponent
114 * Creates a new instance of <code>AccessibleJViewport</code>.
116 public AccessibleJViewport()
118 // Nothing to do here.
122 * Returns the accessible role of <code>JViewport</code>, which is
123 * {@link AccessibleRole#VIEWPORT}.
125 * @return the accessible role of <code>JViewport</code>
127 public AccessibleRole
getAccessibleRole()
129 return AccessibleRole
.VIEWPORT
;
134 * A {@link java.awt.event.ComponentListener} that listens for
135 * changes of the view's size. This triggers a revalidate() call on the
138 protected class ViewListener
extends ComponentAdapter
implements Serializable
140 private static final long serialVersionUID
= -2812489404285958070L;
143 * Creates a new instance of ViewListener.
145 protected ViewListener()
147 // Nothing to do here.
151 * Receives notification when a component (in this case: the view
152 * component) changes it's size. This simply triggers a revalidate() on the
155 * @param ev the ComponentEvent describing the change
157 public void componentResized(ComponentEvent ev
)
163 public static final int SIMPLE_SCROLL_MODE
= 0;
164 public static final int BLIT_SCROLL_MODE
= 1;
165 public static final int BACKINGSTORE_SCROLL_MODE
= 2;
167 private static final long serialVersionUID
= -6925142919680527970L;
170 * The default scrollmode to be used by all JViewports as determined by
171 * the system property gnu.javax.swing.JViewport.scrollMode.
173 private static final int defaultScrollMode
;
175 protected boolean scrollUnderway
;
176 protected boolean isViewSizeSet
;
179 * This flag indicates whether we use a backing store for drawing.
181 * @deprecated since JDK 1.3
183 protected boolean backingStore
;
186 * The backingstore image used for the backingstore and blit scroll methods.
188 protected Image backingStoreImage
;
191 * The position at which the view has been drawn the last time. This is used
192 * to determine the bittable area.
194 protected Point lastPaintPosition
;
196 ChangeEvent changeEvent
= new ChangeEvent(this);
201 * The width and height of the Viewport's area in terms of view
202 * coordinates. Typically this will be the same as the width and height
203 * of the viewport's bounds, unless the viewport transforms units of
204 * width and height, which it may do, for example if it magnifies or
207 * @see #toViewCoordinates(Dimension)
209 Dimension extentSize
;
212 * The width and height of the view in its own coordinate space.
217 * The ViewListener instance.
219 ViewListener viewListener
;
222 * Stores the location from where to blit. This is a cached Point object used
223 * in blitting calculations.
225 Point cachedBlitFrom
;
228 * Stores the location where to blit to. This is a cached Point object used
229 * in blitting calculations.
234 * Stores the width of the blitted area. This is a cached Dimension object
235 * used in blitting calculations.
237 Dimension cachedBlitSize
;
240 * Stores the bounds of the area that needs to be repainted. This is a cached
241 * Rectangle object used in blitting calculations.
243 Rectangle cachedBlitPaint
;
245 boolean damaged
= true;
248 * A flag indicating if the size of the viewport has changed since the
249 * last repaint. This is used in double buffered painting to check if we
250 * need a new double buffer, or can reuse the old one.
252 boolean sizeChanged
= true;
255 * Initializes the default setting for the scrollMode property.
259 String scrollModeProp
=
260 SystemProperties
.getProperty("gnu.javax.swing.JViewport.scrollMode",
262 if (scrollModeProp
.equalsIgnoreCase("simple"))
263 defaultScrollMode
= SIMPLE_SCROLL_MODE
;
264 else if (scrollModeProp
.equalsIgnoreCase("backingstore"))
265 defaultScrollMode
= BACKINGSTORE_SCROLL_MODE
;
267 defaultScrollMode
= BLIT_SCROLL_MODE
;
273 setScrollMode(defaultScrollMode
);
275 setLayout(createLayoutManager());
276 lastPaintPosition
= new Point();
277 cachedBlitFrom
= new Point();
278 cachedBlitTo
= new Point();
279 cachedBlitSize
= new Dimension();
280 cachedBlitPaint
= new Rectangle();
283 public Dimension
getExtentSize()
285 if (extentSize
== null)
286 return toViewCoordinates(getSize());
291 public Dimension
toViewCoordinates(Dimension size
)
296 public Point
toViewCoordinates(Point p
)
298 Point pos
= getViewPosition();
299 return new Point(p
.x
+ pos
.x
,
303 public void setExtentSize(Dimension newSize
)
305 extentSize
= newSize
;
310 * Returns the viewSize when set, or the preferred size of the set
311 * Component view. If no viewSize and no Component view is set an
312 * empty Dimension is returned.
314 public Dimension
getViewSize()
320 Component view
= getView();
322 return view
.getPreferredSize();
324 return new Dimension();
329 public void setViewSize(Dimension newSize
)
332 Component view
= getView();
335 if (newSize
!= view
.getSize())
337 view
.setSize(viewSize
);
341 isViewSizeSet
= true;
345 * Get the viewport's position in view space. Despite confusing name,
346 * this really does return the viewport's (0,0) position in view space,
347 * not the view's position.
350 public Point
getViewPosition()
352 Component view
= getView();
354 return new Point(0,0);
357 Point p
= view
.getLocation();
364 public void setViewPosition(Point p
)
366 if (getViewPosition().equals(p
))
368 Component view
= getView();
371 Point q
= new Point(-p
.x
, -p
.y
);
373 isViewSizeSet
= false;
379 public Rectangle
getViewRect()
381 return new Rectangle(getViewPosition(),
388 public boolean isBackingStoreEnabled()
390 return scrollMode
== BACKINGSTORE_SCROLL_MODE
;
396 public void setBackingStoreEnabled(boolean b
)
398 if (b
&& scrollMode
!= BACKINGSTORE_SCROLL_MODE
)
400 scrollMode
= BACKINGSTORE_SCROLL_MODE
;
405 public void setScrollMode(int mode
)
411 public int getScrollMode()
416 public Component
getView()
418 if (getComponentCount() == 0)
421 return getComponents()[0];
424 public void setView(Component v
)
426 Component currView
= getView();
427 if (viewListener
!= null && currView
!= null)
428 currView
.removeComponentListener(viewListener
);
432 if (viewListener
== null)
433 viewListener
= createViewListener();
434 v
.addComponentListener(viewListener
);
442 public void reshape(int x
, int y
, int w
, int h
)
444 if (w
!= getWidth() || h
!= getHeight())
446 super.reshape(x
, y
, w
, h
);
454 public final Insets
getInsets()
456 return new Insets(0, 0, 0, 0);
459 public final Insets
getInsets(Insets insets
)
472 * Overridden to return <code>false</code>, so the JViewport's paint method
473 * gets called instead of directly calling the children. This is necessary
474 * in order to get a useful clipping and translation on the children.
476 * @return <code>false</code>
478 public boolean isOptimizedDrawingEnabled()
483 public void paint(Graphics g
)
485 Component view
= getView();
490 Point pos
= getViewPosition();
491 Rectangle viewBounds
= view
.getBounds();
492 Rectangle portBounds
= getBounds();
494 if (viewBounds
.width
== 0
495 || viewBounds
.height
== 0
496 || portBounds
.width
== 0
497 || portBounds
.height
== 0)
500 switch (getScrollMode())
503 case JViewport
.BACKINGSTORE_SCROLL_MODE
:
504 paintBackingStore(g
);
506 case JViewport
.BLIT_SCROLL_MODE
:
509 case JViewport
.SIMPLE_SCROLL_MODE
:
517 public void addChangeListener(ChangeListener listener
)
519 listenerList
.add(ChangeListener
.class, listener
);
522 public void removeChangeListener(ChangeListener listener
)
524 listenerList
.remove(ChangeListener
.class, listener
);
527 public ChangeListener
[] getChangeListeners()
529 return (ChangeListener
[]) getListeners(ChangeListener
.class);
533 * This method returns the String ID of the UI class of Separator.
535 * @return The UI class' String ID.
537 public String
getUIClassID()
543 * This method resets the UI used to the Look and Feel defaults..
545 public void updateUI()
547 setUI((ViewportUI
) UIManager
.getUI(this));
551 * This method returns the viewport's UI delegate.
553 * @return The viewport's UI delegate.
555 public ViewportUI
getUI()
557 return (ViewportUI
) ui
;
561 * This method sets the viewport's UI delegate.
563 * @param ui The viewport's UI delegate.
565 public void setUI(ViewportUI ui
)
570 public final void setBorder(Border border
)
573 throw new IllegalArgumentException();
577 * Scrolls the view so that contentRect becomes visible.
579 * @param contentRect the rectangle to make visible within the view
581 public void scrollRectToVisible(Rectangle contentRect
)
583 Component view
= getView();
587 Point pos
= getViewPosition();
588 Rectangle viewBounds
= getView().getBounds();
589 Rectangle portBounds
= getBounds();
592 getView().validate();
594 // If the bottom boundary of contentRect is below the port
595 // boundaries, scroll up as necessary.
596 if (contentRect
.y
+ contentRect
.height
+ viewBounds
.y
> portBounds
.height
)
597 pos
.y
= contentRect
.y
+ contentRect
.height
- portBounds
.height
;
598 // If contentRect.y is above the port boundaries, scroll down to
600 if (contentRect
.y
+ viewBounds
.y
< 0)
601 pos
.y
= contentRect
.y
;
602 // If the right boundary of contentRect is right from the port
603 // boundaries, scroll left as necessary.
604 if (contentRect
.x
+ contentRect
.width
+ viewBounds
.x
> portBounds
.width
)
605 pos
.x
= contentRect
.x
+ contentRect
.width
- portBounds
.width
;
606 // If contentRect.x is left from the port boundaries, scroll right to
608 if (contentRect
.x
+ viewBounds
.x
< 0)
609 pos
.x
= contentRect
.x
;
610 setViewPosition(pos
);
614 * Returns the accessible context for this <code>JViewport</code>. This
615 * will be an instance of {@link AccessibleJViewport}.
617 * @return the accessible context for this <code>JViewport</code>
619 public AccessibleContext
getAccessibleContext()
621 if (accessibleContext
== null)
622 accessibleContext
= new AccessibleJViewport();
623 return accessibleContext
;
627 * Forward repaint to parent to make sure only one paint is performed by the
630 * @param tm number of milliseconds to defer the repaint request
631 * @param x the X coordinate of the upper left corner of the dirty area
632 * @param y the Y coordinate of the upper left corner of the dirty area
633 * @param w the width of the dirty area
634 * @param h the height of the dirty area
636 public void repaint(long tm
, int x
, int y
, int w
, int h
)
638 Component parent
= getParent();
641 parent
.repaint(tm
, x
+ getX(), y
+ getY(), w
, h
);
645 protected void addImpl(Component comp
, Object constraints
, int index
)
647 if (getComponentCount() > 0)
648 remove(getComponents()[0]);
650 super.addImpl(comp
, constraints
, index
);
653 protected void fireStateChanged()
655 ChangeListener
[] listeners
= getChangeListeners();
656 for (int i
= 0; i
< listeners
.length
; ++i
)
657 listeners
[i
].stateChanged(changeEvent
);
661 * Creates a {@link ViewListener} that is supposed to listen for
662 * size changes on the view component.
664 * @return a ViewListener instance
666 protected ViewListener
createViewListener()
668 return new ViewListener();
672 * Creates the LayoutManager that is used for this viewport. Override
673 * this method if you want to use a custom LayoutManager.
675 * @return a LayoutManager to use for this viewport
677 protected LayoutManager
createLayoutManager()
679 return new ViewportLayout();
683 * Computes the parameters for the blitting scroll method. <code>dx</code>
684 * and <code>dy</code> specifiy the X and Y offset by which the viewport
685 * is scrolled. All other arguments are output parameters and are filled by
688 * <code>blitFrom</code> holds the position of the blit rectangle in the
689 * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea
692 * <code>blitSize</code> holds the size of the blit area and
693 * <code>blitPaint</code> is the area of the view that needs to be painted.
695 * This method returns <code>true</code> if blitting is possible and
696 * <code>false</code> if the viewport has to be repainted completetly without
699 * @param dx the horizontal delta
700 * @param dy the vertical delta
701 * @param blitFrom the position from where to blit; set by this method
702 * @param blitTo the position where to blit area is copied to; set by this
704 * @param blitSize the size of the blitted area; set by this method
705 * @param blitPaint the area that needs repainting; set by this method
707 * @return <code>true</code> if blitting is possible,
708 * <code>false</code> otherwise
710 protected boolean computeBlit(int dx
, int dy
, Point blitFrom
, Point blitTo
,
711 Dimension blitSize
, Rectangle blitPaint
)
713 if ((dx
!= 0 && dy
!= 0) || damaged
)
714 // We cannot blit if the viewport is scrolled in both directions at
718 Rectangle portBounds
= SwingUtilities
.calculateInnerArea(this, getBounds());
720 // Compute the blitFrom and blitTo parameters.
721 blitFrom
.x
= portBounds
.x
;
722 blitFrom
.y
= portBounds
.y
;
723 blitTo
.x
= portBounds
.x
;
724 blitTo
.y
= portBounds
.y
;
728 blitFrom
.y
= portBounds
.y
+ dy
;
732 blitTo
.y
= portBounds
.y
- dy
;
736 blitFrom
.x
= portBounds
.x
+ dx
;
740 blitTo
.x
= portBounds
.x
- dx
;
743 // Compute size of the blit area.
746 blitSize
.width
= portBounds
.width
- Math
.abs(dx
);
747 blitSize
.height
= portBounds
.height
;
751 blitSize
.width
= portBounds
.width
;
752 blitSize
.height
= portBounds
.height
- Math
.abs(dy
);
755 // Compute the blitPaint parameter.
756 blitPaint
.setBounds(portBounds
);
759 blitPaint
.y
= portBounds
.y
+ portBounds
.height
- dy
;
760 blitPaint
.height
= dy
;
764 blitPaint
.height
= -dy
;
768 blitPaint
.x
= portBounds
.x
+ portBounds
.width
- dx
;
769 blitPaint
.width
= dx
;
773 blitPaint
.width
= -dx
;
780 * Paints the viewport in case we have a scrollmode of
781 * {@link #SIMPLE_SCROLL_MODE}.
783 * This simply paints the view directly on the surface of the viewport.
785 * @param g the graphics context to use
787 void paintSimple(Graphics g
)
789 // We need to call this to properly clear the background.
792 Point pos
= getViewPosition();
793 Component view
= getView();
794 boolean translated
= false;
797 g
.translate(-pos
.x
, -pos
.y
);
804 g
.translate (pos
.x
, pos
.y
);
809 * Paints the viewport in case we have a scroll mode of
810 * {@link #BACKINGSTORE_SCROLL_MODE}.
812 * This method uses a backing store image to paint the view to, which is then
813 * subsequently painted on the screen. This should make scrolling more
816 * @param g the graphics context to use
818 void paintBackingStore(Graphics g
)
820 // If we have no backing store image yet or the size of the component has
821 // changed, we need to rebuild the backing store.
822 if (backingStoreImage
== null || sizeChanged
)
824 backingStoreImage
= createImage(getWidth(), getHeight());
826 Graphics g2
= backingStoreImage
.getGraphics();
830 // Otherwise we can perform the blitting on the backing store image:
831 // First we move the part that remains visible after scrolling, then
832 // we only need to paint the bit that becomes newly visible.
835 Graphics g2
= backingStoreImage
.getGraphics();
836 Point viewPosition
= getViewPosition();
837 int dx
= viewPosition
.x
- lastPaintPosition
.x
;
838 int dy
= viewPosition
.y
- lastPaintPosition
.y
;
839 boolean canBlit
= computeBlit(dx
, dy
, cachedBlitFrom
, cachedBlitTo
,
840 cachedBlitSize
, cachedBlitPaint
);
843 // Copy the part that remains visible during scrolling.
844 g2
.copyArea(cachedBlitFrom
.x
, cachedBlitFrom
.y
,
845 cachedBlitSize
.width
, cachedBlitSize
.height
,
846 cachedBlitTo
.x
- cachedBlitFrom
.x
,
847 cachedBlitTo
.y
- cachedBlitFrom
.y
);
848 // Now paint the part that becomes newly visible.
849 g2
.setClip(cachedBlitPaint
.x
, cachedBlitPaint
.y
,
850 cachedBlitPaint
.width
, cachedBlitPaint
.height
);
853 // If blitting is not possible for some reason, fall back to repainting
861 // Actually draw the backingstore image to the graphics context.
862 g
.drawImage(backingStoreImage
, 0, 0, this);
863 // Update the lastPaintPosition so that we know what is already drawn when
864 // we paint the next time.
865 lastPaintPosition
.setLocation(getViewPosition());
869 * Paints the viewport in case we have a scrollmode of
870 * {@link #BLIT_SCROLL_MODE}.
872 * This paints the viewport using a backingstore and a blitting algorithm.
873 * Only the newly exposed area of the view is painted from the view painting
874 * methods, the remainder is copied from the backing store.
876 * @param g the graphics context to use
878 void paintBlit(Graphics g
)
880 // We cannot perform blitted painting as it is described in Sun's API docs.
881 // There it is suggested that this painting method should blit directly
882 // on the parent window's surface. This is not possible because when using
883 // Swing's double buffering (at least our implementation), it would
884 // immediatly be painted when the buffer is painted on the screen. For this
885 // to work we would need a kind of hole in the buffer image. And honestly
886 // I find this method not very elegant.
887 // The alternative, blitting directly on the buffer image, is also not
888 // possible because the buffer image gets cleared everytime when an opaque
889 // parent component is drawn on it.
891 // What we do instead is falling back to the backing store approach which
892 // is in fact a mixed blitting/backing store approach where the blitting
893 // is performed on the backing store image and this is then drawn to the
894 // graphics context. This is very robust and works independent of the
895 // painting mechanism that is used by Swing. And it should have comparable
896 // performance characteristics as the blitting method.
897 paintBackingStore(g
);