Frames | No Frames |
1: /* JViewport.java -- 2: Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 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. 10: 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. 15: 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. 20: 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. 25: 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. */ 37: 38: 39: package javax.swing; 40: 41: import java.awt.Component; 42: import java.awt.Dimension; 43: import java.awt.Graphics; 44: import java.awt.Image; 45: import java.awt.Insets; 46: import java.awt.LayoutManager; 47: import java.awt.Point; 48: import java.awt.Rectangle; 49: import java.awt.event.ComponentAdapter; 50: import java.awt.event.ComponentEvent; 51: import java.io.Serializable; 52: 53: import javax.accessibility.Accessible; 54: import javax.accessibility.AccessibleContext; 55: import javax.accessibility.AccessibleRole; 56: import javax.swing.border.Border; 57: import javax.swing.event.ChangeEvent; 58: import javax.swing.event.ChangeListener; 59: import javax.swing.plaf.ViewportUI; 60: 61: /** 62: * 63: * <pre> 64: * _ 65: * +-------------------------------+ ...........Y1 \ 66: * | view | . \ 67: * | (this component's child) | . > VY 68: * | | . / = Y2-Y1 69: * | +------------------------------+ ....Y2_/ 70: * | | viewport | | . 71: * | | (this component) | | . 72: * | | | | . 73: * | | | | . 74: * | | | | . 75: * | | | | . 76: * | +------------------------------+ ....Y3 77: * | | . 78: * | . | . . 79: * | . | . . 80: * +---------.---------------------+ ...........Y4 81: * . . . . 82: * . . . . 83: * . . . . 84: * X1.......X2.....................X3.......X4 85: * \____ ___/ 86: * \/ 87: * VX = X2-X1 88: *</pre> 89: * 90: * <p>A viewport is, like all swing components, located at some position in 91: * the swing component tree; that location is exactly the same as any other 92: * components: the viewport's "bounds".</p> 93: * 94: * <p>But in terms of drawing its child, the viewport thinks of itself as 95: * covering a particular position <em>of the view's coordinate space</em>. 96: * For example, the {@link #getViewPosition} method returns 97: * the position <code>(VX,VY)</code> shown above, which is an position in 98: * "view space", even though this is <em>implemented</em> by positioning 99: * the underlying child at position <code>(-VX,-VY)</code></p> 100: * 101: */ 102: public class JViewport extends JComponent implements Accessible 103: { 104: /** 105: * Provides accessibility support for <code>JViewport</code>. 106: * 107: * @author Roman Kennke (roman@kennke.org) 108: */ 109: protected class AccessibleJViewport extends AccessibleJComponent 110: { 111: /** 112: * Creates a new instance of <code>AccessibleJViewport</code>. 113: */ 114: public AccessibleJViewport() 115: { 116: // Nothing to do here. 117: } 118: 119: /** 120: * Returns the accessible role of <code>JViewport</code>, which is 121: * {@link AccessibleRole#VIEWPORT}. 122: * 123: * @return the accessible role of <code>JViewport</code> 124: */ 125: public AccessibleRole getAccessibleRole() 126: { 127: return AccessibleRole.VIEWPORT; 128: } 129: } 130: 131: /** 132: * A {@link java.awt.event.ComponentListener} that listens for 133: * changes of the view's size. This triggers a revalidate() call on the 134: * viewport. 135: */ 136: protected class ViewListener extends ComponentAdapter implements Serializable 137: { 138: private static final long serialVersionUID = -2812489404285958070L; 139: 140: /** 141: * Creates a new instance of ViewListener. 142: */ 143: protected ViewListener() 144: { 145: // Nothing to do here. 146: } 147: 148: /** 149: * Receives notification when a component (in this case: the view 150: * component) changes it's size. This simply triggers a revalidate() on the 151: * viewport. 152: * 153: * @param ev the ComponentEvent describing the change 154: */ 155: public void componentResized(ComponentEvent ev) 156: { 157: revalidate(); 158: } 159: } 160: 161: public static final int SIMPLE_SCROLL_MODE = 0; 162: public static final int BLIT_SCROLL_MODE = 1; 163: public static final int BACKINGSTORE_SCROLL_MODE = 2; 164: 165: private static final long serialVersionUID = -6925142919680527970L; 166: 167: protected boolean scrollUnderway; 168: protected boolean isViewSizeSet; 169: 170: /** 171: * This flag indicates whether we use a backing store for drawing. 172: * 173: * @deprecated since JDK 1.3 174: */ 175: protected boolean backingStore; 176: 177: /** 178: * The backingstore image used for the backingstore and blit scroll methods. 179: */ 180: protected Image backingStoreImage; 181: 182: /** 183: * The position at which the view has been drawn the last time. This is used 184: * to determine the bittable area. 185: */ 186: protected Point lastPaintPosition; 187: 188: ChangeEvent changeEvent = new ChangeEvent(this); 189: 190: int scrollMode; 191: 192: /** 193: * The width and height of the Viewport's area in terms of view 194: * coordinates. Typically this will be the same as the width and height 195: * of the viewport's bounds, unless the viewport transforms units of 196: * width and height, which it may do, for example if it magnifies or 197: * rotates its view. 198: * 199: * @see #toViewCoordinates(Dimension) 200: */ 201: Dimension extentSize; 202: 203: /** 204: * The width and height of the view in its own coordinate space. 205: */ 206: Dimension viewSize; 207: 208: /** 209: * The ViewListener instance. 210: */ 211: ViewListener viewListener; 212: 213: /** 214: * Stores the location from where to blit. This is a cached Point object used 215: * in blitting calculations. 216: */ 217: Point cachedBlitFrom; 218: 219: /** 220: * Stores the location where to blit to. This is a cached Point object used 221: * in blitting calculations. 222: */ 223: Point cachedBlitTo; 224: 225: /** 226: * Stores the width of the blitted area. This is a cached Dimension object 227: * used in blitting calculations. 228: */ 229: Dimension cachedBlitSize; 230: 231: /** 232: * Stores the bounds of the area that needs to be repainted. This is a cached 233: * Rectangle object used in blitting calculations. 234: */ 235: Rectangle cachedBlitPaint; 236: 237: boolean damaged = true; 238: 239: /** 240: * A flag indicating if the size of the viewport has changed since the 241: * last repaint. This is used in double buffered painting to check if we 242: * need a new double buffer, or can reuse the old one. 243: */ 244: boolean sizeChanged = true; 245: 246: public JViewport() 247: { 248: setOpaque(true); 249: String scrollModeProp = 250: System.getProperty("gnu.javax.swing.JViewport.scrollMode", 251: "BLIT"); 252: int myScrollMode; 253: if (scrollModeProp.equalsIgnoreCase("simple")) 254: myScrollMode = SIMPLE_SCROLL_MODE; 255: else if (scrollModeProp.equalsIgnoreCase("backingstore")) 256: myScrollMode = BACKINGSTORE_SCROLL_MODE; 257: else 258: myScrollMode = BLIT_SCROLL_MODE; 259: setScrollMode(myScrollMode); 260: 261: updateUI(); 262: setLayout(createLayoutManager()); 263: lastPaintPosition = new Point(); 264: cachedBlitFrom = new Point(); 265: cachedBlitTo = new Point(); 266: cachedBlitSize = new Dimension(); 267: cachedBlitPaint = new Rectangle(); 268: } 269: 270: public Dimension getExtentSize() 271: { 272: if (extentSize == null) 273: return toViewCoordinates(getSize()); 274: else 275: return extentSize; 276: } 277: 278: public Dimension toViewCoordinates(Dimension size) 279: { 280: return size; 281: } 282: 283: public Point toViewCoordinates(Point p) 284: { 285: Point pos = getViewPosition(); 286: return new Point(p.x + pos.x, 287: p.y + pos.y); 288: } 289: 290: public void setExtentSize(Dimension newSize) 291: { 292: extentSize = newSize; 293: fireStateChanged(); 294: } 295: 296: /** 297: * Returns the viewSize when set, or the preferred size of the set 298: * Component view. If no viewSize and no Component view is set an 299: * empty Dimension is returned. 300: */ 301: public Dimension getViewSize() 302: { 303: if (isViewSizeSet) 304: return viewSize; 305: else 306: { 307: Component view = getView(); 308: if (view != null) 309: return view.getPreferredSize(); 310: else 311: return new Dimension(); 312: } 313: } 314: 315: 316: public void setViewSize(Dimension newSize) 317: { 318: viewSize = newSize; 319: Component view = getView(); 320: if (view != null) 321: { 322: if (newSize != view.getSize()) 323: { 324: view.setSize(viewSize); 325: fireStateChanged(); 326: } 327: } 328: isViewSizeSet = true; 329: } 330: 331: /** 332: * Get the viewport's position in view space. Despite confusing name, 333: * this really does return the viewport's (0,0) position in view space, 334: * not the view's position. 335: */ 336: 337: public Point getViewPosition() 338: { 339: Component view = getView(); 340: if (view == null) 341: return new Point(0,0); 342: else 343: { 344: Point p = view.getLocation(); 345: p.x = -p.x; 346: p.y = -p.y; 347: return p; 348: } 349: } 350: 351: public void setViewPosition(Point p) 352: { 353: if (getViewPosition().equals(p)) 354: return; 355: Component view = getView(); 356: if (view != null) 357: { 358: Point q = new Point(-p.x, -p.y); 359: view.setLocation(q); 360: isViewSizeSet = false; 361: fireStateChanged(); 362: } 363: repaint(); 364: } 365: 366: public Rectangle getViewRect() 367: { 368: return new Rectangle(getViewPosition(), 369: getExtentSize()); 370: } 371: 372: /** 373: * @deprecated 1.4 374: */ 375: public boolean isBackingStoreEnabled() 376: { 377: return scrollMode == BACKINGSTORE_SCROLL_MODE; 378: } 379: 380: /** 381: * @deprecated 1.4 382: */ 383: public void setBackingStoreEnabled(boolean b) 384: { 385: if (b && scrollMode != BACKINGSTORE_SCROLL_MODE) 386: { 387: scrollMode = BACKINGSTORE_SCROLL_MODE; 388: fireStateChanged(); 389: } 390: } 391: 392: public void setScrollMode(int mode) 393: { 394: scrollMode = mode; 395: fireStateChanged(); 396: } 397: 398: public int getScrollMode() 399: { 400: return scrollMode; 401: } 402: 403: public Component getView() 404: { 405: if (getComponentCount() == 0) 406: return null; 407: 408: return getComponents()[0]; 409: } 410: 411: public void setView(Component v) 412: { 413: if (viewListener != null) 414: getView().removeComponentListener(viewListener); 415: 416: if (v != null) 417: { 418: if (viewListener == null) 419: viewListener = createViewListener(); 420: v.addComponentListener(viewListener); 421: add(v); 422: fireStateChanged(); 423: } 424: revalidate(); 425: repaint(); 426: } 427: 428: public void reshape(int x, int y, int w, int h) 429: { 430: if (w != getWidth() || h != getHeight()) 431: sizeChanged = true; 432: super.reshape(x, y, w, h); 433: if (sizeChanged) 434: { 435: damaged = true; 436: fireStateChanged(); 437: } 438: } 439: 440: public final Insets getInsets() 441: { 442: return new Insets(0, 0, 0, 0); 443: } 444: 445: public final Insets getInsets(Insets insets) 446: { 447: if (insets == null) 448: return getInsets(); 449: insets.top = 0; 450: insets.bottom = 0; 451: insets.left = 0; 452: insets.right = 0; 453: return insets; 454: } 455: 456: 457: /** 458: * Overridden to return <code>false</code>, so the JViewport's paint method 459: * gets called instead of directly calling the children. This is necessary 460: * in order to get a useful clipping and translation on the children. 461: * 462: * @return <code>false</code> 463: */ 464: public boolean isOptimizedDrawingEnabled() 465: { 466: return false; 467: } 468: 469: public void paint(Graphics g) 470: { 471: Component view = getView(); 472: 473: if (view == null) 474: return; 475: 476: Point pos = getViewPosition(); 477: Rectangle viewBounds = view.getBounds(); 478: Rectangle portBounds = getBounds(); 479: 480: if (viewBounds.width == 0 481: || viewBounds.height == 0 482: || portBounds.width == 0 483: || portBounds.height == 0) 484: return; 485: 486: switch (getScrollMode()) 487: { 488: 489: case JViewport.BACKINGSTORE_SCROLL_MODE: 490: paintBackingStore(g); 491: break; 492: case JViewport.BLIT_SCROLL_MODE: 493: paintBlit(g); 494: break; 495: case JViewport.SIMPLE_SCROLL_MODE: 496: default: 497: paintSimple(g); 498: break; 499: } 500: damaged = false; 501: } 502: 503: public void addChangeListener(ChangeListener listener) 504: { 505: listenerList.add(ChangeListener.class, listener); 506: } 507: 508: public void removeChangeListener(ChangeListener listener) 509: { 510: listenerList.remove(ChangeListener.class, listener); 511: } 512: 513: public ChangeListener[] getChangeListeners() 514: { 515: return (ChangeListener[]) getListeners(ChangeListener.class); 516: } 517: 518: /** 519: * This method returns the String ID of the UI class of Separator. 520: * 521: * @return The UI class' String ID. 522: */ 523: public String getUIClassID() 524: { 525: return "ViewportUI"; 526: } 527: 528: /** 529: * This method resets the UI used to the Look and Feel defaults.. 530: */ 531: public void updateUI() 532: { 533: setUI((ViewportUI) UIManager.getUI(this)); 534: } 535: 536: /** 537: * This method returns the viewport's UI delegate. 538: * 539: * @return The viewport's UI delegate. 540: */ 541: public ViewportUI getUI() 542: { 543: return (ViewportUI) ui; 544: } 545: 546: /** 547: * This method sets the viewport's UI delegate. 548: * 549: * @param ui The viewport's UI delegate. 550: */ 551: public void setUI(ViewportUI ui) 552: { 553: super.setUI(ui); 554: } 555: 556: public final void setBorder(Border border) 557: { 558: if (border != null) 559: throw new IllegalArgumentException(); 560: } 561: 562: /** 563: * Scrolls the view so that contentRect becomes visible. 564: * 565: * @param contentRect the rectangle to make visible within the view 566: */ 567: public void scrollRectToVisible(Rectangle contentRect) 568: { 569: Component view = getView(); 570: if (view == null) 571: return; 572: 573: Point pos = getViewPosition(); 574: Rectangle viewBounds = getView().getBounds(); 575: Rectangle portBounds = getBounds(); 576: 577: if (isShowing()) 578: getView().validate(); 579: 580: // If the bottom boundary of contentRect is below the port 581: // boundaries, scroll up as necessary. 582: if (contentRect.y + contentRect.height + viewBounds.y > portBounds.height) 583: pos.y = contentRect.y + contentRect.height - portBounds.height; 584: // If contentRect.y is above the port boundaries, scroll down to 585: // contentRect.y. 586: if (contentRect.y + viewBounds.y < 0) 587: pos.y = contentRect.y; 588: // If the right boundary of contentRect is right from the port 589: // boundaries, scroll left as necessary. 590: if (contentRect.x + contentRect.width + viewBounds.x > portBounds.width) 591: pos.x = contentRect.x + contentRect.width - portBounds.width; 592: // If contentRect.x is left from the port boundaries, scroll right to 593: // contentRect.x. 594: if (contentRect.x + viewBounds.x < 0) 595: pos.x = contentRect.x; 596: setViewPosition(pos); 597: } 598: 599: /** 600: * Returns the accessible context for this <code>JViewport</code>. This 601: * will be an instance of {@link AccessibleJViewport}. 602: * 603: * @return the accessible context for this <code>JViewport</code> 604: */ 605: public AccessibleContext getAccessibleContext() 606: { 607: if (accessibleContext == null) 608: accessibleContext = new AccessibleJViewport(); 609: return accessibleContext; 610: } 611: 612: /** 613: * Forward repaint to parent to make sure only one paint is performed by the 614: * RepaintManager. 615: * 616: * @param tm number of milliseconds to defer the repaint request 617: * @param x the X coordinate of the upper left corner of the dirty area 618: * @param y the Y coordinate of the upper left corner of the dirty area 619: * @param w the width of the dirty area 620: * @param h the height of the dirty area 621: */ 622: public void repaint(long tm, int x, int y, int w, int h) 623: { 624: Component parent = getParent(); 625: if (parent != null) 626: { 627: parent.repaint(tm, x + getX(), y + getY(), w, h); 628: } 629: } 630: 631: protected void addImpl(Component comp, Object constraints, int index) 632: { 633: if (getComponentCount() > 0) 634: remove(getComponents()[0]); 635: 636: super.addImpl(comp, constraints, index); 637: } 638: 639: protected void fireStateChanged() 640: { 641: ChangeListener[] listeners = getChangeListeners(); 642: for (int i = 0; i < listeners.length; ++i) 643: listeners[i].stateChanged(changeEvent); 644: } 645: 646: /** 647: * Creates a {@link ViewListener} that is supposed to listen for 648: * size changes on the view component. 649: * 650: * @return a ViewListener instance 651: */ 652: protected ViewListener createViewListener() 653: { 654: return new ViewListener(); 655: } 656: 657: /** 658: * Creates the LayoutManager that is used for this viewport. Override 659: * this method if you want to use a custom LayoutManager. 660: * 661: * @return a LayoutManager to use for this viewport 662: */ 663: protected LayoutManager createLayoutManager() 664: { 665: return new ViewportLayout(); 666: } 667: 668: /** 669: * Computes the parameters for the blitting scroll method. <code>dx</code> 670: * and <code>dy</code> specifiy the X and Y offset by which the viewport 671: * is scrolled. All other arguments are output parameters and are filled by 672: * this method. 673: * 674: * <code>blitFrom</code> holds the position of the blit rectangle in the 675: * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea 676: * is copied to. 677: * 678: * <code>blitSize</code> holds the size of the blit area and 679: * <code>blitPaint</code> is the area of the view that needs to be painted. 680: * 681: * This method returns <code>true</code> if blitting is possible and 682: * <code>false</code> if the viewport has to be repainted completetly without 683: * blitting. 684: * 685: * @param dx the horizontal delta 686: * @param dy the vertical delta 687: * @param blitFrom the position from where to blit; set by this method 688: * @param blitTo the position where to blit area is copied to; set by this 689: * method 690: * @param blitSize the size of the blitted area; set by this method 691: * @param blitPaint the area that needs repainting; set by this method 692: * 693: * @return <code>true</code> if blitting is possible, 694: * <code>false</code> otherwise 695: */ 696: protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo, 697: Dimension blitSize, Rectangle blitPaint) 698: { 699: if ((dx != 0 && dy != 0) || damaged) 700: // We cannot blit if the viewport is scrolled in both directions at 701: // once. 702: return false; 703: 704: Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds()); 705: 706: // Compute the blitFrom and blitTo parameters. 707: blitFrom.x = portBounds.x; 708: blitFrom.y = portBounds.y; 709: blitTo.x = portBounds.x; 710: blitTo.y = portBounds.y; 711: 712: if (dy > 0) 713: { 714: blitFrom.y = portBounds.y + dy; 715: } 716: else if (dy < 0) 717: { 718: blitTo.y = portBounds.y - dy; 719: } 720: else if (dx > 0) 721: { 722: blitFrom.x = portBounds.x + dx; 723: } 724: else if (dx < 0) 725: { 726: blitTo.x = portBounds.x - dx; 727: } 728: 729: // Compute size of the blit area. 730: if (dx != 0) 731: { 732: blitSize.width = portBounds.width - Math.abs(dx); 733: blitSize.height = portBounds.height; 734: } 735: else if (dy != 0) 736: { 737: blitSize.width = portBounds.width; 738: blitSize.height = portBounds.height - Math.abs(dy); 739: } 740: 741: // Compute the blitPaint parameter. 742: blitPaint.setBounds(portBounds); 743: if (dy > 0) 744: { 745: blitPaint.y = portBounds.y + portBounds.height - dy; 746: blitPaint.height = dy; 747: } 748: else if (dy < 0) 749: { 750: blitPaint.height = -dy; 751: } 752: if (dx > 0) 753: { 754: blitPaint.x = portBounds.x + portBounds.width - dx; 755: blitPaint.width = dx; 756: } 757: else if (dx < 0) 758: { 759: blitPaint.width = -dx; 760: } 761: 762: return true; 763: } 764: 765: /** 766: * Paints the viewport in case we have a scrollmode of 767: * {@link #SIMPLE_SCROLL_MODE}. 768: * 769: * This simply paints the view directly on the surface of the viewport. 770: * 771: * @param g the graphics context to use 772: */ 773: void paintSimple(Graphics g) 774: { 775: Point pos = getViewPosition(); 776: Component view = getView(); 777: boolean translated = false; 778: try 779: { 780: g.translate(-pos.x, -pos.y); 781: translated = true; 782: view.paint(g); 783: } 784: finally 785: { 786: if (translated) 787: g.translate (pos.x, pos.y); 788: } 789: } 790: 791: /** 792: * Paints the viewport in case we have a scroll mode of 793: * {@link #BACKINGSTORE_SCROLL_MODE}. 794: * 795: * This method uses a backing store image to paint the view to, which is then 796: * subsequently painted on the screen. This should make scrolling more 797: * smooth. 798: * 799: * @param g the graphics context to use 800: */ 801: void paintBackingStore(Graphics g) 802: { 803: // If we have no backing store image yet or the size of the component has 804: // changed, we need to rebuild the backing store. 805: if (backingStoreImage == null || sizeChanged) 806: { 807: backingStoreImage = createImage(getWidth(), getHeight()); 808: sizeChanged = false; 809: Graphics g2 = backingStoreImage.getGraphics(); 810: paintSimple(g2); 811: g2.dispose(); 812: } 813: // Otherwise we can perform the blitting on the backing store image: 814: // First we move the part that remains visible after scrolling, then 815: // we only need to paint the bit that becomes newly visible. 816: else 817: { 818: Graphics g2 = backingStoreImage.getGraphics(); 819: Point viewPosition = getViewPosition(); 820: int dx = viewPosition.x - lastPaintPosition.x; 821: int dy = viewPosition.y - lastPaintPosition.y; 822: boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo, 823: cachedBlitSize, cachedBlitPaint); 824: if (canBlit) 825: { 826: // Copy the part that remains visible during scrolling. 827: g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, 828: cachedBlitSize.width, cachedBlitSize.height, 829: cachedBlitTo.x - cachedBlitFrom.x, 830: cachedBlitTo.y - cachedBlitFrom.y); 831: // Now paint the part that becomes newly visible. 832: g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y, 833: cachedBlitPaint.width, cachedBlitPaint.height); 834: paintSimple(g2); 835: } 836: // If blitting is not possible for some reason, fall back to repainting 837: // everything. 838: else 839: { 840: paintSimple(g2); 841: } 842: g2.dispose(); 843: } 844: // Actually draw the backingstore image to the graphics context. 845: g.drawImage(backingStoreImage, 0, 0, this); 846: // Update the lastPaintPosition so that we know what is already drawn when 847: // we paint the next time. 848: lastPaintPosition.setLocation(getViewPosition()); 849: } 850: 851: /** 852: * Paints the viewport in case we have a scrollmode of 853: * {@link #BLIT_SCROLL_MODE}. 854: * 855: * This paints the viewport using a backingstore and a blitting algorithm. 856: * Only the newly exposed area of the view is painted from the view painting 857: * methods, the remainder is copied from the backing store. 858: * 859: * @param g the graphics context to use 860: */ 861: void paintBlit(Graphics g) 862: { 863: // We cannot perform blitted painting as it is described in Sun's API docs. 864: // There it is suggested that this painting method should blit directly 865: // on the parent window's surface. This is not possible because when using 866: // Swing's double buffering (at least our implementation), it would 867: // immediatly be painted when the buffer is painted on the screen. For this 868: // to work we would need a kind of hole in the buffer image. And honestly 869: // I find this method not very elegant. 870: // The alternative, blitting directly on the buffer image, is also not 871: // possible because the buffer image gets cleared everytime when an opaque 872: // parent component is drawn on it. 873: 874: // What we do instead is falling back to the backing store approach which 875: // is in fact a mixed blitting/backing store approach where the blitting 876: // is performed on the backing store image and this is then drawn to the 877: // graphics context. This is very robust and works independent of the 878: // painting mechanism that is used by Swing. And it should have comparable 879: // performance characteristics as the blitting method. 880: paintBackingStore(g); 881: } 882: }