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

    1   /*
    2    * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   package javax.swing.plaf.basic;
   26   
   27   
   28   import sun.swing.DefaultLookup;
   29   import sun.swing.UIAction;
   30   
   31   import java.awt;
   32   import java.awt.event;
   33   
   34   import java.beans;
   35   
   36   import javax.swing;
   37   import javax.swing.event;
   38   import javax.swing.plaf;
   39   
   40   
   41   /**
   42    * Implementation of ScrollBarUI for the Basic Look and Feel
   43    *
   44    * @author Rich Schiavi
   45    * @author David Kloba
   46    * @author Hans Muller
   47    */
   48   public class BasicScrollBarUI
   49       extends ScrollBarUI implements LayoutManager, SwingConstants
   50   {
   51       private static final int POSITIVE_SCROLL = 1;
   52       private static final int NEGATIVE_SCROLL = -1;
   53   
   54       private static final int MIN_SCROLL = 2;
   55       private static final int MAX_SCROLL = 3;
   56   
   57       // NOTE: DO NOT use this field directly, SynthScrollBarUI assumes you'll
   58       // call getMinimumThumbSize to access it.
   59       protected Dimension minimumThumbSize;
   60       protected Dimension maximumThumbSize;
   61   
   62       protected Color thumbHighlightColor;
   63       protected Color thumbLightShadowColor;
   64       protected Color thumbDarkShadowColor;
   65       protected Color thumbColor;
   66       protected Color trackColor;
   67       protected Color trackHighlightColor;
   68   
   69       protected JScrollBar scrollbar;
   70       protected JButton incrButton;
   71       protected JButton decrButton;
   72       protected boolean isDragging;
   73       protected TrackListener trackListener;
   74       protected ArrowButtonListener buttonListener;
   75       protected ModelListener modelListener;
   76   
   77       protected Rectangle thumbRect;
   78       protected Rectangle trackRect;
   79   
   80       protected int trackHighlight;
   81   
   82       protected static final int NO_HIGHLIGHT = 0;
   83       protected static final int DECREASE_HIGHLIGHT = 1;
   84       protected static final int INCREASE_HIGHLIGHT = 2;
   85   
   86       protected ScrollListener scrollListener;
   87       protected PropertyChangeListener propertyChangeListener;
   88       protected Timer scrollTimer;
   89   
   90       private final static int scrollSpeedThrottle = 60; // delay in milli seconds
   91   
   92       /** True indicates a middle click will absolutely position the
   93        * scrollbar. */
   94       private boolean supportsAbsolutePositioning;
   95   
   96       /**
   97        * Hint as to what width (when vertical) or height (when horizontal)
   98        * should be.
   99        *
  100        * @since 1.7
  101        */
  102       protected int scrollBarWidth;
  103   
  104       private Handler handler;
  105   
  106       private boolean thumbActive;
  107   
  108       /**
  109        * Determine whether scrollbar layout should use cached value or adjusted
  110        * value returned by scrollbar's <code>getValue</code>.
  111        */
  112       private boolean useCachedValue = false;
  113       /**
  114        * The scrollbar value is cached to save real value if the view is adjusted.
  115        */
  116       private int scrollBarValue;
  117   
  118       /**
  119        * Distance between the increment button and the track. This may be a negative
  120        * number. If negative, then an overlap between the button and track will occur,
  121        * which is useful for shaped buttons.
  122        *
  123        * @since 1.7
  124        */
  125       protected int incrGap;
  126   
  127       /**
  128        * Distance between the decrement button and the track. This may be a negative
  129        * number. If negative, then an overlap between the button and track will occur,
  130        * which is useful for shaped buttons.
  131        *
  132        * @since 1.7
  133        */
  134       protected int decrGap;
  135   
  136       static void loadActionMap(LazyActionMap map) {
  137           map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
  138           map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
  139           map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
  140           map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
  141           map.put(new Actions(Actions.MIN_SCROLL));
  142           map.put(new Actions(Actions.MAX_SCROLL));
  143       }
  144   
  145   
  146       public static ComponentUI createUI(JComponent c)    {
  147           return new BasicScrollBarUI();
  148       }
  149   
  150   
  151       protected void configureScrollBarColors()
  152       {
  153           LookAndFeel.installColors(scrollbar, "ScrollBar.background",
  154                                     "ScrollBar.foreground");
  155           thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
  156           thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
  157           thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
  158           thumbColor = UIManager.getColor("ScrollBar.thumb");
  159           trackColor = UIManager.getColor("ScrollBar.track");
  160           trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
  161       }
  162   
  163   
  164       public void installUI(JComponent c)   {
  165           scrollbar = (JScrollBar)c;
  166           thumbRect = new Rectangle(0, 0, 0, 0);
  167           trackRect = new Rectangle(0, 0, 0, 0);
  168           installDefaults();
  169           installComponents();
  170           installListeners();
  171           installKeyboardActions();
  172       }
  173   
  174       public void uninstallUI(JComponent c) {
  175           scrollbar = (JScrollBar)c;
  176           uninstallListeners();
  177           uninstallDefaults();
  178           uninstallComponents();
  179           uninstallKeyboardActions();
  180           thumbRect = null;
  181           scrollbar = null;
  182           incrButton = null;
  183           decrButton = null;
  184       }
  185   
  186   
  187       protected void installDefaults()
  188       {
  189           scrollBarWidth = UIManager.getInt("ScrollBar.width");
  190           if (scrollBarWidth <= 0) {
  191               scrollBarWidth = 16;
  192           }
  193           minimumThumbSize = (Dimension)UIManager.get("ScrollBar.minimumThumbSize");
  194           maximumThumbSize = (Dimension)UIManager.get("ScrollBar.maximumThumbSize");
  195   
  196           Boolean absB = (Boolean)UIManager.get("ScrollBar.allowsAbsolutePositioning");
  197           supportsAbsolutePositioning = (absB != null) ? absB.booleanValue() :
  198                                         false;
  199   
  200           trackHighlight = NO_HIGHLIGHT;
  201           if (scrollbar.getLayout() == null ||
  202                        (scrollbar.getLayout() instanceof UIResource)) {
  203               scrollbar.setLayout(this);
  204           }
  205           configureScrollBarColors();
  206           LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
  207           LookAndFeel.installProperty(scrollbar, "opaque", Boolean.TRUE);
  208   
  209           scrollBarValue = scrollbar.getValue();
  210   
  211           incrGap = UIManager.getInt("ScrollBar.incrementButtonGap");
  212           decrGap = UIManager.getInt("ScrollBar.decrementButtonGap");
  213   
  214           // TODO this can be removed when incrGap/decrGap become protected
  215           // handle scaling for sizeVarients for special case components. The
  216           // key "JComponent.sizeVariant" scales for large/small/mini
  217           // components are based on Apples LAF
  218           String scaleKey = (String)scrollbar.getClientProperty(
  219                   "JComponent.sizeVariant");
  220           if (scaleKey != null){
  221               if ("large".equals(scaleKey)){
  222                   scrollBarWidth *= 1.15;
  223                   incrGap *= 1.15;
  224                   decrGap *= 1.15;
  225               } else if ("small".equals(scaleKey)){
  226                   scrollBarWidth *= 0.857;
  227                   incrGap *= 0.857;
  228                   decrGap *= 0.714;
  229               } else if ("mini".equals(scaleKey)){
  230                   scrollBarWidth *= 0.714;
  231                   incrGap *= 0.714;
  232                   decrGap *= 0.714;
  233               }
  234           }
  235       }
  236   
  237   
  238       protected void installComponents(){
  239           switch (scrollbar.getOrientation()) {
  240           case JScrollBar.VERTICAL:
  241               incrButton = createIncreaseButton(SOUTH);
  242               decrButton = createDecreaseButton(NORTH);
  243               break;
  244   
  245           case JScrollBar.HORIZONTAL:
  246               if (scrollbar.getComponentOrientation().isLeftToRight()) {
  247                   incrButton = createIncreaseButton(EAST);
  248                   decrButton = createDecreaseButton(WEST);
  249               } else {
  250                   incrButton = createIncreaseButton(WEST);
  251                   decrButton = createDecreaseButton(EAST);
  252               }
  253               break;
  254           }
  255           scrollbar.add(incrButton);
  256           scrollbar.add(decrButton);
  257           // Force the children's enabled state to be updated.
  258           scrollbar.setEnabled(scrollbar.isEnabled());
  259       }
  260   
  261       protected void uninstallComponents(){
  262           scrollbar.remove(incrButton);
  263           scrollbar.remove(decrButton);
  264       }
  265   
  266   
  267       protected void installListeners(){
  268           trackListener = createTrackListener();
  269           buttonListener = createArrowButtonListener();
  270           modelListener = createModelListener();
  271           propertyChangeListener = createPropertyChangeListener();
  272   
  273           scrollbar.addMouseListener(trackListener);
  274           scrollbar.addMouseMotionListener(trackListener);
  275           scrollbar.getModel().addChangeListener(modelListener);
  276           scrollbar.addPropertyChangeListener(propertyChangeListener);
  277           scrollbar.addFocusListener(getHandler());
  278   
  279           if (incrButton != null) {
  280               incrButton.addMouseListener(buttonListener);
  281           }
  282           if (decrButton != null) {
  283               decrButton.addMouseListener(buttonListener);
  284           }
  285   
  286           scrollListener = createScrollListener();
  287           scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
  288           scrollTimer.setInitialDelay(300);  // default InitialDelay?
  289       }
  290   
  291   
  292       protected void installKeyboardActions(){
  293           LazyActionMap.installLazyActionMap(scrollbar, BasicScrollBarUI.class,
  294                                              "ScrollBar.actionMap");
  295   
  296           InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
  297           SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
  298                                            inputMap);
  299           inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  300           SwingUtilities.replaceUIInputMap(scrollbar,
  301                      JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
  302       }
  303   
  304       protected void uninstallKeyboardActions(){
  305           SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
  306                                            null);
  307           SwingUtilities.replaceUIActionMap(scrollbar, null);
  308       }
  309   
  310       private InputMap getInputMap(int condition) {
  311           if (condition == JComponent.WHEN_FOCUSED) {
  312               InputMap keyMap = (InputMap)DefaultLookup.get(
  313                           scrollbar, this, "ScrollBar.focusInputMap");
  314               InputMap rtlKeyMap;
  315   
  316               if (scrollbar.getComponentOrientation().isLeftToRight() ||
  317                   ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.focusInputMap.RightToLeft")) == null)) {
  318                   return keyMap;
  319               } else {
  320                   rtlKeyMap.setParent(keyMap);
  321                   return rtlKeyMap;
  322               }
  323           }
  324           else if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  325               InputMap keyMap = (InputMap)DefaultLookup.get(
  326                           scrollbar, this, "ScrollBar.ancestorInputMap");
  327               InputMap rtlKeyMap;
  328   
  329               if (scrollbar.getComponentOrientation().isLeftToRight() ||
  330                   ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.ancestorInputMap.RightToLeft")) == null)) {
  331                   return keyMap;
  332               } else {
  333                   rtlKeyMap.setParent(keyMap);
  334                   return rtlKeyMap;
  335               }
  336           }
  337           return null;
  338       }
  339   
  340   
  341       protected void uninstallListeners() {
  342           scrollTimer.stop();
  343           scrollTimer = null;
  344   
  345           if (decrButton != null){
  346               decrButton.removeMouseListener(buttonListener);
  347           }
  348           if (incrButton != null){
  349               incrButton.removeMouseListener(buttonListener);
  350           }
  351   
  352           scrollbar.getModel().removeChangeListener(modelListener);
  353           scrollbar.removeMouseListener(trackListener);
  354           scrollbar.removeMouseMotionListener(trackListener);
  355           scrollbar.removePropertyChangeListener(propertyChangeListener);
  356           scrollbar.removeFocusListener(getHandler());
  357           handler = null;
  358       }
  359   
  360   
  361       protected void uninstallDefaults(){
  362           LookAndFeel.uninstallBorder(scrollbar);
  363           if (scrollbar.getLayout() == this) {
  364               scrollbar.setLayout(null);
  365           }
  366       }
  367   
  368   
  369       private Handler getHandler() {
  370           if (handler == null) {
  371               handler = new Handler();
  372           }
  373           return handler;
  374       }
  375   
  376       protected TrackListener createTrackListener(){
  377           return new TrackListener();
  378       }
  379   
  380       protected ArrowButtonListener createArrowButtonListener(){
  381           return new ArrowButtonListener();
  382       }
  383   
  384       protected ModelListener createModelListener(){
  385           return new ModelListener();
  386       }
  387   
  388       protected ScrollListener createScrollListener(){
  389           return new ScrollListener();
  390       }
  391   
  392       protected PropertyChangeListener createPropertyChangeListener() {
  393           return getHandler();
  394       }
  395   
  396       private void updateThumbState(int x, int y) {
  397           Rectangle rect = getThumbBounds();
  398   
  399           setThumbRollover(rect.contains(x, y));
  400       }
  401   
  402       /**
  403        * Sets whether or not the mouse is currently over the thumb.
  404        *
  405        * @param active True indicates the thumb is currently active.
  406        * @since 1.5
  407        */
  408       protected void setThumbRollover(boolean active) {
  409           if (thumbActive != active) {
  410               thumbActive = active;
  411               scrollbar.repaint(getThumbBounds());
  412           }
  413       }
  414   
  415       /**
  416        * Returns true if the mouse is currently over the thumb.
  417        *
  418        * @return true if the thumb is currently active
  419        * @since 1.5
  420        */
  421       public boolean isThumbRollover() {
  422           return thumbActive;
  423       }
  424   
  425       public void paint(Graphics g, JComponent c) {
  426           paintTrack(g, c, getTrackBounds());
  427           Rectangle thumbBounds = getThumbBounds();
  428           if (thumbBounds.intersects(g.getClipBounds())) {
  429               paintThumb(g, c, thumbBounds);
  430           }
  431       }
  432   
  433   
  434       /**
  435        * A vertical scrollbar's preferred width is the maximum of
  436        * preferred widths of the (non <code>null</code>)
  437        * increment/decrement buttons,
  438        * and the minimum width of the thumb. The preferred height is the
  439        * sum of the preferred heights of the same parts.  The basis for
  440        * the preferred size of a horizontal scrollbar is similar.
  441        * <p>
  442        * The <code>preferredSize</code> is only computed once, subsequent
  443        * calls to this method just return a cached size.
  444        *
  445        * @param c the <code>JScrollBar</code> that's delegating this method to us
  446        * @return the preferred size of a Basic JScrollBar
  447        * @see #getMaximumSize
  448        * @see #getMinimumSize
  449        */
  450       public Dimension getPreferredSize(JComponent c) {
  451           return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
  452               ? new Dimension(scrollBarWidth, 48)
  453               : new Dimension(48, scrollBarWidth);
  454       }
  455   
  456   
  457       /**
  458        * @param c The JScrollBar that's delegating this method to us.
  459        * @return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  460        * @see #getMinimumSize
  461        * @see #getPreferredSize
  462        */
  463       public Dimension getMaximumSize(JComponent c) {
  464           return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  465       }
  466   
  467       protected JButton createDecreaseButton(int orientation)  {
  468           return new BasicArrowButton(orientation,
  469                                       UIManager.getColor("ScrollBar.thumb"),
  470                                       UIManager.getColor("ScrollBar.thumbShadow"),
  471                                       UIManager.getColor("ScrollBar.thumbDarkShadow"),
  472                                       UIManager.getColor("ScrollBar.thumbHighlight"));
  473       }
  474   
  475       protected JButton createIncreaseButton(int orientation)  {
  476           return new BasicArrowButton(orientation,
  477                                       UIManager.getColor("ScrollBar.thumb"),
  478                                       UIManager.getColor("ScrollBar.thumbShadow"),
  479                                       UIManager.getColor("ScrollBar.thumbDarkShadow"),
  480                                       UIManager.getColor("ScrollBar.thumbHighlight"));
  481       }
  482   
  483   
  484       protected void paintDecreaseHighlight(Graphics g)
  485       {
  486           Insets insets = scrollbar.getInsets();
  487           Rectangle thumbR = getThumbBounds();
  488           g.setColor(trackHighlightColor);
  489   
  490           if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  491               //paint the distance between the start of the track and top of the thumb
  492               int x = insets.left;
  493               int y = trackRect.y;
  494               int w = scrollbar.getWidth() - (insets.left + insets.right);
  495               int h = thumbR.y - y;
  496               g.fillRect(x, y, w, h);
  497           } else {
  498               //if left-to-right, fill the area between the start of the track and
  499               //the left edge of the thumb. If right-to-left, fill the area between
  500               //the end of the thumb and end of the track.
  501               int x, w;
  502               if (scrollbar.getComponentOrientation().isLeftToRight()) {
  503                  x = trackRect.x;
  504                   w = thumbR.x - x;
  505               } else {
  506                   x = thumbR.x + thumbR.width;
  507                   w = trackRect.x + trackRect.width - x;
  508               }
  509               int y = insets.top;
  510               int h = scrollbar.getHeight() - (insets.top + insets.bottom);
  511               g.fillRect(x, y, w, h);
  512           }
  513       }
  514   
  515   
  516       protected void paintIncreaseHighlight(Graphics g)
  517       {
  518           Insets insets = scrollbar.getInsets();
  519           Rectangle thumbR = getThumbBounds();
  520           g.setColor(trackHighlightColor);
  521   
  522           if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  523               //fill the area between the bottom of the thumb and the end of the track.
  524               int x = insets.left;
  525               int y = thumbR.y + thumbR.height;
  526               int w = scrollbar.getWidth() - (insets.left + insets.right);
  527               int h = trackRect.y + trackRect.height - y;
  528               g.fillRect(x, y, w, h);
  529           }
  530           else {
  531               //if left-to-right, fill the area between the right of the thumb and the
  532               //end of the track. If right-to-left, then fill the area to the left of
  533               //the thumb and the start of the track.
  534               int x, w;
  535               if (scrollbar.getComponentOrientation().isLeftToRight()) {
  536                   x = thumbR.x + thumbR.width;
  537                   w = trackRect.x + trackRect.width - x;
  538               } else {
  539                   x = trackRect.x;
  540                   w = thumbR.x - x;
  541               }
  542               int y = insets.top;
  543               int h = scrollbar.getHeight() - (insets.top + insets.bottom);
  544               g.fillRect(x, y, w, h);
  545           }
  546       }
  547   
  548   
  549       protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
  550       {
  551           g.setColor(trackColor);
  552           g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
  553   
  554           if(trackHighlight == DECREASE_HIGHLIGHT)        {
  555               paintDecreaseHighlight(g);
  556           }
  557           else if(trackHighlight == INCREASE_HIGHLIGHT)           {
  558               paintIncreaseHighlight(g);
  559           }
  560       }
  561   
  562   
  563       protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
  564       {
  565           if(thumbBounds.isEmpty() || !scrollbar.isEnabled())     {
  566               return;
  567           }
  568   
  569           int w = thumbBounds.width;
  570           int h = thumbBounds.height;
  571   
  572           g.translate(thumbBounds.x, thumbBounds.y);
  573   
  574           g.setColor(thumbDarkShadowColor);
  575           g.drawRect(0, 0, w-1, h-1);
  576           g.setColor(thumbColor);
  577           g.fillRect(0, 0, w-1, h-1);
  578   
  579           g.setColor(thumbHighlightColor);
  580           g.drawLine(1, 1, 1, h-2);
  581           g.drawLine(2, 1, w-3, 1);
  582   
  583           g.setColor(thumbLightShadowColor);
  584           g.drawLine(2, h-2, w-2, h-2);
  585           g.drawLine(w-2, 1, w-2, h-3);
  586   
  587           g.translate(-thumbBounds.x, -thumbBounds.y);
  588       }
  589   
  590   
  591       /**
  592        * Returns the smallest acceptable size for the thumb.  If the scrollbar
  593        * becomes so small that this size isn't available, the thumb will be
  594        * hidden.
  595        * <p>
  596        * <b>Warning </b>: the value returned by this method should not be
  597        * be modified, it's a shared static constant.
  598        *
  599        * @return The smallest acceptable size for the thumb.
  600        * @see #getMaximumThumbSize
  601        */
  602       protected Dimension getMinimumThumbSize() {
  603           return minimumThumbSize;
  604       }
  605   
  606       /**
  607        * Returns the largest acceptable size for the thumb.  To create a fixed
  608        * size thumb one make this method and <code>getMinimumThumbSize</code>
  609        * return the same value.
  610        * <p>
  611        * <b>Warning </b>: the value returned by this method should not be
  612        * be modified, it's a shared static constant.
  613        *
  614        * @return The largest acceptable size for the thumb.
  615        * @see #getMinimumThumbSize
  616        */
  617       protected Dimension getMaximumThumbSize()   {
  618           return maximumThumbSize;
  619       }
  620   
  621   
  622       /*
  623        * LayoutManager Implementation
  624        */
  625   
  626       public void addLayoutComponent(String name, Component child) {}
  627       public void removeLayoutComponent(Component child) {}
  628   
  629       public Dimension preferredLayoutSize(Container scrollbarContainer)  {
  630           return getPreferredSize((JComponent)scrollbarContainer);
  631       }
  632   
  633       public Dimension minimumLayoutSize(Container scrollbarContainer) {
  634           return getMinimumSize((JComponent)scrollbarContainer);
  635       }
  636   
  637       private int getValue(JScrollBar sb) {
  638           return (useCachedValue) ? scrollBarValue : sb.getValue();
  639       }
  640   
  641       protected void layoutVScrollbar(JScrollBar sb)
  642       {
  643           Dimension sbSize = sb.getSize();
  644           Insets sbInsets = sb.getInsets();
  645   
  646           /*
  647            * Width and left edge of the buttons and thumb.
  648            */
  649           int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
  650           int itemX = sbInsets.left;
  651   
  652           /* Nominal locations of the buttons, assuming their preferred
  653            * size will fit.
  654            */
  655           boolean squareButtons = DefaultLookup.getBoolean(
  656               scrollbar, this, "ScrollBar.squareButtons", false);
  657           int decrButtonH = squareButtons ? itemW :
  658                             decrButton.getPreferredSize().height;
  659           int decrButtonY = sbInsets.top;
  660   
  661           int incrButtonH = squareButtons ? itemW :
  662                             incrButton.getPreferredSize().height;
  663           int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
  664   
  665           /* The thumb must fit within the height left over after we
  666            * subtract the preferredSize of the buttons and the insets
  667            * and the gaps
  668            */
  669           int sbInsetsH = sbInsets.top + sbInsets.bottom;
  670           int sbButtonsH = decrButtonH + incrButtonH;
  671           int gaps = decrGap + incrGap;
  672           float trackH = sbSize.height - (sbInsetsH + sbButtonsH) - gaps;
  673   
  674           /* Compute the height and origin of the thumb.   The case
  675            * where the thumb is at the bottom edge is handled specially
  676            * to avoid numerical problems in computing thumbY.  Enforce
  677            * the thumbs min/max dimensions.  If the thumb doesn't
  678            * fit in the track (trackH) we'll hide it later.
  679            */
  680           float min = sb.getMinimum();
  681           float extent = sb.getVisibleAmount();
  682           float range = sb.getMaximum() - min;
  683           float value = getValue(sb);
  684   
  685           int thumbH = (range <= 0)
  686               ? getMaximumThumbSize().height : (int)(trackH * (extent / range));
  687           thumbH = Math.max(thumbH, getMinimumThumbSize().height);
  688           thumbH = Math.min(thumbH, getMaximumThumbSize().height);
  689   
  690           int thumbY = incrButtonY - incrGap - thumbH;
  691           if (value < (sb.getMaximum() - sb.getVisibleAmount())) {
  692               float thumbRange = trackH - thumbH;
  693               thumbY = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
  694               thumbY +=  decrButtonY + decrButtonH + decrGap;
  695           }
  696   
  697           /* If the buttons don't fit, allocate half of the available
  698            * space to each and move the lower one (incrButton) down.
  699            */
  700           int sbAvailButtonH = (sbSize.height - sbInsetsH);
  701           if (sbAvailButtonH < sbButtonsH) {
  702               incrButtonH = decrButtonH = sbAvailButtonH / 2;
  703               incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
  704           }
  705           decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
  706           incrButton.setBounds(itemX, incrButtonY, itemW, incrButtonH);
  707   
  708           /* Update the trackRect field.
  709            */
  710           int itrackY = decrButtonY + decrButtonH + decrGap;
  711           int itrackH = incrButtonY - incrGap - itrackY;
  712           trackRect.setBounds(itemX, itrackY, itemW, itrackH);
  713   
  714           /* If the thumb isn't going to fit, zero it's bounds.  Otherwise
  715            * make sure it fits between the buttons.  Note that setting the
  716            * thumbs bounds will cause a repaint.
  717            */
  718           if(thumbH >= (int)trackH)       {
  719               if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
  720                   // This is used primarily for GTK L&F, which expands the
  721                   // thumb to fit the track when it would otherwise be hidden.
  722                   setThumbBounds(itemX, itrackY, itemW, itrackH);
  723               } else {
  724                   // Other L&F's simply hide the thumb in this case.
  725                   setThumbBounds(0, 0, 0, 0);
  726               }
  727           }
  728           else {
  729               if ((thumbY + thumbH) > incrButtonY - incrGap) {
  730                   thumbY = incrButtonY - incrGap - thumbH;
  731               }
  732               if (thumbY  < (decrButtonY + decrButtonH + decrGap)) {
  733                   thumbY = decrButtonY + decrButtonH + decrGap + 1;
  734               }
  735               setThumbBounds(itemX, thumbY, itemW, thumbH);
  736           }
  737       }
  738   
  739   
  740       protected void layoutHScrollbar(JScrollBar sb)
  741       {
  742           Dimension sbSize = sb.getSize();
  743           Insets sbInsets = sb.getInsets();
  744   
  745           /* Height and top edge of the buttons and thumb.
  746            */
  747           int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
  748           int itemY = sbInsets.top;
  749   
  750           boolean ltr = sb.getComponentOrientation().isLeftToRight();
  751   
  752           /* Nominal locations of the buttons, assuming their preferred
  753            * size will fit.
  754            */
  755           boolean squareButtons = DefaultLookup.getBoolean(
  756               scrollbar, this, "ScrollBar.squareButtons", false);
  757           int leftButtonW = squareButtons ? itemH :
  758                             decrButton.getPreferredSize().width;
  759           int rightButtonW = squareButtons ? itemH :
  760                             incrButton.getPreferredSize().width;
  761           if (!ltr) {
  762               int temp = leftButtonW;
  763               leftButtonW = rightButtonW;
  764               rightButtonW = temp;
  765           }
  766           int leftButtonX = sbInsets.left;
  767           int rightButtonX = sbSize.width - (sbInsets.right + rightButtonW);
  768           int leftGap = ltr ? decrGap : incrGap;
  769           int rightGap = ltr ? incrGap : decrGap;
  770   
  771           /* The thumb must fit within the width left over after we
  772            * subtract the preferredSize of the buttons and the insets
  773            * and the gaps
  774            */
  775           int sbInsetsW = sbInsets.left + sbInsets.right;
  776           int sbButtonsW = leftButtonW + rightButtonW;
  777           float trackW = sbSize.width - (sbInsetsW + sbButtonsW) - (leftGap + rightGap);
  778   
  779           /* Compute the width and origin of the thumb.  Enforce
  780            * the thumbs min/max dimensions.  The case where the thumb
  781            * is at the right edge is handled specially to avoid numerical
  782            * problems in computing thumbX.  If the thumb doesn't
  783            * fit in the track (trackH) we'll hide it later.
  784            */
  785           float min = sb.getMinimum();
  786           float max = sb.getMaximum();
  787           float extent = sb.getVisibleAmount();
  788           float range = max - min;
  789           float value = getValue(sb);
  790   
  791           int thumbW = (range <= 0)
  792               ? getMaximumThumbSize().width : (int)(trackW * (extent / range));
  793           thumbW = Math.max(thumbW, getMinimumThumbSize().width);
  794           thumbW = Math.min(thumbW, getMaximumThumbSize().width);
  795   
  796           int thumbX = ltr ? rightButtonX - rightGap - thumbW : leftButtonX + leftButtonW + leftGap;
  797           if (value < (max - sb.getVisibleAmount())) {
  798               float thumbRange = trackW - thumbW;
  799               if( ltr ) {
  800                   thumbX = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
  801               } else {
  802                   thumbX = (int)(0.5f + (thumbRange * ((max - extent - value) / (range - extent))));
  803               }
  804               thumbX += leftButtonX + leftButtonW + leftGap;
  805           }
  806   
  807           /* If the buttons don't fit, allocate half of the available
  808            * space to each and move the right one over.
  809            */
  810           int sbAvailButtonW = (sbSize.width - sbInsetsW);
  811           if (sbAvailButtonW < sbButtonsW) {
  812               rightButtonW = leftButtonW = sbAvailButtonW / 2;
  813               rightButtonX = sbSize.width - (sbInsets.right + rightButtonW + rightGap);
  814           }
  815   
  816           (ltr ? decrButton : incrButton).setBounds(leftButtonX, itemY, leftButtonW, itemH);
  817           (ltr ? incrButton : decrButton).setBounds(rightButtonX, itemY, rightButtonW, itemH);
  818   
  819           /* Update the trackRect field.
  820            */
  821           int itrackX = leftButtonX + leftButtonW + leftGap;
  822           int itrackW = rightButtonX - rightGap - itrackX;
  823           trackRect.setBounds(itrackX, itemY, itrackW, itemH);
  824   
  825           /* Make sure the thumb fits between the buttons.  Note
  826            * that setting the thumbs bounds causes a repaint.
  827            */
  828           if (thumbW >= (int)trackW) {
  829               if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
  830                   // This is used primarily for GTK L&F, which expands the
  831                   // thumb to fit the track when it would otherwise be hidden.
  832                   setThumbBounds(itrackX, itemY, itrackW, itemH);
  833               } else {
  834                   // Other L&F's simply hide the thumb in this case.
  835                   setThumbBounds(0, 0, 0, 0);
  836               }
  837           }
  838           else {
  839               if (thumbX + thumbW > rightButtonX - rightGap) {
  840                   thumbX = rightButtonX - rightGap - thumbW;
  841               }
  842               if (thumbX  < leftButtonX + leftButtonW + leftGap) {
  843                   thumbX = leftButtonX + leftButtonW + leftGap + 1;
  844               }
  845               setThumbBounds(thumbX, itemY, thumbW, itemH);
  846           }
  847       }
  848   
  849       public void layoutContainer(Container scrollbarContainer)
  850       {
  851           /* If the user is dragging the value, we'll assume that the
  852            * scrollbars layout is OK modulo the thumb which is being
  853            * handled by the dragging code.
  854            */
  855           if (isDragging) {
  856               return;
  857           }
  858   
  859           JScrollBar scrollbar = (JScrollBar)scrollbarContainer;
  860           switch (scrollbar.getOrientation()) {
  861           case JScrollBar.VERTICAL:
  862               layoutVScrollbar(scrollbar);
  863               break;
  864   
  865           case JScrollBar.HORIZONTAL:
  866               layoutHScrollbar(scrollbar);
  867               break;
  868           }
  869       }
  870   
  871   
  872       /**
  873        * Set the bounds of the thumb and force a repaint that includes
  874        * the old thumbBounds and the new one.
  875        *
  876        * @see #getThumbBounds
  877        */
  878       protected void setThumbBounds(int x, int y, int width, int height)
  879       {
  880           /* If the thumbs bounds haven't changed, we're done.
  881            */
  882           if ((thumbRect.x == x) &&
  883               (thumbRect.y == y) &&
  884               (thumbRect.width == width) &&
  885               (thumbRect.height == height)) {
  886               return;
  887           }
  888   
  889           /* Update thumbRect, and repaint the union of x,y,w,h and
  890            * the old thumbRect.
  891            */
  892           int minX = Math.min(x, thumbRect.x);
  893           int minY = Math.min(y, thumbRect.y);
  894           int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
  895           int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);
  896   
  897           thumbRect.setBounds(x, y, width, height);
  898           scrollbar.repaint(minX, minY, maxX - minX, maxY - minY);
  899   
  900           // Once there is API to determine the mouse location this will need
  901           // to be changed.
  902           setThumbRollover(false);
  903       }
  904   
  905   
  906       /**
  907        * Return the current size/location of the thumb.
  908        * <p>
  909        * <b>Warning </b>: the value returned by this method should not be
  910        * be modified, it's a reference to the actual rectangle, not a copy.
  911        *
  912        * @return The current size/location of the thumb.
  913        * @see #setThumbBounds
  914        */
  915       protected Rectangle getThumbBounds() {
  916           return thumbRect;
  917       }
  918   
  919   
  920       /**
  921        * Returns the current bounds of the track, i.e. the space in between
  922        * the increment and decrement buttons, less the insets.  The value
  923        * returned by this method is updated each time the scrollbar is
  924        * laid out (validated).
  925        * <p>
  926        * <b>Warning </b>: the value returned by this method should not be
  927        * be modified, it's a reference to the actual rectangle, not a copy.
  928        *
  929        * @return the current bounds of the scrollbar track
  930        * @see #layoutContainer
  931        */
  932       protected Rectangle getTrackBounds() {
  933           return trackRect;
  934       }
  935   
  936       /*
  937        * Method for scrolling by a block increment.
  938        * Added for mouse wheel scrolling support, RFE 4202656.
  939        */
  940       static void scrollByBlock(JScrollBar scrollbar, int direction) {
  941           // This method is called from BasicScrollPaneUI to implement wheel
  942           // scrolling, and also from scrollByBlock().
  943               int oldValue = scrollbar.getValue();
  944               int blockIncrement = scrollbar.getBlockIncrement(direction);
  945               int delta = blockIncrement * ((direction > 0) ? +1 : -1);
  946               int newValue = oldValue + delta;
  947   
  948               // Check for overflow.
  949               if (delta > 0 && newValue < oldValue) {
  950                   newValue = scrollbar.getMaximum();
  951               }
  952               else if (delta < 0 && newValue > oldValue) {
  953                   newValue = scrollbar.getMinimum();
  954               }
  955   
  956               scrollbar.setValue(newValue);
  957       }
  958   
  959       protected void scrollByBlock(int direction)
  960       {
  961           scrollByBlock(scrollbar, direction);
  962               trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT : DECREASE_HIGHLIGHT;
  963               Rectangle dirtyRect = getTrackBounds();
  964               scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
  965       }
  966   
  967       /*
  968        * Method for scrolling by a unit increment.
  969        * Added for mouse wheel scrolling support, RFE 4202656.
  970        *
  971        * If limitByBlock is set to true, the scrollbar will scroll at least 1
  972        * unit increment, but will not scroll farther than the block increment.
  973        * See BasicScrollPaneUI.Handler.mouseWheelMoved().
  974        */
  975       static void scrollByUnits(JScrollBar scrollbar, int direction,
  976                                 int units, boolean limitToBlock) {
  977           // This method is called from BasicScrollPaneUI to implement wheel
  978           // scrolling, as well as from scrollByUnit().
  979           int delta;
  980           int limit = -1;
  981   
  982           if (limitToBlock) {
  983               if (direction < 0) {
  984                   limit = scrollbar.getValue() -
  985                                            scrollbar.getBlockIncrement(direction);
  986               }
  987               else {
  988                   limit = scrollbar.getValue() +
  989                                            scrollbar.getBlockIncrement(direction);
  990               }
  991           }
  992   
  993           for (int i=0; i<units; i++) {
  994               if (direction > 0) {
  995                   delta = scrollbar.getUnitIncrement(direction);
  996               }
  997               else {
  998                   delta = -scrollbar.getUnitIncrement(direction);
  999               }
 1000   
 1001               int oldValue = scrollbar.getValue();
 1002               int newValue = oldValue + delta;
 1003   
 1004               // Check for overflow.
 1005               if (delta > 0 && newValue < oldValue) {
 1006                   newValue = scrollbar.getMaximum();
 1007               }
 1008               else if (delta < 0 && newValue > oldValue) {
 1009                   newValue = scrollbar.getMinimum();
 1010               }
 1011               if (oldValue == newValue) {
 1012                   break;
 1013               }
 1014   
 1015               if (limitToBlock && i > 0) {
 1016                   assert limit != -1;
 1017                   if ((direction < 0 && newValue < limit) ||
 1018                       (direction > 0 && newValue > limit)) {
 1019                       break;
 1020                   }
 1021               }
 1022               scrollbar.setValue(newValue);
 1023           }
 1024       }
 1025   
 1026       protected void scrollByUnit(int direction)  {
 1027           scrollByUnits(scrollbar, direction, 1, false);
 1028       }
 1029   
 1030       /**
 1031        * Indicates whether the user can absolutely position the thumb with
 1032        * a mouse gesture (usually the middle mouse button).
 1033        *
 1034        * @return true if a mouse gesture can absolutely position the thumb
 1035        * @since 1.5
 1036        */
 1037       public boolean getSupportsAbsolutePositioning() {
 1038           return supportsAbsolutePositioning;
 1039       }
 1040   
 1041       /**
 1042        * A listener to listen for model changes.
 1043        *
 1044        */
 1045       protected class ModelListener implements ChangeListener {
 1046           public void stateChanged(ChangeEvent e) {
 1047               if (!useCachedValue) {
 1048                   scrollBarValue = scrollbar.getValue();
 1049               }
 1050               layoutContainer(scrollbar);
 1051               useCachedValue = false;
 1052           }
 1053       }
 1054   
 1055   
 1056       /**
 1057        * Track mouse drags.
 1058        */
 1059       protected class TrackListener
 1060           extends MouseAdapter implements MouseMotionListener
 1061       {
 1062           protected transient int offset;
 1063           protected transient int currentMouseX, currentMouseY;
 1064           private transient int direction = +1;
 1065   
 1066           public void mouseReleased(MouseEvent e)
 1067           {
 1068               if (isDragging) {
 1069                   updateThumbState(e.getX(), e.getY());
 1070               }
 1071               if (SwingUtilities.isRightMouseButton(e) ||
 1072                   (!getSupportsAbsolutePositioning() &&
 1073                    SwingUtilities.isMiddleMouseButton(e)))
 1074                   return;
 1075               if(!scrollbar.isEnabled())
 1076                   return;
 1077   
 1078               Rectangle r = getTrackBounds();
 1079               scrollbar.repaint(r.x, r.y, r.width, r.height);
 1080   
 1081               trackHighlight = NO_HIGHLIGHT;
 1082               isDragging = false;
 1083               offset = 0;
 1084               scrollTimer.stop();
 1085               useCachedValue = true;
 1086               scrollbar.setValueIsAdjusting(false);
 1087           }
 1088   
 1089   
 1090           /**
 1091            * If the mouse is pressed above the "thumb" component
 1092            * then reduce the scrollbars value by one page ("page up"),
 1093            * otherwise increase it by one page.  If there is no
 1094            * thumb then page up if the mouse is in the upper half
 1095            * of the track.
 1096            */
 1097           public void mousePressed(MouseEvent e)
 1098           {
 1099               if (SwingUtilities.isRightMouseButton(e) ||
 1100                   (!getSupportsAbsolutePositioning() &&
 1101                    SwingUtilities.isMiddleMouseButton(e)))
 1102                   return;
 1103               if(!scrollbar.isEnabled())
 1104                   return;
 1105   
 1106               if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
 1107                   scrollbar.requestFocus();
 1108               }
 1109   
 1110               useCachedValue = true;
 1111               scrollbar.setValueIsAdjusting(true);
 1112   
 1113               currentMouseX = e.getX();
 1114               currentMouseY = e.getY();
 1115   
 1116               // Clicked in the Thumb area?
 1117               if(getThumbBounds().contains(currentMouseX, currentMouseY)) {
 1118                   switch (scrollbar.getOrientation()) {
 1119                   case JScrollBar.VERTICAL:
 1120                       offset = currentMouseY - getThumbBounds().y;
 1121                       break;
 1122                   case JScrollBar.HORIZONTAL:
 1123                       offset = currentMouseX - getThumbBounds().x;
 1124                       break;
 1125                   }
 1126                   isDragging = true;
 1127                   return;
 1128               }
 1129               else if (getSupportsAbsolutePositioning() &&
 1130                        SwingUtilities.isMiddleMouseButton(e)) {
 1131                   switch (scrollbar.getOrientation()) {
 1132                   case JScrollBar.VERTICAL:
 1133                       offset = getThumbBounds().height / 2;
 1134                       break;
 1135                   case JScrollBar.HORIZONTAL:
 1136                       offset = getThumbBounds().width / 2;
 1137                       break;
 1138                   }
 1139                   isDragging = true;
 1140                   setValueFrom(e);
 1141                   return;
 1142               }
 1143               isDragging = false;
 1144   
 1145               Dimension sbSize = scrollbar.getSize();
 1146               direction = +1;
 1147   
 1148               switch (scrollbar.getOrientation()) {
 1149               case JScrollBar.VERTICAL:
 1150                   if (getThumbBounds().isEmpty()) {
 1151                       int scrollbarCenter = sbSize.height / 2;
 1152                       direction = (currentMouseY < scrollbarCenter) ? -1 : +1;
 1153                   } else {
 1154                       int thumbY = getThumbBounds().y;
 1155                       direction = (currentMouseY < thumbY) ? -1 : +1;
 1156                   }
 1157                   break;
 1158               case JScrollBar.HORIZONTAL:
 1159                   if (getThumbBounds().isEmpty()) {
 1160                       int scrollbarCenter = sbSize.width / 2;
 1161                       direction = (currentMouseX < scrollbarCenter) ? -1 : +1;
 1162                   } else {
 1163                       int thumbX = getThumbBounds().x;
 1164                       direction = (currentMouseX < thumbX) ? -1 : +1;
 1165                   }
 1166                   if (!scrollbar.getComponentOrientation().isLeftToRight()) {
 1167                       direction = -direction;
 1168                   }
 1169                   break;
 1170               }
 1171               scrollByBlock(direction);
 1172   
 1173               scrollTimer.stop();
 1174               scrollListener.setDirection(direction);
 1175               scrollListener.setScrollByBlock(true);
 1176               startScrollTimerIfNecessary();
 1177           }
 1178   
 1179   
 1180           /**
 1181            * Set the models value to the position of the thumb's top of Vertical
 1182            * scrollbar, or the left/right of Horizontal scrollbar in
 1183            * left-to-right/right-to-left scrollbar relative to the origin of the
 1184            * track.
 1185            */
 1186           public void mouseDragged(MouseEvent e) {
 1187               if (SwingUtilities.isRightMouseButton(e) ||
 1188                   (!getSupportsAbsolutePositioning() &&
 1189                    SwingUtilities.isMiddleMouseButton(e)))
 1190                   return;
 1191               if(!scrollbar.isEnabled() || getThumbBounds().isEmpty()) {
 1192                   return;
 1193               }
 1194               if (isDragging) {
 1195                   setValueFrom(e);
 1196               } else {
 1197                   currentMouseX = e.getX();
 1198                   currentMouseY = e.getY();
 1199                   updateThumbState(currentMouseX, currentMouseY);
 1200                   startScrollTimerIfNecessary();
 1201               }
 1202           }
 1203   
 1204           private void setValueFrom(MouseEvent e) {
 1205               boolean active = isThumbRollover();
 1206               BoundedRangeModel model = scrollbar.getModel();
 1207               Rectangle thumbR = getThumbBounds();
 1208               float trackLength;
 1209               int thumbMin, thumbMax, thumbPos;
 1210   
 1211               if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
 1212                   thumbMin = trackRect.y;
 1213                   thumbMax = trackRect.y + trackRect.height - thumbR.height;
 1214                   thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getY() - offset)));
 1215                   setThumbBounds(thumbR.x, thumbPos, thumbR.width, thumbR.height);
 1216                   trackLength = getTrackBounds().height;
 1217               }
 1218               else {
 1219                   thumbMin = trackRect.x;
 1220                   thumbMax = trackRect.x + trackRect.width - thumbR.width;
 1221                   thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getX() - offset)));
 1222                   setThumbBounds(thumbPos, thumbR.y, thumbR.width, thumbR.height);
 1223                   trackLength = getTrackBounds().width;
 1224               }
 1225   
 1226               /* Set the scrollbars value.  If the thumb has reached the end of
 1227                * the scrollbar, then just set the value to its maximum.  Otherwise
 1228                * compute the value as accurately as possible.
 1229                */
 1230               if (thumbPos == thumbMax) {
 1231                   if (scrollbar.getOrientation() == JScrollBar.VERTICAL ||
 1232                       scrollbar.getComponentOrientation().isLeftToRight()) {
 1233                       scrollbar.setValue(model.getMaximum() - model.getExtent());
 1234                   } else {
 1235                       scrollbar.setValue(model.getMinimum());
 1236                   }
 1237               }
 1238               else {
 1239                   float valueMax = model.getMaximum() - model.getExtent();
 1240                   float valueRange = valueMax - model.getMinimum();
 1241                   float thumbValue = thumbPos - thumbMin;
 1242                   float thumbRange = thumbMax - thumbMin;
 1243                   int value;
 1244                   if (scrollbar.getOrientation() == JScrollBar.VERTICAL ||
 1245                       scrollbar.getComponentOrientation().isLeftToRight()) {
 1246                       value = (int)(0.5 + ((thumbValue / thumbRange) * valueRange));
 1247                   } else {
 1248                       value = (int)(0.5 + (((thumbMax - thumbPos) / thumbRange) * valueRange));
 1249                   }
 1250   
 1251                   useCachedValue = true;
 1252                   scrollBarValue = value + model.getMinimum();
 1253                   scrollbar.setValue(adjustValueIfNecessary(scrollBarValue));
 1254               }
 1255               setThumbRollover(active);
 1256           }
 1257   
 1258           private int adjustValueIfNecessary(int value) {
 1259               if (scrollbar.getParent() instanceof JScrollPane) {
 1260                   JScrollPane scrollpane = (JScrollPane)scrollbar.getParent();
 1261                   JViewport viewport = scrollpane.getViewport();
 1262                   Component view = viewport.getView();
 1263                   if (view instanceof JList) {
 1264                       JList list = (JList)view;
 1265                       if (DefaultLookup.getBoolean(list, list.getUI(),
 1266                                                    "List.lockToPositionOnScroll", false)) {
 1267                           int adjustedValue = value;
 1268                           int mode = list.getLayoutOrientation();
 1269                           int orientation = scrollbar.getOrientation();
 1270                           if (orientation == JScrollBar.VERTICAL && mode == JList.VERTICAL) {
 1271                               int index = list.locationToIndex(new Point(0, value));
 1272                               Rectangle rect = list.getCellBounds(index, index);
 1273                               if (rect != null) {
 1274                                   adjustedValue = rect.y;
 1275                               }
 1276                           }
 1277                           if (orientation == JScrollBar.HORIZONTAL &&
 1278                               (mode == JList.VERTICAL_WRAP || mode == JList.HORIZONTAL_WRAP)) {
 1279                               if (scrollpane.getComponentOrientation().isLeftToRight()) {
 1280                                   int index = list.locationToIndex(new Point(value, 0));
 1281                                   Rectangle rect = list.getCellBounds(index, index);
 1282                                   if (rect != null) {
 1283                                       adjustedValue = rect.x;
 1284                                   }
 1285                               }
 1286                               else {
 1287                                   Point loc = new Point(value, 0);
 1288                                   int extent = viewport.getExtentSize().width;
 1289                                   loc.x += extent - 1;
 1290                                   int index = list.locationToIndex(loc);
 1291                                   Rectangle rect = list.getCellBounds(index, index);
 1292                                   if (rect != null) {
 1293                                       adjustedValue = rect.x + rect.width - extent;
 1294                                   }
 1295                               }
 1296                           }
 1297                           value = adjustedValue;
 1298   
 1299                       }
 1300                   }
 1301               }
 1302               return value;
 1303           }
 1304   
 1305           private void startScrollTimerIfNecessary() {
 1306               if (scrollTimer.isRunning()) {
 1307                   return;
 1308               }
 1309   
 1310               Rectangle tb = getThumbBounds();
 1311   
 1312               switch (scrollbar.getOrientation()) {
 1313               case JScrollBar.VERTICAL:
 1314                   if (direction > 0) {
 1315                       if (tb.y + tb.height < trackListener.currentMouseY) {
 1316                           scrollTimer.start();
 1317                       }
 1318                   } else if (tb.y > trackListener.currentMouseY) {
 1319                       scrollTimer.start();
 1320                   }
 1321                   break;
 1322               case JScrollBar.HORIZONTAL:
 1323                   if ((direction > 0 && isMouseAfterThumb())
 1324                           || (direction < 0 && isMouseBeforeThumb())) {
 1325   
 1326                       scrollTimer.start();
 1327                   }
 1328                   break;
 1329               }
 1330           }
 1331   
 1332           public void mouseMoved(MouseEvent e) {
 1333               if (!isDragging) {
 1334                   updateThumbState(e.getX(), e.getY());
 1335               }
 1336           }
 1337   
 1338           /**
 1339            * Invoked when the mouse exits the scrollbar.
 1340            *
 1341            * @param e MouseEvent further describing the event
 1342            * @since 1.5
 1343            */
 1344           public void mouseExited(MouseEvent e) {
 1345               if (!isDragging) {
 1346                   setThumbRollover(false);
 1347               }
 1348           }
 1349       }
 1350   
 1351   
 1352       /**
 1353        * Listener for cursor keys.
 1354        */
 1355       protected class ArrowButtonListener extends MouseAdapter
 1356       {
 1357           // Because we are handling both mousePressed and Actions
 1358           // we need to make sure we don't fire under both conditions.
 1359           // (keyfocus on scrollbars causes action without mousePress
 1360           boolean handledEvent;
 1361   
 1362           public void mousePressed(MouseEvent e)          {
 1363               if(!scrollbar.isEnabled()) { return; }
 1364               // not an unmodified left mouse button
 1365               //if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
 1366               if( ! SwingUtilities.isLeftMouseButton(e)) { return; }
 1367   
 1368               int direction = (e.getSource() == incrButton) ? 1 : -1;
 1369   
 1370               scrollByUnit(direction);
 1371               scrollTimer.stop();
 1372               scrollListener.setDirection(direction);
 1373               scrollListener.setScrollByBlock(false);
 1374               scrollTimer.start();
 1375   
 1376               handledEvent = true;
 1377               if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
 1378                   scrollbar.requestFocus();
 1379               }
 1380           }
 1381   
 1382           public void mouseReleased(MouseEvent e)         {
 1383               scrollTimer.stop();
 1384               handledEvent = false;
 1385               scrollbar.setValueIsAdjusting(false);
 1386           }
 1387       }
 1388   
 1389   
 1390       /**
 1391        * Listener for scrolling events initiated in the
 1392        * <code>ScrollPane</code>.
 1393        */
 1394       protected class ScrollListener implements ActionListener
 1395       {
 1396           int direction = +1;
 1397           boolean useBlockIncrement;
 1398   
 1399           public ScrollListener() {
 1400               direction = +1;
 1401               useBlockIncrement = false;
 1402           }
 1403   
 1404           public ScrollListener(int dir, boolean block)   {
 1405               direction = dir;
 1406               useBlockIncrement = block;
 1407           }
 1408   
 1409           public void setDirection(int direction) { this.direction = direction; }
 1410           public void setScrollByBlock(boolean block) { this.useBlockIncrement = block; }
 1411   
 1412           public void actionPerformed(ActionEvent e) {
 1413               if(useBlockIncrement)       {
 1414                   scrollByBlock(direction);
 1415                   // Stop scrolling if the thumb catches up with the mouse
 1416                   if(scrollbar.getOrientation() == JScrollBar.VERTICAL)   {
 1417                       if(direction > 0)   {
 1418                           if(getThumbBounds().y + getThumbBounds().height
 1419                                   >= trackListener.currentMouseY)
 1420                                       ((Timer)e.getSource()).stop();
 1421                       } else if(getThumbBounds().y <= trackListener.currentMouseY)        {
 1422                           ((Timer)e.getSource()).stop();
 1423                       }
 1424                   } else {
 1425                       if ((direction > 0 && !isMouseAfterThumb())
 1426                              || (direction < 0 && !isMouseBeforeThumb())) {
 1427   
 1428                          ((Timer)e.getSource()).stop();
 1429                       }
 1430                   }
 1431               } else {
 1432                   scrollByUnit(direction);
 1433               }
 1434   
 1435               if(direction > 0
 1436                   && scrollbar.getValue()+scrollbar.getVisibleAmount()
 1437                           >= scrollbar.getMaximum())
 1438                   ((Timer)e.getSource()).stop();
 1439               else if(direction < 0
 1440                   && scrollbar.getValue() <= scrollbar.getMinimum())
 1441                   ((Timer)e.getSource()).stop();
 1442           }
 1443       }
 1444   
 1445       private boolean isMouseLeftOfThumb() {
 1446           return trackListener.currentMouseX < getThumbBounds().x;
 1447       }
 1448   
 1449       private boolean isMouseRightOfThumb() {
 1450           Rectangle tb = getThumbBounds();
 1451           return trackListener.currentMouseX > tb.x + tb.width;
 1452       }
 1453   
 1454       private boolean isMouseBeforeThumb() {
 1455           return scrollbar.getComponentOrientation().isLeftToRight()
 1456               ? isMouseLeftOfThumb()
 1457               : isMouseRightOfThumb();
 1458       }
 1459   
 1460       private boolean isMouseAfterThumb() {
 1461           return scrollbar.getComponentOrientation().isLeftToRight()
 1462               ? isMouseRightOfThumb()
 1463               : isMouseLeftOfThumb();
 1464       }
 1465   
 1466       private void updateButtonDirections() {
 1467           int orient = scrollbar.getOrientation();
 1468           if (scrollbar.getComponentOrientation().isLeftToRight()) {
 1469               if (incrButton instanceof BasicArrowButton) {
 1470                   ((BasicArrowButton)incrButton).setDirection(
 1471                           orient == HORIZONTAL? EAST : SOUTH);
 1472               }
 1473               if (decrButton instanceof BasicArrowButton) {
 1474                   ((BasicArrowButton)decrButton).setDirection(
 1475                           orient == HORIZONTAL? WEST : NORTH);
 1476               }
 1477           }
 1478           else {
 1479               if (incrButton instanceof BasicArrowButton) {
 1480                   ((BasicArrowButton)incrButton).setDirection(
 1481                           orient == HORIZONTAL? WEST : SOUTH);
 1482               }
 1483               if (decrButton instanceof BasicArrowButton) {
 1484                   ((BasicArrowButton)decrButton).setDirection(
 1485                           orient == HORIZONTAL ? EAST : NORTH);
 1486               }
 1487           }
 1488       }
 1489   
 1490       public class PropertyChangeHandler implements PropertyChangeListener
 1491       {
 1492           // NOTE: This class exists only for backward compatability. All
 1493           // its functionality has been moved into Handler. If you need to add
 1494           // new functionality add it to the Handler, but make sure this
 1495           // class calls into the Handler.
 1496   
 1497           public void propertyChange(PropertyChangeEvent e) {
 1498               getHandler().propertyChange(e);
 1499           }
 1500       }
 1501   
 1502   
 1503       /**
 1504        * Used for scrolling the scrollbar.
 1505        */
 1506       private static class Actions extends UIAction {
 1507           private static final String POSITIVE_UNIT_INCREMENT =
 1508                                       "positiveUnitIncrement";
 1509           private static final String POSITIVE_BLOCK_INCREMENT =
 1510                                       "positiveBlockIncrement";
 1511           private static final String NEGATIVE_UNIT_INCREMENT =
 1512                                       "negativeUnitIncrement";
 1513           private static final String NEGATIVE_BLOCK_INCREMENT =
 1514                                       "negativeBlockIncrement";
 1515           private static final String MIN_SCROLL = "minScroll";
 1516           private static final String MAX_SCROLL = "maxScroll";
 1517   
 1518           Actions(String name) {
 1519               super(name);
 1520           }
 1521   
 1522           public void actionPerformed(ActionEvent e) {
 1523               JScrollBar scrollBar = (JScrollBar)e.getSource();
 1524               String key = getName();
 1525               if (key == POSITIVE_UNIT_INCREMENT) {
 1526                   scroll(scrollBar, POSITIVE_SCROLL, false);
 1527               }
 1528               else if (key == POSITIVE_BLOCK_INCREMENT) {
 1529                   scroll(scrollBar, POSITIVE_SCROLL, true);
 1530               }
 1531               else if (key == NEGATIVE_UNIT_INCREMENT) {
 1532                   scroll(scrollBar, NEGATIVE_SCROLL, false);
 1533               }
 1534               else if (key == NEGATIVE_BLOCK_INCREMENT) {
 1535                   scroll(scrollBar, NEGATIVE_SCROLL, true);
 1536               }
 1537               else if (key == MIN_SCROLL) {
 1538                   scroll(scrollBar, BasicScrollBarUI.MIN_SCROLL, true);
 1539               }
 1540               else if (key == MAX_SCROLL) {
 1541                   scroll(scrollBar, BasicScrollBarUI.MAX_SCROLL, true);
 1542               }
 1543           }
 1544           private void scroll(JScrollBar scrollBar, int dir, boolean block) {
 1545   
 1546               if (dir == NEGATIVE_SCROLL || dir == POSITIVE_SCROLL) {
 1547                   int amount;
 1548                   // Don't use the BasicScrollBarUI.scrollByXXX methods as we
 1549                   // don't want to use an invokeLater to reset the trackHighlight
 1550                   // via an invokeLater
 1551                   if (block) {
 1552                       if (dir == NEGATIVE_SCROLL) {
 1553                           amount = -1 * scrollBar.getBlockIncrement(-1);
 1554                       }
 1555                       else {
 1556                           amount = scrollBar.getBlockIncrement(1);
 1557                       }
 1558                   }
 1559                   else {
 1560                       if (dir == NEGATIVE_SCROLL) {
 1561                           amount = -1 * scrollBar.getUnitIncrement(-1);
 1562                       }
 1563                       else {
 1564                           amount = scrollBar.getUnitIncrement(1);
 1565                       }
 1566                   }
 1567                   scrollBar.setValue(scrollBar.getValue() + amount);
 1568               }
 1569               else if (dir == BasicScrollBarUI.MIN_SCROLL) {
 1570                   scrollBar.setValue(scrollBar.getMinimum());
 1571               }
 1572               else if (dir == BasicScrollBarUI.MAX_SCROLL) {
 1573                   scrollBar.setValue(scrollBar.getMaximum());
 1574               }
 1575           }
 1576       }
 1577   
 1578   
 1579       //
 1580       // EventHandler
 1581       //
 1582       private class Handler implements FocusListener, PropertyChangeListener {
 1583           //
 1584           // FocusListener
 1585           //
 1586           public void focusGained(FocusEvent e) {
 1587               scrollbar.repaint();
 1588           }
 1589   
 1590           public void focusLost(FocusEvent e) {
 1591               scrollbar.repaint();
 1592           }
 1593   
 1594   
 1595           //
 1596           // PropertyChangeListener
 1597           //
 1598           public void propertyChange(PropertyChangeEvent e) {
 1599               String propertyName = e.getPropertyName();
 1600   
 1601               if ("model" == propertyName) {
 1602                   BoundedRangeModel oldModel = (BoundedRangeModel)e.getOldValue();
 1603                   BoundedRangeModel newModel = (BoundedRangeModel)e.getNewValue();
 1604                   oldModel.removeChangeListener(modelListener);
 1605                   newModel.addChangeListener(modelListener);
 1606                   scrollBarValue = scrollbar.getValue();
 1607                   scrollbar.repaint();
 1608                   scrollbar.revalidate();
 1609               } else if ("orientation" == propertyName) {
 1610                   updateButtonDirections();
 1611               } else if ("componentOrientation" == propertyName) {
 1612                   updateButtonDirections();
 1613                   InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
 1614                   SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED, inputMap);
 1615               }
 1616           }
 1617       }
 1618   }

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