< prev index next >

src/java.desktop/share/classes/javax/swing/JViewport.java

Print this page




  29 import java.awt.event.*;
  30 import java.awt.peer.ComponentPeer;
  31 import java.beans.Transient;
  32 import javax.swing.plaf.ViewportUI;
  33 
  34 import javax.swing.event.*;
  35 import javax.swing.border.*;
  36 import javax.accessibility.*;
  37 
  38 import java.io.Serializable;
  39 
  40 import sun.awt.AWTAccessor;
  41 
  42 /**
  43  * The "viewport" or "porthole" through which you see the underlying
  44  * information. When you scroll, what moves is the viewport. It is like
  45  * peering through a camera's viewfinder. Moving the viewfinder upwards
  46  * brings new things into view at the top of the picture and loses
  47  * things that were at the bottom.
  48  * <p>
  49  * By default, <code>JViewport</code> is opaque. To change this, use the
  50  * <code>setOpaque</code> method.
  51  * <p>
  52  * <b>NOTE:</b>We have implemented a faster scrolling algorithm that
  53  * does not require a buffer to draw in. The algorithm works as follows:
  54  * <ol><li>The view and parent view and checked to see if they are
  55  * <code>JComponents</code>,
  56  * if they aren't, stop and repaint the whole viewport.
  57  * <li>If the viewport is obscured by an ancestor, stop and repaint the whole
  58  * viewport.
  59  * <li>Compute the region that will become visible, if it is as big as
  60  * the viewport, stop and repaint the whole view region.
  61  * <li>Obtain the ancestor <code>Window</code>'s graphics and
  62  * do a <code>copyArea</code> on the scrolled region.
  63  * <li>Message the view to repaint the newly visible region.
  64  * <li>The next time paint is invoked on the viewport, if the clip region
  65  * is smaller than the viewport size a timer is kicked off to repaint the
  66  * whole region.
  67  * </ol>
  68  * In general this approach is much faster. Compared to the backing store
  69  * approach this avoids the overhead of maintaining an offscreen buffer and
  70  * having to do two <code>copyArea</code>s.
  71  * Compared to the non backing store case this
  72  * approach will greatly reduce the painted region.
  73  * <p>
  74  * This approach can cause slower times than the backing store approach
  75  * when the viewport is obscured by another window, or partially offscreen.
  76  * When another window
  77  * obscures the viewport the copyArea will copy garbage and a
  78  * paint event will be generated by the system to inform us we need to
  79  * paint the newly exposed region. The only way to handle this is to
  80  * repaint the whole viewport, which can cause slower performance than the
  81  * backing store case. In most applications very rarely will the user be
  82  * scrolling while the viewport is obscured by another window or offscreen,
  83  * so this optimization is usually worth the performance hit when obscured.
  84  * <p>
  85  * <strong>Warning:</strong> Swing is not thread safe. For more
  86  * information see <a
  87  * href="package-summary.html#threading">Swing's Threading
  88  * Policy</a>.
  89  * <p>
  90  * <strong>Warning:</strong>
  91  * Serialized objects of this class will not be compatible with
  92  * future Swing releases. The current serialization support is
  93  * appropriate for short term storage or RMI between applications running
  94  * the same version of Swing.  As of 1.4, support for long term storage
  95  * of all JavaBeans&trade;
  96  * has been added to the <code>java.beans</code> package.
  97  * Please see {@link java.beans.XMLEncoder}.
  98  *
  99  * @author Hans Muller
 100  * @author Philip Milne
 101  * @see JScrollPane
 102  * @since 1.2
 103  */
 104 @SuppressWarnings("serial") // Same-version serialization only
 105 public class JViewport extends JComponent implements Accessible
 106 {
 107     /**
 108      * @see #getUIClassID
 109      * @see #readObject
 110      */
 111     private static final String uiClassID = "ViewportUI";
 112 
 113     /** Property used to indicate window blitting should not be done.
 114      */
 115     static final Object EnableWindowBlit = "EnableWindowBlit";
 116 
 117     /**
 118      * True when the viewport dimensions have been determined.
 119      * The default is false.
 120      */
 121     protected boolean isViewSizeSet = false;
 122 
 123     /**
 124      * The last <code>viewPosition</code> that we've painted, so we know how
 125      * much of the backing store image is valid.
 126      */
 127     protected Point lastPaintPosition = null;
 128 
 129     /**
 130      * True when this viewport is maintaining an offscreen image of its
 131      * contents, so that some scrolling can take place using fast "bit-blit"
 132      * operations instead of by accessing the view object to construct the
 133      * display.  The default is <code>false</code>.
 134      *
 135      * @deprecated As of Java 2 platform v1.3
 136      * @see #setScrollMode
 137      */
 138     @Deprecated
 139     protected boolean backingStore = false;
 140 
 141     /** The view image used for a backing store. */
 142     protected transient Image backingStoreImage = null;
 143 
 144     /**
 145      * The <code>scrollUnderway</code> flag is used for components like
 146      * <code>JList</code>.  When the downarrow key is pressed on a
 147      * <code>JList</code> and the selected
 148      * cell is the last in the list, the <code>scrollpane</code> autoscrolls.
 149      * Here, the old selected cell needs repainting and so we need
 150      * a flag to make the viewport do the optimized painting
 151      * only when there is an explicit call to
 152      * <code>setViewPosition(Point)</code>.
 153      * When <code>setBounds</code> is called through other routes,
 154      * the flag is off and the view repaints normally.  Another approach
 155      * would be to remove this from the <code>JViewport</code>
 156      * class and have the <code>JList</code> manage this case by using
 157      * <code>setBackingStoreEnabled</code>.  The default is
 158      * <code>false</code>.
 159      */
 160     protected boolean scrollUnderway = false;
 161 
 162     /*
 163      * Listener that is notified each time the view changes size.
 164      */
 165     private ComponentListener viewListener = null;
 166 
 167     /* Only one <code>ChangeEvent</code> is needed per
 168      * <code>JViewport</code> instance since the
 169      * event's only (read-only) state is the source property.  The source
 170      * of events generated here is always "this".
 171      */
 172     private transient ChangeEvent changeEvent = null;
 173 
 174     /**
 175       * Use <code>graphics.copyArea</code> to implement scrolling.
 176       * This is the fastest for most applications.
 177       *
 178       * @see #setScrollMode
 179       * @since 1.3
 180       */
 181     public static final int BLIT_SCROLL_MODE = 1;
 182 
 183     /**
 184       * Draws viewport contents into an offscreen image.
 185       * This was previously the default mode for <code>JTable</code>.
 186       * This mode may offer advantages over "blit mode"
 187       * in some cases, but it requires a large chunk of extra RAM.
 188       *
 189       * @see #setScrollMode
 190       * @since 1.3
 191       */
 192     public static final int BACKINGSTORE_SCROLL_MODE = 2;
 193 
 194     /**
 195       * This mode uses the very simple method of redrawing the entire
 196       * contents of the scrollpane each time it is scrolled.
 197       * This was the default behavior in Swing 1.0 and Swing 1.1.
 198       * Either of the other two options will provide better performance
 199       * in most cases.
 200       *
 201       * @see #setScrollMode
 202       * @since 1.3
 203       */
 204     public static final int SIMPLE_SCROLL_MODE = 0;
 205 


 218     // not guaranteed to receive the paint event before other mouse events,
 219     // so we can not be sure we haven't already copied garbage a bunch of
 220     // times to different parts of the view. For that reason when a blit
 221     // happens and the Component is obscured (the check for obscurity
 222     // is not supported on all platforms and is checked via ComponentPeer
 223     // methods) the ivar repaintAll is set to true. When paint is received
 224     // if repaintAll is true (we previously did a blit) it is set to
 225     // false, and if the clip region is smaller than the viewport
 226     // waitingForRepaint is set to true and a timer is started. When
 227     // the timer fires if waitingForRepaint is true, repaint is invoked.
 228     // In the mean time, if the view is asked to scroll and waitingForRepaint
 229     // is true, a blit will not happen, instead the non-backing store case
 230     // of scrolling will happen, which will reset waitingForRepaint.
 231     // waitingForRepaint is set to false in paint when the clip rect is
 232     // bigger (or equal) to the size of the viewport.
 233     // A Timer is used instead of just a repaint as it appeared to offer
 234     // better performance.
 235 
 236 
 237     /**
 238      * This is set to true in <code>setViewPosition</code>
 239      * if doing a window blit and the viewport is obscured.
 240      */
 241     private transient boolean repaintAll;
 242 
 243     /**
 244      * This is set to true in paint, if <code>repaintAll</code>
 245      * is true and the clip rectangle does not match the bounds.
 246      * If true, and scrolling happens the
 247      * repaint manager is not cleared which then allows for the repaint
 248      * previously invoked to succeed.
 249      */
 250     private transient boolean waitingForRepaint;
 251 
 252     /**
 253      * Instead of directly invoking repaint, a <code>Timer</code>
 254      * is started and when it fires, repaint is invoked.
 255      */
 256     private transient Timer repaintTimer;
 257 
 258     /**
 259      * Set to true in paintView when paint is invoked.
 260      */
 261     private transient boolean inBlitPaint;
 262 
 263     /**
 264      * Whether or not a valid view has been installed.
 265      */
 266     private boolean hasHadValidView;
 267 
 268     /**
 269      * When view is changed we have to synchronize scrollbar values
 270      * with viewport (see the BasicScrollPaneUI#syncScrollPaneWithViewport method).
 271      * This flag allows to invoke that method while ScrollPaneLayout#layoutContainer
 272      * is running.
 273      */
 274     private boolean viewChanged;
 275 
 276     /** Creates a <code>JViewport</code>. */
 277     public JViewport() {
 278         super();
 279         setLayout(createLayoutManager());
 280         setOpaque(true);
 281         updateUI();
 282         setInheritsPopupMenu(true);
 283     }
 284 
 285 
 286 
 287     /**
 288      * Returns the L&amp;F object that renders this component.
 289      *
 290      * @return a <code>ViewportUI</code> object
 291      * @since 1.3
 292      */
 293     public ViewportUI getUI() {
 294         return (ViewportUI)ui;
 295     }
 296 
 297 
 298     /**
 299      * Sets the L&amp;F object that renders this component.
 300      *
 301      * @param ui  the <code>ViewportUI</code> L&amp;F object
 302      * @see UIDefaults#getUI
 303      * @beaninfo
 304      *        bound: true
 305      *       hidden: true
 306      *    attribute: visualUpdate true
 307      *  description: The UI object that implements the Component's LookAndFeel.
 308      * @since 1.3
 309      */
 310     public void setUI(ViewportUI ui) {
 311         super.setUI(ui);
 312     }
 313 
 314 
 315     /**
 316      * Resets the UI property to a value from the current look and feel.
 317      *
 318      * @see JComponent#updateUI
 319      */
 320     public void updateUI() {
 321         setUI((ViewportUI)UIManager.getUI(this));
 322     }
 323 
 324 
 325     /**
 326      * Returns a string that specifies the name of the L&amp;F class
 327      * that renders this component.
 328      *
 329      * @return the string "ViewportUI"
 330      *
 331      * @see JComponent#getUIClassID
 332      * @see UIDefaults#getUI
 333      */
 334     public String getUIClassID() {
 335         return uiClassID;
 336     }
 337 
 338 
 339     /**
 340      * Sets the <code>JViewport</code>'s one lightweight child,
 341      * which can be <code>null</code>.
 342      * (Since there is only one child which occupies the entire viewport,
 343      * the <code>constraints</code> and <code>index</code>
 344      * arguments are ignored.)
 345      *
 346      * @param child       the lightweight <code>child</code> of the viewport
 347      * @param constraints the <code>constraints</code> to be respected
 348      * @param index       the index
 349      * @see #setView
 350      */
 351     protected void addImpl(Component child, Object constraints, int index) {
 352       setView(child);
 353     }
 354 
 355 
 356     /**
 357      * Removes the <code>Viewport</code>s one lightweight child.
 358      *
 359      * @see #setView
 360      */
 361     public void remove(Component child) {
 362         child.removeComponentListener(viewListener);
 363         super.remove(child);
 364     }
 365 
 366     /**
 367      * Scrolls the view so that <code>Rectangle</code>
 368      * within the view becomes visible.
 369      * <p>
 370      * This attempts to validate the view before scrolling if the
 371      * view is currently not valid - <code>isValid</code> returns false.
 372      * To avoid excessive validation when the containment hierarchy is
 373      * being created this will not validate if one of the ancestors does not
 374      * have a peer, or there is no validate root ancestor, or one of the
 375      * ancestors is not a <code>Window</code> or <code>Applet</code>.
 376      * <p>
 377      * Note that this method will not scroll outside of the
 378      * valid viewport; for example, if <code>contentRect</code> is larger
 379      * than the viewport, scrolling will be confined to the viewport's
 380      * bounds.
 381      *
 382      * @param contentRect the <code>Rectangle</code> to display
 383      * @see JComponent#isValidateRoot
 384      * @see java.awt.Component#isValid
 385      */
 386     public void scrollRectToVisible(Rectangle contentRect) {
 387         Component view = getView();
 388 
 389         if (view == null) {
 390             return;
 391         } else {
 392             if (!view.isValid()) {
 393                 // If the view is not valid, validate. scrollRectToVisible
 394                 // may fail if the view is not valid first, contentRect
 395                 // could be bigger than invalid size.
 396                 validateView();
 397             }
 398             int dx, dy;
 399 
 400             dx = positionAdjustment(getWidth(), contentRect.width, contentRect.x);
 401             dy = positionAdjustment(getHeight(), contentRect.height, contentRect.y);
 402 


 448                     // true, the backing store is blitted.  This fails
 449                     // if between the time setViewPosition is invoked
 450                     // and paint is received another repaint is queued
 451                     // indicating part of the view is invalid. There
 452                     // is no way for JViewport to notice another
 453                     // repaint has occurred and it ends up blitting
 454                     // what is now a dirty region and the repaint is
 455                     // never delivered.
 456                     // It just so happens JTable encounters this
 457                     // behavior by way of scrollRectToVisible, for
 458                     // this reason scrollUnderway is set to false
 459                     // here, which effectively disables the backing
 460                     // store.
 461                     scrollUnderway = false;
 462                 }
 463             }
 464         }
 465     }
 466 
 467     /**
 468      * Ascends the <code>Viewport</code>'s parents stopping when
 469      * a component is found that returns
 470      * <code>true</code> to <code>isValidateRoot</code>.
 471      * If all the <code>Component</code>'s  parents are visible,
 472      * <code>validate</code> will then be invoked on it. The
 473      * <code>RepaintManager</code> is then invoked with
 474      * <code>removeInvalidComponent</code>. This
 475      * is the synchronous version of a <code>revalidate</code>.
 476      */
 477     private void validateView() {
 478         Component validateRoot = SwingUtilities.getValidateRoot(this, false);
 479 
 480         if (validateRoot == null) {
 481             return;
 482         }
 483 
 484         // Validate the root.
 485         validateRoot.validate();
 486 
 487         // And let the RepaintManager it does not have to validate from
 488         // validateRoot anymore.
 489         RepaintManager rm = RepaintManager.currentManager(this);
 490 
 491         if (rm != null) {
 492             rm.removeInvalidComponent((JComponent)validateRoot);
 493         }
 494     }
 495 


 532         // ----    |     ->   |---- |
 533         //   +-----+          +-----+
 534         if (childAt <= 0 && childWidth <= parentWidth)   {
 535             return -childAt;
 536         }
 537 
 538         //   +-----+             +-----+
 539         //-------- |      ->   --------|
 540         //   +-----+             +-----+
 541         if (childAt < 0 && childWidth >= parentWidth)    {
 542             return -childAt + parentWidth - childWidth;
 543         }
 544 
 545         return 0;
 546     }
 547 
 548 
 549     /**
 550      * The viewport "scrolls" its child (called the "view") by the
 551      * normal parent/child clipping (typically the view is moved in
 552      * the opposite direction of the scroll).  A non-<code>null</code> border,
 553      * or non-zero insets, isn't supported, to prevent the geometry
 554      * of this component from becoming complex enough to inhibit
 555      * subclassing.  To create a <code>JViewport</code> with a border,
 556      * add it to a <code>JPanel</code> that has a border.
 557      * <p>Note:  If <code>border</code> is non-<code>null</code>, this
 558      * method will throw an exception as borders are not supported on
 559      * a <code>JViewPort</code>.
 560      *
 561      * @param border the <code>Border</code> to set
 562      * @exception IllegalArgumentException this method is not implemented
 563      */
 564     public final void setBorder(Border border) {
 565         if (border != null) {
 566             throw new IllegalArgumentException("JViewport.setBorder() not supported");
 567         }
 568     }
 569 
 570 
 571     /**
 572      * Returns the insets (border) dimensions as (0,0,0,0), since borders
 573      * are not supported on a <code>JViewport</code>.
 574      *
 575      * @return a <code>Rectangle</code> of zero dimension and zero origin
 576      * @see #setBorder
 577      */
 578     public final Insets getInsets() {
 579         return new Insets(0, 0, 0, 0);
 580     }
 581 
 582     /**
 583      * Returns an <code>Insets</code> object containing this
 584      * <code>JViewport</code>s inset values.  The passed-in
 585      * <code>Insets</code> object will be reinitialized, and
 586      * all existing values within this object are overwritten.
 587      *
 588      * @param insets the <code>Insets</code> object which can be reused
 589      * @return this viewports inset values
 590      * @see #getInsets
 591      * @beaninfo
 592      *   expert: true
 593      */
 594     public final Insets getInsets(Insets insets) {
 595         insets.left = insets.top = insets.right = insets.bottom = 0;
 596         return insets;
 597     }
 598 
 599 
 600     private Graphics getBackingStoreGraphics(Graphics g) {
 601         Graphics bsg = backingStoreImage.getGraphics();
 602         bsg.setColor(g.getColor());
 603         bsg.setFont(g.getFont());
 604         bsg.setClip(g.getClipBounds());
 605         return bsg;
 606     }
 607 
 608 


 611         try {
 612             super.paint(bsg);
 613             g.drawImage(backingStoreImage, 0, 0, this);
 614         } finally {
 615             bsg.dispose();
 616         }
 617     }
 618 
 619     private void paintViaBackingStore(Graphics g, Rectangle oClip) {
 620         Graphics bsg = getBackingStoreGraphics(g);
 621         try {
 622             super.paint(bsg);
 623             g.setClip(oClip);
 624             g.drawImage(backingStoreImage, 0, 0, this);
 625         } finally {
 626             bsg.dispose();
 627         }
 628     }
 629 
 630     /**
 631      * The <code>JViewport</code> overrides the default implementation of
 632      * this method (in <code>JComponent</code>) to return false.
 633      * This ensures
 634      * that the drawing machinery will call the <code>Viewport</code>'s
 635      * <code>paint</code>
 636      * implementation rather than messaging the <code>JViewport</code>'s
 637      * children directly.
 638      *
 639      * @return false
 640      */
 641     public boolean isOptimizedDrawingEnabled() {
 642         return false;
 643     }
 644 
 645     /**
 646      * Returns true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE} to cause
 647      * painting to originate from {@code JViewport}, or one of its
 648      * ancestors. Otherwise returns {@code false}.
 649      *
 650      * @return true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE}.
 651      * @see JComponent#isPaintingOrigin()
 652      */
 653     protected boolean isPaintingOrigin() {
 654         return scrollMode == BACKINGSTORE_SCROLL_MODE;
 655     }
 656 
 657 
 658     /**
 659      * Only used by the paint method below.
 660      */
 661     private Point getViewLocation() {
 662         Component view = getView();
 663         if (view != null) {
 664             return view.getLocation();
 665         }
 666         else {
 667             return new Point(0,0);
 668         }
 669     }
 670 
 671     /**
 672      * Depending on whether the <code>backingStore</code> is enabled,
 673      * either paint the image through the backing store or paint
 674      * just the recently exposed part, using the backing store
 675      * to "blit" the remainder.
 676      * <blockquote>
 677      * The term "blit" is the pronounced version of the PDP-10
 678      * BLT (BLock Transfer) instruction, which copied a block of
 679      * bits. (In case you were curious.)
 680      * </blockquote>
 681      *
 682      * @param g the <code>Graphics</code> context within which to paint
 683      */
 684     public void paint(Graphics g)
 685     {
 686         int width = getWidth();
 687         int height = getHeight();
 688 
 689         if ((width <= 0) || (height <= 0)) {
 690             return;
 691         }
 692 
 693         if (inBlitPaint) {
 694             // We invoked paint as part of copyArea cleanup, let it through.
 695             super.paint(g);
 696             return;
 697         }
 698 
 699         if (repaintAll) {
 700             repaintAll = false;
 701             Rectangle clipB = g.getClipBounds();
 702             if (clipB.width < getWidth() ||


 804                         // Paint the rest of the view; the part that has just been exposed.
 805                         Rectangle r = viewBounds.intersection(blitPaint);
 806                         bsg.setClip(r);
 807                         super.paint(bsg);
 808 
 809                         // Copy whole of the backing store to g.
 810                         g.drawImage(backingStoreImage, 0, 0, this);
 811                     } finally {
 812                         bsg.dispose();
 813                     }
 814                 }
 815             }
 816         }
 817         lastPaintPosition = getViewLocation();
 818         scrollUnderway = false;
 819     }
 820 
 821 
 822     /**
 823      * Sets the bounds of this viewport.  If the viewport's width
 824      * or height has changed, fire a <code>StateChanged</code> event.
 825      *
 826      * @param x left edge of the origin
 827      * @param y top edge of the origin
 828      * @param w width in pixels
 829      * @param h height in pixels
 830      *
 831      * @see JComponent#reshape(int, int, int, int)
 832      */
 833     @SuppressWarnings("deprecation")
 834     public void reshape(int x, int y, int w, int h) {
 835         boolean sizeChanged = (getWidth() != w) || (getHeight() != h);
 836         if (sizeChanged) {
 837             backingStoreImage = null;
 838         }
 839         super.reshape(x, y, w, h);
 840         if (sizeChanged || viewChanged) {
 841             viewChanged = false;
 842 
 843             fireStateChanged();
 844         }


 861       * @see #BACKINGSTORE_SCROLL_MODE
 862       * @see #SIMPLE_SCROLL_MODE
 863       *
 864       * @beaninfo
 865       *        bound: false
 866       *  description: Method of moving contents for incremental scrolls.
 867       *         enum: BLIT_SCROLL_MODE JViewport.BLIT_SCROLL_MODE
 868       *               BACKINGSTORE_SCROLL_MODE JViewport.BACKINGSTORE_SCROLL_MODE
 869       *               SIMPLE_SCROLL_MODE JViewport.SIMPLE_SCROLL_MODE
 870       *
 871       * @since 1.3
 872       */
 873     public void setScrollMode(int mode) {
 874         scrollMode = mode;
 875         backingStore = mode == BACKINGSTORE_SCROLL_MODE;
 876     }
 877 
 878     /**
 879       * Returns the current scrolling mode.
 880       *
 881       * @return the <code>scrollMode</code> property
 882       * @see #setScrollMode
 883       * @since 1.3
 884       */
 885     public int getScrollMode() {
 886         return scrollMode;
 887     }
 888 
 889     /**
 890      * Returns <code>true</code> if this viewport is maintaining
 891      * an offscreen image of its contents.
 892      *
 893      * @return <code>true</code> if <code>scrollMode</code> is
 894      *    <code>BACKINGSTORE_SCROLL_MODE</code>
 895      *
 896      * @deprecated As of Java 2 platform v1.3, replaced by
 897      *             <code>getScrollMode()</code>.
 898      */
 899     @Deprecated
 900     public boolean isBackingStoreEnabled() {
 901         return scrollMode == BACKINGSTORE_SCROLL_MODE;
 902     }
 903 
 904 
 905     /**
 906      * If true if this viewport will maintain an offscreen
 907      * image of its contents.  The image is used to reduce the cost
 908      * of small one dimensional changes to the <code>viewPosition</code>.
 909      * Rather than repainting the entire viewport we use
 910      * <code>Graphics.copyArea</code> to effect some of the scroll.
 911      *
 912      * @param enabled if true, maintain an offscreen backing store
 913      *
 914      * @deprecated As of Java 2 platform v1.3, replaced by
 915      *             <code>setScrollMode()</code>.
 916      */
 917     @Deprecated
 918     public void setBackingStoreEnabled(boolean enabled) {
 919         if (enabled) {
 920             setScrollMode(BACKINGSTORE_SCROLL_MODE);
 921         } else {
 922             setScrollMode(BLIT_SCROLL_MODE);
 923         }
 924     }
 925 
 926     private boolean isBlitting() {
 927         Component view = getView();
 928         return (scrollMode == BLIT_SCROLL_MODE) &&
 929                (view instanceof JComponent) && view.isOpaque();
 930     }
 931 
 932 
 933     /**
 934      * Returns the <code>JViewport</code>'s one child or <code>null</code>.
 935      *
 936      * @return the viewports child, or <code>null</code> if none exists
 937      *
 938      * @see #setView
 939      */
 940     public Component getView() {
 941         return (getComponentCount() > 0) ? getComponent(0) : null;
 942     }
 943 
 944     /**
 945      * Sets the <code>JViewport</code>'s one lightweight child
 946      * (<code>view</code>), which can be <code>null</code>.
 947      *
 948      * @param view the viewport's new lightweight child
 949      *
 950      * @see #getView
 951      */
 952     public void setView(Component view) {
 953 
 954         /* Remove the viewport's existing children, if any.
 955          * Note that removeAll() isn't used here because it
 956          * doesn't call remove() (which JViewport overrides).
 957          */
 958         int n = getComponentCount();
 959         for(int i = n - 1; i >= 0; i--) {
 960             remove(getComponent(i));
 961         }
 962 
 963         isViewSizeSet = false;
 964 
 965         if (view != null) {
 966             super.addImpl(view, null, -1);


 971         if (hasHadValidView) {
 972             // Only fire a change if a view has been installed.
 973             fireStateChanged();
 974         }
 975         else if (view != null) {
 976             hasHadValidView = true;
 977         }
 978 
 979         viewChanged = true;
 980 
 981         revalidate();
 982         repaint();
 983     }
 984 
 985 
 986     /**
 987      * If the view's size hasn't been explicitly set, return the
 988      * preferred size, otherwise return the view's current size.
 989      * If there is no view, return 0,0.
 990      *
 991      * @return a <code>Dimension</code> object specifying the size of the view
 992      */
 993     public Dimension getViewSize() {
 994         Component view = getView();
 995 
 996         if (view == null) {
 997             return new Dimension(0,0);
 998         }
 999         else if (isViewSizeSet) {
1000             return view.getSize();
1001         }
1002         else {
1003             return view.getPreferredSize();
1004         }
1005     }
1006 
1007 
1008     /**
1009      * Sets the size of the view.  A state changed event will be fired.
1010      *
1011      * @param newSize a <code>Dimension</code> object specifying the new
1012      *          size of the view
1013      */
1014     public void setViewSize(Dimension newSize) {
1015         Component view = getView();
1016         if (view != null) {
1017             Dimension oldSize = view.getSize();
1018             if (!newSize.equals(oldSize)) {
1019                 // scrollUnderway will be true if this is invoked as the
1020                 // result of a validate and setViewPosition was previously
1021                 // invoked.
1022                 scrollUnderway = false;
1023                 view.setSize(newSize);
1024                 isViewSizeSet = true;
1025                 fireStateChanged();
1026             }
1027         }
1028     }
1029 
1030     /**
1031      * Returns the view coordinates that appear in the upper left
1032      * hand corner of the viewport, or 0,0 if there's no view.
1033      *
1034      * @return a <code>Point</code> object giving the upper left coordinates
1035      */
1036     public Point getViewPosition() {
1037         Component view = getView();
1038         if (view != null) {
1039             Point p = view.getLocation();
1040             p.x = -p.x;
1041             p.y = -p.y;
1042             return p;
1043         }
1044         else {
1045             return new Point(0,0);
1046         }
1047     }
1048 
1049 
1050     /**
1051      * Sets the view coordinates that appear in the upper left
1052      * hand corner of the viewport, does nothing if there's no view.
1053      *
1054      * @param p  a <code>Point</code> object giving the upper left coordinates
1055      */
1056     public void setViewPosition(Point p)
1057     {
1058         Component view = getView();
1059         if (view == null) {
1060             return;
1061         }
1062 
1063         int oldX, oldY, x = p.x, y = p.y;
1064 
1065         /* Collect the old x,y values for the views location
1066          * and do the song and dance to avoid allocating
1067          * a Rectangle object if we don't have to.
1068          */
1069         if (view instanceof JComponent) {
1070             JComponent c = (JComponent)view;
1071             oldX = c.getX();
1072             oldY = c.getY();
1073         }
1074         else {


1115                 else {
1116                     // The visible region is dirty, no point in doing copyArea
1117                     view.setLocation(newX, newY);
1118                     repaintAll = false;
1119                 }
1120             }
1121             else {
1122                 scrollUnderway = true;
1123                 // This calls setBounds(), and then repaint().
1124                 view.setLocation(newX, newY);
1125                 repaintAll = false;
1126             }
1127             // we must validate the hierarchy to not break the hw/lw mixing
1128             revalidate();
1129             fireStateChanged();
1130         }
1131     }
1132 
1133 
1134     /**
1135      * Returns a rectangle whose origin is <code>getViewPosition</code>
1136      * and size is <code>getExtentSize</code>.
1137      * This is the visible part of the view, in view coordinates.
1138      *
1139      * @return a <code>Rectangle</code> giving the visible part of
1140      *          the view using view coordinates.
1141      */
1142     public Rectangle getViewRect() {
1143         return new Rectangle(getViewPosition(), getExtentSize());
1144     }
1145 
1146 
1147     /**
1148      * Computes the parameters for a blit where the backing store image
1149      * currently contains <code>oldLoc</code> in the upper left hand corner
1150      * and we're scrolling to <code>newLoc</code>.
1151      * The parameters are modified
1152      * to return the values required for the blit.
1153      *
1154      * @param dx  the horizontal delta
1155      * @param dy  the vertical delta
1156      * @param blitFrom the <code>Point</code> we're blitting from
1157      * @param blitTo the <code>Point</code> we're blitting to
1158      * @param blitSize the <code>Dimension</code> of the area to blit
1159      * @param blitPaint the area to blit
1160      * @return  true if the parameters are modified and we're ready to blit;
1161      *          false otherwise
1162      */
1163     protected boolean computeBlit(
1164         int dx,
1165         int dy,
1166         Point blitFrom,
1167         Point blitTo,
1168         Dimension blitSize,
1169         Rectangle blitPaint)
1170     {
1171         int dxAbs = Math.abs(dx);
1172         int dyAbs = Math.abs(dy);
1173         Dimension extentSize = getExtentSize();
1174 
1175         if ((dx == 0) && (dy != 0) && (dyAbs < extentSize.height)) {
1176             if (dy < 0) {
1177                 blitFrom.y = -dy;
1178                 blitTo.y = 0;


1210             blitPaint.y = blitFrom.y = blitTo.y = 0;
1211 
1212             blitSize.width = extentSize.width - dxAbs;
1213             blitSize.height = extentSize.height;
1214 
1215             blitPaint.width = dxAbs;
1216             blitPaint.height = extentSize.height;
1217 
1218             return true;
1219         }
1220 
1221         else {
1222             return false;
1223         }
1224     }
1225 
1226 
1227     /**
1228      * Returns the size of the visible part of the view in view coordinates.
1229      *
1230      * @return a <code>Dimension</code> object giving the size of the view
1231      */
1232     @Transient
1233     public Dimension getExtentSize() {
1234         return getSize();
1235     }
1236 
1237 
1238     /**
1239      * Converts a size in pixel coordinates to view coordinates.
1240      * Subclasses of viewport that support "logical coordinates"
1241      * will override this method.
1242      *
1243      * @param size  a <code>Dimension</code> object using pixel coordinates
1244      * @return a <code>Dimension</code> object converted to view coordinates
1245      */
1246     public Dimension toViewCoordinates(Dimension size) {
1247         return new Dimension(size);
1248     }
1249 
1250     /**
1251      * Converts a point in pixel coordinates to view coordinates.
1252      * Subclasses of viewport that support "logical coordinates"
1253      * will override this method.
1254      *
1255      * @param p  a <code>Point</code> object using pixel coordinates
1256      * @return a <code>Point</code> object converted to view coordinates
1257      */
1258     public Point toViewCoordinates(Point p) {
1259         return new Point(p);
1260     }
1261 
1262 
1263     /**
1264      * Sets the size of the visible part of the view using view coordinates.
1265      *
1266      * @param newExtent  a <code>Dimension</code> object specifying
1267      *          the size of the view
1268      */
1269     public void setExtentSize(Dimension newExtent) {
1270         Dimension oldExtent = getExtentSize();
1271         if (!newExtent.equals(oldExtent)) {
1272             setSize(newExtent);
1273             fireStateChanged();
1274         }
1275     }
1276 
1277     /**
1278      * A listener for the view.
1279      * <p>
1280      * <strong>Warning:</strong>
1281      * Serialized objects of this class will not be compatible with
1282      * future Swing releases. The current serialization support is
1283      * appropriate for short term storage or RMI between applications running
1284      * the same version of Swing.  As of 1.4, support for long term storage
1285      * of all JavaBeans&trade;
1286      * has been added to the <code>java.beans</code> package.
1287      * Please see {@link java.beans.XMLEncoder}.
1288      */
1289     @SuppressWarnings("serial") // Same-version serialization only
1290     protected class ViewListener extends ComponentAdapter implements Serializable
1291     {
1292         public void componentResized(ComponentEvent e) {
1293             fireStateChanged();
1294             revalidate();
1295         }
1296     }
1297 
1298     /**
1299      * Creates a listener for the view.
1300      * @return a <code>ViewListener</code>
1301      */
1302     protected ViewListener createViewListener() {
1303         return new ViewListener();
1304     }
1305 
1306 
1307     /**
1308      * Subclassers can override this to install a different
1309      * layout manager (or <code>null</code>) in the constructor.  Returns
1310      * the <code>LayoutManager</code> to install on the <code>JViewport</code>.
1311      *
1312      * @return a <code>LayoutManager</code>
1313      */
1314     protected LayoutManager createLayoutManager() {
1315         return ViewportLayout.SHARED_INSTANCE;
1316     }
1317 
1318 
1319     /**
1320      * Adds a <code>ChangeListener</code> to the list that is
1321      * notified each time the view's
1322      * size, position, or the viewport's extent size has changed.
1323      *
1324      * @param l the <code>ChangeListener</code> to add
1325      * @see #removeChangeListener
1326      * @see #setViewPosition
1327      * @see #setViewSize
1328      * @see #setExtentSize
1329      */
1330     public void addChangeListener(ChangeListener l) {
1331         listenerList.add(ChangeListener.class, l);
1332     }
1333 
1334     /**
1335      * Removes a <code>ChangeListener</code> from the list that's notified each
1336      * time the views size, position, or the viewports extent size
1337      * has changed.
1338      *
1339      * @param l the <code>ChangeListener</code> to remove
1340      * @see #addChangeListener
1341      */
1342     public void removeChangeListener(ChangeListener l) {
1343         listenerList.remove(ChangeListener.class, l);
1344     }
1345 
1346     /**
1347      * Returns an array of all the <code>ChangeListener</code>s added
1348      * to this JViewport with addChangeListener().
1349      *
1350      * @return all of the <code>ChangeListener</code>s added or an empty
1351      *         array if no listeners have been added
1352      * @since 1.4
1353      */
1354     public ChangeListener[] getChangeListeners() {
1355         return listenerList.getListeners(ChangeListener.class);
1356     }
1357 
1358     /**
1359      * Notifies all <code>ChangeListeners</code> when the views
1360      * size, position, or the viewports extent size has changed.
1361      *
1362      * @see #addChangeListener
1363      * @see #removeChangeListener
1364      * @see EventListenerList
1365      */
1366     protected void fireStateChanged()
1367     {
1368         Object[] listeners = listenerList.getListenerList();
1369         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1370             if (listeners[i] == ChangeListener.class) {
1371                 if (changeEvent == null) {
1372                     changeEvent = new ChangeEvent(this);
1373                 }
1374                 ((ChangeListener)listeners[i + 1]).stateChanged(changeEvent);
1375             }
1376         }
1377     }
1378 
1379     /**
1380      * Always repaint in the parents coordinate system to make sure
1381      * only one paint is performed by the <code>RepaintManager</code>.
1382      *
1383      * @param     tm   maximum time in milliseconds before update
1384      * @param     x    the <code>x</code> coordinate (pixels over from left)
1385      * @param     y    the <code>y</code> coordinate (pixels down from top)
1386      * @param     w    the width
1387      * @param     h   the height
1388      * @see       java.awt.Component#update(java.awt.Graphics)
1389      */
1390     public void repaint(long tm, int x, int y, int w, int h) {
1391         Container parent = getParent();
1392         if(parent != null)
1393             parent.repaint(tm,x+getX(),y+getY(),w,h);
1394         else
1395             super.repaint(tm,x,y,w,h);
1396     }
1397 
1398 
1399     /**
1400      * Returns a string representation of this <code>JViewport</code>.
1401      * This method
1402      * is intended to be used only for debugging purposes, and the
1403      * content and format of the returned string may vary between
1404      * implementations. The returned string may be empty but may not
1405      * be <code>null</code>.
1406      *
1407      * @return  a string representation of this <code>JViewport</code>
1408      */
1409     protected String paramString() {
1410         String isViewSizeSetString = (isViewSizeSet ?
1411                                       "true" : "false");
1412         String lastPaintPositionString = (lastPaintPosition != null ?
1413                                           lastPaintPosition.toString() : "");
1414         String scrollUnderwayString = (scrollUnderway ?
1415                                        "true" : "false");
1416 
1417         return super.paramString() +
1418         ",isViewSizeSet=" + isViewSizeSetString +
1419         ",lastPaintPosition=" + lastPaintPositionString +
1420         ",scrollUnderway=" + scrollUnderwayString;
1421     }
1422 
1423     //
1424     // Following is used when doBlit is true.
1425     //
1426 
1427     /**
1428      * Notifies listeners of a property change. This is subclassed to update
1429      * the <code>windowBlit</code> property.
1430      * (The <code>putClientProperty</code> property is final).
1431      *
1432      * @param propertyName a string containing the property name
1433      * @param oldValue the old value of the property
1434      * @param newValue  the new value of the property
1435      */
1436     protected void firePropertyChange(String propertyName, Object oldValue,
1437                                       Object newValue) {
1438         super.firePropertyChange(propertyName, oldValue, newValue);
1439         if (propertyName.equals(EnableWindowBlit)) {
1440             if (newValue != null) {
1441                 setScrollMode(BLIT_SCROLL_MODE);
1442             } else {
1443                 setScrollMode(SIMPLE_SCROLL_MODE);
1444             }
1445         }
1446     }
1447 
1448     /**
1449      * Returns true if the component needs to be completely repainted after
1450      * a blit and a paint is received.


1478 
1479     private Timer createRepaintTimer() {
1480         Timer timer = new Timer(300, new ActionListener() {
1481             public void actionPerformed(ActionEvent ae) {
1482                 // waitingForRepaint will be false if a paint came down
1483                 // with the complete clip rect, in which case we don't
1484                 // have to cause a repaint.
1485                 if (waitingForRepaint) {
1486                     repaint();
1487                 }
1488             }
1489         });
1490         timer.setRepeats(false);
1491         return timer;
1492     }
1493 
1494     /**
1495      * If the repaint manager has a dirty region for the view, the view is
1496      * asked to paint.
1497      *
1498      * @param g  the <code>Graphics</code> context within which to paint
1499      */
1500     private void flushViewDirtyRegion(Graphics g, Rectangle dirty) {
1501         JComponent view = (JComponent) getView();
1502         if(dirty != null && dirty.width > 0 && dirty.height > 0) {
1503             dirty.x += view.getX();
1504             dirty.y += view.getY();
1505             Rectangle clip = g.getClipBounds();
1506             if (clip == null) {
1507                 // Only happens in 1.2
1508                 g.setClip(0, 0, getWidth(), getHeight());
1509             }
1510             g.clipRect(dirty.x, dirty.y, dirty.width, dirty.height);
1511             clip = g.getClipBounds();
1512             // Only paint the dirty region if it is visible.
1513             if (clip.width > 0 && clip.height > 0) {
1514                 paintView(g);
1515             }
1516         }
1517     }
1518 
1519     /**
1520      * Used when blitting.
1521      *
1522      * @param g  the <code>Graphics</code> context within which to paint
1523      * @return true if blitting succeeded; otherwise false
1524      */
1525     private boolean windowBlitPaint(Graphics g) {
1526         int width = getWidth();
1527         int height = getHeight();
1528 
1529         if ((width == 0) || (height == 0)) {
1530             return false;
1531         }
1532 
1533         boolean retValue;
1534         RepaintManager rm = RepaintManager.currentManager(this);
1535         JComponent view = (JComponent) getView();
1536 
1537         if (lastPaintPosition == null ||
1538             lastPaintPosition.equals(getViewLocation())) {
1539             paintView(g);
1540             retValue = false;
1541         } else {
1542             // The image was scrolled. Manipulate the backing store and flush


1596         if (g instanceof Graphics2D) {
1597             Graphics2D g2d = (Graphics2D) g;
1598             oldComposite = g2d.getComposite();
1599             g2d.setComposite(AlphaComposite.Src);
1600         }
1601         rm.copyArea(this, g, blitFromX, blitFromY, blitW, blitH, bdx, bdy,
1602                     false);
1603         if (oldComposite != null) {
1604             ((Graphics2D) g).setComposite(oldComposite);
1605         }
1606         // Paint the newly exposed region.
1607         int x = view.getX();
1608         int y = view.getY();
1609         g.translate(x, y);
1610         g.setClip(clipX, clipY, clipW, clipH);
1611         view.paintForceDoubleBuffered(g);
1612         g.translate(-x, -y);
1613     }
1614 
1615     /**
1616      * Called to paint the view, usually when <code>blitPaint</code>
1617      * can not blit.
1618      *
1619      * @param g the <code>Graphics</code> context within which to paint
1620      */
1621     private void paintView(Graphics g) {
1622         Rectangle clip = g.getClipBounds();
1623         JComponent view = (JComponent)getView();
1624 
1625         if (view.getWidth() >= getWidth()) {
1626             // Graphics is relative to JViewport, need to map to view's
1627             // coordinates space.
1628             int x = view.getX();
1629             int y = view.getY();
1630             g.translate(x, y);
1631             g.setClip(clip.x - x, clip.y - y, clip.width, clip.height);
1632             view.paintForceDoubleBuffered(g);
1633             g.translate(-x, -y);
1634             g.setClip(clip.x, clip.y, clip.width, clip.height);
1635         }
1636         else {
1637             // To avoid any problems that may result from the viewport being
1638             // bigger than the view we start painting from the viewport.
1639             try {
1640                 inBlitPaint = true;
1641                 paintForceDoubleBuffered(g);
1642             } finally {
1643                 inBlitPaint = false;
1644             }
1645         }
1646     }
1647 
1648     /**
1649      * Returns true if the viewport is not obscured by one of its ancestors,
1650      * or its ancestors children and if the viewport is showing. Blitting
1651      * when the view isn't showing will work,
1652      * or rather <code>copyArea</code> will work,
1653      * but will not produce the expected behavior.
1654      */
1655     private boolean canUseWindowBlitter() {
1656         if (!isShowing() || (!(getParent() instanceof JComponent) &&
1657                              !(getView() instanceof JComponent))) {
1658             return false;
1659         }
1660         if (isPainting()) {
1661             // We're in the process of painting, don't blit. If we were
1662             // to blit we would draw on top of what we're already drawing,
1663             // so bail.
1664             return false;
1665         }
1666 
1667         Rectangle dirtyRegion = RepaintManager.currentManager(this).
1668                                 getDirtyRegion((JComponent)getParent());
1669 
1670         if (dirtyRegion != null && dirtyRegion.width > 0 &&
1671             dirtyRegion.height > 0) {
1672             // Part of the scrollpane needs to be repainted too, don't blit.


1728 ////////////////
1729 
1730     /**
1731      * Gets the AccessibleContext associated with this JViewport.
1732      * For viewports, the AccessibleContext takes the form of an
1733      * AccessibleJViewport.
1734      * A new AccessibleJViewport instance is created if necessary.
1735      *
1736      * @return an AccessibleJViewport that serves as the
1737      *         AccessibleContext of this JViewport
1738      */
1739     public AccessibleContext getAccessibleContext() {
1740         if (accessibleContext == null) {
1741             accessibleContext = new AccessibleJViewport();
1742         }
1743         return accessibleContext;
1744     }
1745 
1746     /**
1747      * This class implements accessibility support for the
1748      * <code>JViewport</code> class.  It provides an implementation of the
1749      * Java Accessibility API appropriate to viewport user-interface elements.
1750      * <p>
1751      * <strong>Warning:</strong>
1752      * Serialized objects of this class will not be compatible with
1753      * future Swing releases. The current serialization support is
1754      * appropriate for short term storage or RMI between applications running
1755      * the same version of Swing.  As of 1.4, support for long term storage
1756      * of all JavaBeans&trade;
1757      * has been added to the <code>java.beans</code> package.
1758      * Please see {@link java.beans.XMLEncoder}.
1759      */
1760     @SuppressWarnings("serial") // Same-version serialization only
1761     protected class AccessibleJViewport extends AccessibleJComponent {
1762         /**
1763          * Get the role of this object.
1764          *
1765          * @return an instance of AccessibleRole describing the role of
1766          * the object
1767          */
1768         public AccessibleRole getAccessibleRole() {
1769             return AccessibleRole.VIEWPORT;
1770         }
1771     } // inner class AccessibleJViewport
1772 }


  29 import java.awt.event.*;
  30 import java.awt.peer.ComponentPeer;
  31 import java.beans.Transient;
  32 import javax.swing.plaf.ViewportUI;
  33 
  34 import javax.swing.event.*;
  35 import javax.swing.border.*;
  36 import javax.accessibility.*;
  37 
  38 import java.io.Serializable;
  39 
  40 import sun.awt.AWTAccessor;
  41 
  42 /**
  43  * The "viewport" or "porthole" through which you see the underlying
  44  * information. When you scroll, what moves is the viewport. It is like
  45  * peering through a camera's viewfinder. Moving the viewfinder upwards
  46  * brings new things into view at the top of the picture and loses
  47  * things that were at the bottom.
  48  * <p>
  49  * By default, {@code JViewport} is opaque. To change this, use the
  50  * {@code setOpaque} method.
  51  * <p>
  52  * <b>NOTE:</b>We have implemented a faster scrolling algorithm that
  53  * does not require a buffer to draw in. The algorithm works as follows:
  54  * <ol><li>The view and parent view and checked to see if they are
  55  * {@code JComponents},
  56  * if they aren't, stop and repaint the whole viewport.
  57  * <li>If the viewport is obscured by an ancestor, stop and repaint the whole
  58  * viewport.
  59  * <li>Compute the region that will become visible, if it is as big as
  60  * the viewport, stop and repaint the whole view region.
  61  * <li>Obtain the ancestor {@code Window}'s graphics and
  62  * do a {@code copyArea} on the scrolled region.
  63  * <li>Message the view to repaint the newly visible region.
  64  * <li>The next time paint is invoked on the viewport, if the clip region
  65  * is smaller than the viewport size a timer is kicked off to repaint the
  66  * whole region.
  67  * </ol>
  68  * In general this approach is much faster. Compared to the backing store
  69  * approach this avoids the overhead of maintaining an offscreen buffer and
  70  * having to do two {@code copyArea}s.
  71  * Compared to the non backing store case this
  72  * approach will greatly reduce the painted region.
  73  * <p>
  74  * This approach can cause slower times than the backing store approach
  75  * when the viewport is obscured by another window, or partially offscreen.
  76  * When another window
  77  * obscures the viewport the copyArea will copy garbage and a
  78  * paint event will be generated by the system to inform us we need to
  79  * paint the newly exposed region. The only way to handle this is to
  80  * repaint the whole viewport, which can cause slower performance than the
  81  * backing store case. In most applications very rarely will the user be
  82  * scrolling while the viewport is obscured by another window or offscreen,
  83  * so this optimization is usually worth the performance hit when obscured.
  84  * <p>
  85  * <strong>Warning:</strong> Swing is not thread safe. For more
  86  * information see <a
  87  * href="package-summary.html#threading">Swing's Threading
  88  * Policy</a>.
  89  * <p>
  90  * <strong>Warning:</strong>
  91  * Serialized objects of this class will not be compatible with
  92  * future Swing releases. The current serialization support is
  93  * appropriate for short term storage or RMI between applications running
  94  * the same version of Swing.  As of 1.4, support for long term storage
  95  * of all JavaBeans&trade;
  96  * has been added to the {@code java.beans} package.
  97  * Please see {@link java.beans.XMLEncoder}.
  98  *
  99  * @author Hans Muller
 100  * @author Philip Milne
 101  * @see JScrollPane
 102  * @since 1.2
 103  */
 104 @SuppressWarnings("serial") // Same-version serialization only
 105 public class JViewport extends JComponent implements Accessible
 106 {
 107     /**
 108      * @see #getUIClassID
 109      * @see #readObject
 110      */
 111     private static final String uiClassID = "ViewportUI";
 112 
 113     /** Property used to indicate window blitting should not be done.
 114      */
 115     static final Object EnableWindowBlit = "EnableWindowBlit";
 116 
 117     /**
 118      * True when the viewport dimensions have been determined.
 119      * The default is false.
 120      */
 121     protected boolean isViewSizeSet = false;
 122 
 123     /**
 124      * The last {@code viewPosition} that we've painted, so we know how
 125      * much of the backing store image is valid.
 126      */
 127     protected Point lastPaintPosition = null;
 128 
 129     /**
 130      * True when this viewport is maintaining an offscreen image of its
 131      * contents, so that some scrolling can take place using fast "bit-blit"
 132      * operations instead of by accessing the view object to construct the
 133      * display.  The default is {@code false}.
 134      *
 135      * @deprecated As of Java 2 platform v1.3
 136      * @see #setScrollMode
 137      */
 138     @Deprecated
 139     protected boolean backingStore = false;
 140 
 141     /** The view image used for a backing store. */
 142     protected transient Image backingStoreImage = null;
 143 
 144     /**
 145      * The {@code scrollUnderway} flag is used for components like
 146      * {@code JList}.  When the downarrow key is pressed on a
 147      * {@code JList} and the selected
 148      * cell is the last in the list, the {@code scrollpane} autoscrolls.
 149      * Here, the old selected cell needs repainting and so we need
 150      * a flag to make the viewport do the optimized painting
 151      * only when there is an explicit call to
 152      * {@code setViewPosition(Point)}.
 153      * When {@code setBounds} is called through other routes,
 154      * the flag is off and the view repaints normally.  Another approach
 155      * would be to remove this from the {@code JViewport}
 156      * class and have the {@code JList} manage this case by using
 157      * {@code setBackingStoreEnabled}.  The default is
 158      * {@code false}.
 159      */
 160     protected boolean scrollUnderway = false;
 161 
 162     /*
 163      * Listener that is notified each time the view changes size.
 164      */
 165     private ComponentListener viewListener = null;
 166 
 167     /* Only one {@code ChangeEvent} is needed per
 168      * {@code JViewport} instance since the
 169      * event's only (read-only) state is the source property.  The source
 170      * of events generated here is always "this".
 171      */
 172     private transient ChangeEvent changeEvent = null;
 173 
 174     /**
 175       * Use {@code graphics.copyArea} to implement scrolling.
 176       * This is the fastest for most applications.
 177       *
 178       * @see #setScrollMode
 179       * @since 1.3
 180       */
 181     public static final int BLIT_SCROLL_MODE = 1;
 182 
 183     /**
 184       * Draws viewport contents into an offscreen image.
 185       * This was previously the default mode for {@code JTable}.
 186       * This mode may offer advantages over "blit mode"
 187       * in some cases, but it requires a large chunk of extra RAM.
 188       *
 189       * @see #setScrollMode
 190       * @since 1.3
 191       */
 192     public static final int BACKINGSTORE_SCROLL_MODE = 2;
 193 
 194     /**
 195       * This mode uses the very simple method of redrawing the entire
 196       * contents of the scrollpane each time it is scrolled.
 197       * This was the default behavior in Swing 1.0 and Swing 1.1.
 198       * Either of the other two options will provide better performance
 199       * in most cases.
 200       *
 201       * @see #setScrollMode
 202       * @since 1.3
 203       */
 204     public static final int SIMPLE_SCROLL_MODE = 0;
 205 


 218     // not guaranteed to receive the paint event before other mouse events,
 219     // so we can not be sure we haven't already copied garbage a bunch of
 220     // times to different parts of the view. For that reason when a blit
 221     // happens and the Component is obscured (the check for obscurity
 222     // is not supported on all platforms and is checked via ComponentPeer
 223     // methods) the ivar repaintAll is set to true. When paint is received
 224     // if repaintAll is true (we previously did a blit) it is set to
 225     // false, and if the clip region is smaller than the viewport
 226     // waitingForRepaint is set to true and a timer is started. When
 227     // the timer fires if waitingForRepaint is true, repaint is invoked.
 228     // In the mean time, if the view is asked to scroll and waitingForRepaint
 229     // is true, a blit will not happen, instead the non-backing store case
 230     // of scrolling will happen, which will reset waitingForRepaint.
 231     // waitingForRepaint is set to false in paint when the clip rect is
 232     // bigger (or equal) to the size of the viewport.
 233     // A Timer is used instead of just a repaint as it appeared to offer
 234     // better performance.
 235 
 236 
 237     /**
 238      * This is set to true in {@code setViewPosition}
 239      * if doing a window blit and the viewport is obscured.
 240      */
 241     private transient boolean repaintAll;
 242 
 243     /**
 244      * This is set to true in paint, if {@code repaintAll}
 245      * is true and the clip rectangle does not match the bounds.
 246      * If true, and scrolling happens the
 247      * repaint manager is not cleared which then allows for the repaint
 248      * previously invoked to succeed.
 249      */
 250     private transient boolean waitingForRepaint;
 251 
 252     /**
 253      * Instead of directly invoking repaint, a {@code Timer}
 254      * is started and when it fires, repaint is invoked.
 255      */
 256     private transient Timer repaintTimer;
 257 
 258     /**
 259      * Set to true in paintView when paint is invoked.
 260      */
 261     private transient boolean inBlitPaint;
 262 
 263     /**
 264      * Whether or not a valid view has been installed.
 265      */
 266     private boolean hasHadValidView;
 267 
 268     /**
 269      * When view is changed we have to synchronize scrollbar values
 270      * with viewport (see the BasicScrollPaneUI#syncScrollPaneWithViewport method).
 271      * This flag allows to invoke that method while ScrollPaneLayout#layoutContainer
 272      * is running.
 273      */
 274     private boolean viewChanged;
 275 
 276     /** Creates a {@code JViewport}. */
 277     public JViewport() {
 278         super();
 279         setLayout(createLayoutManager());
 280         setOpaque(true);
 281         updateUI();
 282         setInheritsPopupMenu(true);
 283     }
 284 
 285 
 286 
 287     /**
 288      * Returns the L&amp;F object that renders this component.
 289      *
 290      * @return a {@code ViewportUI} object
 291      * @since 1.3
 292      */
 293     public ViewportUI getUI() {
 294         return (ViewportUI)ui;
 295     }
 296 
 297 
 298     /**
 299      * Sets the L&amp;F object that renders this component.
 300      *
 301      * @param ui  the {@code ViewportUI} L&amp;F object
 302      * @see UIDefaults#getUI
 303      * @beaninfo
 304      *        bound: true
 305      *       hidden: true
 306      *    attribute: visualUpdate true
 307      *  description: The UI object that implements the Component's LookAndFeel.
 308      * @since 1.3
 309      */
 310     public void setUI(ViewportUI ui) {
 311         super.setUI(ui);
 312     }
 313 
 314 
 315     /**
 316      * Resets the UI property to a value from the current look and feel.
 317      *
 318      * @see JComponent#updateUI
 319      */
 320     public void updateUI() {
 321         setUI((ViewportUI)UIManager.getUI(this));
 322     }
 323 
 324 
 325     /**
 326      * Returns a string that specifies the name of the L&amp;F class
 327      * that renders this component.
 328      *
 329      * @return the string "ViewportUI"
 330      *
 331      * @see JComponent#getUIClassID
 332      * @see UIDefaults#getUI
 333      */
 334     public String getUIClassID() {
 335         return uiClassID;
 336     }
 337 
 338 
 339     /**
 340      * Sets the {@code JViewport}'s one lightweight child,
 341      * which can be {@code null}.
 342      * (Since there is only one child which occupies the entire viewport,
 343      * the {@code constraints} and {@code index}
 344      * arguments are ignored.)
 345      *
 346      * @param child       the lightweight {@code child} of the viewport
 347      * @param constraints the {@code constraints} to be respected
 348      * @param index       the index
 349      * @see #setView
 350      */
 351     protected void addImpl(Component child, Object constraints, int index) {
 352       setView(child);
 353     }
 354 
 355 
 356     /**
 357      * Removes the {@code Viewport}s one lightweight child.
 358      *
 359      * @see #setView
 360      */
 361     public void remove(Component child) {
 362         child.removeComponentListener(viewListener);
 363         super.remove(child);
 364     }
 365 
 366     /**
 367      * Scrolls the view so that {@code Rectangle}
 368      * within the view becomes visible.
 369      * <p>
 370      * This attempts to validate the view before scrolling if the
 371      * view is currently not valid - {@code isValid} returns false.
 372      * To avoid excessive validation when the containment hierarchy is
 373      * being created this will not validate if one of the ancestors does not
 374      * have a peer, or there is no validate root ancestor, or one of the
 375      * ancestors is not a {@code Window} or {@code Applet}.
 376      * <p>
 377      * Note that this method will not scroll outside of the
 378      * valid viewport; for example, if {@code contentRect} is larger
 379      * than the viewport, scrolling will be confined to the viewport's
 380      * bounds.
 381      *
 382      * @param contentRect the {@code Rectangle} to display
 383      * @see JComponent#isValidateRoot
 384      * @see java.awt.Component#isValid
 385      */
 386     public void scrollRectToVisible(Rectangle contentRect) {
 387         Component view = getView();
 388 
 389         if (view == null) {
 390             return;
 391         } else {
 392             if (!view.isValid()) {
 393                 // If the view is not valid, validate. scrollRectToVisible
 394                 // may fail if the view is not valid first, contentRect
 395                 // could be bigger than invalid size.
 396                 validateView();
 397             }
 398             int dx, dy;
 399 
 400             dx = positionAdjustment(getWidth(), contentRect.width, contentRect.x);
 401             dy = positionAdjustment(getHeight(), contentRect.height, contentRect.y);
 402 


 448                     // true, the backing store is blitted.  This fails
 449                     // if between the time setViewPosition is invoked
 450                     // and paint is received another repaint is queued
 451                     // indicating part of the view is invalid. There
 452                     // is no way for JViewport to notice another
 453                     // repaint has occurred and it ends up blitting
 454                     // what is now a dirty region and the repaint is
 455                     // never delivered.
 456                     // It just so happens JTable encounters this
 457                     // behavior by way of scrollRectToVisible, for
 458                     // this reason scrollUnderway is set to false
 459                     // here, which effectively disables the backing
 460                     // store.
 461                     scrollUnderway = false;
 462                 }
 463             }
 464         }
 465     }
 466 
 467     /**
 468      * Ascends the {@code Viewport}'s parents stopping when
 469      * a component is found that returns
 470      * {@code true} to {@code isValidateRoot}.
 471      * If all the {@code Component}'s  parents are visible,
 472      * {@code validate} will then be invoked on it. The
 473      * {@code RepaintManager} is then invoked with
 474      * {@code removeInvalidComponent}. This
 475      * is the synchronous version of a {@code revalidate}.
 476      */
 477     private void validateView() {
 478         Component validateRoot = SwingUtilities.getValidateRoot(this, false);
 479 
 480         if (validateRoot == null) {
 481             return;
 482         }
 483 
 484         // Validate the root.
 485         validateRoot.validate();
 486 
 487         // And let the RepaintManager it does not have to validate from
 488         // validateRoot anymore.
 489         RepaintManager rm = RepaintManager.currentManager(this);
 490 
 491         if (rm != null) {
 492             rm.removeInvalidComponent((JComponent)validateRoot);
 493         }
 494     }
 495 


 532         // ----    |     ->   |---- |
 533         //   +-----+          +-----+
 534         if (childAt <= 0 && childWidth <= parentWidth)   {
 535             return -childAt;
 536         }
 537 
 538         //   +-----+             +-----+
 539         //-------- |      ->   --------|
 540         //   +-----+             +-----+
 541         if (childAt < 0 && childWidth >= parentWidth)    {
 542             return -childAt + parentWidth - childWidth;
 543         }
 544 
 545         return 0;
 546     }
 547 
 548 
 549     /**
 550      * The viewport "scrolls" its child (called the "view") by the
 551      * normal parent/child clipping (typically the view is moved in
 552      * the opposite direction of the scroll).  A non-{@code null} border,
 553      * or non-zero insets, isn't supported, to prevent the geometry
 554      * of this component from becoming complex enough to inhibit
 555      * subclassing.  To create a {@code JViewport} with a border,
 556      * add it to a {@code JPanel} that has a border.
 557      * <p>Note:  If {@code border} is non-{@code null}, this
 558      * method will throw an exception as borders are not supported on
 559      * a {@code JViewPort}.
 560      *
 561      * @param border the {@code Border} to set
 562      * @exception IllegalArgumentException this method is not implemented
 563      */
 564     public final void setBorder(Border border) {
 565         if (border != null) {
 566             throw new IllegalArgumentException("JViewport.setBorder() not supported");
 567         }
 568     }
 569 
 570 
 571     /**
 572      * Returns the insets (border) dimensions as (0,0,0,0), since borders
 573      * are not supported on a {@code JViewport}.
 574      *
 575      * @return a {@code Rectangle} of zero dimension and zero origin
 576      * @see #setBorder
 577      */
 578     public final Insets getInsets() {
 579         return new Insets(0, 0, 0, 0);
 580     }
 581 
 582     /**
 583      * Returns an {@code Insets} object containing this
 584      * {@code JViewport}s inset values.  The passed-in
 585      * {@code Insets} object will be reinitialized, and
 586      * all existing values within this object are overwritten.
 587      *
 588      * @param insets the {@code Insets} object which can be reused
 589      * @return this viewports inset values
 590      * @see #getInsets
 591      * @beaninfo
 592      *   expert: true
 593      */
 594     public final Insets getInsets(Insets insets) {
 595         insets.left = insets.top = insets.right = insets.bottom = 0;
 596         return insets;
 597     }
 598 
 599 
 600     private Graphics getBackingStoreGraphics(Graphics g) {
 601         Graphics bsg = backingStoreImage.getGraphics();
 602         bsg.setColor(g.getColor());
 603         bsg.setFont(g.getFont());
 604         bsg.setClip(g.getClipBounds());
 605         return bsg;
 606     }
 607 
 608 


 611         try {
 612             super.paint(bsg);
 613             g.drawImage(backingStoreImage, 0, 0, this);
 614         } finally {
 615             bsg.dispose();
 616         }
 617     }
 618 
 619     private void paintViaBackingStore(Graphics g, Rectangle oClip) {
 620         Graphics bsg = getBackingStoreGraphics(g);
 621         try {
 622             super.paint(bsg);
 623             g.setClip(oClip);
 624             g.drawImage(backingStoreImage, 0, 0, this);
 625         } finally {
 626             bsg.dispose();
 627         }
 628     }
 629 
 630     /**
 631      * The {@code JViewport} overrides the default implementation of
 632      * this method (in {@code JComponent}) to return false.
 633      * This ensures
 634      * that the drawing machinery will call the {@code Viewport}'s
 635      * {@code paint}
 636      * implementation rather than messaging the {@code JViewport}'s
 637      * children directly.
 638      *
 639      * @return false
 640      */
 641     public boolean isOptimizedDrawingEnabled() {
 642         return false;
 643     }
 644 
 645     /**
 646      * Returns true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE} to cause
 647      * painting to originate from {@code JViewport}, or one of its
 648      * ancestors. Otherwise returns {@code false}.
 649      *
 650      * @return true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE}.
 651      * @see JComponent#isPaintingOrigin()
 652      */
 653     protected boolean isPaintingOrigin() {
 654         return scrollMode == BACKINGSTORE_SCROLL_MODE;
 655     }
 656 
 657 
 658     /**
 659      * Only used by the paint method below.
 660      */
 661     private Point getViewLocation() {
 662         Component view = getView();
 663         if (view != null) {
 664             return view.getLocation();
 665         }
 666         else {
 667             return new Point(0,0);
 668         }
 669     }
 670 
 671     /**
 672      * Depending on whether the {@code backingStore} is enabled,
 673      * either paint the image through the backing store or paint
 674      * just the recently exposed part, using the backing store
 675      * to "blit" the remainder.
 676      * <blockquote>
 677      * The term "blit" is the pronounced version of the PDP-10
 678      * BLT (BLock Transfer) instruction, which copied a block of
 679      * bits. (In case you were curious.)
 680      * </blockquote>
 681      *
 682      * @param g the {@code Graphics} context within which to paint
 683      */
 684     public void paint(Graphics g)
 685     {
 686         int width = getWidth();
 687         int height = getHeight();
 688 
 689         if ((width <= 0) || (height <= 0)) {
 690             return;
 691         }
 692 
 693         if (inBlitPaint) {
 694             // We invoked paint as part of copyArea cleanup, let it through.
 695             super.paint(g);
 696             return;
 697         }
 698 
 699         if (repaintAll) {
 700             repaintAll = false;
 701             Rectangle clipB = g.getClipBounds();
 702             if (clipB.width < getWidth() ||


 804                         // Paint the rest of the view; the part that has just been exposed.
 805                         Rectangle r = viewBounds.intersection(blitPaint);
 806                         bsg.setClip(r);
 807                         super.paint(bsg);
 808 
 809                         // Copy whole of the backing store to g.
 810                         g.drawImage(backingStoreImage, 0, 0, this);
 811                     } finally {
 812                         bsg.dispose();
 813                     }
 814                 }
 815             }
 816         }
 817         lastPaintPosition = getViewLocation();
 818         scrollUnderway = false;
 819     }
 820 
 821 
 822     /**
 823      * Sets the bounds of this viewport.  If the viewport's width
 824      * or height has changed, fire a {@code StateChanged} event.
 825      *
 826      * @param x left edge of the origin
 827      * @param y top edge of the origin
 828      * @param w width in pixels
 829      * @param h height in pixels
 830      *
 831      * @see JComponent#reshape(int, int, int, int)
 832      */
 833     @SuppressWarnings("deprecation")
 834     public void reshape(int x, int y, int w, int h) {
 835         boolean sizeChanged = (getWidth() != w) || (getHeight() != h);
 836         if (sizeChanged) {
 837             backingStoreImage = null;
 838         }
 839         super.reshape(x, y, w, h);
 840         if (sizeChanged || viewChanged) {
 841             viewChanged = false;
 842 
 843             fireStateChanged();
 844         }


 861       * @see #BACKINGSTORE_SCROLL_MODE
 862       * @see #SIMPLE_SCROLL_MODE
 863       *
 864       * @beaninfo
 865       *        bound: false
 866       *  description: Method of moving contents for incremental scrolls.
 867       *         enum: BLIT_SCROLL_MODE JViewport.BLIT_SCROLL_MODE
 868       *               BACKINGSTORE_SCROLL_MODE JViewport.BACKINGSTORE_SCROLL_MODE
 869       *               SIMPLE_SCROLL_MODE JViewport.SIMPLE_SCROLL_MODE
 870       *
 871       * @since 1.3
 872       */
 873     public void setScrollMode(int mode) {
 874         scrollMode = mode;
 875         backingStore = mode == BACKINGSTORE_SCROLL_MODE;
 876     }
 877 
 878     /**
 879       * Returns the current scrolling mode.
 880       *
 881       * @return the {@code scrollMode} property
 882       * @see #setScrollMode
 883       * @since 1.3
 884       */
 885     public int getScrollMode() {
 886         return scrollMode;
 887     }
 888 
 889     /**
 890      * Returns {@code true} if this viewport is maintaining
 891      * an offscreen image of its contents.
 892      *
 893      * @return {@code true} if {@code scrollMode} is
 894      *    {@code BACKINGSTORE_SCROLL_MODE}
 895      *
 896      * @deprecated As of Java 2 platform v1.3, replaced by
 897      *             {@code getScrollMode()}.
 898      */
 899     @Deprecated
 900     public boolean isBackingStoreEnabled() {
 901         return scrollMode == BACKINGSTORE_SCROLL_MODE;
 902     }
 903 
 904 
 905     /**
 906      * If true if this viewport will maintain an offscreen
 907      * image of its contents.  The image is used to reduce the cost
 908      * of small one dimensional changes to the {@code viewPosition}.
 909      * Rather than repainting the entire viewport we use
 910      * {@code Graphics.copyArea} to effect some of the scroll.
 911      *
 912      * @param enabled if true, maintain an offscreen backing store
 913      *
 914      * @deprecated As of Java 2 platform v1.3, replaced by
 915      *             {@code setScrollMode()}.
 916      */
 917     @Deprecated
 918     public void setBackingStoreEnabled(boolean enabled) {
 919         if (enabled) {
 920             setScrollMode(BACKINGSTORE_SCROLL_MODE);
 921         } else {
 922             setScrollMode(BLIT_SCROLL_MODE);
 923         }
 924     }
 925 
 926     private boolean isBlitting() {
 927         Component view = getView();
 928         return (scrollMode == BLIT_SCROLL_MODE) &&
 929                (view instanceof JComponent) && view.isOpaque();
 930     }
 931 
 932 
 933     /**
 934      * Returns the {@code JViewport}'s one child or {@code null}.
 935      *
 936      * @return the viewports child, or {@code null} if none exists
 937      *
 938      * @see #setView
 939      */
 940     public Component getView() {
 941         return (getComponentCount() > 0) ? getComponent(0) : null;
 942     }
 943 
 944     /**
 945      * Sets the {@code JViewport}'s one lightweight child
 946      * ({@code view}), which can be {@code null}.
 947      *
 948      * @param view the viewport's new lightweight child
 949      *
 950      * @see #getView
 951      */
 952     public void setView(Component view) {
 953 
 954         /* Remove the viewport's existing children, if any.
 955          * Note that removeAll() isn't used here because it
 956          * doesn't call remove() (which JViewport overrides).
 957          */
 958         int n = getComponentCount();
 959         for(int i = n - 1; i >= 0; i--) {
 960             remove(getComponent(i));
 961         }
 962 
 963         isViewSizeSet = false;
 964 
 965         if (view != null) {
 966             super.addImpl(view, null, -1);


 971         if (hasHadValidView) {
 972             // Only fire a change if a view has been installed.
 973             fireStateChanged();
 974         }
 975         else if (view != null) {
 976             hasHadValidView = true;
 977         }
 978 
 979         viewChanged = true;
 980 
 981         revalidate();
 982         repaint();
 983     }
 984 
 985 
 986     /**
 987      * If the view's size hasn't been explicitly set, return the
 988      * preferred size, otherwise return the view's current size.
 989      * If there is no view, return 0,0.
 990      *
 991      * @return a {@code Dimension} object specifying the size of the view
 992      */
 993     public Dimension getViewSize() {
 994         Component view = getView();
 995 
 996         if (view == null) {
 997             return new Dimension(0,0);
 998         }
 999         else if (isViewSizeSet) {
1000             return view.getSize();
1001         }
1002         else {
1003             return view.getPreferredSize();
1004         }
1005     }
1006 
1007 
1008     /**
1009      * Sets the size of the view.  A state changed event will be fired.
1010      *
1011      * @param newSize a {@code Dimension} object specifying the new
1012      *          size of the view
1013      */
1014     public void setViewSize(Dimension newSize) {
1015         Component view = getView();
1016         if (view != null) {
1017             Dimension oldSize = view.getSize();
1018             if (!newSize.equals(oldSize)) {
1019                 // scrollUnderway will be true if this is invoked as the
1020                 // result of a validate and setViewPosition was previously
1021                 // invoked.
1022                 scrollUnderway = false;
1023                 view.setSize(newSize);
1024                 isViewSizeSet = true;
1025                 fireStateChanged();
1026             }
1027         }
1028     }
1029 
1030     /**
1031      * Returns the view coordinates that appear in the upper left
1032      * hand corner of the viewport, or 0,0 if there's no view.
1033      *
1034      * @return a {@code Point} object giving the upper left coordinates
1035      */
1036     public Point getViewPosition() {
1037         Component view = getView();
1038         if (view != null) {
1039             Point p = view.getLocation();
1040             p.x = -p.x;
1041             p.y = -p.y;
1042             return p;
1043         }
1044         else {
1045             return new Point(0,0);
1046         }
1047     }
1048 
1049 
1050     /**
1051      * Sets the view coordinates that appear in the upper left
1052      * hand corner of the viewport, does nothing if there's no view.
1053      *
1054      * @param p  a {@code Point} object giving the upper left coordinates
1055      */
1056     public void setViewPosition(Point p)
1057     {
1058         Component view = getView();
1059         if (view == null) {
1060             return;
1061         }
1062 
1063         int oldX, oldY, x = p.x, y = p.y;
1064 
1065         /* Collect the old x,y values for the views location
1066          * and do the song and dance to avoid allocating
1067          * a Rectangle object if we don't have to.
1068          */
1069         if (view instanceof JComponent) {
1070             JComponent c = (JComponent)view;
1071             oldX = c.getX();
1072             oldY = c.getY();
1073         }
1074         else {


1115                 else {
1116                     // The visible region is dirty, no point in doing copyArea
1117                     view.setLocation(newX, newY);
1118                     repaintAll = false;
1119                 }
1120             }
1121             else {
1122                 scrollUnderway = true;
1123                 // This calls setBounds(), and then repaint().
1124                 view.setLocation(newX, newY);
1125                 repaintAll = false;
1126             }
1127             // we must validate the hierarchy to not break the hw/lw mixing
1128             revalidate();
1129             fireStateChanged();
1130         }
1131     }
1132 
1133 
1134     /**
1135      * Returns a rectangle whose origin is {@code getViewPosition}
1136      * and size is {@code getExtentSize}.
1137      * This is the visible part of the view, in view coordinates.
1138      *
1139      * @return a {@code Rectangle} giving the visible part of
1140      *          the view using view coordinates.
1141      */
1142     public Rectangle getViewRect() {
1143         return new Rectangle(getViewPosition(), getExtentSize());
1144     }
1145 
1146 
1147     /**
1148      * Computes the parameters for a blit where the backing store image
1149      * currently contains {@code oldLoc} in the upper left hand corner
1150      * and we're scrolling to {@code newLoc}.
1151      * The parameters are modified
1152      * to return the values required for the blit.
1153      *
1154      * @param dx  the horizontal delta
1155      * @param dy  the vertical delta
1156      * @param blitFrom the {@code Point} we're blitting from
1157      * @param blitTo the {@code Point} we're blitting to
1158      * @param blitSize the {@code Dimension} of the area to blit
1159      * @param blitPaint the area to blit
1160      * @return  true if the parameters are modified and we're ready to blit;
1161      *          false otherwise
1162      */
1163     protected boolean computeBlit(
1164         int dx,
1165         int dy,
1166         Point blitFrom,
1167         Point blitTo,
1168         Dimension blitSize,
1169         Rectangle blitPaint)
1170     {
1171         int dxAbs = Math.abs(dx);
1172         int dyAbs = Math.abs(dy);
1173         Dimension extentSize = getExtentSize();
1174 
1175         if ((dx == 0) && (dy != 0) && (dyAbs < extentSize.height)) {
1176             if (dy < 0) {
1177                 blitFrom.y = -dy;
1178                 blitTo.y = 0;


1210             blitPaint.y = blitFrom.y = blitTo.y = 0;
1211 
1212             blitSize.width = extentSize.width - dxAbs;
1213             blitSize.height = extentSize.height;
1214 
1215             blitPaint.width = dxAbs;
1216             blitPaint.height = extentSize.height;
1217 
1218             return true;
1219         }
1220 
1221         else {
1222             return false;
1223         }
1224     }
1225 
1226 
1227     /**
1228      * Returns the size of the visible part of the view in view coordinates.
1229      *
1230      * @return a {@code Dimension} object giving the size of the view
1231      */
1232     @Transient
1233     public Dimension getExtentSize() {
1234         return getSize();
1235     }
1236 
1237 
1238     /**
1239      * Converts a size in pixel coordinates to view coordinates.
1240      * Subclasses of viewport that support "logical coordinates"
1241      * will override this method.
1242      *
1243      * @param size  a {@code Dimension} object using pixel coordinates
1244      * @return a {@code Dimension} object converted to view coordinates
1245      */
1246     public Dimension toViewCoordinates(Dimension size) {
1247         return new Dimension(size);
1248     }
1249 
1250     /**
1251      * Converts a point in pixel coordinates to view coordinates.
1252      * Subclasses of viewport that support "logical coordinates"
1253      * will override this method.
1254      *
1255      * @param p  a {@code Point} object using pixel coordinates
1256      * @return a {@code Point} object converted to view coordinates
1257      */
1258     public Point toViewCoordinates(Point p) {
1259         return new Point(p);
1260     }
1261 
1262 
1263     /**
1264      * Sets the size of the visible part of the view using view coordinates.
1265      *
1266      * @param newExtent  a {@code Dimension} object specifying
1267      *          the size of the view
1268      */
1269     public void setExtentSize(Dimension newExtent) {
1270         Dimension oldExtent = getExtentSize();
1271         if (!newExtent.equals(oldExtent)) {
1272             setSize(newExtent);
1273             fireStateChanged();
1274         }
1275     }
1276 
1277     /**
1278      * A listener for the view.
1279      * <p>
1280      * <strong>Warning:</strong>
1281      * Serialized objects of this class will not be compatible with
1282      * future Swing releases. The current serialization support is
1283      * appropriate for short term storage or RMI between applications running
1284      * the same version of Swing.  As of 1.4, support for long term storage
1285      * of all JavaBeans&trade;
1286      * has been added to the {@code java.beans} package.
1287      * Please see {@link java.beans.XMLEncoder}.
1288      */
1289     @SuppressWarnings("serial") // Same-version serialization only
1290     protected class ViewListener extends ComponentAdapter implements Serializable
1291     {
1292         public void componentResized(ComponentEvent e) {
1293             fireStateChanged();
1294             revalidate();
1295         }
1296     }
1297 
1298     /**
1299      * Creates a listener for the view.
1300      * @return a {@code ViewListener}
1301      */
1302     protected ViewListener createViewListener() {
1303         return new ViewListener();
1304     }
1305 
1306 
1307     /**
1308      * Subclassers can override this to install a different
1309      * layout manager (or {@code null}) in the constructor.  Returns
1310      * the {@code LayoutManager} to install on the {@code JViewport}.
1311      *
1312      * @return a {@code LayoutManager}
1313      */
1314     protected LayoutManager createLayoutManager() {
1315         return ViewportLayout.SHARED_INSTANCE;
1316     }
1317 
1318 
1319     /**
1320      * Adds a {@code ChangeListener} to the list that is
1321      * notified each time the view's
1322      * size, position, or the viewport's extent size has changed.
1323      *
1324      * @param l the {@code ChangeListener} to add
1325      * @see #removeChangeListener
1326      * @see #setViewPosition
1327      * @see #setViewSize
1328      * @see #setExtentSize
1329      */
1330     public void addChangeListener(ChangeListener l) {
1331         listenerList.add(ChangeListener.class, l);
1332     }
1333 
1334     /**
1335      * Removes a {@code ChangeListener} from the list that's notified each
1336      * time the views size, position, or the viewports extent size
1337      * has changed.
1338      *
1339      * @param l the {@code ChangeListener} to remove
1340      * @see #addChangeListener
1341      */
1342     public void removeChangeListener(ChangeListener l) {
1343         listenerList.remove(ChangeListener.class, l);
1344     }
1345 
1346     /**
1347      * Returns an array of all the {@code ChangeListener}s added
1348      * to this JViewport with addChangeListener().
1349      *
1350      * @return all of the {@code ChangeListener}s added or an empty
1351      *         array if no listeners have been added
1352      * @since 1.4
1353      */
1354     public ChangeListener[] getChangeListeners() {
1355         return listenerList.getListeners(ChangeListener.class);
1356     }
1357 
1358     /**
1359      * Notifies all {@code ChangeListeners} when the views
1360      * size, position, or the viewports extent size has changed.
1361      *
1362      * @see #addChangeListener
1363      * @see #removeChangeListener
1364      * @see EventListenerList
1365      */
1366     protected void fireStateChanged()
1367     {
1368         Object[] listeners = listenerList.getListenerList();
1369         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1370             if (listeners[i] == ChangeListener.class) {
1371                 if (changeEvent == null) {
1372                     changeEvent = new ChangeEvent(this);
1373                 }
1374                 ((ChangeListener)listeners[i + 1]).stateChanged(changeEvent);
1375             }
1376         }
1377     }
1378 
1379     /**
1380      * Always repaint in the parents coordinate system to make sure
1381      * only one paint is performed by the {@code RepaintManager}.
1382      *
1383      * @param     tm   maximum time in milliseconds before update
1384      * @param     x    the {@code x} coordinate (pixels over from left)
1385      * @param     y    the {@code y} coordinate (pixels down from top)
1386      * @param     w    the width
1387      * @param     h   the height
1388      * @see       java.awt.Component#update(java.awt.Graphics)
1389      */
1390     public void repaint(long tm, int x, int y, int w, int h) {
1391         Container parent = getParent();
1392         if(parent != null)
1393             parent.repaint(tm,x+getX(),y+getY(),w,h);
1394         else
1395             super.repaint(tm,x,y,w,h);
1396     }
1397 
1398 
1399     /**
1400      * Returns a string representation of this {@code JViewport}.
1401      * This method
1402      * is intended to be used only for debugging purposes, and the
1403      * content and format of the returned string may vary between
1404      * implementations. The returned string may be empty but may not
1405      * be {@code null}.
1406      *
1407      * @return  a string representation of this {@code JViewport}
1408      */
1409     protected String paramString() {
1410         String isViewSizeSetString = (isViewSizeSet ?
1411                                       "true" : "false");
1412         String lastPaintPositionString = (lastPaintPosition != null ?
1413                                           lastPaintPosition.toString() : "");
1414         String scrollUnderwayString = (scrollUnderway ?
1415                                        "true" : "false");
1416 
1417         return super.paramString() +
1418         ",isViewSizeSet=" + isViewSizeSetString +
1419         ",lastPaintPosition=" + lastPaintPositionString +
1420         ",scrollUnderway=" + scrollUnderwayString;
1421     }
1422 
1423     //
1424     // Following is used when doBlit is true.
1425     //
1426 
1427     /**
1428      * Notifies listeners of a property change. This is subclassed to update
1429      * the {@code windowBlit} property.
1430      * (The {@code putClientProperty} property is final).
1431      *
1432      * @param propertyName a string containing the property name
1433      * @param oldValue the old value of the property
1434      * @param newValue  the new value of the property
1435      */
1436     protected void firePropertyChange(String propertyName, Object oldValue,
1437                                       Object newValue) {
1438         super.firePropertyChange(propertyName, oldValue, newValue);
1439         if (propertyName.equals(EnableWindowBlit)) {
1440             if (newValue != null) {
1441                 setScrollMode(BLIT_SCROLL_MODE);
1442             } else {
1443                 setScrollMode(SIMPLE_SCROLL_MODE);
1444             }
1445         }
1446     }
1447 
1448     /**
1449      * Returns true if the component needs to be completely repainted after
1450      * a blit and a paint is received.


1478 
1479     private Timer createRepaintTimer() {
1480         Timer timer = new Timer(300, new ActionListener() {
1481             public void actionPerformed(ActionEvent ae) {
1482                 // waitingForRepaint will be false if a paint came down
1483                 // with the complete clip rect, in which case we don't
1484                 // have to cause a repaint.
1485                 if (waitingForRepaint) {
1486                     repaint();
1487                 }
1488             }
1489         });
1490         timer.setRepeats(false);
1491         return timer;
1492     }
1493 
1494     /**
1495      * If the repaint manager has a dirty region for the view, the view is
1496      * asked to paint.
1497      *
1498      * @param g  the {@code Graphics} context within which to paint
1499      */
1500     private void flushViewDirtyRegion(Graphics g, Rectangle dirty) {
1501         JComponent view = (JComponent) getView();
1502         if(dirty != null && dirty.width > 0 && dirty.height > 0) {
1503             dirty.x += view.getX();
1504             dirty.y += view.getY();
1505             Rectangle clip = g.getClipBounds();
1506             if (clip == null) {
1507                 // Only happens in 1.2
1508                 g.setClip(0, 0, getWidth(), getHeight());
1509             }
1510             g.clipRect(dirty.x, dirty.y, dirty.width, dirty.height);
1511             clip = g.getClipBounds();
1512             // Only paint the dirty region if it is visible.
1513             if (clip.width > 0 && clip.height > 0) {
1514                 paintView(g);
1515             }
1516         }
1517     }
1518 
1519     /**
1520      * Used when blitting.
1521      *
1522      * @param g  the {@code Graphics} context within which to paint
1523      * @return true if blitting succeeded; otherwise false
1524      */
1525     private boolean windowBlitPaint(Graphics g) {
1526         int width = getWidth();
1527         int height = getHeight();
1528 
1529         if ((width == 0) || (height == 0)) {
1530             return false;
1531         }
1532 
1533         boolean retValue;
1534         RepaintManager rm = RepaintManager.currentManager(this);
1535         JComponent view = (JComponent) getView();
1536 
1537         if (lastPaintPosition == null ||
1538             lastPaintPosition.equals(getViewLocation())) {
1539             paintView(g);
1540             retValue = false;
1541         } else {
1542             // The image was scrolled. Manipulate the backing store and flush


1596         if (g instanceof Graphics2D) {
1597             Graphics2D g2d = (Graphics2D) g;
1598             oldComposite = g2d.getComposite();
1599             g2d.setComposite(AlphaComposite.Src);
1600         }
1601         rm.copyArea(this, g, blitFromX, blitFromY, blitW, blitH, bdx, bdy,
1602                     false);
1603         if (oldComposite != null) {
1604             ((Graphics2D) g).setComposite(oldComposite);
1605         }
1606         // Paint the newly exposed region.
1607         int x = view.getX();
1608         int y = view.getY();
1609         g.translate(x, y);
1610         g.setClip(clipX, clipY, clipW, clipH);
1611         view.paintForceDoubleBuffered(g);
1612         g.translate(-x, -y);
1613     }
1614 
1615     /**
1616      * Called to paint the view, usually when {@code blitPaint}
1617      * can not blit.
1618      *
1619      * @param g the {@code Graphics} context within which to paint
1620      */
1621     private void paintView(Graphics g) {
1622         Rectangle clip = g.getClipBounds();
1623         JComponent view = (JComponent)getView();
1624 
1625         if (view.getWidth() >= getWidth()) {
1626             // Graphics is relative to JViewport, need to map to view's
1627             // coordinates space.
1628             int x = view.getX();
1629             int y = view.getY();
1630             g.translate(x, y);
1631             g.setClip(clip.x - x, clip.y - y, clip.width, clip.height);
1632             view.paintForceDoubleBuffered(g);
1633             g.translate(-x, -y);
1634             g.setClip(clip.x, clip.y, clip.width, clip.height);
1635         }
1636         else {
1637             // To avoid any problems that may result from the viewport being
1638             // bigger than the view we start painting from the viewport.
1639             try {
1640                 inBlitPaint = true;
1641                 paintForceDoubleBuffered(g);
1642             } finally {
1643                 inBlitPaint = false;
1644             }
1645         }
1646     }
1647 
1648     /**
1649      * Returns true if the viewport is not obscured by one of its ancestors,
1650      * or its ancestors children and if the viewport is showing. Blitting
1651      * when the view isn't showing will work,
1652      * or rather {@code copyArea} will work,
1653      * but will not produce the expected behavior.
1654      */
1655     private boolean canUseWindowBlitter() {
1656         if (!isShowing() || (!(getParent() instanceof JComponent) &&
1657                              !(getView() instanceof JComponent))) {
1658             return false;
1659         }
1660         if (isPainting()) {
1661             // We're in the process of painting, don't blit. If we were
1662             // to blit we would draw on top of what we're already drawing,
1663             // so bail.
1664             return false;
1665         }
1666 
1667         Rectangle dirtyRegion = RepaintManager.currentManager(this).
1668                                 getDirtyRegion((JComponent)getParent());
1669 
1670         if (dirtyRegion != null && dirtyRegion.width > 0 &&
1671             dirtyRegion.height > 0) {
1672             // Part of the scrollpane needs to be repainted too, don't blit.


1728 ////////////////
1729 
1730     /**
1731      * Gets the AccessibleContext associated with this JViewport.
1732      * For viewports, the AccessibleContext takes the form of an
1733      * AccessibleJViewport.
1734      * A new AccessibleJViewport instance is created if necessary.
1735      *
1736      * @return an AccessibleJViewport that serves as the
1737      *         AccessibleContext of this JViewport
1738      */
1739     public AccessibleContext getAccessibleContext() {
1740         if (accessibleContext == null) {
1741             accessibleContext = new AccessibleJViewport();
1742         }
1743         return accessibleContext;
1744     }
1745 
1746     /**
1747      * This class implements accessibility support for the
1748      * {@code JViewport} class.  It provides an implementation of the
1749      * Java Accessibility API appropriate to viewport user-interface elements.
1750      * <p>
1751      * <strong>Warning:</strong>
1752      * Serialized objects of this class will not be compatible with
1753      * future Swing releases. The current serialization support is
1754      * appropriate for short term storage or RMI between applications running
1755      * the same version of Swing.  As of 1.4, support for long term storage
1756      * of all JavaBeans&trade;
1757      * has been added to the {@code java.beans} package.
1758      * Please see {@link java.beans.XMLEncoder}.
1759      */
1760     @SuppressWarnings("serial") // Same-version serialization only
1761     protected class AccessibleJViewport extends AccessibleJComponent {
1762         /**
1763          * Get the role of this object.
1764          *
1765          * @return an instance of AccessibleRole describing the role of
1766          * the object
1767          */
1768         public AccessibleRole getAccessibleRole() {
1769             return AccessibleRole.VIEWPORT;
1770         }
1771     } // inner class AccessibleJViewport
1772 }
< prev index next >