Source for javax.swing.JViewport

   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: }