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