| /* JViewport.java -- |
| Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 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 gnu.classpath.SystemProperties; |
| |
| import java.awt.Component; |
| import java.awt.Dimension; |
| import java.awt.Graphics; |
| import java.awt.Image; |
| import java.awt.Insets; |
| import java.awt.LayoutManager; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.awt.Shape; |
| import java.awt.event.ComponentAdapter; |
| import java.awt.event.ComponentEvent; |
| import java.io.Serializable; |
| |
| import javax.accessibility.Accessible; |
| import javax.accessibility.AccessibleContext; |
| import javax.accessibility.AccessibleRole; |
| import javax.swing.border.Border; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import javax.swing.plaf.ViewportUI; |
| |
| /** |
| * |
| * <pre> |
| * _ |
| * +-------------------------------+ ...........Y1 \ |
| * | view | . \ |
| * | (this component's child) | . > VY |
| * | | . / = Y2-Y1 |
| * | +------------------------------+ ....Y2_/ |
| * | | viewport | | . |
| * | | (this component) | | . |
| * | | | | . |
| * | | | | . |
| * | | | | . |
| * | | | | . |
| * | +------------------------------+ ....Y3 |
| * | | . |
| * | . | . . |
| * | . | . . |
| * +---------.---------------------+ ...........Y4 |
| * . . . . |
| * . . . . |
| * . . . . |
| * X1.......X2.....................X3.......X4 |
| * \____ ___/ |
| * \/ |
| * VX = X2-X1 |
| *</pre> |
| * |
| * <p>A viewport is, like all swing components, located at some position in |
| * the swing component tree; that location is exactly the same as any other |
| * components: the viewport's "bounds".</p> |
| * |
| * <p>But in terms of drawing its child, the viewport thinks of itself as |
| * covering a particular position <em>of the view's coordinate space</em>. |
| * For example, the {@link #getViewPosition} method returns |
| * the position <code>(VX,VY)</code> shown above, which is an position in |
| * "view space", even though this is <em>implemented</em> by positioning |
| * the underlying child at position <code>(-VX,-VY)</code></p> |
| * |
| */ |
| public class JViewport extends JComponent implements Accessible |
| { |
| /** |
| * Provides accessibility support for <code>JViewport</code>. |
| * |
| * @author Roman Kennke (roman@kennke.org) |
| */ |
| protected class AccessibleJViewport extends AccessibleJComponent |
| { |
| /** |
| * Creates a new instance of <code>AccessibleJViewport</code>. |
| */ |
| protected AccessibleJViewport() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Returns the accessible role of <code>JViewport</code>, which is |
| * {@link AccessibleRole#VIEWPORT}. |
| * |
| * @return the accessible role of <code>JViewport</code> |
| */ |
| public AccessibleRole getAccessibleRole() |
| { |
| return AccessibleRole.VIEWPORT; |
| } |
| } |
| |
| /** |
| * A {@link java.awt.event.ComponentListener} that listens for |
| * changes of the view's size. This triggers a revalidate() call on the |
| * viewport. |
| */ |
| protected class ViewListener extends ComponentAdapter implements Serializable |
| { |
| private static final long serialVersionUID = -2812489404285958070L; |
| |
| /** |
| * Creates a new instance of ViewListener. |
| */ |
| protected ViewListener() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Receives notification when a component (in this case: the view |
| * component) changes it's size. This simply triggers a revalidate() on the |
| * viewport. |
| * |
| * @param ev the ComponentEvent describing the change |
| */ |
| public void componentResized(ComponentEvent ev) |
| { |
| // Fire state change, because resizing the view means changing the |
| // extentSize. |
| fireStateChanged(); |
| revalidate(); |
| } |
| } |
| |
| public static final int SIMPLE_SCROLL_MODE = 0; |
| public static final int BLIT_SCROLL_MODE = 1; |
| public static final int BACKINGSTORE_SCROLL_MODE = 2; |
| |
| private static final long serialVersionUID = -6925142919680527970L; |
| |
| /** |
| * The default scrollmode to be used by all JViewports as determined by |
| * the system property gnu.javax.swing.JViewport.scrollMode. |
| */ |
| private static final int defaultScrollMode; |
| |
| protected boolean scrollUnderway; |
| protected boolean isViewSizeSet; |
| |
| /** |
| * This flag indicates whether we use a backing store for drawing. |
| * |
| * @deprecated since JDK 1.3 |
| */ |
| protected boolean backingStore; |
| |
| /** |
| * The backingstore image used for the backingstore and blit scroll methods. |
| */ |
| protected Image backingStoreImage; |
| |
| /** |
| * The position at which the view has been drawn the last time. This is used |
| * to determine the bittable area. |
| */ |
| protected Point lastPaintPosition; |
| |
| ChangeEvent changeEvent = new ChangeEvent(this); |
| |
| int scrollMode; |
| |
| /** |
| * The ViewListener instance. |
| */ |
| ViewListener viewListener; |
| |
| /** |
| * Stores the location from where to blit. This is a cached Point object used |
| * in blitting calculations. |
| */ |
| Point cachedBlitFrom; |
| |
| /** |
| * Stores the location where to blit to. This is a cached Point object used |
| * in blitting calculations. |
| */ |
| Point cachedBlitTo; |
| |
| /** |
| * Stores the width of the blitted area. This is a cached Dimension object |
| * used in blitting calculations. |
| */ |
| Dimension cachedBlitSize; |
| |
| /** |
| * Stores the bounds of the area that needs to be repainted. This is a cached |
| * Rectangle object used in blitting calculations. |
| */ |
| Rectangle cachedBlitPaint; |
| |
| boolean damaged = true; |
| |
| /** |
| * A flag indicating if the size of the viewport has changed since the |
| * last repaint. This is used in double buffered painting to check if we |
| * need a new double buffer, or can reuse the old one. |
| */ |
| boolean sizeChanged = true; |
| |
| /** |
| * Indicates if this JViewport is the paint root or not. If it is not, then |
| * we may not assume that the offscreen buffer still has the right content |
| * because parent components may have cleared the background already. |
| */ |
| private boolean isPaintRoot = false; |
| |
| /** |
| * Initializes the default setting for the scrollMode property. |
| */ |
| static |
| { |
| String scrollModeProp = |
| SystemProperties.getProperty("gnu.swing.scrollmode", "BACKINGSTORE"); |
| if (scrollModeProp.equalsIgnoreCase("simple")) |
| defaultScrollMode = SIMPLE_SCROLL_MODE; |
| else if (scrollModeProp.equalsIgnoreCase("backingstore")) |
| defaultScrollMode = BACKINGSTORE_SCROLL_MODE; |
| else |
| defaultScrollMode = BLIT_SCROLL_MODE; |
| } |
| |
| public JViewport() |
| { |
| setOpaque(true); |
| setScrollMode(defaultScrollMode); |
| updateUI(); |
| setLayout(createLayoutManager()); |
| lastPaintPosition = new Point(); |
| cachedBlitFrom = new Point(); |
| cachedBlitTo = new Point(); |
| cachedBlitSize = new Dimension(); |
| cachedBlitPaint = new Rectangle(); |
| } |
| |
| public Dimension getExtentSize() |
| { |
| return getSize(); |
| } |
| |
| public Dimension toViewCoordinates(Dimension size) |
| { |
| return size; |
| } |
| |
| public Point toViewCoordinates(Point p) |
| { |
| Point pos = getViewPosition(); |
| return new Point(p.x + pos.x, |
| p.y + pos.y); |
| } |
| |
| public void setExtentSize(Dimension newSize) |
| { |
| Dimension oldExtent = getExtentSize(); |
| if (! newSize.equals(oldExtent)) |
| { |
| setSize(newSize); |
| fireStateChanged(); |
| } |
| } |
| |
| /** |
| * Returns the viewSize when set, or the preferred size of the set |
| * Component view. If no viewSize and no Component view is set an |
| * empty Dimension is returned. |
| */ |
| public Dimension getViewSize() |
| { |
| Dimension size; |
| Component view = getView(); |
| if (view != null) |
| { |
| if (isViewSizeSet) |
| size = view.getSize(); |
| else |
| size = view.getPreferredSize(); |
| } |
| else |
| size = new Dimension(0, 0); |
| return size; |
| } |
| |
| |
| public void setViewSize(Dimension newSize) |
| { |
| Component view = getView(); |
| if (view != null) |
| { |
| if (! newSize.equals(view.getSize())) |
| { |
| scrollUnderway = false; |
| view.setSize(newSize); |
| isViewSizeSet = true; |
| fireStateChanged(); |
| } |
| } |
| } |
| |
| /** |
| * Get the viewport's position in view space. Despite confusing name, |
| * this really does return the viewport's (0,0) position in view space, |
| * not the view's position. |
| */ |
| |
| public Point getViewPosition() |
| { |
| Component view = getView(); |
| if (view == null) |
| return new Point(0,0); |
| else |
| { |
| Point p = view.getLocation(); |
| p.x = -p.x; |
| p.y = -p.y; |
| return p; |
| } |
| } |
| |
| public void setViewPosition(Point p) |
| { |
| Component view = getView(); |
| if (view != null && ! p.equals(getViewPosition())) |
| { |
| scrollUnderway = true; |
| view.setLocation(-p.x, -p.y); |
| fireStateChanged(); |
| } |
| } |
| |
| public Rectangle getViewRect() |
| { |
| return new Rectangle(getViewPosition(), getExtentSize()); |
| } |
| |
| /** |
| * @deprecated 1.4 |
| */ |
| public boolean isBackingStoreEnabled() |
| { |
| return scrollMode == BACKINGSTORE_SCROLL_MODE; |
| } |
| |
| /** |
| * @deprecated 1.4 |
| */ |
| public void setBackingStoreEnabled(boolean b) |
| { |
| if (b && scrollMode != BACKINGSTORE_SCROLL_MODE) |
| { |
| scrollMode = BACKINGSTORE_SCROLL_MODE; |
| fireStateChanged(); |
| } |
| } |
| |
| public void setScrollMode(int mode) |
| { |
| scrollMode = mode; |
| fireStateChanged(); |
| } |
| |
| public int getScrollMode() |
| { |
| return scrollMode; |
| } |
| |
| public Component getView() |
| { |
| if (getComponentCount() == 0) |
| return null; |
| |
| return getComponents()[0]; |
| } |
| |
| public void setView(Component v) |
| { |
| Component currView = getView(); |
| if (viewListener != null && currView != null) |
| currView.removeComponentListener(viewListener); |
| |
| if (v != null) |
| { |
| if (viewListener == null) |
| viewListener = createViewListener(); |
| v.addComponentListener(viewListener); |
| add(v); |
| fireStateChanged(); |
| } |
| revalidate(); |
| repaint(); |
| } |
| |
| public void reshape(int x, int y, int w, int h) |
| { |
| if (w != getWidth() || h != getHeight()) |
| sizeChanged = true; |
| super.reshape(x, y, w, h); |
| if (sizeChanged) |
| { |
| damaged = true; |
| fireStateChanged(); |
| } |
| } |
| |
| public final Insets getInsets() |
| { |
| return new Insets(0, 0, 0, 0); |
| } |
| |
| public final Insets getInsets(Insets insets) |
| { |
| if (insets == null) |
| return getInsets(); |
| insets.top = 0; |
| insets.bottom = 0; |
| insets.left = 0; |
| insets.right = 0; |
| return insets; |
| } |
| |
| |
| /** |
| * Overridden to return <code>false</code>, so the JViewport's paint method |
| * gets called instead of directly calling the children. This is necessary |
| * in order to get a useful clipping and translation on the children. |
| * |
| * @return <code>false</code> |
| */ |
| public boolean isOptimizedDrawingEnabled() |
| { |
| return false; |
| } |
| |
| public void paint(Graphics g) |
| { |
| Component view = getView(); |
| |
| if (view == null) |
| return; |
| |
| Rectangle viewBounds = view.getBounds(); |
| Rectangle portBounds = getBounds(); |
| |
| if (viewBounds.width == 0 |
| || viewBounds.height == 0 |
| || portBounds.width == 0 |
| || portBounds.height == 0) |
| return; |
| |
| switch (getScrollMode()) |
| { |
| |
| case JViewport.BACKINGSTORE_SCROLL_MODE: |
| paintBackingStore(g); |
| break; |
| case JViewport.BLIT_SCROLL_MODE: |
| paintBlit(g); |
| break; |
| case JViewport.SIMPLE_SCROLL_MODE: |
| default: |
| paintSimple(g); |
| break; |
| } |
| damaged = false; |
| } |
| |
| public void addChangeListener(ChangeListener listener) |
| { |
| listenerList.add(ChangeListener.class, listener); |
| } |
| |
| public void removeChangeListener(ChangeListener listener) |
| { |
| listenerList.remove(ChangeListener.class, listener); |
| } |
| |
| public ChangeListener[] getChangeListeners() |
| { |
| return (ChangeListener[]) getListeners(ChangeListener.class); |
| } |
| |
| /** |
| * This method returns the String ID of the UI class of Separator. |
| * |
| * @return The UI class' String ID. |
| */ |
| public String getUIClassID() |
| { |
| return "ViewportUI"; |
| } |
| |
| /** |
| * This method resets the UI used to the Look and Feel defaults.. |
| */ |
| public void updateUI() |
| { |
| setUI((ViewportUI) UIManager.getUI(this)); |
| } |
| |
| /** |
| * This method returns the viewport's UI delegate. |
| * |
| * @return The viewport's UI delegate. |
| */ |
| public ViewportUI getUI() |
| { |
| return (ViewportUI) ui; |
| } |
| |
| /** |
| * This method sets the viewport's UI delegate. |
| * |
| * @param ui The viewport's UI delegate. |
| */ |
| public void setUI(ViewportUI ui) |
| { |
| super.setUI(ui); |
| } |
| |
| public final void setBorder(Border border) |
| { |
| if (border != null) |
| throw new IllegalArgumentException(); |
| } |
| |
| /** |
| * Scrolls the view so that contentRect becomes visible. |
| * |
| * @param contentRect the rectangle to make visible within the view |
| */ |
| public void scrollRectToVisible(Rectangle contentRect) |
| { |
| Component view = getView(); |
| if (view == null) |
| return; |
| |
| Point pos = getViewPosition(); |
| // We get the contentRect in the viewport coordinates. But we want to |
| // calculate with view coordinates. |
| int contentX = contentRect.x + pos.x; |
| int contentY = contentRect.y + pos.y; |
| Rectangle viewBounds = getView().getBounds(); |
| Rectangle portBounds = getBounds(); |
| |
| if (isShowing()) |
| getView().validate(); |
| |
| // If the bottom boundary of contentRect is below the port |
| // boundaries, scroll up as necessary. |
| if (contentY + contentRect.height + viewBounds.y > portBounds.height) |
| pos.y = contentY + contentRect.height - portBounds.height; |
| // If contentY is above the port boundaries, scroll down to |
| // contentY. |
| if (contentY + viewBounds.y < 0) |
| pos.y = contentY; |
| // If the right boundary of contentRect is right from the port |
| // boundaries, scroll left as necessary. |
| if (contentX + contentRect.width + viewBounds.x > portBounds.width) |
| pos.x = contentX + contentRect.width - portBounds.width; |
| // If contentX is left from the port boundaries, scroll right to |
| // contentRect.x. |
| if (contentX + viewBounds.x < 0) |
| pos.x = contentX; |
| setViewPosition(pos); |
| } |
| |
| /** |
| * Returns the accessible context for this <code>JViewport</code>. This |
| * will be an instance of {@link AccessibleJViewport}. |
| * |
| * @return the accessible context for this <code>JViewport</code> |
| */ |
| public AccessibleContext getAccessibleContext() |
| { |
| if (accessibleContext == null) |
| accessibleContext = new AccessibleJViewport(); |
| return accessibleContext; |
| } |
| |
| /** |
| * Forward repaint to parent to make sure only one paint is performed by the |
| * RepaintManager. |
| * |
| * @param tm number of milliseconds to defer the repaint request |
| * @param x the X coordinate of the upper left corner of the dirty area |
| * @param y the Y coordinate of the upper left corner of the dirty area |
| * @param w the width of the dirty area |
| * @param h the height of the dirty area |
| */ |
| public void repaint(long tm, int x, int y, int w, int h) |
| { |
| Component parent = getParent(); |
| if (parent != null) |
| parent.repaint(tm, x + getX(), y + getY(), w, h); |
| else |
| super.repaint(tm, x, y, w, h); |
| } |
| |
| protected void addImpl(Component comp, Object constraints, int index) |
| { |
| if (getComponentCount() > 0) |
| remove(getComponents()[0]); |
| |
| super.addImpl(comp, constraints, index); |
| } |
| |
| protected void fireStateChanged() |
| { |
| ChangeListener[] listeners = getChangeListeners(); |
| for (int i = 0; i < listeners.length; ++i) |
| listeners[i].stateChanged(changeEvent); |
| } |
| |
| /** |
| * Creates a {@link ViewListener} that is supposed to listen for |
| * size changes on the view component. |
| * |
| * @return a ViewListener instance |
| */ |
| protected ViewListener createViewListener() |
| { |
| return new ViewListener(); |
| } |
| |
| /** |
| * Creates the LayoutManager that is used for this viewport. Override |
| * this method if you want to use a custom LayoutManager. |
| * |
| * @return a LayoutManager to use for this viewport |
| */ |
| protected LayoutManager createLayoutManager() |
| { |
| return new ViewportLayout(); |
| } |
| |
| /** |
| * Computes the parameters for the blitting scroll method. <code>dx</code> |
| * and <code>dy</code> specifiy the X and Y offset by which the viewport |
| * is scrolled. All other arguments are output parameters and are filled by |
| * this method. |
| * |
| * <code>blitFrom</code> holds the position of the blit rectangle in the |
| * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea |
| * is copied to. |
| * |
| * <code>blitSize</code> holds the size of the blit area and |
| * <code>blitPaint</code> is the area of the view that needs to be painted. |
| * |
| * This method returns <code>true</code> if blitting is possible and |
| * <code>false</code> if the viewport has to be repainted completetly without |
| * blitting. |
| * |
| * @param dx the horizontal delta |
| * @param dy the vertical delta |
| * @param blitFrom the position from where to blit; set by this method |
| * @param blitTo the position where to blit area is copied to; set by this |
| * method |
| * @param blitSize the size of the blitted area; set by this method |
| * @param blitPaint the area that needs repainting; set by this method |
| * |
| * @return <code>true</code> if blitting is possible, |
| * <code>false</code> otherwise |
| */ |
| protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo, |
| Dimension blitSize, Rectangle blitPaint) |
| { |
| if ((dx != 0 && dy != 0) || (dy == 0 && dy == 0) || damaged) |
| // We cannot blit if the viewport is scrolled in both directions at |
| // once. Also, we do not want to blit if the viewport is not scrolled at |
| // all, because that probably means the view component repaints itself |
| // and the buffer needs updating. |
| return false; |
| |
| Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds()); |
| |
| // Compute the blitFrom and blitTo parameters. |
| blitFrom.x = portBounds.x; |
| blitFrom.y = portBounds.y; |
| blitTo.x = portBounds.x; |
| blitTo.y = portBounds.y; |
| |
| if (dy > 0) |
| { |
| blitFrom.y = portBounds.y + dy; |
| } |
| else if (dy < 0) |
| { |
| blitTo.y = portBounds.y - dy; |
| } |
| else if (dx > 0) |
| { |
| blitFrom.x = portBounds.x + dx; |
| } |
| else if (dx < 0) |
| { |
| blitTo.x = portBounds.x - dx; |
| } |
| |
| // Compute size of the blit area. |
| if (dx != 0) |
| { |
| blitSize.width = portBounds.width - Math.abs(dx); |
| blitSize.height = portBounds.height; |
| } |
| else if (dy != 0) |
| { |
| blitSize.width = portBounds.width; |
| blitSize.height = portBounds.height - Math.abs(dy); |
| } |
| |
| // Compute the blitPaint parameter. |
| blitPaint.setBounds(portBounds); |
| if (dy > 0) |
| { |
| blitPaint.y = portBounds.y + portBounds.height - dy; |
| blitPaint.height = dy; |
| } |
| else if (dy < 0) |
| { |
| blitPaint.height = -dy; |
| } |
| if (dx > 0) |
| { |
| blitPaint.x = portBounds.x + portBounds.width - dx; |
| blitPaint.width = dx; |
| } |
| else if (dx < 0) |
| { |
| blitPaint.width = -dx; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Paints the viewport in case we have a scrollmode of |
| * {@link #SIMPLE_SCROLL_MODE}. |
| * |
| * This simply paints the view directly on the surface of the viewport. |
| * |
| * @param g the graphics context to use |
| */ |
| void paintSimple(Graphics g) |
| { |
| // We need to call this to properly clear the background. |
| paintComponent(g); |
| |
| Point pos = getViewPosition(); |
| Component view = getView(); |
| Shape oldClip = g.getClip(); |
| g.clipRect(0, 0, getWidth(), getHeight()); |
| boolean translated = false; |
| try |
| { |
| g.translate(-pos.x, -pos.y); |
| translated = true; |
| view.paint(g); |
| } |
| finally |
| { |
| if (translated) |
| g.translate (pos.x, pos.y); |
| g.setClip(oldClip); |
| } |
| } |
| |
| /** |
| * Paints the viewport in case we have a scroll mode of |
| * {@link #BACKINGSTORE_SCROLL_MODE}. |
| * |
| * This method uses a backing store image to paint the view to, which is then |
| * subsequently painted on the screen. This should make scrolling more |
| * smooth. |
| * |
| * @param g the graphics context to use |
| */ |
| void paintBackingStore(Graphics g) |
| { |
| // If we have no backing store image yet or the size of the component has |
| // changed, we need to rebuild the backing store. |
| if (backingStoreImage == null || sizeChanged) |
| { |
| backingStoreImage = createImage(getWidth(), getHeight()); |
| sizeChanged = false; |
| Graphics g2 = backingStoreImage.getGraphics(); |
| paintSimple(g2); |
| g2.dispose(); |
| } |
| // Otherwise we can perform the blitting on the backing store image: |
| // First we move the part that remains visible after scrolling, then |
| // we only need to paint the bit that becomes newly visible. |
| else |
| { |
| Graphics g2 = backingStoreImage.getGraphics(); |
| Point viewPosition = getViewPosition(); |
| int dx = viewPosition.x - lastPaintPosition.x; |
| int dy = viewPosition.y - lastPaintPosition.y; |
| boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo, |
| cachedBlitSize, cachedBlitPaint); |
| if (canBlit && isPaintRoot) |
| { |
| // Copy the part that remains visible during scrolling. |
| if (cachedBlitSize.width > 0 && cachedBlitSize.height > 0) |
| { |
| g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, |
| cachedBlitSize.width, cachedBlitSize.height, |
| cachedBlitTo.x - cachedBlitFrom.x, |
| cachedBlitTo.y - cachedBlitFrom.y); |
| } |
| // Now paint the part that becomes newly visible. |
| g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y, |
| cachedBlitPaint.width, cachedBlitPaint.height); |
| paintSimple(g2); |
| } |
| // If blitting is not possible for some reason, fall back to repainting |
| // everything. |
| else |
| { |
| // If the image has not been scrolled at all, only the changed |
| // clip must be updated in the buffer. |
| if (dx == 0 && dy == 0) |
| g2.setClip(g.getClip()); |
| |
| paintSimple(g2); |
| } |
| g2.dispose(); |
| } |
| // Actually draw the backingstore image to the graphics context. |
| g.drawImage(backingStoreImage, 0, 0, this); |
| // Update the lastPaintPosition so that we know what is already drawn when |
| // we paint the next time. |
| lastPaintPosition.setLocation(getViewPosition()); |
| } |
| |
| /** |
| * Paints the viewport in case we have a scrollmode of |
| * {@link #BLIT_SCROLL_MODE}. |
| * |
| * This paints the viewport using a backingstore and a blitting algorithm. |
| * Only the newly exposed area of the view is painted from the view painting |
| * methods, the remainder is copied from the backing store. |
| * |
| * @param g the graphics context to use |
| */ |
| void paintBlit(Graphics g) |
| { |
| // First we move the part that remains visible after scrolling, then |
| // we only need to paint the bit that becomes newly visible. |
| Point viewPosition = getViewPosition(); |
| int dx = viewPosition.x - lastPaintPosition.x; |
| int dy = viewPosition.y - lastPaintPosition.y; |
| boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo, |
| cachedBlitSize, cachedBlitPaint); |
| if (canBlit && isPaintRoot) |
| { |
| // Copy the part that remains visible during scrolling. |
| if (cachedBlitSize.width > 0 && cachedBlitSize.width > 0) |
| { |
| g.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, |
| cachedBlitSize.width, cachedBlitSize.height, |
| cachedBlitTo.x - cachedBlitFrom.x, |
| cachedBlitTo.y - cachedBlitFrom.y); |
| } |
| // Now paint the part that becomes newly visible. |
| Shape oldClip = g.getClip(); |
| g.clipRect(cachedBlitPaint.x, cachedBlitPaint.y, |
| cachedBlitPaint.width, cachedBlitPaint.height); |
| try |
| { |
| paintSimple(g); |
| } |
| finally |
| { |
| g.setClip(oldClip); |
| } |
| } |
| // If blitting is not possible for some reason, fall back to repainting |
| // everything. |
| else |
| paintSimple(g); |
| lastPaintPosition.setLocation(getViewPosition()); |
| } |
| |
| /** |
| * Overridden from JComponent to set the {@link #isPaintRoot} flag. |
| * |
| * @param x the rectangle to paint, X coordinate |
| * @param y the rectangle to paint, Y coordinate |
| * @param w the rectangle to paint, width |
| * @param h the rectangle to paint, height |
| */ |
| void paintImmediately2(int x, int y, int w, int h) |
| { |
| isPaintRoot = true; |
| super.paintImmediately2(x, y, w, h); |
| isPaintRoot = false; |
| } |
| |
| /** |
| * Returns true when the JViewport is using a backbuffer, so that we |
| * can update our backbuffer correctly. |
| */ |
| boolean isPaintRoot() |
| { |
| return scrollMode == BACKINGSTORE_SCROLL_MODE; |
| } |
| } |