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