Home » openjdk-7 » javax » swing » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package javax.swing;
   27   
   28   import java.awt;
   29   import java.awt.event;
   30   import java.awt.image.VolatileImage;
   31   import java.awt.peer.ComponentPeer;
   32   import java.applet.Applet;
   33   import java.beans.Transient;
   34   import javax.swing.plaf.ViewportUI;
   35   
   36   import javax.swing.event;
   37   import javax.swing.border;
   38   import javax.accessibility;
   39   
   40   
   41   import java.io.Serializable;
   42   
   43   
   44   /**
   45    * The "viewport" or "porthole" through which you see the underlying
   46    * information. When you scroll, what moves is the viewport. It is like
   47    * peering through a camera's viewfinder. Moving the viewfinder upwards
   48    * brings new things into view at the top of the picture and loses
   49    * things that were at the bottom.
   50    * <p>
   51    * By default, <code>JViewport</code> is opaque. To change this, use the
   52    * <code>setOpaque</code> method.
   53    * <p>
   54    * <b>NOTE:</b>We have implemented a faster scrolling algorithm that
   55    * does not require a buffer to draw in. The algorithm works as follows:
   56    * <ol><li>The view and parent view and checked to see if they are
   57    * <code>JComponents</code>,
   58    * if they aren't, stop and repaint the whole viewport.
   59    * <li>If the viewport is obscured by an ancestor, stop and repaint the whole
   60    * viewport.
   61    * <li>Compute the region that will become visible, if it is as big as
   62    * the viewport, stop and repaint the whole view region.
   63    * <li>Obtain the ancestor <code>Window</code>'s graphics and
   64    * do a <code>copyArea</code> on the scrolled region.
   65    * <li>Message the view to repaint the newly visible region.
   66    * <li>The next time paint is invoked on the viewport, if the clip region
   67    * is smaller than the viewport size a timer is kicked off to repaint the
   68    * whole region.
   69    * </ol>
   70    * In general this approach is much faster. Compared to the backing store
   71    * approach this avoids the overhead of maintaining an offscreen buffer and
   72    * having to do two <code>copyArea</code>s.
   73    * Compared to the non backing store case this
   74    * approach will greatly reduce the painted region.
   75    * <p>
   76    * This approach can cause slower times than the backing store approach
   77    * when the viewport is obscured by another window, or partially offscreen.
   78    * When another window
   79    * obscures the viewport the copyArea will copy garbage and a
   80    * paint event will be generated by the system to inform us we need to
   81    * paint the newly exposed region. The only way to handle this is to
   82    * repaint the whole viewport, which can cause slower performance than the
   83    * backing store case. In most applications very rarely will the user be
   84    * scrolling while the viewport is obscured by another window or offscreen,
   85    * so this optimization is usually worth the performance hit when obscured.
   86    * <p>
   87    * <strong>Warning:</strong> Swing is not thread safe. For more
   88    * information see <a
   89    * href="package-summary.html#threading">Swing's Threading
   90    * Policy</a>.
   91    * <p>
   92    * <strong>Warning:</strong>
   93    * Serialized objects of this class will not be compatible with
   94    * future Swing releases. The current serialization support is
   95    * appropriate for short term storage or RMI between applications running
   96    * the same version of Swing.  As of 1.4, support for long term storage
   97    * of all JavaBeans<sup><font size="-2">TM</font></sup>
   98    * has been added to the <code>java.beans</code> package.
   99    * Please see {@link java.beans.XMLEncoder}.
  100    *
  101    * @author Hans Muller
  102    * @author Philip Milne
  103    * @see JScrollPane
  104    */
  105   public class JViewport extends JComponent implements Accessible
  106   {
  107       /**
  108        * @see #getUIClassID
  109        * @see #readObject
  110        */
  111       private static final String uiClassID = "ViewportUI";
  112   
  113       /** Property used to indicate window blitting should not be done.
  114        */
  115       static final Object EnableWindowBlit = "EnableWindowBlit";
  116   
  117       /**
  118        * True when the viewport dimensions have been determined.
  119        * The default is false.
  120        */
  121       protected boolean isViewSizeSet = false;
  122   
  123       /**
  124        * The last <code>viewPosition</code> that we've painted, so we know how
  125        * much of the backing store image is valid.
  126        */
  127       protected Point lastPaintPosition = null;
  128   
  129       /**
  130        * True when this viewport is maintaining an offscreen image of its
  131        * contents, so that some scrolling can take place using fast "bit-blit"
  132        * operations instead of by accessing the view object to construct the
  133        * display.  The default is <code>false</code>.
  134        *
  135        * @deprecated As of Java 2 platform v1.3
  136        * @see #setScrollMode
  137        */
  138       @Deprecated
  139       protected boolean backingStore = false;
  140   
  141       /** The view image used for a backing store. */
  142       transient protected Image backingStoreImage = null;
  143   
  144       /**
  145        * The <code>scrollUnderway</code> flag is used for components like
  146        * <code>JList</code>.  When the downarrow key is pressed on a
  147        * <code>JList</code> and the selected
  148        * cell is the last in the list, the <code>scrollpane</code> autoscrolls.
  149        * Here, the old selected cell needs repainting and so we need
  150        * a flag to make the viewport do the optimized painting
  151        * only when there is an explicit call to
  152        * <code>setViewPosition(Point)</code>.
  153        * When <code>setBounds</code> is called through other routes,
  154        * the flag is off and the view repaints normally.  Another approach
  155        * would be to remove this from the <code>JViewport</code>
  156        * class and have the <code>JList</code> manage this case by using
  157        * <code>setBackingStoreEnabled</code>.  The default is
  158        * <code>false</code>.
  159        */
  160       protected boolean scrollUnderway = false;
  161   
  162       /*
  163        * Listener that is notified each time the view changes size.
  164        */
  165       private ComponentListener viewListener = null;
  166   
  167       /* Only one <code>ChangeEvent</code> is needed per
  168        * <code>JViewport</code> instance since the
  169        * event's only (read-only) state is the source property.  The source
  170        * of events generated here is always "this".
  171        */
  172       private transient ChangeEvent changeEvent = null;
  173   
  174       /**
  175         * Use <code>graphics.copyArea</code> to implement scrolling.
  176         * This is the fastest for most applications.
  177         *
  178         * @see #setScrollMode
  179         * @since 1.3
  180         */
  181       public static final int BLIT_SCROLL_MODE = 1;
  182   
  183       /**
  184         * Draws viewport contents into an offscreen image.
  185         * This was previously the default mode for <code>JTable</code>.
  186         * This mode may offer advantages over "blit mode"
  187         * in some cases, but it requires a large chunk of extra RAM.
  188         *
  189         * @see #setScrollMode
  190         * @since 1.3
  191         */
  192       public static final int BACKINGSTORE_SCROLL_MODE = 2;
  193   
  194       /**
  195         * This mode uses the very simple method of redrawing the entire
  196         * contents of the scrollpane each time it is scrolled.
  197         * This was the default behavior in Swing 1.0 and Swing 1.1.
  198         * Either of the other two options will provide better performance
  199         * in most cases.
  200         *
  201         * @see #setScrollMode
  202         * @since 1.3
  203         */
  204       public static final int SIMPLE_SCROLL_MODE = 0;
  205   
  206       /**
  207         * @see #setScrollMode
  208         * @since 1.3
  209         */
  210       private int scrollMode = BLIT_SCROLL_MODE;
  211   
  212       //
  213       // Window blitting:
  214       //
  215       // As mentioned in the javadoc when using windowBlit a paint event
  216       // will be generated by the system if copyArea copies a non-visible
  217       // portion of the view (in other words, it copies garbage). We are
  218       // not guaranteed to receive the paint event before other mouse events,
  219       // so we can not be sure we haven't already copied garbage a bunch of
  220       // times to different parts of the view. For that reason when a blit
  221       // happens and the Component is obscured (the check for obscurity
  222       // is not supported on all platforms and is checked via ComponentPeer
  223       // methods) the ivar repaintAll is set to true. When paint is received
  224       // if repaintAll is true (we previously did a blit) it is set to
  225       // false, and if the clip region is smaller than the viewport
  226       // waitingForRepaint is set to true and a timer is started. When
  227       // the timer fires if waitingForRepaint is true, repaint is invoked.
  228       // In the mean time, if the view is asked to scroll and waitingForRepaint
  229       // is true, a blit will not happen, instead the non-backing store case
  230       // of scrolling will happen, which will reset waitingForRepaint.
  231       // waitingForRepaint is set to false in paint when the clip rect is
  232       // bigger (or equal) to the size of the viewport.
  233       // A Timer is used instead of just a repaint as it appeared to offer
  234       // better performance.
  235   
  236   
  237       /**
  238        * This is set to true in <code>setViewPosition</code>
  239        * if doing a window blit and the viewport is obscured.
  240        */
  241       private transient boolean repaintAll;
  242   
  243       /**
  244        * This is set to true in paint, if <code>repaintAll</code>
  245        * is true and the clip rectangle does not match the bounds.
  246        * If true, and scrolling happens the
  247        * repaint manager is not cleared which then allows for the repaint
  248        * previously invoked to succeed.
  249        */
  250       private transient boolean waitingForRepaint;
  251   
  252       /**
  253        * Instead of directly invoking repaint, a <code>Timer</code>
  254        * is started and when it fires, repaint is invoked.
  255        */
  256       private transient Timer repaintTimer;
  257   
  258       /**
  259        * Set to true in paintView when paint is invoked.
  260        */
  261       private transient boolean inBlitPaint;
  262   
  263       /**
  264        * Whether or not a valid view has been installed.
  265        */
  266       private boolean hasHadValidView;
  267   
  268       /** Creates a <code>JViewport</code>. */
  269       public JViewport() {
  270           super();
  271           setLayout(createLayoutManager());
  272           setOpaque(true);
  273           updateUI();
  274           setInheritsPopupMenu(true);
  275       }
  276   
  277   
  278   
  279       /**
  280        * Returns the L&F object that renders this component.
  281        *
  282        * @return a <code>ViewportUI</code> object
  283        * @since 1.3
  284        */
  285       public ViewportUI getUI() {
  286           return (ViewportUI)ui;
  287       }
  288   
  289   
  290       /**
  291        * Sets the L&F object that renders this component.
  292        *
  293        * @param ui  the <code>ViewportUI</code> L&F object
  294        * @see UIDefaults#getUI
  295        * @beaninfo
  296        *        bound: true
  297        *       hidden: true
  298        *    attribute: visualUpdate true
  299        *  description: The UI object that implements the Component's LookAndFeel.
  300        * @since 1.3
  301        */
  302       public void setUI(ViewportUI ui) {
  303           super.setUI(ui);
  304       }
  305   
  306   
  307       /**
  308        * Resets the UI property to a value from the current look and feel.
  309        *
  310        * @see JComponent#updateUI
  311        */
  312       public void updateUI() {
  313           setUI((ViewportUI)UIManager.getUI(this));
  314       }
  315   
  316   
  317       /**
  318        * Returns a string that specifies the name of the L&F class
  319        * that renders this component.
  320        *
  321        * @return the string "ViewportUI"
  322        *
  323        * @see JComponent#getUIClassID
  324        * @see UIDefaults#getUI
  325        */
  326       public String getUIClassID() {
  327           return uiClassID;
  328       }
  329   
  330   
  331       /**
  332        * Sets the <code>JViewport</code>'s one lightweight child,
  333        * which can be <code>null</code>.
  334        * (Since there is only one child which occupies the entire viewport,
  335        * the <code>constraints</code> and <code>index</code>
  336        * arguments are ignored.)
  337        *
  338        * @param child       the lightweight <code>child</code> of the viewport
  339        * @param constraints the <code>constraints</code> to be respected
  340        * @param index       the index
  341        * @see #setView
  342        */
  343       protected void addImpl(Component child, Object constraints, int index) {
  344         setView(child);
  345       }
  346   
  347   
  348       /**
  349        * Removes the <code>Viewport</code>s one lightweight child.
  350        *
  351        * @see #setView
  352        */
  353       public void remove(Component child) {
  354           child.removeComponentListener(viewListener);
  355           super.remove(child);
  356       }
  357   
  358   
  359       /**
  360        * Scrolls the view so that <code>Rectangle</code>
  361        * within the view becomes visible.
  362        * <p>
  363        * This attempts to validate the view before scrolling if the
  364        * view is currently not valid - <code>isValid</code> returns false.
  365        * To avoid excessive validation when the containment hierarchy is
  366        * being created this will not validate if one of the ancestors does not
  367        * have a peer, or there is no validate root ancestor, or one of the
  368        * ancestors is not a <code>Window</code> or <code>Applet</code>.
  369        * <p>
  370        * Note that this method will not scroll outside of the
  371        * valid viewport; for example, if <code>contentRect</code> is larger
  372        * than the viewport, scrolling will be confined to the viewport's
  373        * bounds.
  374        *
  375        * @param contentRect the <code>Rectangle</code> to display
  376        * @see JComponent#isValidateRoot
  377        * @see java.awt.Component#isValid
  378        * @see java.awt.Component#getPeer
  379        */
  380       public void scrollRectToVisible(Rectangle contentRect) {
  381           Component view = getView();
  382   
  383           if (view == null) {
  384               return;
  385           } else {
  386               if (!view.isValid()) {
  387                   // If the view is not valid, validate. scrollRectToVisible
  388                   // may fail if the view is not valid first, contentRect
  389                   // could be bigger than invalid size.
  390                   validateView();
  391               }
  392               int dx, dy;
  393   
  394               dx = positionAdjustment(getWidth(), contentRect.width, contentRect.x);
  395               dy = positionAdjustment(getHeight(), contentRect.height, contentRect.y);
  396   
  397               if (dx != 0 || dy != 0) {
  398                   Point viewPosition = getViewPosition();
  399                   Dimension viewSize = view.getSize();
  400                   int startX = viewPosition.x;
  401                   int startY = viewPosition.y;
  402                   Dimension extent = getExtentSize();
  403   
  404                   viewPosition.x -= dx;
  405                   viewPosition.y -= dy;
  406                   // Only constrain the location if the view is valid. If the
  407                   // the view isn't valid, it typically indicates the view
  408                   // isn't visible yet and most likely has a bogus size as will
  409                   // we, and therefore we shouldn't constrain the scrolling
  410                   if (view.isValid()) {
  411                       if (getParent().getComponentOrientation().isLeftToRight()) {
  412                           if (viewPosition.x + extent.width > viewSize.width) {
  413                               viewPosition.x = Math.max(0, viewSize.width - extent.width);
  414                           } else if (viewPosition.x < 0) {
  415                               viewPosition.x = 0;
  416                           }
  417                       } else {
  418                           if (extent.width > viewSize.width) {
  419                               viewPosition.x = viewSize.width - extent.width;
  420                           } else {
  421                               viewPosition.x = Math.max(0, Math.min(viewSize.width - extent.width, viewPosition.x));
  422                           }
  423                       }
  424                       if (viewPosition.y + extent.height > viewSize.height) {
  425                           viewPosition.y = Math.max(0, viewSize.height -
  426                                                     extent.height);
  427                       }
  428                       else if (viewPosition.y < 0) {
  429                           viewPosition.y = 0;
  430                       }
  431                   }
  432                   if (viewPosition.x != startX || viewPosition.y != startY) {
  433                       setViewPosition(viewPosition);
  434                       // NOTE: How JViewport currently works with the
  435                       // backing store is not foolproof. The sequence of
  436                       // events when setViewPosition
  437                       // (scrollRectToVisible) is called is to reset the
  438                       // views bounds, which causes a repaint on the
  439                       // visible region and sets an ivar indicating
  440                       // scrolling (scrollUnderway). When
  441                       // JViewport.paint is invoked if scrollUnderway is
  442                       // true, the backing store is blitted.  This fails
  443                       // if between the time setViewPosition is invoked
  444                       // and paint is received another repaint is queued
  445                       // indicating part of the view is invalid. There
  446                       // is no way for JViewport to notice another
  447                       // repaint has occured and it ends up blitting
  448                       // what is now a dirty region and the repaint is
  449                       // never delivered.
  450                       // It just so happens JTable encounters this
  451                       // behavior by way of scrollRectToVisible, for
  452                       // this reason scrollUnderway is set to false
  453                       // here, which effectively disables the backing
  454                       // store.
  455                       scrollUnderway = false;
  456                   }
  457               }
  458           }
  459       }
  460   
  461       /**
  462        * Ascends the <code>Viewport</code>'s parents stopping when
  463        * a component is found that returns
  464        * <code>true</code> to <code>isValidateRoot</code>.
  465        * If all the <code>Component</code>'s  parents are visible,
  466        * <code>validate</code> will then be invoked on it. The
  467        * <code>RepaintManager</code> is then invoked with
  468        * <code>removeInvalidComponent</code>. This
  469        * is the synchronous version of a <code>revalidate</code>.
  470        */
  471       private void validateView() {
  472           Component validateRoot = SwingUtilities.getValidateRoot(this, false);
  473   
  474           if (validateRoot == null) {
  475               return;
  476           }
  477   
  478           // Validate the root.
  479           validateRoot.validate();
  480   
  481           // And let the RepaintManager it does not have to validate from
  482           // validateRoot anymore.
  483           RepaintManager rm = RepaintManager.currentManager(this);
  484   
  485           if (rm != null) {
  486               rm.removeInvalidComponent((JComponent)validateRoot);
  487           }
  488       }
  489   
  490        /*  Used by the scrollRectToVisible method to determine the
  491         *  proper direction and amount to move by. The integer variables are named
  492         *  width, but this method is applicable to height also. The code assumes that
  493         *  parentWidth/childWidth are positive and childAt can be negative.
  494         */
  495       private int positionAdjustment(int parentWidth, int childWidth, int childAt)    {
  496   
  497           //   +-----+
  498           //   | --- |     No Change
  499           //   +-----+
  500           if (childAt >= 0 && childWidth + childAt <= parentWidth)    {
  501               return 0;
  502           }
  503   
  504           //   +-----+
  505           //  ---------   No Change
  506           //   +-----+
  507           if (childAt <= 0 && childWidth + childAt >= parentWidth) {
  508               return 0;
  509           }
  510   
  511           //   +-----+          +-----+
  512           //   |   ----    ->   | ----|
  513           //   +-----+          +-----+
  514           if (childAt > 0 && childWidth <= parentWidth)    {
  515               return -childAt + parentWidth - childWidth;
  516           }
  517   
  518           //   +-----+             +-----+
  519           //   |  --------  ->     |--------
  520           //   +-----+             +-----+
  521           if (childAt >= 0 && childWidth >= parentWidth)   {
  522               return -childAt;
  523           }
  524   
  525           //   +-----+          +-----+
  526           // ----    |     ->   |---- |
  527           //   +-----+          +-----+
  528           if (childAt <= 0 && childWidth <= parentWidth)   {
  529               return -childAt;
  530           }
  531   
  532           //   +-----+             +-----+
  533           //-------- |      ->   --------|
  534           //   +-----+             +-----+
  535           if (childAt < 0 && childWidth >= parentWidth)    {
  536               return -childAt + parentWidth - childWidth;
  537           }
  538   
  539           return 0;
  540       }
  541   
  542   
  543       /**
  544        * The viewport "scrolls" its child (called the "view") by the
  545        * normal parent/child clipping (typically the view is moved in
  546        * the opposite direction of the scroll).  A non-<code>null</code> border,
  547        * or non-zero insets, isn't supported, to prevent the geometry
  548        * of this component from becoming complex enough to inhibit
  549        * subclassing.  To create a <code>JViewport</code> with a border,
  550        * add it to a <code>JPanel</code> that has a border.
  551        * <p>Note:  If <code>border</code> is non-<code>null</code>, this
  552        * method will throw an exception as borders are not supported on
  553        * a <code>JViewPort</code>.
  554        *
  555        * @param border the <code>Border</code> to set
  556        * @exception IllegalArgumentException this method is not implemented
  557        */
  558       public final void setBorder(Border border) {
  559           if (border != null) {
  560               throw new IllegalArgumentException("JViewport.setBorder() not supported");
  561           }
  562       }
  563   
  564   
  565       /**
  566        * Returns the insets (border) dimensions as (0,0,0,0), since borders
  567        * are not supported on a <code>JViewport</code>.
  568        *
  569        * @return a <code>Rectange</code> of zero dimension and zero origin
  570        * @see #setBorder
  571        */
  572       public final Insets getInsets() {
  573           return new Insets(0, 0, 0, 0);
  574       }
  575   
  576       /**
  577        * Returns an <code>Insets</code> object containing this
  578        * <code>JViewport</code>s inset values.  The passed-in
  579        * <code>Insets</code> object will be reinitialized, and
  580        * all existing values within this object are overwritten.
  581        *
  582        * @param insets the <code>Insets</code> object which can be reused
  583        * @return this viewports inset values
  584        * @see #getInsets
  585        * @beaninfo
  586        *   expert: true
  587        */
  588       public final Insets getInsets(Insets insets) {
  589           insets.left = insets.top = insets.right = insets.bottom = 0;
  590           return insets;
  591       }
  592   
  593   
  594       private Graphics getBackingStoreGraphics(Graphics g) {
  595           Graphics bsg = backingStoreImage.getGraphics();
  596           bsg.setColor(g.getColor());
  597           bsg.setFont(g.getFont());
  598           bsg.setClip(g.getClipBounds());
  599           return bsg;
  600       }
  601   
  602   
  603       private void paintViaBackingStore(Graphics g) {
  604           Graphics bsg = getBackingStoreGraphics(g);
  605           try {
  606               super.paint(bsg);
  607               g.drawImage(backingStoreImage, 0, 0, this);
  608           } finally {
  609               bsg.dispose();
  610           }
  611       }
  612   
  613       private void paintViaBackingStore(Graphics g, Rectangle oClip) {
  614           Graphics bsg = getBackingStoreGraphics(g);
  615           try {
  616               super.paint(bsg);
  617               g.setClip(oClip);
  618               g.drawImage(backingStoreImage, 0, 0, this);
  619           } finally {
  620               bsg.dispose();
  621           }
  622       }
  623   
  624       /**
  625        * The <code>JViewport</code> overrides the default implementation of
  626        * this method (in <code>JComponent</code>) to return false.
  627        * This ensures
  628        * that the drawing machinery will call the <code>Viewport</code>'s
  629        * <code>paint</code>
  630        * implementation rather than messaging the <code>JViewport</code>'s
  631        * children directly.
  632        *
  633        * @return false
  634        */
  635       public boolean isOptimizedDrawingEnabled() {
  636           return false;
  637       }
  638   
  639       /**
  640        * Returns true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE} to cause
  641        * painting to originate from {@code JViewport}, or one of its
  642        * ancestors. Otherwise returns {@code false}.
  643        *
  644        * @return true if if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE}.
  645        * @see JComponent#isPaintingOrigin()
  646        */
  647       protected boolean isPaintingOrigin() {
  648           return scrollMode == BACKINGSTORE_SCROLL_MODE;
  649       }
  650   
  651   
  652       /**
  653        * Only used by the paint method below.
  654        */
  655       private Point getViewLocation() {
  656           Component view = getView();
  657           if (view != null) {
  658               return view.getLocation();
  659           }
  660           else {
  661               return new Point(0,0);
  662           }
  663       }
  664   
  665       /**
  666        * Depending on whether the <code>backingStore</code> is enabled,
  667        * either paint the image through the backing store or paint
  668        * just the recently exposed part, using the backing store
  669        * to "blit" the remainder.
  670        * <blockquote>
  671        * The term "blit" is the pronounced version of the PDP-10
  672        * BLT (BLock Transfer) instruction, which copied a block of
  673        * bits. (In case you were curious.)
  674        * </blockquote>
  675        *
  676        * @param g the <code>Graphics</code> context within which to paint
  677        */
  678       public void paint(Graphics g)
  679       {
  680           int width = getWidth();
  681           int height = getHeight();
  682   
  683           if ((width <= 0) || (height <= 0)) {
  684               return;
  685           }
  686   
  687           if (inBlitPaint) {
  688               // We invoked paint as part of copyArea cleanup, let it through.
  689               super.paint(g);
  690               return;
  691           }
  692   
  693           if (repaintAll) {
  694               repaintAll = false;
  695               Rectangle clipB = g.getClipBounds();
  696               if (clipB.width < getWidth() ||
  697                   clipB.height < getHeight()) {
  698                   waitingForRepaint = true;
  699                   if (repaintTimer == null) {
  700                       repaintTimer = createRepaintTimer();
  701                   }
  702                   repaintTimer.stop();
  703                   repaintTimer.start();
  704                   // We really don't need to paint, a future repaint will
  705                   // take care of it, but if we don't we get an ugly flicker.
  706               }
  707               else {
  708                   if (repaintTimer != null) {
  709                       repaintTimer.stop();
  710                   }
  711                   waitingForRepaint = false;
  712               }
  713           }
  714           else if (waitingForRepaint) {
  715               // Need a complete repaint before resetting waitingForRepaint
  716               Rectangle clipB = g.getClipBounds();
  717               if (clipB.width >= getWidth() &&
  718                   clipB.height >= getHeight()) {
  719                   waitingForRepaint = false;
  720                   repaintTimer.stop();
  721               }
  722           }
  723   
  724           if (!backingStore || isBlitting() || getView() == null) {
  725               super.paint(g);
  726               lastPaintPosition = getViewLocation();
  727               return;
  728           }
  729   
  730           // If the view is smaller than the viewport and we are not opaque
  731           // (that is, we won't paint our background), we should set the
  732           // clip. Otherwise, as the bounds of the view vary, we will
  733           // blit garbage into the exposed areas.
  734           Rectangle viewBounds = getView().getBounds();
  735           if (!isOpaque()) {
  736               g.clipRect(0, 0, viewBounds.width, viewBounds.height);
  737           }
  738   
  739           if (backingStoreImage == null) {
  740               // Backing store is enabled but this is the first call to paint.
  741               // Create the backing store, paint it and then copy to g.
  742               // The backing store image will be created with the size of
  743               // the viewport. We must make sure the clip region is the
  744               // same size, otherwise when scrolling the backing image
  745               // the region outside of the clipped region will not be painted,
  746               // and result in empty areas.
  747               backingStoreImage = createImage(width, height);
  748               Rectangle clip = g.getClipBounds();
  749               if (clip.width != width || clip.height != height) {
  750                   if (!isOpaque()) {
  751                       g.setClip(0, 0, Math.min(viewBounds.width, width),
  752                                 Math.min(viewBounds.height, height));
  753                   }
  754                   else {
  755                       g.setClip(0, 0, width, height);
  756                   }
  757                   paintViaBackingStore(g, clip);
  758               }
  759               else {
  760                   paintViaBackingStore(g);
  761               }
  762           }
  763           else {
  764               if (!scrollUnderway || lastPaintPosition.equals(getViewLocation())) {
  765                   // No scrolling happened: repaint required area via backing store.
  766                   paintViaBackingStore(g);
  767               } else {
  768                   // The image was scrolled. Manipulate the backing store and flush it to g.
  769                   Point blitFrom = new Point();
  770                   Point blitTo = new Point();
  771                   Dimension blitSize = new Dimension();
  772                   Rectangle blitPaint = new Rectangle();
  773   
  774                   Point newLocation = getViewLocation();
  775                   int dx = newLocation.x - lastPaintPosition.x;
  776                   int dy = newLocation.y - lastPaintPosition.y;
  777                   boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize, blitPaint);
  778                   if (!canBlit) {
  779                       // The image was either moved diagonally or
  780                       // moved by more than the image size: paint normally.
  781                       paintViaBackingStore(g);
  782                   } else {
  783                       int bdx = blitTo.x - blitFrom.x;
  784                       int bdy = blitTo.y - blitFrom.y;
  785   
  786                       // Move the relevant part of the backing store.
  787                       Rectangle clip = g.getClipBounds();
  788                       // We don't want to inherit the clip region when copying
  789                       // bits, if it is inherited it will result in not moving
  790                       // all of the image resulting in garbage appearing on
  791                       // the screen.
  792                       g.setClip(0, 0, width, height);
  793                       Graphics bsg = getBackingStoreGraphics(g);
  794                       try {
  795                           bsg.copyArea(blitFrom.x, blitFrom.y, blitSize.width, blitSize.height, bdx, bdy);
  796   
  797                           g.setClip(clip.x, clip.y, clip.width, clip.height);
  798                           // Paint the rest of the view; the part that has just been exposed.
  799                           Rectangle r = viewBounds.intersection(blitPaint);
  800                           bsg.setClip(r);
  801                           super.paint(bsg);
  802   
  803                           // Copy whole of the backing store to g.
  804                           g.drawImage(backingStoreImage, 0, 0, this);
  805                       } finally {
  806                           bsg.dispose();
  807                       }
  808                   }
  809               }
  810           }
  811           lastPaintPosition = getViewLocation();
  812           scrollUnderway = false;
  813       }
  814   
  815   
  816       /**
  817        * Sets the bounds of this viewport.  If the viewport's width
  818        * or height has changed, fire a <code>StateChanged</code> event.
  819        *
  820        * @param x left edge of the origin
  821        * @param y top edge of the origin
  822        * @param w width in pixels
  823        * @param h height in pixels
  824        *
  825        * @see JComponent#reshape(int, int, int, int)
  826        */
  827       public void reshape(int x, int y, int w, int h) {
  828           boolean sizeChanged = (getWidth() != w) || (getHeight() != h);
  829           if (sizeChanged) {
  830               backingStoreImage = null;
  831           }
  832           super.reshape(x, y, w, h);
  833           if (sizeChanged) {
  834               fireStateChanged();
  835           }
  836       }
  837   
  838   
  839       /**
  840         * Used to control the method of scrolling the viewport contents.
  841         * You may want to change this mode to get maximum performance for your
  842         * use case.
  843         *
  844         * @param mode one of the following values:
  845         * <ul>
  846         * <li> JViewport.BLIT_SCROLL_MODE
  847         * <li> JViewport.BACKINGSTORE_SCROLL_MODE
  848         * <li> JViewport.SIMPLE_SCROLL_MODE
  849         * </ul>
  850         *
  851         * @see #BLIT_SCROLL_MODE
  852         * @see #BACKINGSTORE_SCROLL_MODE
  853         * @see #SIMPLE_SCROLL_MODE
  854         *
  855         * @beaninfo
  856         *        bound: false
  857         *  description: Method of moving contents for incremental scrolls.
  858         *         enum: BLIT_SCROLL_MODE JViewport.BLIT_SCROLL_MODE
  859         *               BACKINGSTORE_SCROLL_MODE JViewport.BACKINGSTORE_SCROLL_MODE
  860         *               SIMPLE_SCROLL_MODE JViewport.SIMPLE_SCROLL_MODE
  861         *
  862         * @since 1.3
  863         */
  864       public void setScrollMode(int mode) {
  865           scrollMode = mode;
  866           backingStore = mode == BACKINGSTORE_SCROLL_MODE;
  867       }
  868   
  869       /**
  870         * Returns the current scrolling mode.
  871         *
  872         * @return the <code>scrollMode</code> property
  873         * @see #setScrollMode
  874         * @since 1.3
  875         */
  876       public int getScrollMode() {
  877           return scrollMode;
  878       }
  879   
  880       /**
  881        * Returns <code>true</code> if this viewport is maintaining
  882        * an offscreen image of its contents.
  883        *
  884        * @return <code>true</code> if <code>scrollMode</code> is
  885        *    <code>BACKINGSTORE_SCROLL_MODE</code>
  886        *
  887        * @deprecated As of Java 2 platform v1.3, replaced by
  888        *             <code>getScrollMode()</code>.
  889        */
  890       @Deprecated
  891       public boolean isBackingStoreEnabled() {
  892           return scrollMode == BACKINGSTORE_SCROLL_MODE;
  893       }
  894   
  895   
  896       /**
  897        * If true if this viewport will maintain an offscreen
  898        * image of its contents.  The image is used to reduce the cost
  899        * of small one dimensional changes to the <code>viewPosition</code>.
  900        * Rather than repainting the entire viewport we use
  901        * <code>Graphics.copyArea</code> to effect some of the scroll.
  902        *
  903        * @param enabled if true, maintain an offscreen backing store
  904        *
  905        * @deprecated As of Java 2 platform v1.3, replaced by
  906        *             <code>setScrollMode()</code>.
  907        */
  908       @Deprecated
  909       public void setBackingStoreEnabled(boolean enabled) {
  910           if (enabled) {
  911               setScrollMode(BACKINGSTORE_SCROLL_MODE);
  912           } else {
  913               setScrollMode(BLIT_SCROLL_MODE);
  914           }
  915       }
  916   
  917       private boolean isBlitting() {
  918           Component view = getView();
  919           return (scrollMode == BLIT_SCROLL_MODE) &&
  920                  (view instanceof JComponent) && view.isOpaque();
  921       }
  922   
  923   
  924       /**
  925        * Returns the <code>JViewport</code>'s one child or <code>null</code>.
  926        *
  927        * @return the viewports child, or <code>null</code> if none exists
  928        *
  929        * @see #setView
  930        */
  931       public Component getView() {
  932           return (getComponentCount() > 0) ? getComponent(0) : null;
  933       }
  934   
  935       /**
  936        * Sets the <code>JViewport</code>'s one lightweight child
  937        * (<code>view</code>), which can be <code>null</code>.
  938        *
  939        * @param view the viewport's new lightweight child
  940        *
  941        * @see #getView
  942        */
  943       public void setView(Component view) {
  944   
  945           /* Remove the viewport's existing children, if any.
  946            * Note that removeAll() isn't used here because it
  947            * doesn't call remove() (which JViewport overrides).
  948            */
  949           int n = getComponentCount();
  950           for(int i = n - 1; i >= 0; i--) {
  951               remove(getComponent(i));
  952           }
  953   
  954           isViewSizeSet = false;
  955   
  956           if (view != null) {
  957               super.addImpl(view, null, -1);
  958               viewListener = createViewListener();
  959               view.addComponentListener(viewListener);
  960           }
  961   
  962           if (hasHadValidView) {
  963               // Only fire a change if a view has been installed.
  964               fireStateChanged();
  965           }
  966           else if (view != null) {
  967               hasHadValidView = true;
  968           }
  969   
  970           revalidate();
  971           repaint();
  972       }
  973   
  974   
  975       /**
  976        * If the view's size hasn't been explicitly set, return the
  977        * preferred size, otherwise return the view's current size.
  978        * If there is no view, return 0,0.
  979        *
  980        * @return a <code>Dimension</code> object specifying the size of the view
  981        */
  982       public Dimension getViewSize() {
  983           Component view = getView();
  984   
  985           if (view == null) {
  986               return new Dimension(0,0);
  987           }
  988           else if (isViewSizeSet) {
  989               return view.getSize();
  990           }
  991           else {
  992               return view.getPreferredSize();
  993           }
  994       }
  995   
  996   
  997       /**
  998        * Sets the size of the view.  A state changed event will be fired.
  999        *
 1000        * @param newSize a <code>Dimension</code> object specifying the new
 1001        *          size of the view
 1002        */
 1003       public void setViewSize(Dimension newSize) {
 1004           Component view = getView();
 1005           if (view != null) {
 1006               Dimension oldSize = view.getSize();
 1007               if (!newSize.equals(oldSize)) {
 1008                   // scrollUnderway will be true if this is invoked as the
 1009                   // result of a validate and setViewPosition was previously
 1010                   // invoked.
 1011                   scrollUnderway = false;
 1012                   view.setSize(newSize);
 1013                   isViewSizeSet = true;
 1014                   fireStateChanged();
 1015               }
 1016           }
 1017       }
 1018   
 1019       /**
 1020        * Returns the view coordinates that appear in the upper left
 1021        * hand corner of the viewport, or 0,0 if there's no view.
 1022        *
 1023        * @return a <code>Point</code> object giving the upper left coordinates
 1024        */
 1025       public Point getViewPosition() {
 1026           Component view = getView();
 1027           if (view != null) {
 1028               Point p = view.getLocation();
 1029               p.x = -p.x;
 1030               p.y = -p.y;
 1031               return p;
 1032           }
 1033           else {
 1034               return new Point(0,0);
 1035           }
 1036       }
 1037   
 1038   
 1039       /**
 1040        * Sets the view coordinates that appear in the upper left
 1041        * hand corner of the viewport, does nothing if there's no view.
 1042        *
 1043        * @param p  a <code>Point</code> object giving the upper left coordinates
 1044        */
 1045       public void setViewPosition(Point p)
 1046       {
 1047           Component view = getView();
 1048           if (view == null) {
 1049               return;
 1050           }
 1051   
 1052           int oldX, oldY, x = p.x, y = p.y;
 1053   
 1054           /* Collect the old x,y values for the views location
 1055            * and do the song and dance to avoid allocating
 1056            * a Rectangle object if we don't have to.
 1057            */
 1058           if (view instanceof JComponent) {
 1059               JComponent c = (JComponent)view;
 1060               oldX = c.getX();
 1061               oldY = c.getY();
 1062           }
 1063           else {
 1064               Rectangle r = view.getBounds();
 1065               oldX = r.x;
 1066               oldY = r.y;
 1067           }
 1068   
 1069           /* The view scrolls in the opposite direction to mouse
 1070            * movement.
 1071            */
 1072           int newX = -x;
 1073           int newY = -y;
 1074   
 1075           if ((oldX != newX) || (oldY != newY)) {
 1076               if (!waitingForRepaint && isBlitting() && canUseWindowBlitter()) {
 1077                   RepaintManager rm = RepaintManager.currentManager(this);
 1078                   // The cast to JComponent will work, if view is not
 1079                   // a JComponent, isBlitting will return false.
 1080                   JComponent jview = (JComponent)view;
 1081                   Rectangle dirty = rm.getDirtyRegion(jview);
 1082                   if (dirty == null || !dirty.contains(jview.getVisibleRect())) {
 1083                       rm.beginPaint();
 1084                       try {
 1085                           Graphics g = JComponent.safelyGetGraphics(this);
 1086                           flushViewDirtyRegion(g, dirty);
 1087                           view.setLocation(newX, newY);
 1088                           g.setClip(0,0,getWidth(), Math.min(getHeight(),
 1089                                                              jview.getHeight()));
 1090                           // Repaint the complete component if the blit succeeded
 1091                           // and needsRepaintAfterBlit returns true.
 1092                           repaintAll = (windowBlitPaint(g) &&
 1093                                         needsRepaintAfterBlit());
 1094                           g.dispose();
 1095                           rm.markCompletelyClean((JComponent)getParent());
 1096                           rm.markCompletelyClean(this);
 1097                           rm.markCompletelyClean(jview);
 1098                       } finally {
 1099                           rm.endPaint();
 1100                       }
 1101                   }
 1102                   else {
 1103                       // The visible region is dirty, no point in doing copyArea
 1104                       view.setLocation(newX, newY);
 1105                       repaintAll = false;
 1106                   }
 1107               }
 1108               else {
 1109                   scrollUnderway = true;
 1110                   // This calls setBounds(), and then repaint().
 1111                   view.setLocation(newX, newY);
 1112                   repaintAll = false;
 1113               }
 1114               // we must validate the hierarchy to not break the hw/lw mixing
 1115               revalidate();
 1116               fireStateChanged();
 1117           }
 1118       }
 1119   
 1120   
 1121       /**
 1122        * Returns a rectangle whose origin is <code>getViewPosition</code>
 1123        * and size is <code>getExtentSize</code>.
 1124        * This is the visible part of the view, in view coordinates.
 1125        *
 1126        * @return a <code>Rectangle</code> giving the visible part of
 1127        *          the view using view coordinates.
 1128        */
 1129       public Rectangle getViewRect() {
 1130           return new Rectangle(getViewPosition(), getExtentSize());
 1131       }
 1132   
 1133   
 1134       /**
 1135        * Computes the parameters for a blit where the backing store image
 1136        * currently contains <code>oldLoc</code> in the upper left hand corner
 1137        * and we're scrolling to <code>newLoc</code>.
 1138        * The parameters are modified
 1139        * to return the values required for the blit.
 1140        *
 1141        * @param dx  the horizontal delta
 1142        * @param dy  the vertical delta
 1143        * @param blitFrom the <code>Point</code> we're blitting from
 1144        * @param blitTo the <code>Point</code> we're blitting to
 1145        * @param blitSize the <code>Dimension</code> of the area to blit
 1146        * @param blitPaint the area to blit
 1147        * @return  true if the parameters are modified and we're ready to blit;
 1148        *          false otherwise
 1149        */
 1150       protected boolean computeBlit(
 1151           int dx,
 1152           int dy,
 1153           Point blitFrom,
 1154           Point blitTo,
 1155           Dimension blitSize,
 1156           Rectangle blitPaint)
 1157       {
 1158           int dxAbs = Math.abs(dx);
 1159           int dyAbs = Math.abs(dy);
 1160           Dimension extentSize = getExtentSize();
 1161   
 1162           if ((dx == 0) && (dy != 0) && (dyAbs < extentSize.height)) {
 1163               if (dy < 0) {
 1164                   blitFrom.y = -dy;
 1165                   blitTo.y = 0;
 1166                   blitPaint.y = extentSize.height + dy;
 1167               }
 1168               else {
 1169                   blitFrom.y = 0;
 1170                   blitTo.y = dy;
 1171                   blitPaint.y = 0;
 1172               }
 1173   
 1174               blitPaint.x = blitFrom.x = blitTo.x = 0;
 1175   
 1176               blitSize.width = extentSize.width;
 1177               blitSize.height = extentSize.height - dyAbs;
 1178   
 1179               blitPaint.width = extentSize.width;
 1180               blitPaint.height = dyAbs;
 1181   
 1182               return true;
 1183           }
 1184   
 1185           else if ((dy == 0) && (dx != 0) && (dxAbs < extentSize.width)) {
 1186               if (dx < 0) {
 1187                   blitFrom.x = -dx;
 1188                   blitTo.x = 0;
 1189                   blitPaint.x = extentSize.width + dx;
 1190               }
 1191               else {
 1192                   blitFrom.x = 0;
 1193                   blitTo.x = dx;
 1194                   blitPaint.x = 0;
 1195               }
 1196   
 1197               blitPaint.y = blitFrom.y = blitTo.y = 0;
 1198   
 1199               blitSize.width = extentSize.width - dxAbs;
 1200               blitSize.height = extentSize.height;
 1201   
 1202               blitPaint.width = dxAbs;
 1203               blitPaint.height = extentSize.height;
 1204   
 1205               return true;
 1206           }
 1207   
 1208           else {
 1209               return false;
 1210           }
 1211       }
 1212   
 1213   
 1214       /**
 1215        * Returns the size of the visible part of the view in view coordinates.
 1216        *
 1217        * @return a <code>Dimension</code> object giving the size of the view
 1218        */
 1219       @Transient
 1220       public Dimension getExtentSize() {
 1221           return getSize();
 1222       }
 1223   
 1224   
 1225       /**
 1226        * Converts a size in pixel coordinates to view coordinates.
 1227        * Subclasses of viewport that support "logical coordinates"
 1228        * will override this method.
 1229        *
 1230        * @param size  a <code>Dimension</code> object using pixel coordinates
 1231        * @return a <code>Dimension</code> object converted to view coordinates
 1232        */
 1233       public Dimension toViewCoordinates(Dimension size) {
 1234           return new Dimension(size);
 1235       }
 1236   
 1237       /**
 1238        * Converts a point in pixel coordinates to view coordinates.
 1239        * Subclasses of viewport that support "logical coordinates"
 1240        * will override this method.
 1241        *
 1242        * @param p  a <code>Point</code> object using pixel coordinates
 1243        * @return a <code>Point</code> object converted to view coordinates
 1244        */
 1245       public Point toViewCoordinates(Point p) {
 1246           return new Point(p);
 1247       }
 1248   
 1249   
 1250       /**
 1251        * Sets the size of the visible part of the view using view coordinates.
 1252        *
 1253        * @param newExtent  a <code>Dimension</code> object specifying
 1254        *          the size of the view
 1255        */
 1256       public void setExtentSize(Dimension newExtent) {
 1257           Dimension oldExtent = getExtentSize();
 1258           if (!newExtent.equals(oldExtent)) {
 1259               setSize(newExtent);
 1260               fireStateChanged();
 1261           }
 1262       }
 1263   
 1264       /**
 1265        * A listener for the view.
 1266        * <p>
 1267        * <strong>Warning:</strong>
 1268        * Serialized objects of this class will not be compatible with
 1269        * future Swing releases. The current serialization support is
 1270        * appropriate for short term storage or RMI between applications running
 1271        * the same version of Swing.  As of 1.4, support for long term storage
 1272        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1273        * has been added to the <code>java.beans</code> package.
 1274        * Please see {@link java.beans.XMLEncoder}.
 1275        */
 1276       protected class ViewListener extends ComponentAdapter implements Serializable
 1277       {
 1278           public void componentResized(ComponentEvent e) {
 1279               fireStateChanged();
 1280               revalidate();
 1281           }
 1282       }
 1283   
 1284       /**
 1285        * Creates a listener for the view.
 1286        * @return a <code>ViewListener</code>
 1287        */
 1288       protected ViewListener createViewListener() {
 1289           return new ViewListener();
 1290       }
 1291   
 1292   
 1293       /**
 1294        * Subclassers can override this to install a different
 1295        * layout manager (or <code>null</code>) in the constructor.  Returns
 1296        * the <code>LayoutManager</code> to install on the <code>JViewport</code>.
 1297        *
 1298        * @return a <code>LayoutManager</code>
 1299        */
 1300       protected LayoutManager createLayoutManager() {
 1301           return ViewportLayout.SHARED_INSTANCE;
 1302       }
 1303   
 1304   
 1305       /**
 1306        * Adds a <code>ChangeListener</code> to the list that is
 1307        * notified each time the view's
 1308        * size, position, or the viewport's extent size has changed.
 1309        *
 1310        * @param l the <code>ChangeListener</code> to add
 1311        * @see #removeChangeListener
 1312        * @see #setViewPosition
 1313        * @see #setViewSize
 1314        * @see #setExtentSize
 1315        */
 1316       public void addChangeListener(ChangeListener l) {
 1317           listenerList.add(ChangeListener.class, l);
 1318       }
 1319   
 1320       /**
 1321        * Removes a <code>ChangeListener</code> from the list that's notified each
 1322        * time the views size, position, or the viewports extent size
 1323        * has changed.
 1324        *
 1325        * @param l the <code>ChangeListener</code> to remove
 1326        * @see #addChangeListener
 1327        */
 1328       public void removeChangeListener(ChangeListener l) {
 1329           listenerList.remove(ChangeListener.class, l);
 1330       }
 1331   
 1332       /**
 1333        * Returns an array of all the <code>ChangeListener</code>s added
 1334        * to this JViewport with addChangeListener().
 1335        *
 1336        * @return all of the <code>ChangeListener</code>s added or an empty
 1337        *         array if no listeners have been added
 1338        * @since 1.4
 1339        */
 1340       public ChangeListener[] getChangeListeners() {
 1341           return listenerList.getListeners(ChangeListener.class);
 1342       }
 1343   
 1344       /**
 1345        * Notifies all <code>ChangeListeners</code> when the views
 1346        * size, position, or the viewports extent size has changed.
 1347        *
 1348        * @see #addChangeListener
 1349        * @see #removeChangeListener
 1350        * @see EventListenerList
 1351        */
 1352       protected void fireStateChanged()
 1353       {
 1354           Object[] listeners = listenerList.getListenerList();
 1355           for (int i = listeners.length - 2; i >= 0; i -= 2) {
 1356               if (listeners[i] == ChangeListener.class) {
 1357                   if (changeEvent == null) {
 1358                       changeEvent = new ChangeEvent(this);
 1359                   }
 1360                   ((ChangeListener)listeners[i + 1]).stateChanged(changeEvent);
 1361               }
 1362           }
 1363       }
 1364   
 1365       /**
 1366        * Always repaint in the parents coordinate system to make sure
 1367        * only one paint is performed by the <code>RepaintManager</code>.
 1368        *
 1369        * @param     tm   maximum time in milliseconds before update
 1370        * @param     x    the <code>x</code> coordinate (pixels over from left)
 1371        * @param     y    the <code>y</code> coordinate (pixels down from top)
 1372        * @param     w    the width
 1373        * @param     h   the height
 1374        * @see       java.awt.Component#update(java.awt.Graphics)
 1375        */
 1376       public void repaint(long tm, int x, int y, int w, int h) {
 1377           Container parent = getParent();
 1378           if(parent != null)
 1379               parent.repaint(tm,x+getX(),y+getY(),w,h);
 1380           else
 1381               super.repaint(tm,x,y,w,h);
 1382       }
 1383   
 1384   
 1385       /**
 1386        * Returns a string representation of this <code>JViewport</code>.
 1387        * This method
 1388        * is intended to be used only for debugging purposes, and the
 1389        * content and format of the returned string may vary between
 1390        * implementations. The returned string may be empty but may not
 1391        * be <code>null</code>.
 1392        *
 1393        * @return  a string representation of this <code>JViewport</code>
 1394        */
 1395       protected String paramString() {
 1396           String isViewSizeSetString = (isViewSizeSet ?
 1397                                         "true" : "false");
 1398           String lastPaintPositionString = (lastPaintPosition != null ?
 1399                                             lastPaintPosition.toString() : "");
 1400           String scrollUnderwayString = (scrollUnderway ?
 1401                                          "true" : "false");
 1402   
 1403           return super.paramString() +
 1404           ",isViewSizeSet=" + isViewSizeSetString +
 1405           ",lastPaintPosition=" + lastPaintPositionString +
 1406           ",scrollUnderway=" + scrollUnderwayString;
 1407       }
 1408   
 1409       //
 1410       // Following is used when doBlit is true.
 1411       //
 1412   
 1413       /**
 1414        * Notifies listeners of a property change. This is subclassed to update
 1415        * the <code>windowBlit</code> property.
 1416        * (The <code>putClientProperty</code> property is final).
 1417        *
 1418        * @param propertyName a string containing the property name
 1419        * @param oldValue the old value of the property
 1420        * @param newValue  the new value of the property
 1421        */
 1422       protected void firePropertyChange(String propertyName, Object oldValue,
 1423                                         Object newValue) {
 1424           super.firePropertyChange(propertyName, oldValue, newValue);
 1425           if (propertyName.equals(EnableWindowBlit)) {
 1426               if (newValue != null) {
 1427                   setScrollMode(BLIT_SCROLL_MODE);
 1428               } else {
 1429                   setScrollMode(SIMPLE_SCROLL_MODE);
 1430               }
 1431           }
 1432       }
 1433   
 1434       /**
 1435        * Returns true if the component needs to be completely repainted after
 1436        * a blit and a paint is received.
 1437        */
 1438       private boolean needsRepaintAfterBlit() {
 1439           // Find the first heavy weight ancestor. isObscured and
 1440           // canDetermineObscurity are only appropriate for heavy weights.
 1441           Component heavyParent = getParent();
 1442   
 1443           while (heavyParent != null && heavyParent.isLightweight()) {
 1444               heavyParent = heavyParent.getParent();
 1445           }
 1446   
 1447           if (heavyParent != null) {
 1448               ComponentPeer peer = heavyParent.getPeer();
 1449   
 1450               if (peer != null && peer.canDetermineObscurity() &&
 1451                                   !peer.isObscured()) {
 1452                   // The peer says we aren't obscured, therefore we can assume
 1453                   // that we won't later be messaged to paint a portion that
 1454                   // we tried to blit that wasn't valid.
 1455                   // It is certainly possible that when we blited we were
 1456                   // obscured, and by the time this is invoked we aren't, but the
 1457                   // chances of that happening are pretty slim.
 1458                   return false;
 1459               }
 1460           }
 1461           return true;
 1462       }
 1463   
 1464       private Timer createRepaintTimer() {
 1465           Timer timer = new Timer(300, new ActionListener() {
 1466               public void actionPerformed(ActionEvent ae) {
 1467                   // waitingForRepaint will be false if a paint came down
 1468                   // with the complete clip rect, in which case we don't
 1469                   // have to cause a repaint.
 1470                   if (waitingForRepaint) {
 1471                       repaint();
 1472                   }
 1473               }
 1474           });
 1475           timer.setRepeats(false);
 1476           return timer;
 1477       }
 1478   
 1479       /**
 1480        * If the repaint manager has a dirty region for the view, the view is
 1481        * asked to paint.
 1482        *
 1483        * @param g  the <code>Graphics</code> context within which to paint
 1484        */
 1485       private void flushViewDirtyRegion(Graphics g, Rectangle dirty) {
 1486           JComponent view = (JComponent) getView();
 1487           if(dirty != null && dirty.width > 0 && dirty.height > 0) {
 1488               dirty.x += view.getX();
 1489               dirty.y += view.getY();
 1490               Rectangle clip = g.getClipBounds();
 1491               if (clip == null) {
 1492                   // Only happens in 1.2
 1493                   g.setClip(0, 0, getWidth(), getHeight());
 1494               }
 1495               g.clipRect(dirty.x, dirty.y, dirty.width, dirty.height);
 1496               clip = g.getClipBounds();
 1497               // Only paint the dirty region if it is visible.
 1498               if (clip.width > 0 && clip.height > 0) {
 1499                   paintView(g);
 1500               }
 1501           }
 1502       }
 1503   
 1504       /**
 1505        * Used when blitting.
 1506        *
 1507        * @param g  the <code>Graphics</code> context within which to paint
 1508        * @return true if blitting succeeded; otherwise false
 1509        */
 1510       private boolean windowBlitPaint(Graphics g) {
 1511           int width = getWidth();
 1512           int height = getHeight();
 1513   
 1514           if ((width == 0) || (height == 0)) {
 1515               return false;
 1516           }
 1517   
 1518           boolean retValue;
 1519           RepaintManager rm = RepaintManager.currentManager(this);
 1520           JComponent view = (JComponent) getView();
 1521   
 1522           if (lastPaintPosition == null ||
 1523               lastPaintPosition.equals(getViewLocation())) {
 1524               paintView(g);
 1525               retValue = false;
 1526           } else {
 1527               // The image was scrolled. Manipulate the backing store and flush
 1528               // it to g.
 1529               Point blitFrom = new Point();
 1530               Point blitTo = new Point();
 1531               Dimension blitSize = new Dimension();
 1532               Rectangle blitPaint = new Rectangle();
 1533   
 1534               Point newLocation = getViewLocation();
 1535               int dx = newLocation.x - lastPaintPosition.x;
 1536               int dy = newLocation.y - lastPaintPosition.y;
 1537               boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize,
 1538                                             blitPaint);
 1539               if (!canBlit) {
 1540                   paintView(g);
 1541                   retValue = false;
 1542               } else {
 1543                   // Prepare the rest of the view; the part that has just been
 1544                   // exposed.
 1545                   Rectangle r = view.getBounds().intersection(blitPaint);
 1546                   r.x -= view.getX();
 1547                   r.y -= view.getY();
 1548   
 1549                   blitDoubleBuffered(view, g, r.x, r.y, r.width, r.height,
 1550                                      blitFrom.x, blitFrom.y, blitTo.x, blitTo.y,
 1551                                      blitSize.width, blitSize.height);
 1552                   retValue = true;
 1553               }
 1554           }
 1555           lastPaintPosition = getViewLocation();
 1556           return retValue;
 1557       }
 1558   
 1559       //
 1560       // NOTE: the code below uses paintForceDoubleBuffered for historical
 1561       // reasons.  If we're going to allow a blit we've already accounted for
 1562       // everything that paintImmediately and _paintImmediately does, for that
 1563       // reason we call into paintForceDoubleBuffered to diregard whether or
 1564       // not setDoubleBuffered(true) was invoked on the view.
 1565       //
 1566   
 1567       private void blitDoubleBuffered(JComponent view, Graphics g,
 1568                                       int clipX, int clipY, int clipW, int clipH,
 1569                                       int blitFromX, int blitFromY, int blitToX, int blitToY,
 1570                                       int blitW, int blitH) {
 1571           // NOTE:
 1572           //   blitFrom/blitTo are in JViewport coordinates system
 1573           //     not the views coordinate space.
 1574           //   clip* are in the views coordinate space.
 1575           RepaintManager rm = RepaintManager.currentManager(this);
 1576           int bdx = blitToX - blitFromX;
 1577           int bdy = blitToY - blitFromY;
 1578   
 1579           // Shift the scrolled region
 1580           rm.copyArea(this, g, blitFromX, blitFromY, blitW, blitH, bdx, bdy,
 1581                       false);
 1582   
 1583           // Paint the newly exposed region.
 1584           int x = view.getX();
 1585           int y = view.getY();
 1586           g.translate(x, y);
 1587           g.setClip(clipX, clipY, clipW, clipH);
 1588           view.paintForceDoubleBuffered(g);
 1589           g.translate(-x, -y);
 1590       }
 1591   
 1592       /**
 1593        * Called to paint the view, usually when <code>blitPaint</code>
 1594        * can not blit.
 1595        *
 1596        * @param g the <code>Graphics</code> context within which to paint
 1597        */
 1598       private void paintView(Graphics g) {
 1599           Rectangle clip = g.getClipBounds();
 1600           JComponent view = (JComponent)getView();
 1601   
 1602           if (view.getWidth() >= getWidth()) {
 1603               // Graphics is relative to JViewport, need to map to view's
 1604               // coordinates space.
 1605               int x = view.getX();
 1606               int y = view.getY();
 1607               g.translate(x, y);
 1608               g.setClip(clip.x - x, clip.y - y, clip.width, clip.height);
 1609               view.paintForceDoubleBuffered(g);
 1610               g.translate(-x, -y);
 1611               g.setClip(clip.x, clip.y, clip.width, clip.height);
 1612           }
 1613           else {
 1614               // To avoid any problems that may result from the viewport being
 1615               // bigger than the view we start painting from the viewport.
 1616               try {
 1617                   inBlitPaint = true;
 1618                   paintForceDoubleBuffered(g);
 1619               } finally {
 1620                   inBlitPaint = false;
 1621               }
 1622           }
 1623       }
 1624   
 1625       /**
 1626        * Returns true if the viewport is not obscured by one of its ancestors,
 1627        * or its ancestors children and if the viewport is showing. Blitting
 1628        * when the view isn't showing will work,
 1629        * or rather <code>copyArea</code> will work,
 1630        * but will not produce the expected behavior.
 1631        */
 1632       private boolean canUseWindowBlitter() {
 1633           if (!isShowing() || (!(getParent() instanceof JComponent) &&
 1634                                !(getView() instanceof JComponent))) {
 1635               return false;
 1636           }
 1637           if (isPainting()) {
 1638               // We're in the process of painting, don't blit. If we were
 1639               // to blit we would draw on top of what we're already drawing,
 1640               // so bail.
 1641               return false;
 1642           }
 1643   
 1644           Rectangle dirtyRegion = RepaintManager.currentManager(this).
 1645                                   getDirtyRegion((JComponent)getParent());
 1646   
 1647           if (dirtyRegion != null && dirtyRegion.width > 0 &&
 1648               dirtyRegion.height > 0) {
 1649               // Part of the scrollpane needs to be repainted too, don't blit.
 1650               return false;
 1651           }
 1652   
 1653           Rectangle clip = new Rectangle(0,0,getWidth(),getHeight());
 1654           Rectangle oldClip = new Rectangle();
 1655           Rectangle tmp2 = null;
 1656           Container parent;
 1657           Component lastParent = null;
 1658           int x, y, w, h;
 1659   
 1660           for(parent = this; parent != null && isLightweightComponent(parent); parent = parent.getParent()) {
 1661               x = parent.getX();
 1662               y = parent.getY();
 1663               w = parent.getWidth();
 1664               h = parent.getHeight();
 1665   
 1666               oldClip.setBounds(clip);
 1667               SwingUtilities.computeIntersection(0, 0, w, h, clip);
 1668               if(!clip.equals(oldClip))
 1669                   return false;
 1670   
 1671               if(lastParent != null && parent instanceof JComponent &&
 1672                  !((JComponent)parent).isOptimizedDrawingEnabled()) {
 1673                   Component comps[] = parent.getComponents();
 1674                   int index = 0;
 1675   
 1676                   for(int i = comps.length - 1 ;i >= 0; i--) {
 1677                       if(comps[i] == lastParent) {
 1678                           index = i - 1;
 1679                           break;
 1680                       }
 1681                   }
 1682   
 1683                   while(index >= 0) {
 1684                       tmp2 = comps[index].getBounds(tmp2);
 1685   
 1686                       if(tmp2.intersects(clip))
 1687                           return false;
 1688                       index--;
 1689                   }
 1690               }
 1691               clip.x += x;
 1692               clip.y += y;
 1693               lastParent = parent;
 1694           }
 1695           if (parent == null) {
 1696               // No Window parent.
 1697               return false;
 1698           }
 1699           return true;
 1700       }
 1701   
 1702   
 1703   /////////////////
 1704   // Accessibility support
 1705   ////////////////
 1706   
 1707       /**
 1708        * Gets the AccessibleContext associated with this JViewport.
 1709        * For viewports, the AccessibleContext takes the form of an
 1710        * AccessibleJViewport.
 1711        * A new AccessibleJViewport instance is created if necessary.
 1712        *
 1713        * @return an AccessibleJViewport that serves as the
 1714        *         AccessibleContext of this JViewport
 1715        */
 1716       public AccessibleContext getAccessibleContext() {
 1717           if (accessibleContext == null) {
 1718               accessibleContext = new AccessibleJViewport();
 1719           }
 1720           return accessibleContext;
 1721       }
 1722   
 1723       /**
 1724        * This class implements accessibility support for the
 1725        * <code>JViewport</code> class.  It provides an implementation of the
 1726        * Java Accessibility API appropriate to viewport user-interface elements.
 1727        * <p>
 1728        * <strong>Warning:</strong>
 1729        * Serialized objects of this class will not be compatible with
 1730        * future Swing releases. The current serialization support is
 1731        * appropriate for short term storage or RMI between applications running
 1732        * the same version of Swing.  As of 1.4, support for long term storage
 1733        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1734        * has been added to the <code>java.beans</code> package.
 1735        * Please see {@link java.beans.XMLEncoder}.
 1736        */
 1737       protected class AccessibleJViewport extends AccessibleJComponent {
 1738           /**
 1739            * Get the role of this object.
 1740            *
 1741            * @return an instance of AccessibleRole describing the role of
 1742            * the object
 1743            */
 1744           public AccessibleRole getAccessibleRole() {
 1745               return AccessibleRole.VIEWPORT;
 1746           }
 1747       } // inner class AccessibleJViewport
 1748   }

Home » openjdk-7 » javax » swing » [javadoc | source]