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