001 /* JViewport.java --
002 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing;
040
041 import gnu.classpath.SystemProperties;
042
043 import java.awt.Component;
044 import java.awt.Dimension;
045 import java.awt.Graphics;
046 import java.awt.Image;
047 import java.awt.Insets;
048 import java.awt.LayoutManager;
049 import java.awt.Point;
050 import java.awt.Rectangle;
051 import java.awt.Shape;
052 import java.awt.event.ComponentAdapter;
053 import java.awt.event.ComponentEvent;
054 import java.io.Serializable;
055
056 import javax.accessibility.Accessible;
057 import javax.accessibility.AccessibleContext;
058 import javax.accessibility.AccessibleRole;
059 import javax.swing.border.Border;
060 import javax.swing.event.ChangeEvent;
061 import javax.swing.event.ChangeListener;
062 import javax.swing.plaf.ViewportUI;
063
064 /**
065 *
066 * <pre>
067 * _
068 * +-------------------------------+ ...........Y1 \
069 * | view | . \
070 * | (this component's child) | . > VY
071 * | | . / = Y2-Y1
072 * | +------------------------------+ ....Y2_/
073 * | | viewport | | .
074 * | | (this component) | | .
075 * | | | | .
076 * | | | | .
077 * | | | | .
078 * | | | | .
079 * | +------------------------------+ ....Y3
080 * | | .
081 * | . | . .
082 * | . | . .
083 * +---------.---------------------+ ...........Y4
084 * . . . .
085 * . . . .
086 * . . . .
087 * X1.......X2.....................X3.......X4
088 * \____ ___/
089 * \/
090 * VX = X2-X1
091 *</pre>
092 *
093 * <p>A viewport is, like all swing components, located at some position in
094 * the swing component tree; that location is exactly the same as any other
095 * components: the viewport's "bounds".</p>
096 *
097 * <p>But in terms of drawing its child, the viewport thinks of itself as
098 * covering a particular position <em>of the view's coordinate space</em>.
099 * For example, the {@link #getViewPosition} method returns
100 * the position <code>(VX,VY)</code> shown above, which is an position in
101 * "view space", even though this is <em>implemented</em> by positioning
102 * the underlying child at position <code>(-VX,-VY)</code></p>
103 *
104 */
105 public class JViewport extends JComponent implements Accessible
106 {
107 /**
108 * Provides accessibility support for <code>JViewport</code>.
109 *
110 * @author Roman Kennke (roman@kennke.org)
111 */
112 protected class AccessibleJViewport extends AccessibleJComponent
113 {
114 /**
115 * Creates a new instance of <code>AccessibleJViewport</code>.
116 */
117 protected AccessibleJViewport()
118 {
119 // Nothing to do here.
120 }
121
122 /**
123 * Returns the accessible role of <code>JViewport</code>, which is
124 * {@link AccessibleRole#VIEWPORT}.
125 *
126 * @return the accessible role of <code>JViewport</code>
127 */
128 public AccessibleRole getAccessibleRole()
129 {
130 return AccessibleRole.VIEWPORT;
131 }
132 }
133
134 /**
135 * A {@link java.awt.event.ComponentListener} that listens for
136 * changes of the view's size. This triggers a revalidate() call on the
137 * viewport.
138 */
139 protected class ViewListener extends ComponentAdapter implements Serializable
140 {
141 private static final long serialVersionUID = -2812489404285958070L;
142
143 /**
144 * Creates a new instance of ViewListener.
145 */
146 protected ViewListener()
147 {
148 // Nothing to do here.
149 }
150
151 /**
152 * Receives notification when a component (in this case: the view
153 * component) changes it's size. This simply triggers a revalidate() on the
154 * viewport.
155 *
156 * @param ev the ComponentEvent describing the change
157 */
158 public void componentResized(ComponentEvent ev)
159 {
160 // Fire state change, because resizing the view means changing the
161 // extentSize.
162 fireStateChanged();
163 revalidate();
164 }
165 }
166
167 public static final int SIMPLE_SCROLL_MODE = 0;
168 public static final int BLIT_SCROLL_MODE = 1;
169 public static final int BACKINGSTORE_SCROLL_MODE = 2;
170
171 private static final long serialVersionUID = -6925142919680527970L;
172
173 /**
174 * The default scrollmode to be used by all JViewports as determined by
175 * the system property gnu.javax.swing.JViewport.scrollMode.
176 */
177 private static final int defaultScrollMode;
178
179 protected boolean scrollUnderway;
180 protected boolean isViewSizeSet;
181
182 /**
183 * This flag indicates whether we use a backing store for drawing.
184 *
185 * @deprecated since JDK 1.3
186 */
187 protected boolean backingStore;
188
189 /**
190 * The backingstore image used for the backingstore and blit scroll methods.
191 */
192 protected Image backingStoreImage;
193
194 /**
195 * The position at which the view has been drawn the last time. This is used
196 * to determine the bittable area.
197 */
198 protected Point lastPaintPosition;
199
200 ChangeEvent changeEvent = new ChangeEvent(this);
201
202 int scrollMode;
203
204 /**
205 * The ViewListener instance.
206 */
207 ViewListener viewListener;
208
209 /**
210 * Stores the location from where to blit. This is a cached Point object used
211 * in blitting calculations.
212 */
213 Point cachedBlitFrom;
214
215 /**
216 * Stores the location where to blit to. This is a cached Point object used
217 * in blitting calculations.
218 */
219 Point cachedBlitTo;
220
221 /**
222 * Stores the width of the blitted area. This is a cached Dimension object
223 * used in blitting calculations.
224 */
225 Dimension cachedBlitSize;
226
227 /**
228 * Stores the bounds of the area that needs to be repainted. This is a cached
229 * Rectangle object used in blitting calculations.
230 */
231 Rectangle cachedBlitPaint;
232
233 boolean damaged = true;
234
235 /**
236 * A flag indicating if the size of the viewport has changed since the
237 * last repaint. This is used in double buffered painting to check if we
238 * need a new double buffer, or can reuse the old one.
239 */
240 boolean sizeChanged = true;
241
242 /**
243 * Indicates if this JViewport is the paint root or not. If it is not, then
244 * we may not assume that the offscreen buffer still has the right content
245 * because parent components may have cleared the background already.
246 */
247 private boolean isPaintRoot = false;
248
249 /**
250 * Initializes the default setting for the scrollMode property.
251 */
252 static
253 {
254 String scrollModeProp =
255 SystemProperties.getProperty("gnu.swing.scrollmode", "BACKINGSTORE");
256 if (scrollModeProp.equalsIgnoreCase("simple"))
257 defaultScrollMode = SIMPLE_SCROLL_MODE;
258 else if (scrollModeProp.equalsIgnoreCase("backingstore"))
259 defaultScrollMode = BACKINGSTORE_SCROLL_MODE;
260 else
261 defaultScrollMode = BLIT_SCROLL_MODE;
262 }
263
264 public JViewport()
265 {
266 setOpaque(true);
267 setScrollMode(defaultScrollMode);
268 updateUI();
269 setLayout(createLayoutManager());
270 lastPaintPosition = new Point();
271 cachedBlitFrom = new Point();
272 cachedBlitTo = new Point();
273 cachedBlitSize = new Dimension();
274 cachedBlitPaint = new Rectangle();
275 }
276
277 public Dimension getExtentSize()
278 {
279 return getSize();
280 }
281
282 public Dimension toViewCoordinates(Dimension size)
283 {
284 return size;
285 }
286
287 public Point toViewCoordinates(Point p)
288 {
289 Point pos = getViewPosition();
290 return new Point(p.x + pos.x,
291 p.y + pos.y);
292 }
293
294 public void setExtentSize(Dimension newSize)
295 {
296 Dimension oldExtent = getExtentSize();
297 if (! newSize.equals(oldExtent))
298 {
299 setSize(newSize);
300 fireStateChanged();
301 }
302 }
303
304 /**
305 * Returns the viewSize when set, or the preferred size of the set
306 * Component view. If no viewSize and no Component view is set an
307 * empty Dimension is returned.
308 */
309 public Dimension getViewSize()
310 {
311 Dimension size;
312 Component view = getView();
313 if (view != null)
314 {
315 if (isViewSizeSet)
316 size = view.getSize();
317 else
318 size = view.getPreferredSize();
319 }
320 else
321 size = new Dimension(0, 0);
322 return size;
323 }
324
325
326 public void setViewSize(Dimension newSize)
327 {
328 Component view = getView();
329 if (view != null)
330 {
331 if (! newSize.equals(view.getSize()))
332 {
333 scrollUnderway = false;
334 view.setSize(newSize);
335 isViewSizeSet = true;
336 fireStateChanged();
337 }
338 }
339 }
340
341 /**
342 * Get the viewport's position in view space. Despite confusing name,
343 * this really does return the viewport's (0,0) position in view space,
344 * not the view's position.
345 */
346
347 public Point getViewPosition()
348 {
349 Component view = getView();
350 if (view == null)
351 return new Point(0,0);
352 else
353 {
354 Point p = view.getLocation();
355 p.x = -p.x;
356 p.y = -p.y;
357 return p;
358 }
359 }
360
361 public void setViewPosition(Point p)
362 {
363 Component view = getView();
364 if (view != null && ! p.equals(getViewPosition()))
365 {
366 scrollUnderway = true;
367 view.setLocation(-p.x, -p.y);
368 fireStateChanged();
369 }
370 }
371
372 public Rectangle getViewRect()
373 {
374 return new Rectangle(getViewPosition(), getExtentSize());
375 }
376
377 /**
378 * @deprecated 1.4
379 */
380 public boolean isBackingStoreEnabled()
381 {
382 return scrollMode == BACKINGSTORE_SCROLL_MODE;
383 }
384
385 /**
386 * @deprecated 1.4
387 */
388 public void setBackingStoreEnabled(boolean b)
389 {
390 if (b && scrollMode != BACKINGSTORE_SCROLL_MODE)
391 {
392 scrollMode = BACKINGSTORE_SCROLL_MODE;
393 fireStateChanged();
394 }
395 }
396
397 public void setScrollMode(int mode)
398 {
399 scrollMode = mode;
400 fireStateChanged();
401 }
402
403 public int getScrollMode()
404 {
405 return scrollMode;
406 }
407
408 public Component getView()
409 {
410 if (getComponentCount() == 0)
411 return null;
412
413 return getComponents()[0];
414 }
415
416 public void setView(Component v)
417 {
418 Component currView = getView();
419 if (viewListener != null && currView != null)
420 currView.removeComponentListener(viewListener);
421
422 if (v != null)
423 {
424 if (viewListener == null)
425 viewListener = createViewListener();
426 v.addComponentListener(viewListener);
427 add(v);
428 fireStateChanged();
429 }
430 revalidate();
431 repaint();
432 }
433
434 public void reshape(int x, int y, int w, int h)
435 {
436 if (w != getWidth() || h != getHeight())
437 sizeChanged = true;
438 super.reshape(x, y, w, h);
439 if (sizeChanged)
440 {
441 damaged = true;
442 fireStateChanged();
443 }
444 }
445
446 public final Insets getInsets()
447 {
448 return new Insets(0, 0, 0, 0);
449 }
450
451 public final Insets getInsets(Insets insets)
452 {
453 if (insets == null)
454 return getInsets();
455 insets.top = 0;
456 insets.bottom = 0;
457 insets.left = 0;
458 insets.right = 0;
459 return insets;
460 }
461
462
463 /**
464 * Overridden to return <code>false</code>, so the JViewport's paint method
465 * gets called instead of directly calling the children. This is necessary
466 * in order to get a useful clipping and translation on the children.
467 *
468 * @return <code>false</code>
469 */
470 public boolean isOptimizedDrawingEnabled()
471 {
472 return false;
473 }
474
475 public void paint(Graphics g)
476 {
477 Component view = getView();
478
479 if (view == null)
480 return;
481
482 Rectangle viewBounds = view.getBounds();
483 Rectangle portBounds = getBounds();
484
485 if (viewBounds.width == 0
486 || viewBounds.height == 0
487 || portBounds.width == 0
488 || portBounds.height == 0)
489 return;
490
491 switch (getScrollMode())
492 {
493
494 case JViewport.BACKINGSTORE_SCROLL_MODE:
495 paintBackingStore(g);
496 break;
497 case JViewport.BLIT_SCROLL_MODE:
498 paintBlit(g);
499 break;
500 case JViewport.SIMPLE_SCROLL_MODE:
501 default:
502 paintSimple(g);
503 break;
504 }
505 damaged = false;
506 }
507
508 public void addChangeListener(ChangeListener listener)
509 {
510 listenerList.add(ChangeListener.class, listener);
511 }
512
513 public void removeChangeListener(ChangeListener listener)
514 {
515 listenerList.remove(ChangeListener.class, listener);
516 }
517
518 public ChangeListener[] getChangeListeners()
519 {
520 return (ChangeListener[]) getListeners(ChangeListener.class);
521 }
522
523 /**
524 * This method returns the String ID of the UI class of Separator.
525 *
526 * @return The UI class' String ID.
527 */
528 public String getUIClassID()
529 {
530 return "ViewportUI";
531 }
532
533 /**
534 * This method resets the UI used to the Look and Feel defaults..
535 */
536 public void updateUI()
537 {
538 setUI((ViewportUI) UIManager.getUI(this));
539 }
540
541 /**
542 * This method returns the viewport's UI delegate.
543 *
544 * @return The viewport's UI delegate.
545 */
546 public ViewportUI getUI()
547 {
548 return (ViewportUI) ui;
549 }
550
551 /**
552 * This method sets the viewport's UI delegate.
553 *
554 * @param ui The viewport's UI delegate.
555 */
556 public void setUI(ViewportUI ui)
557 {
558 super.setUI(ui);
559 }
560
561 public final void setBorder(Border border)
562 {
563 if (border != null)
564 throw new IllegalArgumentException();
565 }
566
567 /**
568 * Scrolls the view so that contentRect becomes visible.
569 *
570 * @param contentRect the rectangle to make visible within the view
571 */
572 public void scrollRectToVisible(Rectangle contentRect)
573 {
574 Component view = getView();
575 if (view == null)
576 return;
577
578 Point pos = getViewPosition();
579 // We get the contentRect in the viewport coordinates. But we want to
580 // calculate with view coordinates.
581 int contentX = contentRect.x + pos.x;
582 int contentY = contentRect.y + pos.y;
583 Rectangle viewBounds = getView().getBounds();
584 Rectangle portBounds = getBounds();
585
586 if (isShowing())
587 getView().validate();
588
589 // If the bottom boundary of contentRect is below the port
590 // boundaries, scroll up as necessary.
591 if (contentY + contentRect.height + viewBounds.y > portBounds.height)
592 pos.y = contentY + contentRect.height - portBounds.height;
593 // If contentY is above the port boundaries, scroll down to
594 // contentY.
595 if (contentY + viewBounds.y < 0)
596 pos.y = contentY;
597 // If the right boundary of contentRect is right from the port
598 // boundaries, scroll left as necessary.
599 if (contentX + contentRect.width + viewBounds.x > portBounds.width)
600 pos.x = contentX + contentRect.width - portBounds.width;
601 // If contentX is left from the port boundaries, scroll right to
602 // contentRect.x.
603 if (contentX + viewBounds.x < 0)
604 pos.x = contentX;
605 setViewPosition(pos);
606 }
607
608 /**
609 * Returns the accessible context for this <code>JViewport</code>. This
610 * will be an instance of {@link AccessibleJViewport}.
611 *
612 * @return the accessible context for this <code>JViewport</code>
613 */
614 public AccessibleContext getAccessibleContext()
615 {
616 if (accessibleContext == null)
617 accessibleContext = new AccessibleJViewport();
618 return accessibleContext;
619 }
620
621 /**
622 * Forward repaint to parent to make sure only one paint is performed by the
623 * RepaintManager.
624 *
625 * @param tm number of milliseconds to defer the repaint request
626 * @param x the X coordinate of the upper left corner of the dirty area
627 * @param y the Y coordinate of the upper left corner of the dirty area
628 * @param w the width of the dirty area
629 * @param h the height of the dirty area
630 */
631 public void repaint(long tm, int x, int y, int w, int h)
632 {
633 Component parent = getParent();
634 if (parent != null)
635 parent.repaint(tm, x + getX(), y + getY(), w, h);
636 else
637 super.repaint(tm, x, y, w, h);
638 }
639
640 protected void addImpl(Component comp, Object constraints, int index)
641 {
642 if (getComponentCount() > 0)
643 remove(getComponents()[0]);
644
645 super.addImpl(comp, constraints, index);
646 }
647
648 protected void fireStateChanged()
649 {
650 ChangeListener[] listeners = getChangeListeners();
651 for (int i = 0; i < listeners.length; ++i)
652 listeners[i].stateChanged(changeEvent);
653 }
654
655 /**
656 * Creates a {@link ViewListener} that is supposed to listen for
657 * size changes on the view component.
658 *
659 * @return a ViewListener instance
660 */
661 protected ViewListener createViewListener()
662 {
663 return new ViewListener();
664 }
665
666 /**
667 * Creates the LayoutManager that is used for this viewport. Override
668 * this method if you want to use a custom LayoutManager.
669 *
670 * @return a LayoutManager to use for this viewport
671 */
672 protected LayoutManager createLayoutManager()
673 {
674 return new ViewportLayout();
675 }
676
677 /**
678 * Computes the parameters for the blitting scroll method. <code>dx</code>
679 * and <code>dy</code> specifiy the X and Y offset by which the viewport
680 * is scrolled. All other arguments are output parameters and are filled by
681 * this method.
682 *
683 * <code>blitFrom</code> holds the position of the blit rectangle in the
684 * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea
685 * is copied to.
686 *
687 * <code>blitSize</code> holds the size of the blit area and
688 * <code>blitPaint</code> is the area of the view that needs to be painted.
689 *
690 * This method returns <code>true</code> if blitting is possible and
691 * <code>false</code> if the viewport has to be repainted completetly without
692 * blitting.
693 *
694 * @param dx the horizontal delta
695 * @param dy the vertical delta
696 * @param blitFrom the position from where to blit; set by this method
697 * @param blitTo the position where to blit area is copied to; set by this
698 * method
699 * @param blitSize the size of the blitted area; set by this method
700 * @param blitPaint the area that needs repainting; set by this method
701 *
702 * @return <code>true</code> if blitting is possible,
703 * <code>false</code> otherwise
704 */
705 protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo,
706 Dimension blitSize, Rectangle blitPaint)
707 {
708 if ((dx != 0 && dy != 0) || (dy == 0 && dy == 0) || damaged)
709 // We cannot blit if the viewport is scrolled in both directions at
710 // once. Also, we do not want to blit if the viewport is not scrolled at
711 // all, because that probably means the view component repaints itself
712 // and the buffer needs updating.
713 return false;
714
715 Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds());
716
717 // Compute the blitFrom and blitTo parameters.
718 blitFrom.x = portBounds.x;
719 blitFrom.y = portBounds.y;
720 blitTo.x = portBounds.x;
721 blitTo.y = portBounds.y;
722
723 if (dy > 0)
724 {
725 blitFrom.y = portBounds.y + dy;
726 }
727 else if (dy < 0)
728 {
729 blitTo.y = portBounds.y - dy;
730 }
731 else if (dx > 0)
732 {
733 blitFrom.x = portBounds.x + dx;
734 }
735 else if (dx < 0)
736 {
737 blitTo.x = portBounds.x - dx;
738 }
739
740 // Compute size of the blit area.
741 if (dx != 0)
742 {
743 blitSize.width = portBounds.width - Math.abs(dx);
744 blitSize.height = portBounds.height;
745 }
746 else if (dy != 0)
747 {
748 blitSize.width = portBounds.width;
749 blitSize.height = portBounds.height - Math.abs(dy);
750 }
751
752 // Compute the blitPaint parameter.
753 blitPaint.setBounds(portBounds);
754 if (dy > 0)
755 {
756 blitPaint.y = portBounds.y + portBounds.height - dy;
757 blitPaint.height = dy;
758 }
759 else if (dy < 0)
760 {
761 blitPaint.height = -dy;
762 }
763 if (dx > 0)
764 {
765 blitPaint.x = portBounds.x + portBounds.width - dx;
766 blitPaint.width = dx;
767 }
768 else if (dx < 0)
769 {
770 blitPaint.width = -dx;
771 }
772
773 return true;
774 }
775
776 /**
777 * Paints the viewport in case we have a scrollmode of
778 * {@link #SIMPLE_SCROLL_MODE}.
779 *
780 * This simply paints the view directly on the surface of the viewport.
781 *
782 * @param g the graphics context to use
783 */
784 void paintSimple(Graphics g)
785 {
786 // We need to call this to properly clear the background.
787 paintComponent(g);
788
789 Point pos = getViewPosition();
790 Component view = getView();
791 Shape oldClip = g.getClip();
792 g.clipRect(0, 0, getWidth(), getHeight());
793 boolean translated = false;
794 try
795 {
796 g.translate(-pos.x, -pos.y);
797 translated = true;
798 view.paint(g);
799 }
800 finally
801 {
802 if (translated)
803 g.translate (pos.x, pos.y);
804 g.setClip(oldClip);
805 }
806 }
807
808 /**
809 * Paints the viewport in case we have a scroll mode of
810 * {@link #BACKINGSTORE_SCROLL_MODE}.
811 *
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.
815 *
816 * @param g the graphics context to use
817 */
818 void paintBackingStore(Graphics g)
819 {
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)
823 {
824 backingStoreImage = createImage(getWidth(), getHeight());
825 sizeChanged = false;
826 Graphics g2 = backingStoreImage.getGraphics();
827 paintSimple(g2);
828 g2.dispose();
829 }
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
834 {
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 && isPaintRoot)
842 {
843 // Copy the part that remains visible during scrolling.
844 if (cachedBlitSize.width > 0 && cachedBlitSize.height > 0)
845 {
846 g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
847 cachedBlitSize.width, cachedBlitSize.height,
848 cachedBlitTo.x - cachedBlitFrom.x,
849 cachedBlitTo.y - cachedBlitFrom.y);
850 }
851 // Now paint the part that becomes newly visible.
852 g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y,
853 cachedBlitPaint.width, cachedBlitPaint.height);
854 paintSimple(g2);
855 }
856 // If blitting is not possible for some reason, fall back to repainting
857 // everything.
858 else
859 {
860 // If the image has not been scrolled at all, only the changed
861 // clip must be updated in the buffer.
862 if (dx == 0 && dy == 0)
863 g2.setClip(g.getClip());
864
865 paintSimple(g2);
866 }
867 g2.dispose();
868 }
869 // Actually draw the backingstore image to the graphics context.
870 g.drawImage(backingStoreImage, 0, 0, this);
871 // Update the lastPaintPosition so that we know what is already drawn when
872 // we paint the next time.
873 lastPaintPosition.setLocation(getViewPosition());
874 }
875
876 /**
877 * Paints the viewport in case we have a scrollmode of
878 * {@link #BLIT_SCROLL_MODE}.
879 *
880 * This paints the viewport using a backingstore and a blitting algorithm.
881 * Only the newly exposed area of the view is painted from the view painting
882 * methods, the remainder is copied from the backing store.
883 *
884 * @param g the graphics context to use
885 */
886 void paintBlit(Graphics g)
887 {
888 // First we move the part that remains visible after scrolling, then
889 // we only need to paint the bit that becomes newly visible.
890 Point viewPosition = getViewPosition();
891 int dx = viewPosition.x - lastPaintPosition.x;
892 int dy = viewPosition.y - lastPaintPosition.y;
893 boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo,
894 cachedBlitSize, cachedBlitPaint);
895 if (canBlit && isPaintRoot)
896 {
897 // Copy the part that remains visible during scrolling.
898 if (cachedBlitSize.width > 0 && cachedBlitSize.width > 0)
899 {
900 g.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
901 cachedBlitSize.width, cachedBlitSize.height,
902 cachedBlitTo.x - cachedBlitFrom.x,
903 cachedBlitTo.y - cachedBlitFrom.y);
904 }
905 // Now paint the part that becomes newly visible.
906 Shape oldClip = g.getClip();
907 g.clipRect(cachedBlitPaint.x, cachedBlitPaint.y,
908 cachedBlitPaint.width, cachedBlitPaint.height);
909 try
910 {
911 paintSimple(g);
912 }
913 finally
914 {
915 g.setClip(oldClip);
916 }
917 }
918 // If blitting is not possible for some reason, fall back to repainting
919 // everything.
920 else
921 paintSimple(g);
922 lastPaintPosition.setLocation(getViewPosition());
923 }
924
925 /**
926 * Overridden from JComponent to set the {@link #isPaintRoot} flag.
927 *
928 * @param x the rectangle to paint, X coordinate
929 * @param y the rectangle to paint, Y coordinate
930 * @param w the rectangle to paint, width
931 * @param h the rectangle to paint, height
932 */
933 void paintImmediately2(int x, int y, int w, int h)
934 {
935 isPaintRoot = true;
936 super.paintImmediately2(x, y, w, h);
937 isPaintRoot = false;
938 }
939
940 /**
941 * Returns true when the JViewport is using a backbuffer, so that we
942 * can update our backbuffer correctly.
943 */
944 boolean isPaintRoot()
945 {
946 return scrollMode == BACKINGSTORE_SCROLL_MODE;
947 }
948 }