Merge from mainline
[official-gcc.git] / libjava / classpath / javax / swing / JViewport.java
blobdebb5742e2c6af927c174fb2a4238b11a170eb8e
1 /* JViewport.java --
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)
9 any later version.
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
19 02110-1301 USA.
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.
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. */
39 package javax.swing;
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;
63 /**
65 * <pre>
66 * _
67 * +-------------------------------+ ...........Y1 \
68 * | view | . \
69 * | (this component's child) | . > VY
70 * | | . / = Y2-Y1
71 * | +------------------------------+ ....Y2_/
72 * | | viewport | | .
73 * | | (this component) | | .
74 * | | | | .
75 * | | | | .
76 * | | | | .
77 * | | | | .
78 * | +------------------------------+ ....Y3
79 * | | .
80 * | . | . .
81 * | . | . .
82 * +---------.---------------------+ ...........Y4
83 * . . . .
84 * . . . .
85 * . . . .
86 * X1.......X2.....................X3.......X4
87 * \____ ___/
88 * \/
89 * VX = X2-X1
90 *</pre>
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
136 * viewport.
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
153 * viewport.
155 * @param ev the ComponentEvent describing the change
157 public void componentResized(ComponentEvent ev)
159 revalidate();
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);
198 int scrollMode;
200 /**
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
205 * rotates its view.
207 * @see #toViewCoordinates(Dimension)
209 Dimension extentSize;
212 * The width and height of the view in its own coordinate space.
214 Dimension viewSize;
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.
231 Point cachedBlitTo;
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.
257 static
259 String scrollModeProp =
260 SystemProperties.getProperty("gnu.javax.swing.JViewport.scrollMode",
261 "BLIT");
262 if (scrollModeProp.equalsIgnoreCase("simple"))
263 defaultScrollMode = SIMPLE_SCROLL_MODE;
264 else if (scrollModeProp.equalsIgnoreCase("backingstore"))
265 defaultScrollMode = BACKINGSTORE_SCROLL_MODE;
266 else
267 defaultScrollMode = BLIT_SCROLL_MODE;
270 public JViewport()
272 setOpaque(true);
273 setScrollMode(defaultScrollMode);
274 updateUI();
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());
287 else
288 return extentSize;
291 public Dimension toViewCoordinates(Dimension size)
293 return size;
296 public Point toViewCoordinates(Point p)
298 Point pos = getViewPosition();
299 return new Point(p.x + pos.x,
300 p.y + pos.y);
303 public void setExtentSize(Dimension newSize)
305 extentSize = newSize;
306 fireStateChanged();
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()
316 if (isViewSizeSet)
317 return viewSize;
318 else
320 Component view = getView();
321 if (view != null)
322 return view.getPreferredSize();
323 else
324 return new Dimension();
329 public void setViewSize(Dimension newSize)
331 viewSize = newSize;
332 Component view = getView();
333 if (view != null)
335 if (newSize != view.getSize())
337 view.setSize(viewSize);
338 fireStateChanged();
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();
353 if (view == null)
354 return new Point(0,0);
355 else
357 Point p = view.getLocation();
358 p.x = -p.x;
359 p.y = -p.y;
360 return p;
364 public void setViewPosition(Point p)
366 if (getViewPosition().equals(p))
367 return;
368 Component view = getView();
369 if (view != null)
371 Point q = new Point(-p.x, -p.y);
372 view.setLocation(q);
373 isViewSizeSet = false;
374 fireStateChanged();
376 repaint();
379 public Rectangle getViewRect()
381 return new Rectangle(getViewPosition(),
382 getExtentSize());
386 * @deprecated 1.4
388 public boolean isBackingStoreEnabled()
390 return scrollMode == BACKINGSTORE_SCROLL_MODE;
394 * @deprecated 1.4
396 public void setBackingStoreEnabled(boolean b)
398 if (b && scrollMode != BACKINGSTORE_SCROLL_MODE)
400 scrollMode = BACKINGSTORE_SCROLL_MODE;
401 fireStateChanged();
405 public void setScrollMode(int mode)
407 scrollMode = mode;
408 fireStateChanged();
411 public int getScrollMode()
413 return scrollMode;
416 public Component getView()
418 if (getComponentCount() == 0)
419 return null;
421 return getComponents()[0];
424 public void setView(Component v)
426 Component currView = getView();
427 if (viewListener != null && currView != null)
428 currView.removeComponentListener(viewListener);
430 if (v != null)
432 if (viewListener == null)
433 viewListener = createViewListener();
434 v.addComponentListener(viewListener);
435 add(v);
436 fireStateChanged();
438 revalidate();
439 repaint();
442 public void reshape(int x, int y, int w, int h)
444 if (w != getWidth() || h != getHeight())
445 sizeChanged = true;
446 super.reshape(x, y, w, h);
447 if (sizeChanged)
449 damaged = true;
450 fireStateChanged();
454 public final Insets getInsets()
456 return new Insets(0, 0, 0, 0);
459 public final Insets getInsets(Insets insets)
461 if (insets == null)
462 return getInsets();
463 insets.top = 0;
464 insets.bottom = 0;
465 insets.left = 0;
466 insets.right = 0;
467 return 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()
480 return false;
483 public void paint(Graphics g)
485 Component view = getView();
487 if (view == null)
488 return;
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)
498 return;
500 switch (getScrollMode())
503 case JViewport.BACKINGSTORE_SCROLL_MODE:
504 paintBackingStore(g);
505 break;
506 case JViewport.BLIT_SCROLL_MODE:
507 paintBlit(g);
508 break;
509 case JViewport.SIMPLE_SCROLL_MODE:
510 default:
511 paintSimple(g);
512 break;
514 damaged = false;
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()
539 return "ViewportUI";
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)
567 super.setUI(ui);
570 public final void setBorder(Border border)
572 if (border != null)
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();
584 if (view == null)
585 return;
587 Point pos = getViewPosition();
588 Rectangle viewBounds = getView().getBounds();
589 Rectangle portBounds = getBounds();
591 if (isShowing())
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
599 // contentRect.y.
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
607 // contentRect.x.
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
628 * RepaintManager.
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();
639 if (parent != null)
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
686 * this method.
688 * <code>blitFrom</code> holds the position of the blit rectangle in the
689 * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea
690 * is copied to.
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
697 * blitting.
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
703 * method
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
715 // once.
716 return false;
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;
726 if (dy > 0)
728 blitFrom.y = portBounds.y + dy;
730 else if (dy < 0)
732 blitTo.y = portBounds.y - dy;
734 else if (dx > 0)
736 blitFrom.x = portBounds.x + dx;
738 else if (dx < 0)
740 blitTo.x = portBounds.x - dx;
743 // Compute size of the blit area.
744 if (dx != 0)
746 blitSize.width = portBounds.width - Math.abs(dx);
747 blitSize.height = portBounds.height;
749 else if (dy != 0)
751 blitSize.width = portBounds.width;
752 blitSize.height = portBounds.height - Math.abs(dy);
755 // Compute the blitPaint parameter.
756 blitPaint.setBounds(portBounds);
757 if (dy > 0)
759 blitPaint.y = portBounds.y + portBounds.height - dy;
760 blitPaint.height = dy;
762 else if (dy < 0)
764 blitPaint.height = -dy;
766 if (dx > 0)
768 blitPaint.x = portBounds.x + portBounds.width - dx;
769 blitPaint.width = dx;
771 else if (dx < 0)
773 blitPaint.width = -dx;
776 return true;
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.
790 paintComponent(g);
792 Point pos = getViewPosition();
793 Component view = getView();
794 boolean translated = false;
797 g.translate(-pos.x, -pos.y);
798 translated = true;
799 view.paint(g);
801 finally
803 if (translated)
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
814 * smooth.
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());
825 sizeChanged = false;
826 Graphics g2 = backingStoreImage.getGraphics();
827 paintSimple(g2);
828 g2.dispose();
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.
833 else
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);
841 if (canBlit)
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);
851 paintSimple(g2);
853 // If blitting is not possible for some reason, fall back to repainting
854 // everything.
855 else
857 paintSimple(g2);
859 g2.dispose();
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);