JViewport.java revision 11926:39bd7fa12bc3
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
31553Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41553Srgrimes *
51553Srgrimes * This code is free software; you can redistribute it and/or modify it
61553Srgrimes * under the terms of the GNU General Public License version 2 only, as
71553Srgrimes * published by the Free Software Foundation.  Oracle designates this
81553Srgrimes * particular file as subject to the "Classpath" exception as provided
91553Srgrimes * by Oracle in the LICENSE file that accompanied this code.
101553Srgrimes *
111553Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
121553Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131553Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141553Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
151553Srgrimes * accompanied this code).
161553Srgrimes *
171553Srgrimes * You should have received a copy of the GNU General Public License version
181553Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
191553Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
201553Srgrimes *
211553Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
221553Srgrimes * or visit www.oracle.com if you need additional information or have any
231553Srgrimes * questions.
241553Srgrimes */
251553Srgrimes
261553Srgrimespackage javax.swing;
271553Srgrimes
281553Srgrimesimport java.awt.*;
291553Srgrimesimport java.awt.event.*;
301553Srgrimesimport java.awt.peer.ComponentPeer;
311553Srgrimesimport java.beans.Transient;
321553Srgrimesimport javax.swing.plaf.ViewportUI;
331553Srgrimes
341553Srgrimesimport javax.swing.event.*;
351553Srgrimesimport javax.swing.border.*;
361553Srgrimesimport javax.accessibility.*;
371553Srgrimes
381553Srgrimesimport java.io.Serializable;
391553Srgrimes
401553Srgrimesimport sun.awt.AWTAccessor;
4129780Scharnier
421553Srgrimes/**
431553Srgrimes * The "viewport" or "porthole" through which you see the underlying
441553Srgrimes * information. When you scroll, what moves is the viewport. It is like
451553Srgrimes * peering through a camera's viewfinder. Moving the viewfinder upwards
46117621Sgad * brings new things into view at the top of the picture and loses
471553Srgrimes * things that were at the bottom.
48117621Sgad * <p>
49117621Sgad * By default, <code>JViewport</code> is opaque. To change this, use the
5029780Scharnier * <code>setOpaque</code> method.
511553Srgrimes * <p>
52117621Sgad * <b>NOTE:</b>We have implemented a faster scrolling algorithm that
53117621Sgad * does not require a buffer to draw in. The algorithm works as follows:
54117621Sgad * <ol><li>The view and parent view and checked to see if they are
551553Srgrimes * <code>JComponents</code>,
561553Srgrimes * if they aren't, stop and repaint the whole viewport.
571553Srgrimes * <li>If the viewport is obscured by an ancestor, stop and repaint the whole
581553Srgrimes * viewport.
591553Srgrimes * <li>Compute the region that will become visible, if it is as big as
601553Srgrimes * the viewport, stop and repaint the whole view region.
611553Srgrimes * <li>Obtain the ancestor <code>Window</code>'s graphics and
621553Srgrimes * do a <code>copyArea</code> on the scrolled region.
631553Srgrimes * <li>Message the view to repaint the newly visible region.
641553Srgrimes * <li>The next time paint is invoked on the viewport, if the clip region
6595434Sgad * is smaller than the viewport size a timer is kicked off to repaint the
6695434Sgad * whole region.
671553Srgrimes * </ol>
681553Srgrimes * In general this approach is much faster. Compared to the backing store
691553Srgrimes * approach this avoids the overhead of maintaining an offscreen buffer and
7031492Swollman * having to do two <code>copyArea</code>s.
7153956Sache * Compared to the non backing store case this
721553Srgrimes * approach will greatly reduce the painted region.
731553Srgrimes * <p>
741553Srgrimes * This approach can cause slower times than the backing store approach
751553Srgrimes * when the viewport is obscured by another window, or partially offscreen.
761553Srgrimes * When another window
771553Srgrimes * obscures the viewport the copyArea will copy garbage and a
78241015Smdf * paint event will be generated by the system to inform us we need to
791553Srgrimes * paint the newly exposed region. The only way to handle this is to
801553Srgrimes * repaint the whole viewport, which can cause slower performance than the
811553Srgrimes * backing store case. In most applications very rarely will the user be
821553Srgrimes * scrolling while the viewport is obscured by another window or offscreen,
831553Srgrimes * so this optimization is usually worth the performance hit when obscured.
841553Srgrimes * <p>
851553Srgrimes * <strong>Warning:</strong> Swing is not thread safe. For more
861553Srgrimes * information see <a
8778300Sgad * href="package-summary.html#threading">Swing's Threading
8823122Smpp * Policy</a>.
891553Srgrimes * <p>
901553Srgrimes * <strong>Warning:</strong>
911553Srgrimes * Serialized objects of this class will not be compatible with
921553Srgrimes * future Swing releases. The current serialization support is
931553Srgrimes * appropriate for short term storage or RMI between applications running
941553Srgrimes * the same version of Swing.  As of 1.4, support for long term storage
9578146Sgad * of all JavaBeans&trade;
961553Srgrimes * has been added to the <code>java.beans</code> package.
971553Srgrimes * Please see {@link java.beans.XMLEncoder}.
981553Srgrimes *
9984695Sgad * @author Hans Muller
1001553Srgrimes * @author Philip Milne
1018857Srgrimes * @see JScrollPane
1021553Srgrimes * @since 1.2
1031553Srgrimes */
1041553Srgrimes@SuppressWarnings("serial") // Same-version serialization only
1051553Srgrimespublic class JViewport extends JComponent implements Accessible
10653956Sache{
1071553Srgrimes    /**
10843507Swollman     * @see #getUIClassID
1091553Srgrimes     * @see #readObject
11061913Swollman     */
1111553Srgrimes    private static final String uiClassID = "ViewportUI";
1121553Srgrimes
1131553Srgrimes    /** Property used to indicate window blitting should not be done.
11478146Sgad     */
11578146Sgad    static final Object EnableWindowBlit = "EnableWindowBlit";
11678146Sgad
11778146Sgad    /**
11878146Sgad     * True when the viewport dimensions have been determined.
11978146Sgad     * The default is false.
12078146Sgad     */
12178146Sgad    protected boolean isViewSizeSet = false;
12278146Sgad
12378146Sgad    /**
12478146Sgad     * The last <code>viewPosition</code> that we've painted, so we know how
12578146Sgad     * much of the backing store image is valid.
12678146Sgad     */
12778146Sgad    protected Point lastPaintPosition = null;
1281553Srgrimes
12927618Simp    /**
13027618Simp     * True when this viewport is maintaining an offscreen image of its
13119202Simp     * contents, so that some scrolling can take place using fast "bit-blit"
13278146Sgad     * operations instead of by accessing the view object to construct the
1331553Srgrimes     * display.  The default is <code>false</code>.
1341553Srgrimes     *
1351553Srgrimes     * @deprecated As of Java 2 platform v1.3
13678146Sgad     * @see #setScrollMode
13778146Sgad     */
1381553Srgrimes    @Deprecated
13915733Sjoerg    protected boolean backingStore = false;
14068275Sgad
1411553Srgrimes    /** The view image used for a backing store. */
14268275Sgad    transient protected Image backingStoreImage = null;
14331492Swollman
1441553Srgrimes    /**
14531492Swollman     * The <code>scrollUnderway</code> flag is used for components like
14627618Simp     * <code>JList</code>.  When the downarrow key is pressed on a
14727618Simp     * <code>JList</code> and the selected
148241852Seadler     * cell is the last in the list, the <code>scrollpane</code> autoscrolls.
1491553Srgrimes     * Here, the old selected cell needs repainting and so we need
1501553Srgrimes     * a flag to make the viewport do the optimized painting
1511553Srgrimes     * only when there is an explicit call to
1521553Srgrimes     * <code>setViewPosition(Point)</code>.
1531553Srgrimes     * When <code>setBounds</code> is called through other routes,
1541553Srgrimes     * the flag is off and the view repaints normally.  Another approach
1551553Srgrimes     * would be to remove this from the <code>JViewport</code>
1561553Srgrimes     * class and have the <code>JList</code> manage this case by using
1571553Srgrimes     * <code>setBackingStoreEnabled</code>.  The default is
15878280Sgad     * <code>false</code>.
15978300Sgad     */
1601553Srgrimes    protected boolean scrollUnderway = false;
1611553Srgrimes
16215733Sjoerg    /*
16315733Sjoerg     * Listener that is notified each time the view changes size.
16461913Swollman     */
16561913Swollman    private ComponentListener viewListener = null;
16615733Sjoerg
16715733Sjoerg    /* Only one <code>ChangeEvent</code> is needed per
16842340Simp     * <code>JViewport</code> instance since the
16942339Simp     * event's only (read-only) state is the source property.  The source
17042339Simp     * of events generated here is always "this".
17115733Sjoerg     */
17215733Sjoerg    private transient ChangeEvent changeEvent = null;
17325787Sbrian
1741553Srgrimes    /**
17515733Sjoerg      * Use <code>graphics.copyArea</code> to implement scrolling.
17615733Sjoerg      * This is the fastest for most applications.
17715733Sjoerg      *
17815733Sjoerg      * @see #setScrollMode
17915733Sjoerg      * @since 1.3
1801553Srgrimes      */
1811553Srgrimes    public static final int BLIT_SCROLL_MODE = 1;
1821553Srgrimes
1831553Srgrimes    /**
18415733Sjoerg      * Draws viewport contents into an offscreen image.
1851553Srgrimes      * This was previously the default mode for <code>JTable</code>.
1861553Srgrimes      * This mode may offer advantages over "blit mode"
18715733Sjoerg      * in some cases, but it requires a large chunk of extra RAM.
1881553Srgrimes      *
18915733Sjoerg      * @see #setScrollMode
1901553Srgrimes      * @since 1.3
1911553Srgrimes      */
19215733Sjoerg    public static final int BACKINGSTORE_SCROLL_MODE = 2;
19315733Sjoerg
1941553Srgrimes    /**
1951553Srgrimes      * This mode uses the very simple method of redrawing the entire
19653956Sache      * contents of the scrollpane each time it is scrolled.
19753956Sache      * This was the default behavior in Swing 1.0 and Swing 1.1.
19853956Sache      * Either of the other two options will provide better performance
19953956Sache      * in most cases.
2001553Srgrimes      *
20115733Sjoerg      * @see #setScrollMode
2021553Srgrimes      * @since 1.3
2031553Srgrimes      */
20415733Sjoerg    public static final int SIMPLE_SCROLL_MODE = 0;
20515733Sjoerg
20643519Swollman    /**
20715733Sjoerg      * @see #setScrollMode
20815733Sjoerg      * @since 1.3
20961913Swollman      */
21061913Swollman    private int scrollMode = BLIT_SCROLL_MODE;
21161913Swollman
21261913Swollman    //
21315733Sjoerg    // Window blitting:
21415733Sjoerg    //
21515733Sjoerg    // As mentioned in the javadoc when using windowBlit a paint event
2161553Srgrimes    // will be generated by the system if copyArea copies a non-visible
21715733Sjoerg    // portion of the view (in other words, it copies garbage). We are
21815733Sjoerg    // not guaranteed to receive the paint event before other mouse events,
2191553Srgrimes    // so we can not be sure we haven't already copied garbage a bunch of
2201553Srgrimes    // times to different parts of the view. For that reason when a blit
22115733Sjoerg    // happens and the Component is obscured (the check for obscurity
2221553Srgrimes    // is not supported on all platforms and is checked via ComponentPeer
2231553Srgrimes    // methods) the ivar repaintAll is set to true. When paint is received
2241553Srgrimes    // if repaintAll is true (we previously did a blit) it is set to
2251553Srgrimes    // false, and if the clip region is smaller than the viewport
2261553Srgrimes    // waitingForRepaint is set to true and a timer is started. When
2271553Srgrimes    // the timer fires if waitingForRepaint is true, repaint is invoked.
22835251Sobrien    // In the mean time, if the view is asked to scroll and waitingForRepaint
22935251Sobrien    // is true, a blit will not happen, instead the non-backing store case
2301553Srgrimes    // of scrolling will happen, which will reset waitingForRepaint.
2311553Srgrimes    // waitingForRepaint is set to false in paint when the clip rect is
23215733Sjoerg    // bigger (or equal) to the size of the viewport.
23315733Sjoerg    // A Timer is used instead of just a repaint as it appeared to offer
23442340Simp    // better performance.
23542339Simp
23642339Simp
2371553Srgrimes    /**
2381553Srgrimes     * This is set to true in <code>setViewPosition</code>
2391553Srgrimes     * if doing a window blit and the viewport is obscured.
2401553Srgrimes     */
2411553Srgrimes    private transient boolean repaintAll;
2421553Srgrimes
24315733Sjoerg    /**
24415733Sjoerg     * This is set to true in paint, if <code>repaintAll</code>
2451553Srgrimes     * is true and the clip rectangle does not match the bounds.
2461553Srgrimes     * If true, and scrolling happens the
24715733Sjoerg     * repaint manager is not cleared which then allows for the repaint
24815733Sjoerg     * previously invoked to succeed.
24915733Sjoerg     */
25015733Sjoerg    private transient boolean waitingForRepaint;
2511553Srgrimes
2521553Srgrimes    /**
2531553Srgrimes     * Instead of directly invoking repaint, a <code>Timer</code>
2541553Srgrimes     * is started and when it fires, repaint is invoked.
25515733Sjoerg     */
25615733Sjoerg    private transient Timer repaintTimer;
2571553Srgrimes
2581553Srgrimes    /**
25915733Sjoerg     * Set to true in paintView when paint is invoked.
26015733Sjoerg     */
26115733Sjoerg    private transient boolean inBlitPaint;
26215733Sjoerg
26315733Sjoerg    /**
26415733Sjoerg     * Whether or not a valid view has been installed.
2651553Srgrimes     */
2661553Srgrimes    private boolean hasHadValidView;
26715733Sjoerg
26815733Sjoerg    /**
2691553Srgrimes     * When view is changed we have to synchronize scrollbar values
27015733Sjoerg     * with viewport (see the BasicScrollPaneUI#syncScrollPaneWithViewport method).
27115733Sjoerg     * This flag allows to invoke that method while ScrollPaneLayout#layoutContainer
27215733Sjoerg     * is running.
27315733Sjoerg     */
2741553Srgrimes    private boolean viewChanged;
2751553Srgrimes
27631492Swollman    /** Creates a <code>JViewport</code>. */
27731492Swollman    public JViewport() {
27831492Swollman        super();
27931492Swollman        setLayout(createLayoutManager());
28031583Sjdp        setOpaque(true);
2811553Srgrimes        updateUI();
2821553Srgrimes        setInheritsPopupMenu(true);
28331492Swollman    }
28431492Swollman
28531492Swollman
28631492Swollman
2871553Srgrimes    /**
28843507Swollman     * Returns the L&amp;F object that renders this component.
28943507Swollman     *
29043507Swollman     * @return a <code>ViewportUI</code> object
29143507Swollman     * @since 1.3
29284695Sgad     */
29343507Swollman    public ViewportUI getUI() {
29484695Sgad        return (ViewportUI)ui;
29584695Sgad    }
29631492Swollman
29731492Swollman
29884695Sgad    /**
29931492Swollman     * Sets the L&amp;F object that renders this component.
3001553Srgrimes     *
30143507Swollman     * @param ui  the <code>ViewportUI</code> L&amp;F object
3021553Srgrimes     * @see UIDefaults#getUI
3031553Srgrimes     * @beaninfo
3041553Srgrimes     *        bound: true
30531492Swollman     *       hidden: true
30631492Swollman     *    attribute: visualUpdate true
30731492Swollman     *  description: The UI object that implements the Component's LookAndFeel.
3081553Srgrimes     * @since 1.3
3091553Srgrimes     */
31084695Sgad    public void setUI(ViewportUI ui) {
3111553Srgrimes        super.setUI(ui);
3121553Srgrimes    }
3131553Srgrimes
3141553Srgrimes
31531492Swollman    /**
3161553Srgrimes     * Resets the UI property to a value from the current look and feel.
3171553Srgrimes     *
3181553Srgrimes     * @see JComponent#updateUI
3191553Srgrimes     */
3201553Srgrimes    public void updateUI() {
32131492Swollman        setUI((ViewportUI)UIManager.getUI(this));
32231492Swollman    }
32331492Swollman
3241553Srgrimes
3251553Srgrimes    /**
3261553Srgrimes     * Returns a string that specifies the name of the L&amp;F class
32731492Swollman     * that renders this component.
3281553Srgrimes     *
329241852Seadler     * @return the string "ViewportUI"
33031492Swollman     *
33131492Swollman     * @see JComponent#getUIClassID
332241852Seadler     * @see UIDefaults#getUI
33378300Sgad     */
33484695Sgad    public String getUIClassID() {
33568149Sgad        return uiClassID;
33656287Sjoe    }
3371553Srgrimes
33823122Smpp
3391553Srgrimes    /**
3401553Srgrimes     * Sets the <code>JViewport</code>'s one lightweight child,
34131492Swollman     * which can be <code>null</code>.
34231492Swollman     * (Since there is only one child which occupies the entire viewport,
3431553Srgrimes     * the <code>constraints</code> and <code>index</code>
3441553Srgrimes     * arguments are ignored.)
34584695Sgad     *
3461553Srgrimes     * @param child       the lightweight <code>child</code> of the viewport
34761913Swollman     * @param constraints the <code>constraints</code> to be respected
34861913Swollman     * @param index       the index
3491553Srgrimes     * @see #setView
3501553Srgrimes     */
3511553Srgrimes    protected void addImpl(Component child, Object constraints, int index) {
35284695Sgad      setView(child);
3531553Srgrimes    }
3541553Srgrimes
3551553Srgrimes
3561553Srgrimes    /**
3571553Srgrimes     * Removes the <code>Viewport</code>s one lightweight child.
3581553Srgrimes     *
35961913Swollman     * @see #setView
36061913Swollman     */
36161913Swollman    public void remove(Component child) {
36261913Swollman        child.removeComponentListener(viewListener);
36361913Swollman        super.remove(child);
36461913Swollman    }
36553956Sache
36653956Sache    /**
3671553Srgrimes     * Scrolls the view so that <code>Rectangle</code>
36853956Sache     * within the view becomes visible.
36953956Sache     * <p>
37053956Sache     * This attempts to validate the view before scrolling if the
37153956Sache     * view is currently not valid - <code>isValid</code> returns false.
37253956Sache     * To avoid excessive validation when the containment hierarchy is
37353956Sache     * being created this will not validate if one of the ancestors does not
3741553Srgrimes     * have a peer, or there is no validate root ancestor, or one of the
3751553Srgrimes     * ancestors is not a <code>Window</code> or <code>Applet</code>.
3761553Srgrimes     * <p>
37715733Sjoerg     * Note that this method will not scroll outside of the
37831492Swollman     * valid viewport; for example, if <code>contentRect</code> is larger
37915733Sjoerg     * than the viewport, scrolling will be confined to the viewport's
38015733Sjoerg     * bounds.
38115733Sjoerg     *
38231492Swollman     * @param contentRect the <code>Rectangle</code> to display
38315733Sjoerg     * @see JComponent#isValidateRoot
38415733Sjoerg     * @see java.awt.Component#isValid
38515733Sjoerg     */
38615733Sjoerg    public void scrollRectToVisible(Rectangle contentRect) {
3871553Srgrimes        Component view = getView();
3881553Srgrimes
3891553Srgrimes        if (view == null) {
390241015Smdf            return;
391241015Smdf        } else {
3921553Srgrimes            if (!view.isValid()) {
3931553Srgrimes                // If the view is not valid, validate. scrollRectToVisible
3941553Srgrimes                // may fail if the view is not valid first, contentRect
3951553Srgrimes                // could be bigger than invalid size.
3961553Srgrimes                validateView();
3971553Srgrimes            }
3981553Srgrimes            int dx, dy;
3991553Srgrimes
4001553Srgrimes            dx = positionAdjustment(getWidth(), contentRect.width, contentRect.x);
4011553Srgrimes            dy = positionAdjustment(getHeight(), contentRect.height, contentRect.y);
4021553Srgrimes
4031553Srgrimes            if (dx != 0 || dy != 0) {
4041553Srgrimes                Point viewPosition = getViewPosition();
4051553Srgrimes                Dimension viewSize = view.getSize();
40678280Sgad                int startX = viewPosition.x;
40778280Sgad                int startY = viewPosition.y;
40868275Sgad                Dimension extent = getExtentSize();
40968275Sgad
41068275Sgad                viewPosition.x -= dx;
41168275Sgad                viewPosition.y -= dy;
41268275Sgad                // Only constrain the location if the view is valid. If the
41368275Sgad                // the view isn't valid, it typically indicates the view
41468275Sgad                // isn't visible yet and most likely has a bogus size as will
41568275Sgad                // we, and therefore we shouldn't constrain the scrolling
41668275Sgad                if (view.isValid()) {
41768275Sgad                    if (getParent().getComponentOrientation().isLeftToRight()) {
418241852Seadler                        if (viewPosition.x + extent.width > viewSize.width) {
41968275Sgad                            viewPosition.x = Math.max(0, viewSize.width - extent.width);
42068275Sgad                        } else if (viewPosition.x < 0) {
42168275Sgad                            viewPosition.x = 0;
42268275Sgad                        }
42368275Sgad                    } else {
42468275Sgad                        if (extent.width > viewSize.width) {
42568275Sgad                            viewPosition.x = viewSize.width - extent.width;
42668275Sgad                        } else {
42768275Sgad                            viewPosition.x = Math.max(0, Math.min(viewSize.width - extent.width, viewPosition.x));
42868275Sgad                        }
42968275Sgad                    }
43068275Sgad                    if (viewPosition.y + extent.height > viewSize.height) {
43168275Sgad                        viewPosition.y = Math.max(0, viewSize.height -
43268275Sgad                                                  extent.height);
43368275Sgad                    }
43468275Sgad                    else if (viewPosition.y < 0) {
43568275Sgad                        viewPosition.y = 0;
43668275Sgad                    }
43768275Sgad                }
43868275Sgad                if (viewPosition.x != startX || viewPosition.y != startY) {
43968275Sgad                    setViewPosition(viewPosition);
44068275Sgad                    // NOTE: How JViewport currently works with the
44168275Sgad                    // backing store is not foolproof. The sequence of
44268275Sgad                    // events when setViewPosition
44368275Sgad                    // (scrollRectToVisible) is called is to reset the
44468275Sgad                    // views bounds, which causes a repaint on the
44568275Sgad                    // visible region and sets an ivar indicating
44668275Sgad                    // scrolling (scrollUnderway). When
44768275Sgad                    // JViewport.paint is invoked if scrollUnderway is
44868275Sgad                    // true, the backing store is blitted.  This fails
44968275Sgad                    // if between the time setViewPosition is invoked
45068275Sgad                    // and paint is received another repaint is queued
45168275Sgad                    // indicating part of the view is invalid. There
45268275Sgad                    // is no way for JViewport to notice another
45368275Sgad                    // repaint has occurred and it ends up blitting
45468275Sgad                    // what is now a dirty region and the repaint is
45568275Sgad                    // never delivered.
456241852Seadler                    // It just so happens JTable encounters this
45768275Sgad                    // behavior by way of scrollRectToVisible, for
45868275Sgad                    // this reason scrollUnderway is set to false
45968275Sgad                    // here, which effectively disables the backing
460241852Seadler                    // store.
46168275Sgad                    scrollUnderway = false;
46268275Sgad                }
46368275Sgad            }
46468275Sgad        }
46568275Sgad    }
46668275Sgad
46768275Sgad    /**
46868275Sgad     * Ascends the <code>Viewport</code>'s parents stopping when
46968275Sgad     * a component is found that returns
470241852Seadler     * <code>true</code> to <code>isValidateRoot</code>.
47168275Sgad     * If all the <code>Component</code>'s  parents are visible,
47268275Sgad     * <code>validate</code> will then be invoked on it. The
47368275Sgad     * <code>RepaintManager</code> is then invoked with
47468275Sgad     * <code>removeInvalidComponent</code>. This
47568275Sgad     * is the synchronous version of a <code>revalidate</code>.
47668275Sgad     */
47768275Sgad    private void validateView() {
47868275Sgad        Component validateRoot = SwingUtilities.getValidateRoot(this, false);
47968275Sgad
48068275Sgad        if (validateRoot == null) {
48168275Sgad            return;
482241852Seadler        }
48368275Sgad
48468275Sgad        // Validate the root.
4851553Srgrimes        validateRoot.validate();
48678280Sgad
48719202Simp        // And let the RepaintManager it does not have to validate from
48831492Swollman        // validateRoot anymore.
48919202Simp        RepaintManager rm = RepaintManager.currentManager(this);
49019202Simp
49178280Sgad        if (rm != null) {
4921553Srgrimes            rm.removeInvalidComponent((JComponent)validateRoot);
4931553Srgrimes        }
4941553Srgrimes    }
4951553Srgrimes
4961553Srgrimes     /*  Used by the scrollRectToVisible method to determine the
4971553Srgrimes      *  proper direction and amount to move by. The integer variables are named
4981553Srgrimes      *  width, but this method is applicable to height also. The code assumes that
4991553Srgrimes      *  parentWidth/childWidth are positive and childAt can be negative.
5001553Srgrimes      */
501241852Seadler    private int positionAdjustment(int parentWidth, int childWidth, int childAt)    {
5021553Srgrimes
50384697Sgad        //   +-----+
5041553Srgrimes        //   | --- |     No Change
50584697Sgad        //   +-----+
5061553Srgrimes        if (childAt >= 0 && childWidth + childAt <= parentWidth)    {
50784697Sgad            return 0;
50878280Sgad        }
50978280Sgad
5101553Srgrimes        //   +-----+
5111553Srgrimes        //  ---------   No Change
5121553Srgrimes        //   +-----+
5131553Srgrimes        if (childAt <= 0 && childWidth + childAt >= parentWidth) {
5141553Srgrimes            return 0;
5151553Srgrimes        }
51678280Sgad
5171553Srgrimes        //   +-----+          +-----+
5181553Srgrimes        //   |   ----    ->   | ----|
5191553Srgrimes        //   +-----+          +-----+
5201553Srgrimes        if (childAt > 0 && childWidth <= parentWidth)    {
521241852Seadler            return -childAt + parentWidth - childWidth;
5221553Srgrimes        }
5231553Srgrimes
52431492Swollman        //   +-----+             +-----+
5251553Srgrimes        //   |  --------  ->     |--------
5261553Srgrimes        //   +-----+             +-----+
5271553Srgrimes        if (childAt >= 0 && childWidth >= parentWidth)   {
5281553Srgrimes            return -childAt;
52927618Simp        }
5301553Srgrimes
5311553Srgrimes        //   +-----+          +-----+
5321553Srgrimes        // ----    |     ->   |---- |
5331553Srgrimes        //   +-----+          +-----+
5341553Srgrimes        if (childAt <= 0 && childWidth <= parentWidth)   {
5351553Srgrimes            return -childAt;
5361553Srgrimes        }
53778146Sgad
5381553Srgrimes        //   +-----+             +-----+
5391553Srgrimes        //-------- |      ->   --------|
5401553Srgrimes        //   +-----+             +-----+
5411553Srgrimes        if (childAt < 0 && childWidth >= parentWidth)    {
5421553Srgrimes            return -childAt + parentWidth - childWidth;
5431553Srgrimes        }
5441553Srgrimes
5451553Srgrimes        return 0;
5461553Srgrimes    }
5471553Srgrimes
5481553Srgrimes
5491553Srgrimes    /**
5501553Srgrimes     * The viewport "scrolls" its child (called the "view") by the
5511553Srgrimes     * normal parent/child clipping (typically the view is moved in
55278280Sgad     * the opposite direction of the scroll).  A non-<code>null</code> border,
5531553Srgrimes     * or non-zero insets, isn't supported, to prevent the geometry
5541553Srgrimes     * of this component from becoming complex enough to inhibit
5551553Srgrimes     * subclassing.  To create a <code>JViewport</code> with a border,
5561553Srgrimes     * add it to a <code>JPanel</code> that has a border.
5571553Srgrimes     * <p>Note:  If <code>border</code> is non-<code>null</code>, this
5581553Srgrimes     * method will throw an exception as borders are not supported on
55931492Swollman     * a <code>JViewPort</code>.
56031492Swollman     *
56178280Sgad     * @param border the <code>Border</code> to set
5621553Srgrimes     * @exception IllegalArgumentException this method is not implemented
5631553Srgrimes     */
5641553Srgrimes    public final void setBorder(Border border) {
5651553Srgrimes        if (border != null) {
5661553Srgrimes            throw new IllegalArgumentException("JViewport.setBorder() not supported");
5678857Srgrimes        }
56878280Sgad    }
56978280Sgad
5701553Srgrimes
5711553Srgrimes    /**
5721553Srgrimes     * Returns the insets (border) dimensions as (0,0,0,0), since borders
5731553Srgrimes     * are not supported on a <code>JViewport</code>.
5741553Srgrimes     *
5751553Srgrimes     * @return a <code>Rectangle</code> of zero dimension and zero origin
5761553Srgrimes     * @see #setBorder
5771553Srgrimes     */
57878146Sgad    public final Insets getInsets() {
57978146Sgad        return new Insets(0, 0, 0, 0);
5801553Srgrimes    }
5811553Srgrimes
58227282Sdima    /**
58327618Simp     * Returns an <code>Insets</code> object containing this
5841553Srgrimes     * <code>JViewport</code>s inset values.  The passed-in
5851553Srgrimes     * <code>Insets</code> object will be reinitialized, and
58627282Sdima     * all existing values within this object are overwritten.
5871553Srgrimes     *
5881553Srgrimes     * @param insets the <code>Insets</code> object which can be reused
5891553Srgrimes     * @return this viewports inset values
5901553Srgrimes     * @see #getInsets
5911553Srgrimes     * @beaninfo
5921553Srgrimes     *   expert: true
5931553Srgrimes     */
5941553Srgrimes    public final Insets getInsets(Insets insets) {
59527635Simp        insets.left = insets.top = insets.right = insets.bottom = 0;
5961553Srgrimes        return insets;
5971553Srgrimes    }
5981553Srgrimes
5991553Srgrimes
6001553Srgrimes    private Graphics getBackingStoreGraphics(Graphics g) {
6011553Srgrimes        Graphics bsg = backingStoreImage.getGraphics();
6021553Srgrimes        bsg.setColor(g.getColor());
60327282Sdima        bsg.setFont(g.getFont());
60427282Sdima        bsg.setClip(g.getClipBounds());
6051553Srgrimes        return bsg;
6061553Srgrimes    }
607241852Seadler
60827618Simp
609241852Seadler    private void paintViaBackingStore(Graphics g) {
61027618Simp        Graphics bsg = getBackingStoreGraphics(g);
6111553Srgrimes        try {
6121553Srgrimes            super.paint(bsg);
6131553Srgrimes            g.drawImage(backingStoreImage, 0, 0, this);
6141553Srgrimes        } finally {
6151553Srgrimes            bsg.dispose();
6161553Srgrimes        }
61778146Sgad    }
6181553Srgrimes
6191553Srgrimes    private void paintViaBackingStore(Graphics g, Rectangle oClip) {
6201553Srgrimes        Graphics bsg = getBackingStoreGraphics(g);
62184696Sgad        try {
6221553Srgrimes            super.paint(bsg);
6231553Srgrimes            g.setClip(oClip);
62419187Simp            g.drawImage(backingStoreImage, 0, 0, this);
6251553Srgrimes        } finally {
6261553Srgrimes            bsg.dispose();
6271553Srgrimes        }
6281553Srgrimes    }
6291553Srgrimes
6301553Srgrimes    /**
6311553Srgrimes     * The <code>JViewport</code> overrides the default implementation of
6321553Srgrimes     * this method (in <code>JComponent</code>) to return false.
6331553Srgrimes     * This ensures
6341553Srgrimes     * that the drawing machinery will call the <code>Viewport</code>'s
6351553Srgrimes     * <code>paint</code>
63678146Sgad     * implementation rather than messaging the <code>JViewport</code>'s
6371553Srgrimes     * children directly.
6381553Srgrimes     *
6391553Srgrimes     * @return false
6401553Srgrimes     */
641241852Seadler    public boolean isOptimizedDrawingEnabled() {
6421553Srgrimes        return false;
6431553Srgrimes    }
6441553Srgrimes
64578280Sgad    /**
6461553Srgrimes     * Returns true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE} to cause
6471553Srgrimes     * painting to originate from {@code JViewport}, or one of its
6481553Srgrimes     * ancestors. Otherwise returns {@code false}.
64978280Sgad     *
65027618Simp     * @return true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE}.
6511553Srgrimes     * @see JComponent#isPaintingOrigin()
652241852Seadler     */
6531553Srgrimes    protected boolean isPaintingOrigin() {
6541553Srgrimes        return scrollMode == BACKINGSTORE_SCROLL_MODE;
6551553Srgrimes    }
6561553Srgrimes
6571553Srgrimes
6581553Srgrimes    /**
6591553Srgrimes     * Only used by the paint method below.
6601553Srgrimes     */
6611553Srgrimes    private Point getViewLocation() {
6621553Srgrimes        Component view = getView();
6631553Srgrimes        if (view != null) {
6641553Srgrimes            return view.getLocation();
6651553Srgrimes        }
6661553Srgrimes        else {
6671553Srgrimes            return new Point(0,0);
66878146Sgad        }
6691553Srgrimes    }
67039084Swollman
6711553Srgrimes    /**
6721553Srgrimes     * Depending on whether the <code>backingStore</code> is enabled,
6731553Srgrimes     * either paint the image through the backing store or paint
6741553Srgrimes     * just the recently exposed part, using the backing store
6751553Srgrimes     * to "blit" the remainder.
6761553Srgrimes     * <blockquote>
677241852Seadler     * The term "blit" is the pronounced version of the PDP-10
6781553Srgrimes     * BLT (BLock Transfer) instruction, which copied a block of
6791553Srgrimes     * bits. (In case you were curious.)
6801553Srgrimes     * </blockquote>
6811553Srgrimes     *
6821553Srgrimes     * @param g the <code>Graphics</code> context within which to paint
6831553Srgrimes     */
6841553Srgrimes    public void paint(Graphics g)
6851553Srgrimes    {
6861553Srgrimes        int width = getWidth();
6871553Srgrimes        int height = getHeight();
6881553Srgrimes
6891553Srgrimes        if ((width <= 0) || (height <= 0)) {
6901553Srgrimes            return;
6911553Srgrimes        }
6921553Srgrimes
6931553Srgrimes        if (inBlitPaint) {
6941553Srgrimes            // We invoked paint as part of copyArea cleanup, let it through.
6951553Srgrimes            super.paint(g);
6961553Srgrimes            return;
6971553Srgrimes        }
6981553Srgrimes
6991553Srgrimes        if (repaintAll) {
7001553Srgrimes            repaintAll = false;
7011553Srgrimes            Rectangle clipB = g.getClipBounds();
70278146Sgad            if (clipB.width < getWidth() ||
7031553Srgrimes                clipB.height < getHeight()) {
7041553Srgrimes                waitingForRepaint = true;
70579743Sgad                if (repaintTimer == null) {
70679743Sgad                    repaintTimer = createRepaintTimer();
70779743Sgad                }
7081553Srgrimes                repaintTimer.stop();
7091553Srgrimes                repaintTimer.start();
71078280Sgad                // We really don't need to paint, a future repaint will
7111553Srgrimes                // take care of it, but if we don't we get an ugly flicker.
7121553Srgrimes            }
7131553Srgrimes            else {
71478280Sgad                if (repaintTimer != null) {
7151553Srgrimes                    repaintTimer.stop();
7161553Srgrimes                }
7171553Srgrimes                waitingForRepaint = false;
71878280Sgad            }
7191553Srgrimes        }
7201553Srgrimes        else if (waitingForRepaint) {
7211553Srgrimes            // Need a complete repaint before resetting waitingForRepaint
72278280Sgad            Rectangle clipB = g.getClipBounds();
7231553Srgrimes            if (clipB.width >= getWidth() &&
7241553Srgrimes                clipB.height >= getHeight()) {
7251553Srgrimes                waitingForRepaint = false;
72678280Sgad                repaintTimer.stop();
7271553Srgrimes            }
7281553Srgrimes        }
72939084Swollman
73039084Swollman        if (!backingStore || isBlitting() || getView() == null) {
73139084Swollman            super.paint(g);
7321553Srgrimes            lastPaintPosition = getViewLocation();
7331553Srgrimes            return;
73478280Sgad        }
73527618Simp
73627618Simp        // If the view is smaller than the viewport and we are not opaque
7371553Srgrimes        // (that is, we won't paint our background), we should set the
7381553Srgrimes        // clip. Otherwise, as the bounds of the view vary, we will
73979743Sgad        // blit garbage into the exposed areas.
74079743Sgad        Rectangle viewBounds = getView().getBounds();
74179743Sgad        if (!isOpaque()) {
74279743Sgad            g.clipRect(0, 0, viewBounds.width, viewBounds.height);
74379743Sgad        }
74427635Simp
7459568Storstenb        if (backingStoreImage == null) {
7461553Srgrimes            // Backing store is enabled but this is the first call to paint.
7471553Srgrimes            // Create the backing store, paint it and then copy to g.
7481553Srgrimes            // The backing store image will be created with the size of
7499568Storstenb            // the viewport. We must make sure the clip region is the
7501553Srgrimes            // same size, otherwise when scrolling the backing image
75179743Sgad            // the region outside of the clipped region will not be painted,
75279743Sgad            // and result in empty areas.
75379743Sgad            backingStoreImage = createImage(width, height);
75479743Sgad            Rectangle clip = g.getClipBounds();
75579743Sgad            if (clip.width != width || clip.height != height) {
75679743Sgad                if (!isOpaque()) {
7571553Srgrimes                    g.setClip(0, 0, Math.min(viewBounds.width, width),
7581553Srgrimes                              Math.min(viewBounds.height, height));
7591553Srgrimes                }
7601553Srgrimes                else {
76178280Sgad                    g.setClip(0, 0, width, height);
7621553Srgrimes                }
7631553Srgrimes                paintViaBackingStore(g, clip);
7641553Srgrimes            }
7651553Srgrimes            else {
7661553Srgrimes                paintViaBackingStore(g);
7671553Srgrimes            }
7681553Srgrimes        }
7691553Srgrimes        else {
7701553Srgrimes            if (!scrollUnderway || lastPaintPosition.equals(getViewLocation())) {
7719568Storstenb                // No scrolling happened: repaint required area via backing store.
77278146Sgad                paintViaBackingStore(g);
7739568Storstenb            } else {
7749568Storstenb                // The image was scrolled. Manipulate the backing store and flush it to g.
7759568Storstenb                Point blitFrom = new Point();
7769568Storstenb                Point blitTo = new Point();
7779568Storstenb                Dimension blitSize = new Dimension();
7789568Storstenb                Rectangle blitPaint = new Rectangle();
7799568Storstenb
7809568Storstenb                Point newLocation = getViewLocation();
7819568Storstenb                int dx = newLocation.x - lastPaintPosition.x;
7829568Storstenb                int dy = newLocation.y - lastPaintPosition.y;
7839568Storstenb                boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize, blitPaint);
7849568Storstenb                if (!canBlit) {
7859568Storstenb                    // The image was either moved diagonally or
7869568Storstenb                    // moved by more than the image size: paint normally.
7871553Srgrimes                    paintViaBackingStore(g);
7881553Srgrimes                } else {
7891553Srgrimes                    int bdx = blitTo.x - blitFrom.x;
7901553Srgrimes                    int bdy = blitTo.y - blitFrom.y;
79178146Sgad
7921553Srgrimes                    // Move the relevant part of the backing store.
7931553Srgrimes                    Rectangle clip = g.getClipBounds();
7941553Srgrimes                    // We don't want to inherit the clip region when copying
7951553Srgrimes                    // bits, if it is inherited it will result in not moving
7961553Srgrimes                    // all of the image resulting in garbage appearing on
7971553Srgrimes                    // the screen.
7981553Srgrimes                    g.setClip(0, 0, width, height);
7991553Srgrimes                    Graphics bsg = getBackingStoreGraphics(g);
8001553Srgrimes                    try {
8011553Srgrimes                        bsg.copyArea(blitFrom.x, blitFrom.y, blitSize.width, blitSize.height, bdx, bdy);
8021553Srgrimes
8031553Srgrimes                        g.setClip(clip.x, clip.y, clip.width, clip.height);
8041553Srgrimes                        // Paint the rest of the view; the part that has just been exposed.
8051553Srgrimes                        Rectangle r = viewBounds.intersection(blitPaint);
8061553Srgrimes                        bsg.setClip(r);
80778146Sgad                        super.paint(bsg);
8081553Srgrimes
8091553Srgrimes                        // Copy whole of the backing store to g.
8101553Srgrimes                        g.drawImage(backingStoreImage, 0, 0, this);
81131492Swollman                    } finally {
81278146Sgad                        bsg.dispose();
81331492Swollman                    }
81431492Swollman                }
81531492Swollman            }
81678146Sgad        }
81731492Swollman        lastPaintPosition = getViewLocation();
81878146Sgad        scrollUnderway = false;
81931492Swollman    }
82078146Sgad
82131492Swollman
8221553Srgrimes    /**
8231553Srgrimes     * Sets the bounds of this viewport.  If the viewport's width
8241553Srgrimes     * or height has changed, fire a <code>StateChanged</code> event.
82515733Sjoerg     *
82615733Sjoerg     * @param x left edge of the origin
82715733Sjoerg     * @param y top edge of the origin
82878146Sgad     * @param w width in pixels
82915733Sjoerg     * @param h height in pixels
83061913Swollman     *
83161913Swollman     * @see JComponent#reshape(int, int, int, int)
83261913Swollman     */
83361913Swollman    @SuppressWarnings("deprecation")
83415733Sjoerg    public void reshape(int x, int y, int w, int h) {
83515733Sjoerg        boolean sizeChanged = (getWidth() != w) || (getHeight() != h);
83615733Sjoerg        if (sizeChanged) {
83715733Sjoerg            backingStoreImage = null;
83815733Sjoerg        }
8391553Srgrimes        super.reshape(x, y, w, h);
8401553Srgrimes        if (sizeChanged || viewChanged) {
8411553Srgrimes            viewChanged = false;
84278146Sgad
8431553Srgrimes            fireStateChanged();
8441553Srgrimes        }
8451553Srgrimes    }
8461553Srgrimes
8471553Srgrimes
84831492Swollman    /**
849241852Seadler      * Used to control the method of scrolling the viewport contents.
850236289Seadler      * You may want to change this mode to get maximum performance for your
85178280Sgad      * use case.
8521553Srgrimes      *
8531553Srgrimes      * @param mode one of the following values:
8541553Srgrimes      * <ul>
85578280Sgad      * <li> JViewport.BLIT_SCROLL_MODE
8561553Srgrimes      * <li> JViewport.BACKINGSTORE_SCROLL_MODE
8571553Srgrimes      * <li> JViewport.SIMPLE_SCROLL_MODE
858241852Seadler      * </ul>
8591553Srgrimes      *
8601553Srgrimes      * @see #BLIT_SCROLL_MODE
8611553Srgrimes      * @see #BACKINGSTORE_SCROLL_MODE
8621553Srgrimes      * @see #SIMPLE_SCROLL_MODE
8631553Srgrimes      *
8641553Srgrimes      * @beaninfo
8651553Srgrimes      *        bound: false
8661553Srgrimes      *  description: Method of moving contents for incremental scrolls.
86778300Sgad      *         enum: BLIT_SCROLL_MODE JViewport.BLIT_SCROLL_MODE
86831492Swollman      *               BACKINGSTORE_SCROLL_MODE JViewport.BACKINGSTORE_SCROLL_MODE
86931492Swollman      *               SIMPLE_SCROLL_MODE JViewport.SIMPLE_SCROLL_MODE
87031492Swollman      *
87131492Swollman      * @since 1.3
8721553Srgrimes      */
8731553Srgrimes    public void setScrollMode(int mode) {
87419202Simp        scrollMode = mode;
8751553Srgrimes        backingStore = mode == BACKINGSTORE_SCROLL_MODE;
8761553Srgrimes    }
8771553Srgrimes
8781553Srgrimes    /**
8791553Srgrimes      * Returns the current scrolling mode.
8801553Srgrimes      *
8811553Srgrimes      * @return the <code>scrollMode</code> property
8821553Srgrimes      * @see #setScrollMode
88378146Sgad      * @since 1.3
8841553Srgrimes      */
8851553Srgrimes    public int getScrollMode() {
8861553Srgrimes        return scrollMode;
8871553Srgrimes    }
88831492Swollman
88978300Sgad    /**
89078300Sgad     * Returns <code>true</code> if this viewport is maintaining
8911553Srgrimes     * an offscreen image of its contents.
8921553Srgrimes     *
893     * @return <code>true</code> if <code>scrollMode</code> is
894     *    <code>BACKINGSTORE_SCROLL_MODE</code>
895     *
896     * @deprecated As of Java 2 platform v1.3, replaced by
897     *             <code>getScrollMode()</code>.
898     */
899    @Deprecated
900    public boolean isBackingStoreEnabled() {
901        return scrollMode == BACKINGSTORE_SCROLL_MODE;
902    }
903
904
905    /**
906     * If true if this viewport will maintain an offscreen
907     * image of its contents.  The image is used to reduce the cost
908     * of small one dimensional changes to the <code>viewPosition</code>.
909     * Rather than repainting the entire viewport we use
910     * <code>Graphics.copyArea</code> to effect some of the scroll.
911     *
912     * @param enabled if true, maintain an offscreen backing store
913     *
914     * @deprecated As of Java 2 platform v1.3, replaced by
915     *             <code>setScrollMode()</code>.
916     */
917    @Deprecated
918    public void setBackingStoreEnabled(boolean enabled) {
919        if (enabled) {
920            setScrollMode(BACKINGSTORE_SCROLL_MODE);
921        } else {
922            setScrollMode(BLIT_SCROLL_MODE);
923        }
924    }
925
926    private boolean isBlitting() {
927        Component view = getView();
928        return (scrollMode == BLIT_SCROLL_MODE) &&
929               (view instanceof JComponent) && view.isOpaque();
930    }
931
932
933    /**
934     * Returns the <code>JViewport</code>'s one child or <code>null</code>.
935     *
936     * @return the viewports child, or <code>null</code> if none exists
937     *
938     * @see #setView
939     */
940    public Component getView() {
941        return (getComponentCount() > 0) ? getComponent(0) : null;
942    }
943
944    /**
945     * Sets the <code>JViewport</code>'s one lightweight child
946     * (<code>view</code>), which can be <code>null</code>.
947     *
948     * @param view the viewport's new lightweight child
949     *
950     * @see #getView
951     */
952    public void setView(Component view) {
953
954        /* Remove the viewport's existing children, if any.
955         * Note that removeAll() isn't used here because it
956         * doesn't call remove() (which JViewport overrides).
957         */
958        int n = getComponentCount();
959        for(int i = n - 1; i >= 0; i--) {
960            remove(getComponent(i));
961        }
962
963        isViewSizeSet = false;
964
965        if (view != null) {
966            super.addImpl(view, null, -1);
967            viewListener = createViewListener();
968            view.addComponentListener(viewListener);
969        }
970
971        if (hasHadValidView) {
972            // Only fire a change if a view has been installed.
973            fireStateChanged();
974        }
975        else if (view != null) {
976            hasHadValidView = true;
977        }
978
979        viewChanged = true;
980
981        revalidate();
982        repaint();
983    }
984
985
986    /**
987     * If the view's size hasn't been explicitly set, return the
988     * preferred size, otherwise return the view's current size.
989     * If there is no view, return 0,0.
990     *
991     * @return a <code>Dimension</code> object specifying the size of the view
992     */
993    public Dimension getViewSize() {
994        Component view = getView();
995
996        if (view == null) {
997            return new Dimension(0,0);
998        }
999        else if (isViewSizeSet) {
1000            return view.getSize();
1001        }
1002        else {
1003            return view.getPreferredSize();
1004        }
1005    }
1006
1007
1008    /**
1009     * Sets the size of the view.  A state changed event will be fired.
1010     *
1011     * @param newSize a <code>Dimension</code> object specifying the new
1012     *          size of the view
1013     */
1014    public void setViewSize(Dimension newSize) {
1015        Component view = getView();
1016        if (view != null) {
1017            Dimension oldSize = view.getSize();
1018            if (!newSize.equals(oldSize)) {
1019                // scrollUnderway will be true if this is invoked as the
1020                // result of a validate and setViewPosition was previously
1021                // invoked.
1022                scrollUnderway = false;
1023                view.setSize(newSize);
1024                isViewSizeSet = true;
1025                fireStateChanged();
1026            }
1027        }
1028    }
1029
1030    /**
1031     * Returns the view coordinates that appear in the upper left
1032     * hand corner of the viewport, or 0,0 if there's no view.
1033     *
1034     * @return a <code>Point</code> object giving the upper left coordinates
1035     */
1036    public Point getViewPosition() {
1037        Component view = getView();
1038        if (view != null) {
1039            Point p = view.getLocation();
1040            p.x = -p.x;
1041            p.y = -p.y;
1042            return p;
1043        }
1044        else {
1045            return new Point(0,0);
1046        }
1047    }
1048
1049
1050    /**
1051     * Sets the view coordinates that appear in the upper left
1052     * hand corner of the viewport, does nothing if there's no view.
1053     *
1054     * @param p  a <code>Point</code> object giving the upper left coordinates
1055     */
1056    public void setViewPosition(Point p)
1057    {
1058        Component view = getView();
1059        if (view == null) {
1060            return;
1061        }
1062
1063        int oldX, oldY, x = p.x, y = p.y;
1064
1065        /* Collect the old x,y values for the views location
1066         * and do the song and dance to avoid allocating
1067         * a Rectangle object if we don't have to.
1068         */
1069        if (view instanceof JComponent) {
1070            JComponent c = (JComponent)view;
1071            oldX = c.getX();
1072            oldY = c.getY();
1073        }
1074        else {
1075            Rectangle r = view.getBounds();
1076            oldX = r.x;
1077            oldY = r.y;
1078        }
1079
1080        /* The view scrolls in the opposite direction to mouse
1081         * movement.
1082         */
1083        int newX = -x;
1084        int newY = -y;
1085
1086        if ((oldX != newX) || (oldY != newY)) {
1087            if (!waitingForRepaint && isBlitting() && canUseWindowBlitter()) {
1088                RepaintManager rm = RepaintManager.currentManager(this);
1089                // The cast to JComponent will work, if view is not
1090                // a JComponent, isBlitting will return false.
1091                JComponent jview = (JComponent)view;
1092                Rectangle dirty = rm.getDirtyRegion(jview);
1093                if (dirty == null || !dirty.contains(jview.getVisibleRect())) {
1094                    rm.beginPaint();
1095                    try {
1096                        Graphics g = JComponent.safelyGetGraphics(this);
1097                        flushViewDirtyRegion(g, dirty);
1098                        view.setLocation(newX, newY);
1099                        Rectangle r = new Rectangle(
1100                            0, 0, getWidth(), Math.min(getHeight(), jview.getHeight()));
1101                        g.setClip(r);
1102                        // Repaint the complete component if the blit succeeded
1103                        // and needsRepaintAfterBlit returns true.
1104                        repaintAll = (windowBlitPaint(g) &&
1105                                      needsRepaintAfterBlit());
1106                        g.dispose();
1107                        rm.notifyRepaintPerformed(this, r.x, r.y, r.width, r.height);
1108                        rm.markCompletelyClean((JComponent)getParent());
1109                        rm.markCompletelyClean(this);
1110                        rm.markCompletelyClean(jview);
1111                    } finally {
1112                        rm.endPaint();
1113                    }
1114                }
1115                else {
1116                    // The visible region is dirty, no point in doing copyArea
1117                    view.setLocation(newX, newY);
1118                    repaintAll = false;
1119                }
1120            }
1121            else {
1122                scrollUnderway = true;
1123                // This calls setBounds(), and then repaint().
1124                view.setLocation(newX, newY);
1125                repaintAll = false;
1126            }
1127            // we must validate the hierarchy to not break the hw/lw mixing
1128            revalidate();
1129            fireStateChanged();
1130        }
1131    }
1132
1133
1134    /**
1135     * Returns a rectangle whose origin is <code>getViewPosition</code>
1136     * and size is <code>getExtentSize</code>.
1137     * This is the visible part of the view, in view coordinates.
1138     *
1139     * @return a <code>Rectangle</code> giving the visible part of
1140     *          the view using view coordinates.
1141     */
1142    public Rectangle getViewRect() {
1143        return new Rectangle(getViewPosition(), getExtentSize());
1144    }
1145
1146
1147    /**
1148     * Computes the parameters for a blit where the backing store image
1149     * currently contains <code>oldLoc</code> in the upper left hand corner
1150     * and we're scrolling to <code>newLoc</code>.
1151     * The parameters are modified
1152     * to return the values required for the blit.
1153     *
1154     * @param dx  the horizontal delta
1155     * @param dy  the vertical delta
1156     * @param blitFrom the <code>Point</code> we're blitting from
1157     * @param blitTo the <code>Point</code> we're blitting to
1158     * @param blitSize the <code>Dimension</code> of the area to blit
1159     * @param blitPaint the area to blit
1160     * @return  true if the parameters are modified and we're ready to blit;
1161     *          false otherwise
1162     */
1163    protected boolean computeBlit(
1164        int dx,
1165        int dy,
1166        Point blitFrom,
1167        Point blitTo,
1168        Dimension blitSize,
1169        Rectangle blitPaint)
1170    {
1171        int dxAbs = Math.abs(dx);
1172        int dyAbs = Math.abs(dy);
1173        Dimension extentSize = getExtentSize();
1174
1175        if ((dx == 0) && (dy != 0) && (dyAbs < extentSize.height)) {
1176            if (dy < 0) {
1177                blitFrom.y = -dy;
1178                blitTo.y = 0;
1179                blitPaint.y = extentSize.height + dy;
1180            }
1181            else {
1182                blitFrom.y = 0;
1183                blitTo.y = dy;
1184                blitPaint.y = 0;
1185            }
1186
1187            blitPaint.x = blitFrom.x = blitTo.x = 0;
1188
1189            blitSize.width = extentSize.width;
1190            blitSize.height = extentSize.height - dyAbs;
1191
1192            blitPaint.width = extentSize.width;
1193            blitPaint.height = dyAbs;
1194
1195            return true;
1196        }
1197
1198        else if ((dy == 0) && (dx != 0) && (dxAbs < extentSize.width)) {
1199            if (dx < 0) {
1200                blitFrom.x = -dx;
1201                blitTo.x = 0;
1202                blitPaint.x = extentSize.width + dx;
1203            }
1204            else {
1205                blitFrom.x = 0;
1206                blitTo.x = dx;
1207                blitPaint.x = 0;
1208            }
1209
1210            blitPaint.y = blitFrom.y = blitTo.y = 0;
1211
1212            blitSize.width = extentSize.width - dxAbs;
1213            blitSize.height = extentSize.height;
1214
1215            blitPaint.width = dxAbs;
1216            blitPaint.height = extentSize.height;
1217
1218            return true;
1219        }
1220
1221        else {
1222            return false;
1223        }
1224    }
1225
1226
1227    /**
1228     * Returns the size of the visible part of the view in view coordinates.
1229     *
1230     * @return a <code>Dimension</code> object giving the size of the view
1231     */
1232    @Transient
1233    public Dimension getExtentSize() {
1234        return getSize();
1235    }
1236
1237
1238    /**
1239     * Converts a size in pixel coordinates to view coordinates.
1240     * Subclasses of viewport that support "logical coordinates"
1241     * will override this method.
1242     *
1243     * @param size  a <code>Dimension</code> object using pixel coordinates
1244     * @return a <code>Dimension</code> object converted to view coordinates
1245     */
1246    public Dimension toViewCoordinates(Dimension size) {
1247        return new Dimension(size);
1248    }
1249
1250    /**
1251     * Converts a point in pixel coordinates to view coordinates.
1252     * Subclasses of viewport that support "logical coordinates"
1253     * will override this method.
1254     *
1255     * @param p  a <code>Point</code> object using pixel coordinates
1256     * @return a <code>Point</code> object converted to view coordinates
1257     */
1258    public Point toViewCoordinates(Point p) {
1259        return new Point(p);
1260    }
1261
1262
1263    /**
1264     * Sets the size of the visible part of the view using view coordinates.
1265     *
1266     * @param newExtent  a <code>Dimension</code> object specifying
1267     *          the size of the view
1268     */
1269    public void setExtentSize(Dimension newExtent) {
1270        Dimension oldExtent = getExtentSize();
1271        if (!newExtent.equals(oldExtent)) {
1272            setSize(newExtent);
1273            fireStateChanged();
1274        }
1275    }
1276
1277    /**
1278     * A listener for the view.
1279     * <p>
1280     * <strong>Warning:</strong>
1281     * Serialized objects of this class will not be compatible with
1282     * future Swing releases. The current serialization support is
1283     * appropriate for short term storage or RMI between applications running
1284     * the same version of Swing.  As of 1.4, support for long term storage
1285     * of all JavaBeans&trade;
1286     * has been added to the <code>java.beans</code> package.
1287     * Please see {@link java.beans.XMLEncoder}.
1288     */
1289    @SuppressWarnings("serial") // Same-version serialization only
1290    protected class ViewListener extends ComponentAdapter implements Serializable
1291    {
1292        public void componentResized(ComponentEvent e) {
1293            fireStateChanged();
1294            revalidate();
1295        }
1296    }
1297
1298    /**
1299     * Creates a listener for the view.
1300     * @return a <code>ViewListener</code>
1301     */
1302    protected ViewListener createViewListener() {
1303        return new ViewListener();
1304    }
1305
1306
1307    /**
1308     * Subclassers can override this to install a different
1309     * layout manager (or <code>null</code>) in the constructor.  Returns
1310     * the <code>LayoutManager</code> to install on the <code>JViewport</code>.
1311     *
1312     * @return a <code>LayoutManager</code>
1313     */
1314    protected LayoutManager createLayoutManager() {
1315        return ViewportLayout.SHARED_INSTANCE;
1316    }
1317
1318
1319    /**
1320     * Adds a <code>ChangeListener</code> to the list that is
1321     * notified each time the view's
1322     * size, position, or the viewport's extent size has changed.
1323     *
1324     * @param l the <code>ChangeListener</code> to add
1325     * @see #removeChangeListener
1326     * @see #setViewPosition
1327     * @see #setViewSize
1328     * @see #setExtentSize
1329     */
1330    public void addChangeListener(ChangeListener l) {
1331        listenerList.add(ChangeListener.class, l);
1332    }
1333
1334    /**
1335     * Removes a <code>ChangeListener</code> from the list that's notified each
1336     * time the views size, position, or the viewports extent size
1337     * has changed.
1338     *
1339     * @param l the <code>ChangeListener</code> to remove
1340     * @see #addChangeListener
1341     */
1342    public void removeChangeListener(ChangeListener l) {
1343        listenerList.remove(ChangeListener.class, l);
1344    }
1345
1346    /**
1347     * Returns an array of all the <code>ChangeListener</code>s added
1348     * to this JViewport with addChangeListener().
1349     *
1350     * @return all of the <code>ChangeListener</code>s added or an empty
1351     *         array if no listeners have been added
1352     * @since 1.4
1353     */
1354    public ChangeListener[] getChangeListeners() {
1355        return listenerList.getListeners(ChangeListener.class);
1356    }
1357
1358    /**
1359     * Notifies all <code>ChangeListeners</code> when the views
1360     * size, position, or the viewports extent size has changed.
1361     *
1362     * @see #addChangeListener
1363     * @see #removeChangeListener
1364     * @see EventListenerList
1365     */
1366    protected void fireStateChanged()
1367    {
1368        Object[] listeners = listenerList.getListenerList();
1369        for (int i = listeners.length - 2; i >= 0; i -= 2) {
1370            if (listeners[i] == ChangeListener.class) {
1371                if (changeEvent == null) {
1372                    changeEvent = new ChangeEvent(this);
1373                }
1374                ((ChangeListener)listeners[i + 1]).stateChanged(changeEvent);
1375            }
1376        }
1377    }
1378
1379    /**
1380     * Always repaint in the parents coordinate system to make sure
1381     * only one paint is performed by the <code>RepaintManager</code>.
1382     *
1383     * @param     tm   maximum time in milliseconds before update
1384     * @param     x    the <code>x</code> coordinate (pixels over from left)
1385     * @param     y    the <code>y</code> coordinate (pixels down from top)
1386     * @param     w    the width
1387     * @param     h   the height
1388     * @see       java.awt.Component#update(java.awt.Graphics)
1389     */
1390    public void repaint(long tm, int x, int y, int w, int h) {
1391        Container parent = getParent();
1392        if(parent != null)
1393            parent.repaint(tm,x+getX(),y+getY(),w,h);
1394        else
1395            super.repaint(tm,x,y,w,h);
1396    }
1397
1398
1399    /**
1400     * Returns a string representation of this <code>JViewport</code>.
1401     * This method
1402     * is intended to be used only for debugging purposes, and the
1403     * content and format of the returned string may vary between
1404     * implementations. The returned string may be empty but may not
1405     * be <code>null</code>.
1406     *
1407     * @return  a string representation of this <code>JViewport</code>
1408     */
1409    protected String paramString() {
1410        String isViewSizeSetString = (isViewSizeSet ?
1411                                      "true" : "false");
1412        String lastPaintPositionString = (lastPaintPosition != null ?
1413                                          lastPaintPosition.toString() : "");
1414        String scrollUnderwayString = (scrollUnderway ?
1415                                       "true" : "false");
1416
1417        return super.paramString() +
1418        ",isViewSizeSet=" + isViewSizeSetString +
1419        ",lastPaintPosition=" + lastPaintPositionString +
1420        ",scrollUnderway=" + scrollUnderwayString;
1421    }
1422
1423    //
1424    // Following is used when doBlit is true.
1425    //
1426
1427    /**
1428     * Notifies listeners of a property change. This is subclassed to update
1429     * the <code>windowBlit</code> property.
1430     * (The <code>putClientProperty</code> property is final).
1431     *
1432     * @param propertyName a string containing the property name
1433     * @param oldValue the old value of the property
1434     * @param newValue  the new value of the property
1435     */
1436    protected void firePropertyChange(String propertyName, Object oldValue,
1437                                      Object newValue) {
1438        super.firePropertyChange(propertyName, oldValue, newValue);
1439        if (propertyName.equals(EnableWindowBlit)) {
1440            if (newValue != null) {
1441                setScrollMode(BLIT_SCROLL_MODE);
1442            } else {
1443                setScrollMode(SIMPLE_SCROLL_MODE);
1444            }
1445        }
1446    }
1447
1448    /**
1449     * Returns true if the component needs to be completely repainted after
1450     * a blit and a paint is received.
1451     */
1452    private boolean needsRepaintAfterBlit() {
1453        // Find the first heavy weight ancestor. isObscured and
1454        // canDetermineObscurity are only appropriate for heavy weights.
1455        Component heavyParent = getParent();
1456
1457        while (heavyParent != null && heavyParent.isLightweight()) {
1458            heavyParent = heavyParent.getParent();
1459        }
1460
1461        if (heavyParent != null) {
1462            ComponentPeer peer = AWTAccessor.getComponentAccessor()
1463                                            .getPeer(heavyParent);
1464
1465            if (peer != null && peer.canDetermineObscurity() &&
1466                                !peer.isObscured()) {
1467                // The peer says we aren't obscured, therefore we can assume
1468                // that we won't later be messaged to paint a portion that
1469                // we tried to blit that wasn't valid.
1470                // It is certainly possible that when we blited we were
1471                // obscured, and by the time this is invoked we aren't, but the
1472                // chances of that happening are pretty slim.
1473                return false;
1474            }
1475        }
1476        return true;
1477    }
1478
1479    private Timer createRepaintTimer() {
1480        Timer timer = new Timer(300, new ActionListener() {
1481            public void actionPerformed(ActionEvent ae) {
1482                // waitingForRepaint will be false if a paint came down
1483                // with the complete clip rect, in which case we don't
1484                // have to cause a repaint.
1485                if (waitingForRepaint) {
1486                    repaint();
1487                }
1488            }
1489        });
1490        timer.setRepeats(false);
1491        return timer;
1492    }
1493
1494    /**
1495     * If the repaint manager has a dirty region for the view, the view is
1496     * asked to paint.
1497     *
1498     * @param g  the <code>Graphics</code> context within which to paint
1499     */
1500    private void flushViewDirtyRegion(Graphics g, Rectangle dirty) {
1501        JComponent view = (JComponent) getView();
1502        if(dirty != null && dirty.width > 0 && dirty.height > 0) {
1503            dirty.x += view.getX();
1504            dirty.y += view.getY();
1505            Rectangle clip = g.getClipBounds();
1506            if (clip == null) {
1507                // Only happens in 1.2
1508                g.setClip(0, 0, getWidth(), getHeight());
1509            }
1510            g.clipRect(dirty.x, dirty.y, dirty.width, dirty.height);
1511            clip = g.getClipBounds();
1512            // Only paint the dirty region if it is visible.
1513            if (clip.width > 0 && clip.height > 0) {
1514                paintView(g);
1515            }
1516        }
1517    }
1518
1519    /**
1520     * Used when blitting.
1521     *
1522     * @param g  the <code>Graphics</code> context within which to paint
1523     * @return true if blitting succeeded; otherwise false
1524     */
1525    private boolean windowBlitPaint(Graphics g) {
1526        int width = getWidth();
1527        int height = getHeight();
1528
1529        if ((width == 0) || (height == 0)) {
1530            return false;
1531        }
1532
1533        boolean retValue;
1534        RepaintManager rm = RepaintManager.currentManager(this);
1535        JComponent view = (JComponent) getView();
1536
1537        if (lastPaintPosition == null ||
1538            lastPaintPosition.equals(getViewLocation())) {
1539            paintView(g);
1540            retValue = false;
1541        } else {
1542            // The image was scrolled. Manipulate the backing store and flush
1543            // it to g.
1544            Point blitFrom = new Point();
1545            Point blitTo = new Point();
1546            Dimension blitSize = new Dimension();
1547            Rectangle blitPaint = new Rectangle();
1548
1549            Point newLocation = getViewLocation();
1550            int dx = newLocation.x - lastPaintPosition.x;
1551            int dy = newLocation.y - lastPaintPosition.y;
1552            boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize,
1553                                          blitPaint);
1554            if (!canBlit) {
1555                paintView(g);
1556                retValue = false;
1557            } else {
1558                // Prepare the rest of the view; the part that has just been
1559                // exposed.
1560                Rectangle r = view.getBounds().intersection(blitPaint);
1561                r.x -= view.getX();
1562                r.y -= view.getY();
1563
1564                blitDoubleBuffered(view, g, r.x, r.y, r.width, r.height,
1565                                   blitFrom.x, blitFrom.y, blitTo.x, blitTo.y,
1566                                   blitSize.width, blitSize.height);
1567                retValue = true;
1568            }
1569        }
1570        lastPaintPosition = getViewLocation();
1571        return retValue;
1572    }
1573
1574    //
1575    // NOTE: the code below uses paintForceDoubleBuffered for historical
1576    // reasons.  If we're going to allow a blit we've already accounted for
1577    // everything that paintImmediately and _paintImmediately does, for that
1578    // reason we call into paintForceDoubleBuffered to diregard whether or
1579    // not setDoubleBuffered(true) was invoked on the view.
1580    //
1581
1582    private void blitDoubleBuffered(JComponent view, Graphics g,
1583                                    int clipX, int clipY, int clipW, int clipH,
1584                                    int blitFromX, int blitFromY, int blitToX, int blitToY,
1585                                    int blitW, int blitH) {
1586        // NOTE:
1587        //   blitFrom/blitTo are in JViewport coordinates system
1588        //     not the views coordinate space.
1589        //   clip* are in the views coordinate space.
1590        RepaintManager rm = RepaintManager.currentManager(this);
1591        int bdx = blitToX - blitFromX;
1592        int bdy = blitToY - blitFromY;
1593
1594        Composite oldComposite = null;
1595        // Shift the scrolled region
1596        if (g instanceof Graphics2D) {
1597            Graphics2D g2d = (Graphics2D) g;
1598            oldComposite = g2d.getComposite();
1599            g2d.setComposite(AlphaComposite.Src);
1600        }
1601        rm.copyArea(this, g, blitFromX, blitFromY, blitW, blitH, bdx, bdy,
1602                    false);
1603        if (oldComposite != null) {
1604            ((Graphics2D) g).setComposite(oldComposite);
1605        }
1606        // Paint the newly exposed region.
1607        int x = view.getX();
1608        int y = view.getY();
1609        g.translate(x, y);
1610        g.setClip(clipX, clipY, clipW, clipH);
1611        view.paintForceDoubleBuffered(g);
1612        g.translate(-x, -y);
1613    }
1614
1615    /**
1616     * Called to paint the view, usually when <code>blitPaint</code>
1617     * can not blit.
1618     *
1619     * @param g the <code>Graphics</code> context within which to paint
1620     */
1621    private void paintView(Graphics g) {
1622        Rectangle clip = g.getClipBounds();
1623        JComponent view = (JComponent)getView();
1624
1625        if (view.getWidth() >= getWidth()) {
1626            // Graphics is relative to JViewport, need to map to view's
1627            // coordinates space.
1628            int x = view.getX();
1629            int y = view.getY();
1630            g.translate(x, y);
1631            g.setClip(clip.x - x, clip.y - y, clip.width, clip.height);
1632            view.paintForceDoubleBuffered(g);
1633            g.translate(-x, -y);
1634            g.setClip(clip.x, clip.y, clip.width, clip.height);
1635        }
1636        else {
1637            // To avoid any problems that may result from the viewport being
1638            // bigger than the view we start painting from the viewport.
1639            try {
1640                inBlitPaint = true;
1641                paintForceDoubleBuffered(g);
1642            } finally {
1643                inBlitPaint = false;
1644            }
1645        }
1646    }
1647
1648    /**
1649     * Returns true if the viewport is not obscured by one of its ancestors,
1650     * or its ancestors children and if the viewport is showing. Blitting
1651     * when the view isn't showing will work,
1652     * or rather <code>copyArea</code> will work,
1653     * but will not produce the expected behavior.
1654     */
1655    private boolean canUseWindowBlitter() {
1656        if (!isShowing() || (!(getParent() instanceof JComponent) &&
1657                             !(getView() instanceof JComponent))) {
1658            return false;
1659        }
1660        if (isPainting()) {
1661            // We're in the process of painting, don't blit. If we were
1662            // to blit we would draw on top of what we're already drawing,
1663            // so bail.
1664            return false;
1665        }
1666
1667        Rectangle dirtyRegion = RepaintManager.currentManager(this).
1668                                getDirtyRegion((JComponent)getParent());
1669
1670        if (dirtyRegion != null && dirtyRegion.width > 0 &&
1671            dirtyRegion.height > 0) {
1672            // Part of the scrollpane needs to be repainted too, don't blit.
1673            return false;
1674        }
1675
1676        Rectangle clip = new Rectangle(0,0,getWidth(),getHeight());
1677        Rectangle oldClip = new Rectangle();
1678        Rectangle tmp2 = null;
1679        Container parent;
1680        Component lastParent = null;
1681        int x, y, w, h;
1682
1683        for(parent = this; parent != null && isLightweightComponent(parent); parent = parent.getParent()) {
1684            x = parent.getX();
1685            y = parent.getY();
1686            w = parent.getWidth();
1687            h = parent.getHeight();
1688
1689            oldClip.setBounds(clip);
1690            SwingUtilities.computeIntersection(0, 0, w, h, clip);
1691            if(!clip.equals(oldClip))
1692                return false;
1693
1694            if(lastParent != null && parent instanceof JComponent &&
1695               !((JComponent)parent).isOptimizedDrawingEnabled()) {
1696                Component comps[] = parent.getComponents();
1697                int index = 0;
1698
1699                for(int i = comps.length - 1 ;i >= 0; i--) {
1700                    if(comps[i] == lastParent) {
1701                        index = i - 1;
1702                        break;
1703                    }
1704                }
1705
1706                while(index >= 0) {
1707                    tmp2 = comps[index].getBounds(tmp2);
1708
1709                    if(tmp2.intersects(clip))
1710                        return false;
1711                    index--;
1712                }
1713            }
1714            clip.x += x;
1715            clip.y += y;
1716            lastParent = parent;
1717        }
1718        if (parent == null) {
1719            // No Window parent.
1720            return false;
1721        }
1722        return true;
1723    }
1724
1725
1726/////////////////
1727// Accessibility support
1728////////////////
1729
1730    /**
1731     * Gets the AccessibleContext associated with this JViewport.
1732     * For viewports, the AccessibleContext takes the form of an
1733     * AccessibleJViewport.
1734     * A new AccessibleJViewport instance is created if necessary.
1735     *
1736     * @return an AccessibleJViewport that serves as the
1737     *         AccessibleContext of this JViewport
1738     */
1739    public AccessibleContext getAccessibleContext() {
1740        if (accessibleContext == null) {
1741            accessibleContext = new AccessibleJViewport();
1742        }
1743        return accessibleContext;
1744    }
1745
1746    /**
1747     * This class implements accessibility support for the
1748     * <code>JViewport</code> class.  It provides an implementation of the
1749     * Java Accessibility API appropriate to viewport user-interface elements.
1750     * <p>
1751     * <strong>Warning:</strong>
1752     * Serialized objects of this class will not be compatible with
1753     * future Swing releases. The current serialization support is
1754     * appropriate for short term storage or RMI between applications running
1755     * the same version of Swing.  As of 1.4, support for long term storage
1756     * of all JavaBeans&trade;
1757     * has been added to the <code>java.beans</code> package.
1758     * Please see {@link java.beans.XMLEncoder}.
1759     */
1760    @SuppressWarnings("serial") // Same-version serialization only
1761    protected class AccessibleJViewport extends AccessibleJComponent {
1762        /**
1763         * Get the role of this object.
1764         *
1765         * @return an instance of AccessibleRole describing the role of
1766         * the object
1767         */
1768        public AccessibleRole getAccessibleRole() {
1769            return AccessibleRole.VIEWPORT;
1770        }
1771    } // inner class AccessibleJViewport
1772}
1773