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