1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing;
  27 
  28 import javax.swing.plaf.*;
  29 import javax.swing.border.*;
  30 import javax.swing.event.*;
  31 import javax.accessibility.*;
  32 
  33 import java.awt.Component;
  34 import java.awt.ComponentOrientation;
  35 import java.awt.Rectangle;
  36 import java.awt.Insets;
  37 import java.awt.LayoutManager;
  38 import java.awt.Point;
  39 
  40 import java.io.ObjectOutputStream;
  41 import java.io.IOException;
  42 
  43 import java.beans.PropertyChangeEvent;
  44 import java.beans.PropertyChangeListener;
  45 import java.beans.Transient;
  46 
  47 /**
  48  * Provides a scrollable view of a lightweight component.
  49  * A <code>JScrollPane</code> manages a viewport, optional
  50  * vertical and horizontal scroll bars, and optional row and
  51  * column heading viewports.
  52  * You can find task-oriented documentation of <code>JScrollPane</code> in
  53  *  <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html">How to Use Scroll Panes</a>,
  54  * a section in <em>The Java Tutorial</em>.  Note that
  55  * <code>JScrollPane</code> does not support heavyweight components.
  56  *
  57  * <TABLE STYLE="FLOAT:RIGHT" BORDER="0" SUMMARY="layout">
  58  *    <TR>
  59  *    <TD ALIGN="CENTER">
  60  *      <P STYLE="TEXT-ALIGN:CENTER"><IMG SRC="doc-files/JScrollPane-1.gif"
  61  *      alt="The following text describes this image."
  62  *      WIDTH="256" HEIGHT="248" STYLE="FLOAT:BOTTOM; BORDER:0px">
  63  *    </TD>
  64  *    </TR>
  65  * </TABLE>
  66  * The <code>JViewport</code> provides a window,
  67  * or &quot;viewport&quot; onto a data
  68  * source -- for example, a text file. That data source is the
  69  * &quot;scrollable client&quot; (aka data model) displayed by the
  70  * <code>JViewport</code> view.
  71  * A <code>JScrollPane</code> basically consists of <code>JScrollBar</code>s,
  72  * a <code>JViewport</code>, and the wiring between them,
  73  * as shown in the diagram at right.
  74  * <p>
  75  * In addition to the scroll bars and viewport,
  76  * a <code>JScrollPane</code> can have a
  77  * column header and a row header. Each of these is a
  78  * <code>JViewport</code> object that
  79  * you specify with <code>setRowHeaderView</code>,
  80  * and <code>setColumnHeaderView</code>.
  81  * The column header viewport automatically scrolls left and right, tracking
  82  * the left-right scrolling of the main viewport.
  83  * (It never scrolls vertically, however.)
  84  * The row header acts in a similar fashion.
  85  * <p>
  86  * Where two scroll bars meet, the row header meets the column header,
  87  * or a scroll bar meets one of the headers, both components stop short
  88  * of the corner, leaving a rectangular space which is, by default, empty.
  89  * These spaces can potentially exist in any number of the four corners.
  90  * In the previous diagram, the top right space is present and identified
  91  * by the label "corner component".
  92  * <p>
  93  * Any number of these empty spaces can be replaced by using the
  94  * <code>setCorner</code> method to add a component to a particular corner.
  95  * (Note: The same component cannot be added to multiple corners.)
  96  * This is useful if there's
  97  * some extra decoration or function you'd like to add to the scroll pane.
  98  * The size of each corner component is entirely determined by the size of the
  99  * headers and/or scroll bars that surround it.
 100  * <p>
 101  * A corner component will only be visible if there is an empty space in that
 102  * corner for it to exist in. For example, consider a component set into the
 103  * top right corner of a scroll pane with a column header. If the scroll pane's
 104  * vertical scrollbar is not present, perhaps because the view component hasn't
 105  * grown large enough to require it, then the corner component will not be
 106  * shown (since there is no empty space in that corner created by the meeting
 107  * of the header and vertical scroll bar). Forcing the scroll bar to always be
 108  * shown, using
 109  * <code>setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS)</code>,
 110  * will ensure that the space for the corner component always exists.
 111  * <p>
 112  * To add a border around the main viewport,
 113  * you can use <code>setViewportBorder</code>.
 114  * (Of course, you can also add a border around the whole scroll pane using
 115  * <code>setBorder</code>.)
 116  * <p>
 117  * A common operation to want to do is to set the background color that will
 118  * be used if the main viewport view is smaller than the viewport, or is
 119  * not opaque. This can be accomplished by setting the background color
 120  * of the viewport, via <code>scrollPane.getViewport().setBackground()</code>.
 121  * The reason for setting the color of the viewport and not the scrollpane
 122  * is that by default <code>JViewport</code> is opaque
 123  * which, among other things, means it will completely fill
 124  * in its background using its background color.  Therefore when
 125  * <code>JScrollPane</code> draws its background the viewport will
 126  * usually draw over it.
 127  * <p>
 128  * By default <code>JScrollPane</code> uses <code>ScrollPaneLayout</code>
 129  * to handle the layout of its child Components. <code>ScrollPaneLayout</code>
 130  * determines the size to make the viewport view in one of two ways:
 131  * <ol>
 132  *   <li>If the view implements <code>Scrollable</code>
 133  *       a combination of <code>getPreferredScrollableViewportSize</code>,
 134  *       <code>getScrollableTracksViewportWidth</code> and
 135  *       <code>getScrollableTracksViewportHeight</code>is used, otherwise
 136  *   <li><code>getPreferredSize</code> is used.
 137  * </ol>
 138  * <p>
 139  * <strong>Warning:</strong> Swing is not thread safe. For more
 140  * information see <a
 141  * href="package-summary.html#threading">Swing's Threading
 142  * Policy</a>.
 143  * <p>
 144  * <strong>Warning:</strong>
 145  * Serialized objects of this class will not be compatible with
 146  * future Swing releases. The current serialization support is
 147  * appropriate for short term storage or RMI between applications running
 148  * the same version of Swing.  As of 1.4, support for long term storage
 149  * of all JavaBeans&trade;
 150  * has been added to the <code>java.beans</code> package.
 151  * Please see {@link java.beans.XMLEncoder}.
 152  *
 153  * @see JScrollBar
 154  * @see JViewport
 155  * @see ScrollPaneLayout
 156  * @see Scrollable
 157  * @see Component#getPreferredSize
 158  * @see #setViewportView
 159  * @see #setRowHeaderView
 160  * @see #setColumnHeaderView
 161  * @see #setCorner
 162  * @see #setViewportBorder
 163  *
 164  * @beaninfo
 165  *     attribute: isContainer true
 166  *     attribute: containerDelegate getViewport
 167  *   description: A specialized container that manages a viewport, optional scrollbars and headers
 168  *
 169  * @author Hans Muller
 170  * @since 1.2
 171  */
 172 @SuppressWarnings("serial") // Same-version serialization only
 173 public class JScrollPane extends JComponent implements ScrollPaneConstants, Accessible
 174 {
 175     private Border viewportBorder;
 176 
 177     /**
 178      * @see #getUIClassID
 179      * @see #readObject
 180      */
 181     private static final String uiClassID = "ScrollPaneUI";
 182 
 183     /**
 184      * The display policy for the vertical scrollbar.
 185      * The default is
 186      * <code>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED</code>.
 187      * @see #setVerticalScrollBarPolicy
 188      */
 189     protected int verticalScrollBarPolicy = VERTICAL_SCROLLBAR_AS_NEEDED;
 190 
 191 
 192     /**
 193      * The display policy for the horizontal scrollbar.
 194      * The default is
 195      * <code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED</code>.
 196      * @see #setHorizontalScrollBarPolicy
 197      */
 198     protected int horizontalScrollBarPolicy = HORIZONTAL_SCROLLBAR_AS_NEEDED;
 199 
 200 
 201     /**
 202      * The scrollpane's viewport child.  Default is an empty
 203      * <code>JViewport</code>.
 204      * @see #setViewport
 205      */
 206     protected JViewport viewport;
 207 
 208 
 209     /**
 210      * The scrollpane's vertical scrollbar child.
 211      * Default is a <code>JScrollBar</code>.
 212      * @see #setVerticalScrollBar
 213      */
 214     protected JScrollBar verticalScrollBar;
 215 
 216 
 217     /**
 218      * The scrollpane's horizontal scrollbar child.
 219      * Default is a <code>JScrollBar</code>.
 220      * @see #setHorizontalScrollBar
 221      */
 222     protected JScrollBar horizontalScrollBar;
 223 
 224 
 225     /**
 226      * The row header child.  Default is <code>null</code>.
 227      * @see #setRowHeader
 228      */
 229     protected JViewport rowHeader;
 230 
 231 
 232     /**
 233      * The column header child.  Default is <code>null</code>.
 234      * @see #setColumnHeader
 235      */
 236     protected JViewport columnHeader;
 237 
 238 
 239     /**
 240      * The component to display in the lower left corner.
 241      * Default is <code>null</code>.
 242      * @see #setCorner
 243      */
 244     protected Component lowerLeft;
 245 
 246 
 247     /**
 248      * The component to display in the lower right corner.
 249      * Default is <code>null</code>.
 250      * @see #setCorner
 251      */
 252     protected Component lowerRight;
 253 
 254 
 255     /**
 256      * The component to display in the upper left corner.
 257      * Default is <code>null</code>.
 258      * @see #setCorner
 259      */
 260     protected Component upperLeft;
 261 
 262 
 263     /**
 264      * The component to display in the upper right corner.
 265      * Default is <code>null</code>.
 266      * @see #setCorner
 267      */
 268     protected Component upperRight;
 269 
 270     /*
 271      * State flag for mouse wheel scrolling
 272      */
 273     private boolean wheelScrollState = true;
 274 
 275     /**
 276      * Creates a <code>JScrollPane</code> that displays the view
 277      * component in a viewport
 278      * whose view position can be controlled with a pair of scrollbars.
 279      * The scrollbar policies specify when the scrollbars are displayed,
 280      * For example, if <code>vsbPolicy</code> is
 281      * <code>VERTICAL_SCROLLBAR_AS_NEEDED</code>
 282      * then the vertical scrollbar only appears if the view doesn't fit
 283      * vertically. The available policy settings are listed at
 284      * {@link #setVerticalScrollBarPolicy} and
 285      * {@link #setHorizontalScrollBarPolicy}.
 286      *
 287      * @see #setViewportView
 288      *
 289      * @param view the component to display in the scrollpanes viewport
 290      * @param vsbPolicy an integer that specifies the vertical
 291      *          scrollbar policy
 292      * @param hsbPolicy an integer that specifies the horizontal
 293      *          scrollbar policy
 294      */
 295     public JScrollPane(Component view, int vsbPolicy, int hsbPolicy)
 296     {
 297         setLayout(new ScrollPaneLayout.UIResource());
 298         setVerticalScrollBarPolicy(vsbPolicy);
 299         setHorizontalScrollBarPolicy(hsbPolicy);
 300         setViewport(createViewport());
 301         setVerticalScrollBar(createVerticalScrollBar());
 302         setHorizontalScrollBar(createHorizontalScrollBar());
 303         if (view != null) {
 304             setViewportView(view);
 305         }
 306         setUIProperty("opaque",true);
 307         updateUI();
 308 
 309         if (!this.getComponentOrientation().isLeftToRight()) {
 310             viewport.setViewPosition(new Point(Integer.MAX_VALUE, 0));
 311         }
 312     }
 313 
 314 
 315     /**
 316      * Creates a <code>JScrollPane</code> that displays the
 317      * contents of the specified
 318      * component, where both horizontal and vertical scrollbars appear
 319      * whenever the component's contents are larger than the view.
 320      *
 321      * @see #setViewportView
 322      * @param view the component to display in the scrollpane's viewport
 323      */
 324     public JScrollPane(Component view) {
 325         this(view, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
 326     }
 327 
 328 
 329     /**
 330      * Creates an empty (no viewport view) <code>JScrollPane</code>
 331      * with specified
 332      * scrollbar policies. The available policy settings are listed at
 333      * {@link #setVerticalScrollBarPolicy} and
 334      * {@link #setHorizontalScrollBarPolicy}.
 335      *
 336      * @see #setViewportView
 337      *
 338      * @param vsbPolicy an integer that specifies the vertical
 339      *          scrollbar policy
 340      * @param hsbPolicy an integer that specifies the horizontal
 341      *          scrollbar policy
 342      */
 343     public JScrollPane(int vsbPolicy, int hsbPolicy) {
 344         this(null, vsbPolicy, hsbPolicy);
 345     }
 346 
 347 
 348     /**
 349      * Creates an empty (no viewport view) <code>JScrollPane</code>
 350      * where both horizontal and vertical scrollbars appear when needed.
 351      */
 352     public JScrollPane() {
 353         this(null, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
 354     }
 355 
 356 
 357     /**
 358      * Returns the look and feel (L&amp;F) object that renders this component.
 359      *
 360      * @return the <code>ScrollPaneUI</code> object that renders this
 361      *                          component
 362      * @see #setUI
 363      * @beaninfo
 364      *        bound: true
 365      *       hidden: true
 366      *    attribute: visualUpdate true
 367      *  description: The UI object that implements the Component's LookAndFeel.
 368      */
 369     public ScrollPaneUI getUI() {
 370         return (ScrollPaneUI)ui;
 371     }
 372 
 373 
 374     /**
 375      * Sets the <code>ScrollPaneUI</code> object that provides the
 376      * look and feel (L&amp;F) for this component.
 377      *
 378      * @param ui the <code>ScrollPaneUI</code> L&amp;F object
 379      * @see #getUI
 380      */
 381     public void setUI(ScrollPaneUI ui) {
 382         super.setUI(ui);
 383     }
 384 
 385 
 386     /**
 387      * Replaces the current <code>ScrollPaneUI</code> object with a version
 388      * from the current default look and feel.
 389      * To be called when the default look and feel changes.
 390      *
 391      * @see JComponent#updateUI
 392      * @see UIManager#getUI
 393      */
 394     public void updateUI() {
 395         setUI((ScrollPaneUI)UIManager.getUI(this));
 396     }
 397 
 398 
 399     /**
 400      * Returns the suffix used to construct the name of the L&amp;F class used to
 401      * render this component.
 402      *
 403      * @return the string "ScrollPaneUI"
 404      * @see JComponent#getUIClassID
 405      * @see UIDefaults#getUI
 406      *
 407      * @beaninfo
 408      *    hidden: true
 409      */
 410     public String getUIClassID() {
 411         return uiClassID;
 412     }
 413 
 414 
 415 
 416     /**
 417      * Sets the layout manager for this <code>JScrollPane</code>.
 418      * This method overrides <code>setLayout</code> in
 419      * <code>java.awt.Container</code> to ensure that only
 420      * <code>LayoutManager</code>s which
 421      * are subclasses of <code>ScrollPaneLayout</code> can be used in a
 422      * <code>JScrollPane</code>. If <code>layout</code> is non-null, this
 423      * will invoke <code>syncWithScrollPane</code> on it.
 424      *
 425      * @param layout the specified layout manager
 426      * @exception ClassCastException if layout is not a
 427      *                  <code>ScrollPaneLayout</code>
 428      * @see java.awt.Container#getLayout
 429      * @see java.awt.Container#setLayout
 430      *
 431      * @beaninfo
 432      *    hidden: true
 433      */
 434     public void setLayout(LayoutManager layout) {
 435         if (layout instanceof ScrollPaneLayout) {
 436             super.setLayout(layout);
 437             ((ScrollPaneLayout)layout).syncWithScrollPane(this);
 438         }
 439         else if (layout == null) {
 440             super.setLayout(layout);
 441         }
 442         else {
 443             String s = "layout of JScrollPane must be a ScrollPaneLayout";
 444             throw new ClassCastException(s);
 445         }
 446     }
 447 
 448     /**
 449      * Overridden to return true so that any calls to <code>revalidate</code>
 450      * on any descendants of this <code>JScrollPane</code> will cause the
 451      * entire tree beginning with this <code>JScrollPane</code> to be
 452      * validated.
 453      *
 454      * @return true
 455      * @see java.awt.Container#validate
 456      * @see JComponent#revalidate
 457      * @see JComponent#isValidateRoot
 458      * @see java.awt.Container#isValidateRoot
 459      *
 460      * @beaninfo
 461      *    hidden: true
 462      */
 463     @Override
 464     public boolean isValidateRoot() {
 465         return true;
 466     }
 467 
 468 
 469     /**
 470      * Returns the vertical scroll bar policy value.
 471      * @return the <code>verticalScrollBarPolicy</code> property
 472      * @see #setVerticalScrollBarPolicy
 473      */
 474     public int getVerticalScrollBarPolicy() {
 475         return verticalScrollBarPolicy;
 476     }
 477 
 478 
 479     /**
 480      * Determines when the vertical scrollbar appears in the scrollpane.
 481      * Legal values are:
 482      * <ul>
 483      * <li><code>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED</code>
 484      * <li><code>ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER</code>
 485      * <li><code>ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS</code>
 486      * </ul>
 487      *
 488      * @param policy one of the three values listed above
 489      * @exception IllegalArgumentException if <code>policy</code>
 490      *                          is not one of the legal values shown above
 491      * @see #getVerticalScrollBarPolicy
 492      *
 493      * @beaninfo
 494      *   preferred: true
 495      *       bound: true
 496      * description: The scrollpane vertical scrollbar policy
 497      *        enum: VERTICAL_SCROLLBAR_AS_NEEDED ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
 498      *              VERTICAL_SCROLLBAR_NEVER ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER
 499      *              VERTICAL_SCROLLBAR_ALWAYS ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
 500      */
 501     public void setVerticalScrollBarPolicy(int policy) {
 502         switch (policy) {
 503         case VERTICAL_SCROLLBAR_AS_NEEDED:
 504         case VERTICAL_SCROLLBAR_NEVER:
 505         case VERTICAL_SCROLLBAR_ALWAYS:
 506                 break;
 507         default:
 508             throw new IllegalArgumentException("invalid verticalScrollBarPolicy");
 509         }
 510         int old = verticalScrollBarPolicy;
 511         verticalScrollBarPolicy = policy;
 512         firePropertyChange("verticalScrollBarPolicy", old, policy);
 513         revalidate();
 514         repaint();
 515     }
 516 
 517 
 518     /**
 519      * Returns the horizontal scroll bar policy value.
 520      * @return the <code>horizontalScrollBarPolicy</code> property
 521      * @see #setHorizontalScrollBarPolicy
 522      */
 523     public int getHorizontalScrollBarPolicy() {
 524         return horizontalScrollBarPolicy;
 525     }
 526 
 527 
 528     /**
 529      * Determines when the horizontal scrollbar appears in the scrollpane.
 530      * The options are:<ul>
 531      * <li><code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED</code>
 532      * <li><code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER</code>
 533      * <li><code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS</code>
 534      * </ul>
 535      *
 536      * @param policy one of the three values listed above
 537      * @exception IllegalArgumentException if <code>policy</code>
 538      *                          is not one of the legal values shown above
 539      * @see #getHorizontalScrollBarPolicy
 540      *
 541      * @beaninfo
 542      *   preferred: true
 543      *       bound: true
 544      * description: The scrollpane scrollbar policy
 545      *        enum: HORIZONTAL_SCROLLBAR_AS_NEEDED ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
 546      *              HORIZONTAL_SCROLLBAR_NEVER ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
 547      *              HORIZONTAL_SCROLLBAR_ALWAYS ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
 548      */
 549     public void setHorizontalScrollBarPolicy(int policy) {
 550         switch (policy) {
 551         case HORIZONTAL_SCROLLBAR_AS_NEEDED:
 552         case HORIZONTAL_SCROLLBAR_NEVER:
 553         case HORIZONTAL_SCROLLBAR_ALWAYS:
 554                 break;
 555         default:
 556             throw new IllegalArgumentException("invalid horizontalScrollBarPolicy");
 557         }
 558         int old = horizontalScrollBarPolicy;
 559         horizontalScrollBarPolicy = policy;
 560         firePropertyChange("horizontalScrollBarPolicy", old, policy);
 561         revalidate();
 562         repaint();
 563     }
 564 
 565 
 566     /**
 567      * Returns the <code>Border</code> object that surrounds the viewport.
 568      *
 569      * @return the <code>viewportBorder</code> property
 570      * @see #setViewportBorder
 571      */
 572     public Border getViewportBorder() {
 573         return viewportBorder;
 574     }
 575 
 576 
 577     /**
 578      * Adds a border around the viewport.  Note that the border isn't
 579      * set on the viewport directly, <code>JViewport</code> doesn't support
 580      * the <code>JComponent</code> border property.
 581      * Similarly setting the <code>JScrollPane</code>s
 582      * viewport doesn't affect the <code>viewportBorder</code> property.
 583      * <p>
 584      * The default value of this property is computed by the look
 585      * and feel implementation.
 586      *
 587      * @param viewportBorder the border to be added
 588      * @see #getViewportBorder
 589      * @see #setViewport
 590      *
 591      * @beaninfo
 592      *   preferred: true
 593      *       bound: true
 594      * description: The border around the viewport.
 595      */
 596     public void setViewportBorder(Border viewportBorder) {
 597         Border oldValue = this.viewportBorder;
 598         this.viewportBorder = viewportBorder;
 599         firePropertyChange("viewportBorder", oldValue, viewportBorder);
 600     }
 601 
 602 
 603     /**
 604      * Returns the bounds of the viewport's border.
 605      *
 606      * @return a <code>Rectangle</code> object specifying the viewport border
 607      */
 608     public Rectangle getViewportBorderBounds()
 609     {
 610         Rectangle borderR = new Rectangle(getSize());
 611 
 612         Insets insets = getInsets();
 613         borderR.x = insets.left;
 614         borderR.y = insets.top;
 615         borderR.width -= insets.left + insets.right;
 616         borderR.height -= insets.top + insets.bottom;
 617 
 618         boolean leftToRight = SwingUtilities.isLeftToRight(this);
 619 
 620         /* If there's a visible column header remove the space it
 621          * needs from the top of borderR.
 622          */
 623 
 624         JViewport colHead = getColumnHeader();
 625         if ((colHead != null) && (colHead.isVisible())) {
 626             int colHeadHeight = colHead.getHeight();
 627             borderR.y += colHeadHeight;
 628             borderR.height -= colHeadHeight;
 629         }
 630 
 631         /* If there's a visible row header remove the space it needs
 632          * from the left of borderR.
 633          */
 634 
 635         JViewport rowHead = getRowHeader();
 636         if ((rowHead != null) && (rowHead.isVisible())) {
 637             int rowHeadWidth = rowHead.getWidth();
 638             if ( leftToRight ) {
 639                 borderR.x += rowHeadWidth;
 640             }
 641             borderR.width -= rowHeadWidth;
 642         }
 643 
 644         /* If there's a visible vertical scrollbar remove the space it needs
 645          * from the width of borderR.
 646          */
 647         JScrollBar vsb = getVerticalScrollBar();
 648         if ((vsb != null) && (vsb.isVisible())) {
 649             int vsbWidth = vsb.getWidth();
 650             if ( !leftToRight ) {
 651                 borderR.x += vsbWidth;
 652             }
 653             borderR.width -= vsbWidth;
 654         }
 655 
 656         /* If there's a visible horizontal scrollbar remove the space it needs
 657          * from the height of borderR.
 658          */
 659         JScrollBar hsb = getHorizontalScrollBar();
 660         if ((hsb != null) && (hsb.isVisible())) {
 661             borderR.height -= hsb.getHeight();
 662         }
 663 
 664         return borderR;
 665     }
 666 
 667 
 668     /**
 669      * By default <code>JScrollPane</code> creates scrollbars
 670      * that are instances
 671      * of this class.  <code>Scrollbar</code> overrides the
 672      * <code>getUnitIncrement</code> and <code>getBlockIncrement</code>
 673      * methods so that, if the viewport's view is a <code>Scrollable</code>,
 674      * the view is asked to compute these values. Unless
 675      * the unit/block increment have been explicitly set.
 676      * <p>
 677      * <strong>Warning:</strong>
 678      * Serialized objects of this class will not be compatible with
 679      * future Swing releases. The current serialization support is
 680      * appropriate for short term storage or RMI between applications running
 681      * the same version of Swing.  As of 1.4, support for long term storage
 682      * of all JavaBeans&trade;
 683      * has been added to the <code>java.beans</code> package.
 684      * Please see {@link java.beans.XMLEncoder}.
 685      *
 686      * @see Scrollable
 687      * @see JScrollPane#createVerticalScrollBar
 688      * @see JScrollPane#createHorizontalScrollBar
 689      */
 690     @SuppressWarnings("serial") // Same-version serialization only
 691     protected class ScrollBar extends JScrollBar implements UIResource
 692     {
 693         /**
 694          * Set to true when the unit increment has been explicitly set.
 695          * If this is false the viewport's view is obtained and if it
 696          * is an instance of <code>Scrollable</code> the unit increment
 697          * from it is used.
 698          */
 699         private boolean unitIncrementSet;
 700         /**
 701          * Set to true when the block increment has been explicitly set.
 702          * If this is false the viewport's view is obtained and if it
 703          * is an instance of <code>Scrollable</code> the block increment
 704          * from it is used.
 705          */
 706         private boolean blockIncrementSet;
 707 
 708         /**
 709          * Creates a scrollbar with the specified orientation.
 710          * The options are:
 711          * <ul>
 712          * <li><code>ScrollPaneConstants.VERTICAL</code>
 713          * <li><code>ScrollPaneConstants.HORIZONTAL</code>
 714          * </ul>
 715          *
 716          * @param orientation  an integer specifying one of the legal
 717          *      orientation values shown above
 718          * @since 1.4
 719          */
 720         public ScrollBar(int orientation) {
 721             super(orientation);
 722             this.putClientProperty("JScrollBar.fastWheelScrolling",
 723                                    Boolean.TRUE);
 724         }
 725 
 726         /**
 727          * Messages super to set the value, and resets the
 728          * <code>unitIncrementSet</code> instance variable to true.
 729          *
 730          * @param unitIncrement the new unit increment value, in pixels
 731          */
 732         public void setUnitIncrement(int unitIncrement) {
 733             unitIncrementSet = true;
 734             this.putClientProperty("JScrollBar.fastWheelScrolling", null);
 735             super.setUnitIncrement(unitIncrement);
 736         }
 737 
 738         /**
 739          * Computes the unit increment for scrolling if the viewport's
 740          * view is a <code>Scrollable</code> object.
 741          * Otherwise return <code>super.getUnitIncrement</code>.
 742          *
 743          * @param direction less than zero to scroll up/left,
 744          *      greater than zero for down/right
 745          * @return an integer, in pixels, containing the unit increment
 746          * @see Scrollable#getScrollableUnitIncrement
 747          */
 748         public int getUnitIncrement(int direction) {
 749             JViewport vp = getViewport();
 750             if (!unitIncrementSet && (vp != null) &&
 751                 (vp.getView() instanceof Scrollable)) {
 752                 Scrollable view = (Scrollable)(vp.getView());
 753                 Rectangle vr = vp.getViewRect();
 754                 return view.getScrollableUnitIncrement(vr, getOrientation(), direction);
 755             }
 756             else {
 757                 return super.getUnitIncrement(direction);
 758             }
 759         }
 760 
 761         /**
 762          * Messages super to set the value, and resets the
 763          * <code>blockIncrementSet</code> instance variable to true.
 764          *
 765          * @param blockIncrement the new block increment value, in pixels
 766          */
 767         public void setBlockIncrement(int blockIncrement) {
 768             blockIncrementSet = true;
 769             this.putClientProperty("JScrollBar.fastWheelScrolling", null);
 770             super.setBlockIncrement(blockIncrement);
 771         }
 772 
 773         /**
 774          * Computes the block increment for scrolling if the viewport's
 775          * view is a <code>Scrollable</code> object.  Otherwise
 776          * the <code>blockIncrement</code> equals the viewport's width
 777          * or height.  If there's no viewport return
 778          * <code>super.getBlockIncrement</code>.
 779          *
 780          * @param direction less than zero to scroll up/left,
 781          *      greater than zero for down/right
 782          * @return an integer, in pixels, containing the block increment
 783          * @see Scrollable#getScrollableBlockIncrement
 784          */
 785         public int getBlockIncrement(int direction) {
 786             JViewport vp = getViewport();
 787             if (blockIncrementSet || vp == null) {
 788                 return super.getBlockIncrement(direction);
 789             }
 790             else if (vp.getView() instanceof Scrollable) {
 791                 Scrollable view = (Scrollable)(vp.getView());
 792                 Rectangle vr = vp.getViewRect();
 793                 return view.getScrollableBlockIncrement(vr, getOrientation(), direction);
 794             }
 795             else if (getOrientation() == VERTICAL) {
 796                 return vp.getExtentSize().height;
 797             }
 798             else {
 799                 return vp.getExtentSize().width;
 800             }
 801         }
 802 
 803     }
 804 
 805 
 806     /**
 807      * Returns a <code>JScrollPane.ScrollBar</code> by default.
 808      * Subclasses may override this method to force <code>ScrollPaneUI</code>
 809      * implementations to use a <code>JScrollBar</code> subclass.
 810      * Used by <code>ScrollPaneUI</code> implementations to
 811      * create the horizontal scrollbar.
 812      *
 813      * @return a <code>JScrollBar</code> with a horizontal orientation
 814      * @see JScrollBar
 815      */
 816     public JScrollBar createHorizontalScrollBar() {
 817         return new ScrollBar(JScrollBar.HORIZONTAL);
 818     }
 819 
 820 
 821     /**
 822      * Returns the horizontal scroll bar that controls the viewport's
 823      * horizontal view position.
 824      *
 825      * @return the <code>horizontalScrollBar</code> property
 826      * @see #setHorizontalScrollBar
 827      */
 828     @Transient
 829     public JScrollBar getHorizontalScrollBar() {
 830         return horizontalScrollBar;
 831     }
 832 
 833 
 834     /**
 835      * Adds the scrollbar that controls the viewport's horizontal view
 836      * position to the scrollpane.
 837      * This is usually unnecessary, as <code>JScrollPane</code> creates
 838      * horizontal and vertical scrollbars by default.
 839      *
 840      * @param horizontalScrollBar the horizontal scrollbar to be added
 841      * @see #createHorizontalScrollBar
 842      * @see #getHorizontalScrollBar
 843      *
 844      * @beaninfo
 845      *        expert: true
 846      *         bound: true
 847      *   description: The horizontal scrollbar.
 848      */
 849     public void setHorizontalScrollBar(JScrollBar horizontalScrollBar) {
 850         JScrollBar old = getHorizontalScrollBar();
 851         this.horizontalScrollBar = horizontalScrollBar;
 852         if (horizontalScrollBar != null) {
 853             add(horizontalScrollBar, HORIZONTAL_SCROLLBAR);
 854         }
 855         else if (old != null) {
 856             remove(old);
 857         }
 858         firePropertyChange("horizontalScrollBar", old, horizontalScrollBar);
 859 
 860         revalidate();
 861         repaint();
 862     }
 863 
 864 
 865     /**
 866      * Returns a <code>JScrollPane.ScrollBar</code> by default.  Subclasses
 867      * may override this method to force <code>ScrollPaneUI</code>
 868      * implementations to use a <code>JScrollBar</code> subclass.
 869      * Used by <code>ScrollPaneUI</code> implementations to create the
 870      * vertical scrollbar.
 871      *
 872      * @return a <code>JScrollBar</code> with a vertical orientation
 873      * @see JScrollBar
 874      */
 875     public JScrollBar createVerticalScrollBar() {
 876         return new ScrollBar(JScrollBar.VERTICAL);
 877     }
 878 
 879 
 880     /**
 881      * Returns the vertical scroll bar that controls the viewports
 882      * vertical view position.
 883      *
 884      * @return the <code>verticalScrollBar</code> property
 885      * @see #setVerticalScrollBar
 886      */
 887     @Transient
 888     public JScrollBar getVerticalScrollBar() {
 889         return verticalScrollBar;
 890     }
 891 
 892 
 893     /**
 894      * Adds the scrollbar that controls the viewports vertical view position
 895      * to the scrollpane.  This is usually unnecessary,
 896      * as <code>JScrollPane</code> creates vertical and
 897      * horizontal scrollbars by default.
 898      *
 899      * @param verticalScrollBar the new vertical scrollbar to be added
 900      * @see #createVerticalScrollBar
 901      * @see #getVerticalScrollBar
 902      *
 903      * @beaninfo
 904      *        expert: true
 905      *         bound: true
 906      *   description: The vertical scrollbar.
 907      */
 908     public void setVerticalScrollBar(JScrollBar verticalScrollBar) {
 909         JScrollBar old = getVerticalScrollBar();
 910         this.verticalScrollBar = verticalScrollBar;
 911         add(verticalScrollBar, VERTICAL_SCROLLBAR);
 912         firePropertyChange("verticalScrollBar", old, verticalScrollBar);
 913 
 914         revalidate();
 915         repaint();
 916     }
 917 
 918 
 919     /**
 920      * Returns a new <code>JViewport</code> by default.
 921      * Used to create the
 922      * viewport (as needed) in <code>setViewportView</code>,
 923      * <code>setRowHeaderView</code>, and <code>setColumnHeaderView</code>.
 924      * Subclasses may override this method to return a subclass of
 925      * <code>JViewport</code>.
 926      *
 927      * @return a new <code>JViewport</code>
 928      */
 929     protected JViewport createViewport() {
 930         return new JViewport();
 931     }
 932 
 933 
 934     /**
 935      * Returns the current <code>JViewport</code>.
 936      *
 937      * @see #setViewport
 938      * @return the <code>viewport</code> property
 939      */
 940     public JViewport getViewport() {
 941         return viewport;
 942     }
 943 
 944 
 945     /**
 946      * Removes the old viewport (if there is one); forces the
 947      * viewPosition of the new viewport to be in the +x,+y quadrant;
 948      * syncs up the row and column headers (if there are any) with the
 949      * new viewport; and finally syncs the scrollbars and
 950      * headers with the new viewport.
 951      * <p>
 952      * Most applications will find it more convenient to use
 953      * <code>setViewportView</code>
 954      * to add a viewport and a view to the scrollpane.
 955      *
 956      * @param viewport the new viewport to be used; if viewport is
 957      *          <code>null</code>, the old viewport is still removed
 958      *          and the new viewport is set to <code>null</code>
 959      * @see #createViewport
 960      * @see #getViewport
 961      * @see #setViewportView
 962      *
 963      * @beaninfo
 964      *       expert: true
 965      *        bound: true
 966      *    attribute: visualUpdate true
 967      *  description: The viewport child for this scrollpane
 968      *
 969      */
 970     public void setViewport(JViewport viewport) {
 971         JViewport old = getViewport();
 972         this.viewport = viewport;
 973         if (viewport != null) {
 974             add(viewport, VIEWPORT);
 975         }
 976         else if (old != null) {
 977             remove(old);
 978         }
 979         firePropertyChange("viewport", old, viewport);
 980 
 981         if (accessibleContext != null) {
 982             ((AccessibleJScrollPane)accessibleContext).resetViewPort();
 983         }
 984 
 985         revalidate();
 986         repaint();
 987     }
 988 
 989 
 990     /**
 991      * Creates a viewport if necessary and then sets its view.  Applications
 992      * that don't provide the view directly to the <code>JScrollPane</code>
 993      * constructor
 994      * should use this method to specify the scrollable child that's going
 995      * to be displayed in the scrollpane. For example:
 996      * <pre>
 997      * JScrollPane scrollpane = new JScrollPane();
 998      * scrollpane.setViewportView(myBigComponentToScroll);
 999      * </pre>
1000      * Applications should not add children directly to the scrollpane.
1001      *
1002      * @param view the component to add to the viewport
1003      * @see #setViewport
1004      * @see JViewport#setView
1005      */
1006     public void setViewportView(Component view) {
1007         if (getViewport() == null) {
1008             setViewport(createViewport());
1009         }
1010         getViewport().setView(view);
1011     }
1012 
1013 
1014 
1015     /**
1016      * Returns the row header.
1017      * @return the <code>rowHeader</code> property
1018      * @see #setRowHeader
1019      */
1020     @Transient
1021     public JViewport getRowHeader() {
1022         return rowHeader;
1023     }
1024 
1025 
1026     /**
1027      * Removes the old rowHeader, if it exists; if the new rowHeader
1028      * isn't <code>null</code>, syncs the y coordinate of its
1029      * viewPosition with
1030      * the viewport (if there is one) and then adds it to the scroll pane.
1031      * <p>
1032      * Most applications will find it more convenient to use
1033      * <code>setRowHeaderView</code>
1034      * to add a row header component and its viewport to the scroll pane.
1035      *
1036      * @param rowHeader the new row header to be used; if <code>null</code>
1037      *          the old row header is still removed and the new rowHeader
1038      *          is set to <code>null</code>
1039      * @see #getRowHeader
1040      * @see #setRowHeaderView
1041      *
1042      * @beaninfo
1043      *        bound: true
1044      *       expert: true
1045      *  description: The row header child for this scrollpane
1046      */
1047     public void setRowHeader(JViewport rowHeader) {
1048         JViewport old = getRowHeader();
1049         this.rowHeader = rowHeader;
1050         if (rowHeader != null) {
1051             add(rowHeader, ROW_HEADER);
1052         }
1053         else if (old != null) {
1054             remove(old);
1055         }
1056         firePropertyChange("rowHeader", old, rowHeader);
1057         revalidate();
1058         repaint();
1059     }
1060 
1061 
1062     /**
1063      * Creates a row-header viewport if necessary, sets
1064      * its view and then adds the row-header viewport
1065      * to the scrollpane.  For example:
1066      * <pre>
1067      * JScrollPane scrollpane = new JScrollPane();
1068      * scrollpane.setViewportView(myBigComponentToScroll);
1069      * scrollpane.setRowHeaderView(myBigComponentsRowHeader);
1070      * </pre>
1071      *
1072      * @see #setRowHeader
1073      * @see JViewport#setView
1074      * @param view the component to display as the row header
1075      */
1076     public void setRowHeaderView(Component view) {
1077         if (getRowHeader() == null) {
1078             setRowHeader(createViewport());
1079         }
1080         getRowHeader().setView(view);
1081     }
1082 
1083 
1084 
1085     /**
1086      * Returns the column header.
1087      * @return the <code>columnHeader</code> property
1088      * @see #setColumnHeader
1089      */
1090     @Transient
1091     public JViewport getColumnHeader() {
1092         return columnHeader;
1093     }
1094 
1095 
1096     /**
1097      * Removes the old columnHeader, if it exists; if the new columnHeader
1098      * isn't <code>null</code>, syncs the x coordinate of its viewPosition
1099      * with the viewport (if there is one) and then adds it to the scroll pane.
1100      * <p>
1101      * Most applications will find it more convenient to use
1102      * <code>setColumnHeaderView</code>
1103      * to add a column header component and its viewport to the scroll pane.
1104      *
1105      * @see #getColumnHeader
1106      * @see #setColumnHeaderView
1107      *
1108      * @beaninfo
1109      *        bound: true
1110      *  description: The column header child for this scrollpane
1111      *    attribute: visualUpdate true
1112      */
1113     public void setColumnHeader(JViewport columnHeader) {
1114         JViewport old = getColumnHeader();
1115         this.columnHeader = columnHeader;
1116         if (columnHeader != null) {
1117             add(columnHeader, COLUMN_HEADER);
1118         }
1119         else if (old != null) {
1120             remove(old);
1121         }
1122         firePropertyChange("columnHeader", old, columnHeader);
1123 
1124         revalidate();
1125         repaint();
1126     }
1127 
1128 
1129 
1130     /**
1131      * Creates a column-header viewport if necessary, sets
1132      * its view, and then adds the column-header viewport
1133      * to the scrollpane.  For example:
1134      * <pre>
1135      * JScrollPane scrollpane = new JScrollPane();
1136      * scrollpane.setViewportView(myBigComponentToScroll);
1137      * scrollpane.setColumnHeaderView(myBigComponentsColumnHeader);
1138      * </pre>
1139      *
1140      * @see #setColumnHeader
1141      * @see JViewport#setView
1142      *
1143      * @param view the component to display as the column header
1144      */
1145     public void setColumnHeaderView(Component view) {
1146         if (getColumnHeader() == null) {
1147             setColumnHeader(createViewport());
1148         }
1149         getColumnHeader().setView(view);
1150     }
1151 
1152 
1153     /**
1154      * Returns the component at the specified corner. The
1155      * <code>key</code> value specifying the corner is one of:
1156      * <ul>
1157      * <li>ScrollPaneConstants.LOWER_LEFT_CORNER
1158      * <li>ScrollPaneConstants.LOWER_RIGHT_CORNER
1159      * <li>ScrollPaneConstants.UPPER_LEFT_CORNER
1160      * <li>ScrollPaneConstants.UPPER_RIGHT_CORNER
1161      * <li>ScrollPaneConstants.LOWER_LEADING_CORNER
1162      * <li>ScrollPaneConstants.LOWER_TRAILING_CORNER
1163      * <li>ScrollPaneConstants.UPPER_LEADING_CORNER
1164      * <li>ScrollPaneConstants.UPPER_TRAILING_CORNER
1165      * </ul>
1166      *
1167      * @param key one of the values as shown above
1168      * @return the corner component (which may be <code>null</code>)
1169      *         identified by the given key, or <code>null</code>
1170      *         if the key is invalid
1171      * @see #setCorner
1172      */
1173     public Component getCorner(String key) {
1174         boolean isLeftToRight = getComponentOrientation().isLeftToRight();
1175         if (key.equals(LOWER_LEADING_CORNER)) {
1176             key = isLeftToRight ? LOWER_LEFT_CORNER : LOWER_RIGHT_CORNER;
1177         } else if (key.equals(LOWER_TRAILING_CORNER)) {
1178             key = isLeftToRight ? LOWER_RIGHT_CORNER : LOWER_LEFT_CORNER;
1179         } else if (key.equals(UPPER_LEADING_CORNER)) {
1180             key = isLeftToRight ? UPPER_LEFT_CORNER : UPPER_RIGHT_CORNER;
1181         } else if (key.equals(UPPER_TRAILING_CORNER)) {
1182             key = isLeftToRight ? UPPER_RIGHT_CORNER : UPPER_LEFT_CORNER;
1183         }
1184         if (key.equals(LOWER_LEFT_CORNER)) {
1185             return lowerLeft;
1186         }
1187         else if (key.equals(LOWER_RIGHT_CORNER)) {
1188             return lowerRight;
1189         }
1190         else if (key.equals(UPPER_LEFT_CORNER)) {
1191             return upperLeft;
1192         }
1193         else if (key.equals(UPPER_RIGHT_CORNER)) {
1194             return upperRight;
1195         }
1196         else {
1197             return null;
1198         }
1199     }
1200 
1201 
1202     /**
1203      * Adds a child that will appear in one of the scroll panes
1204      * corners, if there's room.   For example with both scrollbars
1205      * showing (on the right and bottom edges of the scrollpane)
1206      * the lower left corner component will be shown in the space
1207      * between ends of the two scrollbars. Legal values for
1208      * the <b>key</b> are:
1209      * <ul>
1210      * <li>ScrollPaneConstants.LOWER_LEFT_CORNER
1211      * <li>ScrollPaneConstants.LOWER_RIGHT_CORNER
1212      * <li>ScrollPaneConstants.UPPER_LEFT_CORNER
1213      * <li>ScrollPaneConstants.UPPER_RIGHT_CORNER
1214      * <li>ScrollPaneConstants.LOWER_LEADING_CORNER
1215      * <li>ScrollPaneConstants.LOWER_TRAILING_CORNER
1216      * <li>ScrollPaneConstants.UPPER_LEADING_CORNER
1217      * <li>ScrollPaneConstants.UPPER_TRAILING_CORNER
1218      * </ul>
1219      * <p>
1220      * Although "corner" doesn't match any beans property
1221      * signature, <code>PropertyChange</code> events are generated with the
1222      * property name set to the corner key.
1223      *
1224      * @param key identifies which corner the component will appear in
1225      * @param corner one of the following components:
1226      * <ul>
1227      * <li>lowerLeft
1228      * <li>lowerRight
1229      * <li>upperLeft
1230      * <li>upperRight
1231      * </ul>
1232      * @exception IllegalArgumentException if corner key is invalid
1233      */
1234     public void setCorner(String key, Component corner)
1235     {
1236         Component old;
1237         boolean isLeftToRight = getComponentOrientation().isLeftToRight();
1238         if (key.equals(LOWER_LEADING_CORNER)) {
1239             key = isLeftToRight ? LOWER_LEFT_CORNER : LOWER_RIGHT_CORNER;
1240         } else if (key.equals(LOWER_TRAILING_CORNER)) {
1241             key = isLeftToRight ? LOWER_RIGHT_CORNER : LOWER_LEFT_CORNER;
1242         } else if (key.equals(UPPER_LEADING_CORNER)) {
1243             key = isLeftToRight ? UPPER_LEFT_CORNER : UPPER_RIGHT_CORNER;
1244         } else if (key.equals(UPPER_TRAILING_CORNER)) {
1245             key = isLeftToRight ? UPPER_RIGHT_CORNER : UPPER_LEFT_CORNER;
1246         }
1247         if (key.equals(LOWER_LEFT_CORNER)) {
1248             old = lowerLeft;
1249             lowerLeft = corner;
1250         }
1251         else if (key.equals(LOWER_RIGHT_CORNER)) {
1252             old = lowerRight;
1253             lowerRight = corner;
1254         }
1255         else if (key.equals(UPPER_LEFT_CORNER)) {
1256             old = upperLeft;
1257             upperLeft = corner;
1258         }
1259         else if (key.equals(UPPER_RIGHT_CORNER)) {
1260             old = upperRight;
1261             upperRight = corner;
1262         }
1263         else {
1264             throw new IllegalArgumentException("invalid corner key");
1265         }
1266         if (old != null) {
1267             remove(old);
1268         }
1269         if (corner != null) {
1270             add(corner, key);
1271         }
1272         firePropertyChange(key, old, corner);
1273         revalidate();
1274         repaint();
1275     }
1276 
1277     /**
1278      * Sets the orientation for the vertical and horizontal
1279      * scrollbars as determined by the
1280      * <code>ComponentOrientation</code> argument.
1281      *
1282      * @param  co one of the following values:
1283      * <ul>
1284      * <li>java.awt.ComponentOrientation.LEFT_TO_RIGHT
1285      * <li>java.awt.ComponentOrientation.RIGHT_TO_LEFT
1286      * <li>java.awt.ComponentOrientation.UNKNOWN
1287      * </ul>
1288      * @see java.awt.ComponentOrientation
1289      */
1290     public void setComponentOrientation( ComponentOrientation co ) {
1291         super.setComponentOrientation( co );
1292         if( verticalScrollBar != null )
1293             verticalScrollBar.setComponentOrientation( co );
1294         if( horizontalScrollBar != null )
1295             horizontalScrollBar.setComponentOrientation( co );
1296     }
1297 
1298     /**
1299      * Indicates whether or not scrolling will take place in response to the
1300      * mouse wheel.  Wheel scrolling is enabled by default.
1301      *
1302      * @see #setWheelScrollingEnabled
1303      * @since 1.4
1304      * @beaninfo
1305      *       bound: true
1306      * description: Flag for enabling/disabling mouse wheel scrolling
1307      */
1308     public boolean isWheelScrollingEnabled() {return wheelScrollState;}
1309 
1310     /**
1311      * Enables/disables scrolling in response to movement of the mouse wheel.
1312      * Wheel scrolling is enabled by default.
1313      *
1314      * @param handleWheel   <code>true</code> if scrolling should be done
1315      *                      automatically for a MouseWheelEvent,
1316      *                      <code>false</code> otherwise.
1317      * @see #isWheelScrollingEnabled
1318      * @see java.awt.event.MouseWheelEvent
1319      * @see java.awt.event.MouseWheelListener
1320      * @since 1.4
1321      * @beaninfo
1322      *       bound: true
1323      * description: Flag for enabling/disabling mouse wheel scrolling
1324      */
1325     public void setWheelScrollingEnabled(boolean handleWheel) {
1326         boolean old = wheelScrollState;
1327         wheelScrollState = handleWheel;
1328         firePropertyChange("wheelScrollingEnabled", old, handleWheel);
1329     }
1330 
1331     /**
1332      * See <code>readObject</code> and <code>writeObject</code> in
1333      * <code>JComponent</code> for more
1334      * information about serialization in Swing.
1335      */
1336     private void writeObject(ObjectOutputStream s) throws IOException {
1337         s.defaultWriteObject();
1338         if (getUIClassID().equals(uiClassID)) {
1339             byte count = JComponent.getWriteObjCounter(this);
1340             JComponent.setWriteObjCounter(this, --count);
1341             if (count == 0 && ui != null) {
1342                 ui.installUI(this);
1343             }
1344         }
1345     }
1346 
1347 
1348     /**
1349      * Returns a string representation of this <code>JScrollPane</code>.
1350      * This method
1351      * is intended to be used only for debugging purposes, and the
1352      * content and format of the returned string may vary between
1353      * implementations. The returned string may be empty but may not
1354      * be <code>null</code>.
1355      *
1356      * @return  a string representation of this <code>JScrollPane</code>.
1357      */
1358     protected String paramString() {
1359         String viewportBorderString = (viewportBorder != null ?
1360                                        viewportBorder.toString() : "");
1361         String viewportString = (viewport != null ?
1362                                  viewport.toString() : "");
1363         String verticalScrollBarPolicyString;
1364         if (verticalScrollBarPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) {
1365             verticalScrollBarPolicyString = "VERTICAL_SCROLLBAR_AS_NEEDED";
1366         } else if (verticalScrollBarPolicy == VERTICAL_SCROLLBAR_NEVER) {
1367             verticalScrollBarPolicyString = "VERTICAL_SCROLLBAR_NEVER";
1368         } else if (verticalScrollBarPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
1369             verticalScrollBarPolicyString = "VERTICAL_SCROLLBAR_ALWAYS";
1370         } else verticalScrollBarPolicyString = "";
1371         String horizontalScrollBarPolicyString;
1372         if (horizontalScrollBarPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED) {
1373             horizontalScrollBarPolicyString = "HORIZONTAL_SCROLLBAR_AS_NEEDED";
1374         } else if (horizontalScrollBarPolicy == HORIZONTAL_SCROLLBAR_NEVER) {
1375             horizontalScrollBarPolicyString = "HORIZONTAL_SCROLLBAR_NEVER";
1376         } else if (horizontalScrollBarPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
1377             horizontalScrollBarPolicyString = "HORIZONTAL_SCROLLBAR_ALWAYS";
1378         } else horizontalScrollBarPolicyString = "";
1379         String horizontalScrollBarString = (horizontalScrollBar != null ?
1380                                             horizontalScrollBar.toString()
1381                                             : "");
1382         String verticalScrollBarString = (verticalScrollBar != null ?
1383                                           verticalScrollBar.toString() : "");
1384         String columnHeaderString = (columnHeader != null ?
1385                                      columnHeader.toString() : "");
1386         String rowHeaderString = (rowHeader != null ?
1387                                   rowHeader.toString() : "");
1388         String lowerLeftString = (lowerLeft != null ?
1389                                   lowerLeft.toString() : "");
1390         String lowerRightString = (lowerRight != null ?
1391                                   lowerRight.toString() : "");
1392         String upperLeftString = (upperLeft != null ?
1393                                   upperLeft.toString() : "");
1394         String upperRightString = (upperRight != null ?
1395                                   upperRight.toString() : "");
1396 
1397         return super.paramString() +
1398         ",columnHeader=" + columnHeaderString +
1399         ",horizontalScrollBar=" + horizontalScrollBarString +
1400         ",horizontalScrollBarPolicy=" + horizontalScrollBarPolicyString +
1401         ",lowerLeft=" + lowerLeftString +
1402         ",lowerRight=" + lowerRightString +
1403         ",rowHeader=" + rowHeaderString +
1404         ",upperLeft=" + upperLeftString +
1405         ",upperRight=" + upperRightString +
1406         ",verticalScrollBar=" + verticalScrollBarString +
1407         ",verticalScrollBarPolicy=" + verticalScrollBarPolicyString +
1408         ",viewport=" + viewportString +
1409         ",viewportBorder=" + viewportBorderString;
1410     }
1411 
1412 /////////////////
1413 // Accessibility support
1414 ////////////////
1415 
1416     /**
1417      * Gets the AccessibleContext associated with this JScrollPane.
1418      * For scroll panes, the AccessibleContext takes the form of an
1419      * AccessibleJScrollPane.
1420      * A new AccessibleJScrollPane instance is created if necessary.
1421      *
1422      * @return an AccessibleJScrollPane that serves as the
1423      *         AccessibleContext of this JScrollPane
1424      */
1425     public AccessibleContext getAccessibleContext() {
1426         if (accessibleContext == null) {
1427             accessibleContext = new AccessibleJScrollPane();
1428         }
1429         return accessibleContext;
1430     }
1431 
1432     /**
1433      * This class implements accessibility support for the
1434      * <code>JScrollPane</code> class.  It provides an implementation of the
1435      * Java Accessibility API appropriate to scroll pane user-interface
1436      * elements.
1437      * <p>
1438      * <strong>Warning:</strong>
1439      * Serialized objects of this class will not be compatible with
1440      * future Swing releases. The current serialization support is
1441      * appropriate for short term storage or RMI between applications running
1442      * the same version of Swing.  As of 1.4, support for long term storage
1443      * of all JavaBeans&trade;
1444      * has been added to the <code>java.beans</code> package.
1445      * Please see {@link java.beans.XMLEncoder}.
1446      */
1447     @SuppressWarnings("serial") // Same-version serialization only
1448     protected class AccessibleJScrollPane extends AccessibleJComponent
1449         implements ChangeListener, PropertyChangeListener {
1450 
1451         protected JViewport viewPort = null;
1452 
1453         /*
1454          * Resets the viewport ChangeListener and PropertyChangeListener
1455          */
1456         public void resetViewPort() {
1457             if (viewPort != null) {
1458                 viewPort.removeChangeListener(this);
1459                 viewPort.removePropertyChangeListener(this);
1460             }
1461             viewPort = JScrollPane.this.getViewport();
1462             if (viewPort != null) {
1463                 viewPort.addChangeListener(this);
1464                 viewPort.addPropertyChangeListener(this);
1465             }
1466         }
1467 
1468         /**
1469          * AccessibleJScrollPane constructor
1470          */
1471         public AccessibleJScrollPane() {
1472             super();
1473 
1474             resetViewPort();
1475 
1476             // initialize the AccessibleRelationSets for the JScrollPane
1477             // and JScrollBar(s)
1478             JScrollBar scrollBar = getHorizontalScrollBar();
1479             if (scrollBar != null) {
1480                 setScrollBarRelations(scrollBar);
1481             }
1482             scrollBar = getVerticalScrollBar();
1483             if (scrollBar != null) {
1484                 setScrollBarRelations(scrollBar);
1485             }
1486         }
1487 
1488         /**
1489          * Get the role of this object.
1490          *
1491          * @return an instance of AccessibleRole describing the role of the
1492          * object
1493          * @see AccessibleRole
1494          */
1495         public AccessibleRole getAccessibleRole() {
1496             return AccessibleRole.SCROLL_PANE;
1497         }
1498 
1499         /**
1500          * Invoked when the target of the listener has changed its state.
1501          *
1502          * @param e  a <code>ChangeEvent</code> object. Must not be null.
1503          *
1504          * @throws NullPointerException if the parameter is null.
1505          */
1506         public void stateChanged(ChangeEvent e) {
1507             if (e == null) {
1508                 throw new NullPointerException();
1509             }
1510             firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1511                                Boolean.valueOf(false),
1512                                Boolean.valueOf(true));
1513         }
1514 
1515         /**
1516          * This method gets called when a bound property is changed.
1517          * @param e A <code>PropertyChangeEvent</code> object describing
1518          * the event source and the property that has changed. Must not be null.
1519          *
1520          * @throws NullPointerException if the parameter is null.
1521          * @since 1.5
1522          */
1523         public void propertyChange(PropertyChangeEvent e) {
1524             String propertyName = e.getPropertyName();
1525             if (propertyName == "horizontalScrollBar" ||
1526                 propertyName == "verticalScrollBar") {
1527 
1528                 if (e.getNewValue() instanceof JScrollBar) {
1529                     setScrollBarRelations((JScrollBar)e.getNewValue());
1530                 }
1531             }
1532         }
1533 
1534 
1535         /*
1536          * Sets the CONTROLLER_FOR and CONTROLLED_BY AccessibleRelations for
1537          * the JScrollPane and JScrollBar. JScrollBar must not be null.
1538          */
1539         void setScrollBarRelations(JScrollBar scrollBar) {
1540             /*
1541              * The JScrollBar is a CONTROLLER_FOR the JScrollPane.
1542              * The JScrollPane is CONTROLLED_BY the JScrollBar.
1543              */
1544             AccessibleRelation controlledBy =
1545                 new AccessibleRelation(AccessibleRelation.CONTROLLED_BY,
1546                                        scrollBar);
1547             AccessibleRelation controllerFor =
1548                 new AccessibleRelation(AccessibleRelation.CONTROLLER_FOR,
1549                                        JScrollPane.this);
1550 
1551             // set the relation set for the scroll bar
1552             AccessibleContext ac = scrollBar.getAccessibleContext();
1553             ac.getAccessibleRelationSet().add(controllerFor);
1554 
1555             // set the relation set for the scroll pane
1556             getAccessibleRelationSet().add(controlledBy);
1557         }
1558     }
1559 }