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