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