001/* JViewport.java -- 002 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing; 040 041import gnu.classpath.SystemProperties; 042 043import java.awt.Component; 044import java.awt.Dimension; 045import java.awt.Graphics; 046import java.awt.Image; 047import java.awt.Insets; 048import java.awt.LayoutManager; 049import java.awt.Point; 050import java.awt.Rectangle; 051import java.awt.Shape; 052import java.awt.event.ComponentAdapter; 053import java.awt.event.ComponentEvent; 054import java.io.Serializable; 055 056import javax.accessibility.Accessible; 057import javax.accessibility.AccessibleContext; 058import javax.accessibility.AccessibleRole; 059import javax.swing.border.Border; 060import javax.swing.event.ChangeEvent; 061import javax.swing.event.ChangeListener; 062import 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 */ 105public 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}