Source for javax.swing.plaf.basic.BasicScrollBarUI

   1: /* BasicScrollBarUI.java --
   2:    Copyright (C) 2004, 2005, 2006,  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.basic;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.Container;
  44: import java.awt.Dimension;
  45: import java.awt.Graphics;
  46: import java.awt.Insets;
  47: import java.awt.LayoutManager;
  48: import java.awt.Rectangle;
  49: import java.awt.event.ActionEvent;
  50: import java.awt.event.ActionListener;
  51: import java.awt.event.MouseAdapter;
  52: import java.awt.event.MouseEvent;
  53: import java.awt.event.MouseMotionListener;
  54: import java.beans.PropertyChangeEvent;
  55: import java.beans.PropertyChangeListener;
  56: 
  57: import javax.swing.AbstractAction;
  58: import javax.swing.ActionMap;
  59: import javax.swing.BoundedRangeModel;
  60: import javax.swing.InputMap;
  61: import javax.swing.JButton;
  62: import javax.swing.JComponent;
  63: import javax.swing.JScrollBar;
  64: import javax.swing.JSlider;
  65: import javax.swing.LookAndFeel;
  66: import javax.swing.SwingConstants;
  67: import javax.swing.SwingUtilities;
  68: import javax.swing.Timer;
  69: import javax.swing.UIManager;
  70: import javax.swing.event.ChangeEvent;
  71: import javax.swing.event.ChangeListener;
  72: import javax.swing.plaf.ActionMapUIResource;
  73: import javax.swing.plaf.ComponentUI;
  74: import javax.swing.plaf.ScrollBarUI;
  75: 
  76: /**
  77:  * The Basic Look and Feel UI delegate for JScrollBar.
  78:  */
  79: public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
  80:                                                              SwingConstants
  81: {
  82:   /**
  83:    * A helper class that listens to the two JButtons on each end of the
  84:    * JScrollBar.
  85:    */
  86:   protected class ArrowButtonListener extends MouseAdapter
  87:   {
  88:    
  89:     /**
  90:      * Move the thumb in the direction specified by the  button's arrow. If
  91:      * this button is held down, then it should keep moving the thumb.
  92:      *
  93:      * @param e The MouseEvent fired by the JButton.
  94:      */
  95:     public void mousePressed(MouseEvent e)
  96:     {
  97:       scrollTimer.stop();
  98:       scrollListener.setScrollByBlock(false);
  99:       if (e.getSource() == incrButton)
 100:           scrollListener.setDirection(POSITIVE_SCROLL);
 101:       else if (e.getSource() == decrButton)
 102:           scrollListener.setDirection(NEGATIVE_SCROLL);
 103:       scrollTimer.setDelay(100);
 104:       scrollTimer.start();
 105:     }
 106: 
 107:     /**
 108:      * Stops the thumb when the JButton is released.
 109:      *
 110:      * @param e The MouseEvent fired by the JButton.
 111:      */
 112:     public void mouseReleased(MouseEvent e)
 113:     {
 114:       scrollTimer.stop();
 115:       scrollTimer.setDelay(300);
 116:       if (e.getSource() == incrButton)
 117:           scrollByUnit(POSITIVE_SCROLL);
 118:       else if (e.getSource() == decrButton)
 119:         scrollByUnit(NEGATIVE_SCROLL);
 120:     }
 121:   }
 122: 
 123:   /**
 124:    * A helper class that listens to the ScrollBar's model for ChangeEvents.
 125:    */
 126:   protected class ModelListener implements ChangeListener
 127:   {
 128:     /**
 129:      * Called when the model changes.
 130:      *
 131:      * @param e The ChangeEvent fired by the model.
 132:      */
 133:     public void stateChanged(ChangeEvent e)
 134:     {
 135:       calculatePreferredSize();
 136:       updateThumbRect();
 137:       scrollbar.repaint();
 138:     }
 139:   }
 140: 
 141:   /**
 142:    * A helper class that listens to the ScrollBar's properties.
 143:    */
 144:   public class PropertyChangeHandler implements PropertyChangeListener
 145:   {
 146:     /**
 147:      * Called when one of the ScrollBar's properties change.
 148:      *
 149:      * @param e The PropertyChangeEvent fired by the ScrollBar.
 150:      */
 151:     public void propertyChange(PropertyChangeEvent e)
 152:     {
 153:       if (e.getPropertyName().equals("model"))
 154:         {
 155:           ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener);
 156:           scrollbar.getModel().addChangeListener(modelListener);
 157:           updateThumbRect();
 158:         }
 159:       else if (e.getPropertyName().equals("orientation"))
 160:         {
 161:           uninstallListeners();
 162:           uninstallComponents();
 163:           uninstallDefaults();
 164:           installDefaults();
 165:           installComponents();
 166:           installListeners();
 167:         }
 168:       else if (e.getPropertyName().equals("enabled"))
 169:         {
 170:           Boolean b = (Boolean) e.getNewValue();
 171:           if (incrButton != null)
 172:             incrButton.setEnabled(b.booleanValue());
 173:           if (decrButton != null)
 174:             decrButton.setEnabled(b.booleanValue());
 175:         }
 176:     }
 177:   }
 178: 
 179:   /**
 180:    * A helper class that listens for events from the timer that is used to
 181:    * move the thumb.
 182:    */
 183:   protected class ScrollListener implements ActionListener
 184:   {
 185:     /** The direction the thumb moves in. */
 186:     private transient int direction;
 187: 
 188:     /** Whether movement will be in blocks. */
 189:     private transient boolean block;
 190: 
 191:     /**
 192:      * Creates a new ScrollListener object. The default is scrolling
 193:      * positively with block movement.
 194:      */
 195:     public ScrollListener()
 196:     {
 197:       direction = POSITIVE_SCROLL;
 198:       block = true;
 199:     }
 200: 
 201:     /**
 202:      * Creates a new ScrollListener object using the given direction and
 203:      * block.
 204:      *
 205:      * @param dir The direction to move in.
 206:      * @param block Whether movement will be in blocks.
 207:      */
 208:     public ScrollListener(int dir, boolean block)
 209:     {
 210:       direction = dir;
 211:       this.block = block;
 212:     }
 213: 
 214:     /**
 215:      * Sets the direction to scroll in.
 216:      *
 217:      * @param direction The direction to scroll in.
 218:      */
 219:     public void setDirection(int direction)
 220:     {
 221:       this.direction = direction;
 222:     }
 223: 
 224:     /**
 225:      * Sets whether scrolling will be done in blocks.
 226:      *
 227:      * @param block Whether scrolling will be in blocks.
 228:      */
 229:     public void setScrollByBlock(boolean block)
 230:     {
 231:       this.block = block;
 232:     }
 233: 
 234:     /**
 235:      * Called every time the timer reaches its interval.
 236:      *
 237:      * @param e The ActionEvent fired by the timer.
 238:      */
 239:     public void actionPerformed(ActionEvent e)
 240:     {
 241:       if (block)
 242:         {
 243:           // Only need to check it if it's block scrolling
 244:           // We only block scroll if the click occurs
 245:           // in the track.
 246:           if (!trackListener.shouldScroll(direction))
 247:             {
 248:               trackHighlight = NO_HIGHLIGHT;
 249:               scrollbar.repaint();
 250:               return;
 251:             }
 252:             scrollByBlock(direction);
 253:         }
 254:       else
 255:         scrollByUnit(direction);
 256:     }
 257:   }
 258: 
 259:   /**
 260:    * Helper class that listens for movement on the track.
 261:    */
 262:   protected class TrackListener extends MouseAdapter
 263:     implements MouseMotionListener
 264:   {
 265:     /** The current X coordinate of the mouse. */
 266:     protected int currentMouseX;
 267: 
 268:     /** The current Y coordinate of the mouse. */
 269:     protected int currentMouseY;
 270: 
 271:     /**
 272:      * The offset between the current mouse cursor and the  current value of
 273:      * the scrollbar.
 274:      */
 275:     protected int offset;
 276: 
 277:     /**
 278:      * This method is called when the mouse is being dragged.
 279:      *
 280:      * @param e The MouseEvent given.
 281:      */
 282:     public void mouseDragged(MouseEvent e)
 283:     {
 284:       currentMouseX = e.getX();
 285:       currentMouseY = e.getY();
 286:       if (scrollbar.getValueIsAdjusting())
 287:         {
 288:       int value;
 289:       if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
 290:         value = valueForXPosition(currentMouseX) - offset;
 291:       else
 292:         value = valueForYPosition(currentMouseY) - offset;
 293: 
 294:       scrollbar.setValue(value);
 295:         }
 296:     }
 297: 
 298:     /**
 299:      * This method is called when the mouse is moved.
 300:      *
 301:      * @param e The MouseEvent given.
 302:      */
 303:     public void mouseMoved(MouseEvent e)
 304:     {
 305:       if (thumbRect.contains(e.getPoint()))
 306:         thumbRollover = true;
 307:       else
 308:         thumbRollover = false;
 309:     }
 310: 
 311:     /**
 312:      * This method is called when the mouse is pressed. When it is pressed,
 313:      * the thumb should move in blocks towards the cursor.
 314:      *
 315:      * @param e The MouseEvent given.
 316:      */
 317:     public void mousePressed(MouseEvent e)
 318:     {
 319:       currentMouseX = e.getX();
 320:       currentMouseY = e.getY();
 321: 
 322:       int value;
 323:       if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
 324:     value = valueForXPosition(currentMouseX);
 325:       else
 326:     value = valueForYPosition(currentMouseY);
 327: 
 328:       if (! thumbRect.contains(e.getPoint()))
 329:         {
 330:       scrollTimer.stop();
 331:       scrollListener.setScrollByBlock(true);
 332:       if (value > scrollbar.getValue())
 333:         {
 334:           trackHighlight = INCREASE_HIGHLIGHT;
 335:           scrollListener.setDirection(POSITIVE_SCROLL);
 336:         }
 337:       else
 338:         {
 339:           trackHighlight = DECREASE_HIGHLIGHT;
 340:           scrollListener.setDirection(NEGATIVE_SCROLL);
 341:         }
 342:       scrollTimer.setDelay(100);
 343:       scrollTimer.start();
 344:         }
 345:       else
 346:         {
 347:       // We'd like to keep track of where the cursor
 348:       // is inside the thumb.
 349:       // This works because the scrollbar's value represents 
 350:       // "lower" edge of the thumb. The value at which
 351:       // the cursor is at must be greater or equal
 352:       // to that value.
 353: 
 354:       scrollListener.setScrollByBlock(false);
 355:       scrollbar.setValueIsAdjusting(true);
 356:       offset = value - scrollbar.getValue();
 357:         }
 358:       scrollbar.repaint();
 359:     }
 360: 
 361:     /**
 362:      * This method is called when the mouse is released. It should stop
 363:      * movement on the thumb
 364:      *
 365:      * @param e The MouseEvent given.
 366:      */
 367:     public void mouseReleased(MouseEvent e)
 368:     {
 369:       scrollTimer.stop();
 370:       scrollTimer.setDelay(300);
 371:       currentMouseX = e.getX();
 372:       currentMouseY = e.getY();
 373: 
 374:       if (shouldScroll(POSITIVE_SCROLL))
 375:         scrollByBlock(POSITIVE_SCROLL);
 376:       else if (shouldScroll(NEGATIVE_SCROLL))
 377:         scrollByBlock(NEGATIVE_SCROLL);
 378: 
 379:       trackHighlight = NO_HIGHLIGHT;
 380:       scrollListener.setScrollByBlock(false);
 381:       scrollbar.setValueIsAdjusting(true);
 382:       scrollbar.repaint();
 383:     }
 384: 
 385:     /**
 386:      * A helper method that decides whether we should keep scrolling in the
 387:      * given direction.
 388:      *
 389:      * @param direction The direction to check for.
 390:      *
 391:      * @return Whether the thumb should keep scrolling.
 392:      */
 393:     boolean shouldScroll(int direction)
 394:     {
 395:       int value;
 396:       if (scrollbar.getOrientation() == HORIZONTAL)
 397:     value = valueForXPosition(currentMouseX);
 398:       else
 399:     value = valueForYPosition(currentMouseY);
 400: 
 401:       if (thumbRect.contains(currentMouseX, currentMouseY))
 402:         return false;
 403:       
 404:       if (direction == POSITIVE_SCROLL)
 405:     return value > scrollbar.getValue();
 406:       else
 407:     return value < scrollbar.getValue();
 408:     }
 409:   }
 410: 
 411:   /** The listener that listens to the JButtons. */
 412:   protected ArrowButtonListener buttonListener;
 413: 
 414:   /** The listener that listens to the model. */
 415:   protected ModelListener modelListener;
 416: 
 417:   /** The listener that listens to the scrollbar for property changes. */
 418:   protected PropertyChangeListener propertyChangeListener;
 419: 
 420:   /** The listener that listens to the timer. */
 421:   protected ScrollListener scrollListener;
 422: 
 423:   /** The listener that listens for MouseEvents on the track. */
 424:   protected TrackListener trackListener;
 425: 
 426:   /** The JButton that decrements the scrollbar's value. */
 427:   protected JButton decrButton;
 428: 
 429:   /** The JButton that increments the scrollbar's value. */
 430:   protected JButton incrButton;
 431: 
 432:   /** The dimensions of the maximum thumb size. */
 433:   protected Dimension maximumThumbSize;
 434: 
 435:   /** The dimensions of the minimum thumb size. */
 436:   protected Dimension minimumThumbSize;
 437: 
 438:   /** The color of the thumb. */
 439:   protected Color thumbColor;
 440: 
 441:   /** The outer shadow of the thumb. */
 442:   protected Color thumbDarkShadowColor;
 443: 
 444:   /** The top and left edge color for the thumb. */
 445:   protected Color thumbHighlightColor;
 446: 
 447:   /** The outer light shadow for the thumb. */
 448:   protected Color thumbLightShadowColor;
 449: 
 450:   /** The color that is used when the mouse press occurs in the track. */
 451:   protected Color trackHighlightColor;
 452: 
 453:   /** The color of the track. */
 454:   protected Color trackColor;
 455: 
 456:   /** The size and position of the track. */
 457:   protected Rectangle trackRect;
 458: 
 459:   /** The size and position of the thumb. */
 460:   protected Rectangle thumbRect;
 461: 
 462:   /** Indicates that the decrease highlight should be painted. */
 463:   protected static final int DECREASE_HIGHLIGHT = 1;
 464: 
 465:   /** Indicates that the increase highlight should be painted. */
 466:   protected static final int INCREASE_HIGHLIGHT = 2;
 467: 
 468:   /** Indicates that no highlight should be painted. */
 469:   protected static final int NO_HIGHLIGHT = 0;
 470: 
 471:   /** Indicates that the scrolling direction is positive. */
 472:   private static final int POSITIVE_SCROLL = 1;
 473: 
 474:   /** Indicates that the scrolling direction is negative. */
 475:   private static final int NEGATIVE_SCROLL = -1;
 476: 
 477:   /** The cached preferred size for the scrollbar. */
 478:   private transient Dimension preferredSize;
 479: 
 480:   /** The current highlight status. */
 481:   protected int trackHighlight;
 482: 
 483:   /** FIXME: Use this for something (presumably mouseDragged) */
 484:   protected boolean isDragging;
 485: 
 486:   /** The timer used to move the thumb when the mouse is held. */
 487:   protected Timer scrollTimer;
 488: 
 489:   /** The scrollbar this UI is acting for. */
 490:   protected JScrollBar scrollbar;
 491:   
 492:   /** True if the mouse is over the thumb. */
 493:   boolean thumbRollover;
 494: 
 495:   /**
 496:    * This method adds a component to the layout.
 497:    *
 498:    * @param name The name to associate with the component that is added.
 499:    * @param child The Component to add.
 500:    */
 501:   public void addLayoutComponent(String name, Component child)
 502:   {
 503:     // You should not be adding stuff to this component.
 504:     // The contents are fixed.
 505:   }
 506: 
 507:   /**
 508:    * This method configures the scrollbar's colors. This can be  done by
 509:    * looking up the standard colors from the Look and Feel defaults.
 510:    */
 511:   protected void configureScrollBarColors()
 512:   {
 513:     trackColor = UIManager.getColor("ScrollBar.track");
 514:     trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
 515:     thumbColor = UIManager.getColor("ScrollBar.thumb");
 516:     thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
 517:     thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
 518:     thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
 519:   }
 520: 
 521:   /**
 522:    * This method creates an ArrowButtonListener.
 523:    *
 524:    * @return A new ArrowButtonListener.
 525:    */
 526:   protected ArrowButtonListener createArrowButtonListener()
 527:   {
 528:     return new ArrowButtonListener();
 529:   }
 530: 
 531:   /**
 532:    * This method creates a new JButton with the appropriate icon for the
 533:    * orientation.
 534:    *
 535:    * @param orientation The orientation this JButton uses.
 536:    *
 537:    * @return The increase JButton.
 538:    */
 539:   protected JButton createIncreaseButton(int orientation)
 540:   {
 541:     return new BasicArrowButton(orientation);
 542:   }
 543: 
 544:   /**
 545:    * This method creates a new JButton with the appropriate icon for the
 546:    * orientation.
 547:    *
 548:    * @param orientation The orientation this JButton uses.
 549:    *
 550:    * @return The decrease JButton.
 551:    */
 552:   protected JButton createDecreaseButton(int orientation)
 553:   {
 554:     return new BasicArrowButton(orientation);
 555:   }
 556: 
 557:   /**
 558:    * This method creates a new ModelListener.
 559:    *
 560:    * @return A new ModelListener.
 561:    */
 562:   protected ModelListener createModelListener()
 563:   {
 564:     return new ModelListener();
 565:   }
 566: 
 567:   /**
 568:    * This method creates a new PropertyChangeListener.
 569:    *
 570:    * @return A new PropertyChangeListener.
 571:    */
 572:   protected PropertyChangeListener createPropertyChangeListener()
 573:   {
 574:     return new PropertyChangeHandler();
 575:   }
 576: 
 577:   /**
 578:    * This method creates a new ScrollListener.
 579:    *
 580:    * @return A new ScrollListener.
 581:    */
 582:   protected ScrollListener createScrollListener()
 583:   {
 584:     return new ScrollListener();
 585:   }
 586: 
 587:   /**
 588:    * This method creates a new TrackListener.
 589:    *
 590:    * @return A new TrackListener.
 591:    */
 592:   protected TrackListener createTrackListener()
 593:   {
 594:     return new TrackListener();
 595:   }
 596: 
 597:   /**
 598:    * This method returns a new BasicScrollBarUI.
 599:    *
 600:    * @param c The JComponent to create a UI for.
 601:    *
 602:    * @return A new BasicScrollBarUI.
 603:    */
 604:   public static ComponentUI createUI(JComponent c)
 605:   {
 606:     return new BasicScrollBarUI();
 607:   }
 608: 
 609:   /**
 610:    * This method returns the maximum size for this JComponent.
 611:    *
 612:    * @param c The JComponent to measure the maximum size for.
 613:    *
 614:    * @return The maximum size for the component.
 615:    */
 616:   public Dimension getMaximumSize(JComponent c)
 617:   {
 618:     return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
 619:   }
 620: 
 621:   /**
 622:    * This method returns the maximum thumb size.
 623:    *
 624:    * @return The maximum thumb size.
 625:    */
 626:   protected Dimension getMaximumThumbSize()
 627:   {
 628:     return maximumThumbSize;
 629:   }
 630: 
 631:   /**
 632:    * This method returns the minimum size for this JComponent.
 633:    *
 634:    * @param c The JComponent to measure the minimum size for.
 635:    *
 636:    * @return The minimum size for the component.
 637:    */
 638:   public Dimension getMinimumSize(JComponent c)
 639:   {
 640:     return getPreferredSize(c);
 641:   }
 642: 
 643:   /**
 644:    * This method returns the minimum thumb size.
 645:    *
 646:    * @return The minimum thumb size.
 647:    */
 648:   protected Dimension getMinimumThumbSize()
 649:   {
 650:     return minimumThumbSize;
 651:   }
 652: 
 653:   /**
 654:    * This method calculates the preferred size since calling
 655:    * getPreferredSize() returns a cached value.
 656:    * This is package-private to avoid an accessor method.
 657:    */
 658:   void calculatePreferredSize()
 659:   {
 660:     int height;
 661:     int width;
 662:     height = width = 0;
 663: 
 664:     if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
 665:       {
 666:         width += incrButton.getPreferredSize().getWidth();
 667:         width += decrButton.getPreferredSize().getWidth();
 668:         width += 16;
 669:         height = UIManager.getInt("ScrollBar.width");
 670:       }
 671:     else
 672:       {
 673:         height += incrButton.getPreferredSize().getHeight();
 674:         height += decrButton.getPreferredSize().getHeight();
 675:         height += 16;
 676:         width = UIManager.getInt("ScrollBar.width");
 677:       }
 678: 
 679:     Insets insets = scrollbar.getInsets();
 680: 
 681:     height += insets.top + insets.bottom;
 682:     width += insets.left + insets.right;
 683: 
 684:     preferredSize = new Dimension(width, height);
 685:   }
 686: 
 687:   /**
 688:    * This method returns a cached value of the preferredSize. The only
 689:    * restrictions are: If the scrollbar is horizontal, the height should be
 690:    * the maximum of the height of the JButtons and  the minimum width of the
 691:    * thumb. For vertical scrollbars, the  calculation is similar (swap width
 692:    * for height and vice versa).
 693:    *
 694:    * @param c The JComponent to measure.
 695:    *
 696:    * @return The preferredSize.
 697:    */
 698:   public Dimension getPreferredSize(JComponent c)
 699:   {
 700:     calculatePreferredSize();
 701:     return preferredSize;
 702:   }
 703: 
 704:   /**
 705:    * This method returns the thumb's bounds based on the  current value of the
 706:    * scrollbar. This method updates the cached value and returns that.
 707:    *
 708:    * @return The thumb bounds.
 709:    */
 710:   protected Rectangle getThumbBounds()
 711:   {
 712:     return thumbRect;
 713:   }
 714: 
 715:   /**
 716:    * This method calculates the bounds of the track. This method updates the
 717:    * cached value and returns it.
 718:    *
 719:    * @return The track's bounds.
 720:    */
 721:   protected Rectangle getTrackBounds()
 722:   {
 723:     return trackRect;
 724:   }
 725: 
 726:   /**
 727:    * This method installs any addition Components that  are a part of or
 728:    * related to this scrollbar.
 729:    */
 730:   protected void installComponents()
 731:   {
 732:     int orientation = scrollbar.getOrientation();
 733:     switch (orientation)
 734:       {
 735:       case JScrollBar.HORIZONTAL:
 736:         incrButton = createIncreaseButton(EAST);
 737:         decrButton = createDecreaseButton(WEST);
 738:         break;
 739:       default:
 740:         incrButton = createIncreaseButton(SOUTH);
 741:         decrButton = createDecreaseButton(NORTH);
 742:         break;
 743:       }
 744: 
 745:     if (incrButton != null)
 746:       scrollbar.add(incrButton);
 747:     if (decrButton != null)
 748:       scrollbar.add(decrButton);
 749:   }
 750: 
 751:   /**
 752:    * This method installs the defaults for the scrollbar specified by the
 753:    * Basic Look and Feel.
 754:    */
 755:   protected void installDefaults()
 756:   {
 757:     LookAndFeel.installColors(scrollbar, "ScrollBar.background",
 758:                               "ScrollBar.foreground");
 759:     LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
 760:     scrollbar.setOpaque(true);
 761:     scrollbar.setLayout(this);
 762: 
 763:     thumbColor = UIManager.getColor("ScrollBar.thumb");
 764:     thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
 765:     thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
 766:     thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
 767: 
 768:     maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize");
 769:     minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize");
 770:   }
 771: 
 772:   /**
 773:    * Installs the input map from the look and feel defaults, and a 
 774:    * corresponding action map.  Note the the keyboard bindings will only
 775:    * work when the {@link JScrollBar} component has the focus, which is rare.
 776:    */
 777:   protected void installKeyboardActions()
 778:   {
 779:     InputMap keyMap = getInputMap(
 780:         JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 781:     SwingUtilities.replaceUIInputMap(scrollbar, 
 782:         JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
 783:     ActionMap map = getActionMap();
 784:     SwingUtilities.replaceUIActionMap(scrollbar, map);
 785:   }
 786: 
 787:   /**
 788:    * Uninstalls the input map and action map installed by
 789:    * {@link #installKeyboardActions()}.
 790:    */
 791:   protected void uninstallKeyboardActions()
 792:   {
 793:     SwingUtilities.replaceUIActionMap(scrollbar, null);
 794:     SwingUtilities.replaceUIInputMap(scrollbar, 
 795:         JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
 796:   }
 797: 
 798:   InputMap getInputMap(int condition) 
 799:   {
 800:     if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
 801:       return (InputMap) UIManager.get("ScrollBar.focusInputMap");
 802:     return null;
 803:   }
 804:   
 805:   /**
 806:    * Returns the action map for the {@link JScrollBar}.  All scroll bars 
 807:    * share a single action map which is created the first time this method is 
 808:    * called, then stored in the UIDefaults table for subsequent access.
 809:    * 
 810:    * @return The shared action map.
 811:    */
 812:   ActionMap getActionMap() 
 813:   {
 814:     ActionMap map = (ActionMap) UIManager.get("ScrollBar.actionMap");
 815: 
 816:     if (map == null) // first time here
 817:       {
 818:         map = createActionMap();
 819:         if (map != null)
 820:           UIManager.put("ScrollBar.actionMap", map);
 821:       }
 822:     return map;
 823:   }
 824: 
 825:   /**
 826:    * Creates the action map shared by all {@link JSlider} instances.
 827:    * This method is called once by {@link #getActionMap()} when it 
 828:    * finds no action map in the UIDefaults table...after the map is 
 829:    * created, it gets added to the defaults table so that subsequent 
 830:    * calls to {@link #getActionMap()} will return the same shared 
 831:    * instance.
 832:    * 
 833:    * @return The action map.
 834:    */
 835:   ActionMap createActionMap()
 836:   {
 837:     ActionMap map = new ActionMapUIResource();
 838:     map.put("positiveUnitIncrement", 
 839:             new AbstractAction("positiveUnitIncrement") {
 840:               public void actionPerformed(ActionEvent event)
 841:               {
 842:                 JScrollBar sb = (JScrollBar) event.getSource();
 843:                 if (sb.isVisible()) 
 844:                   {
 845:                     int delta = sb.getUnitIncrement(1);
 846:                     sb.setValue(sb.getValue() + delta);
 847:                   }
 848:               }
 849:             }
 850:     );
 851:     map.put("positiveBlockIncrement", 
 852:             new AbstractAction("positiveBlockIncrement") {
 853:               public void actionPerformed(ActionEvent event)
 854:               {
 855:                 JScrollBar sb = (JScrollBar) event.getSource();
 856:                 if (sb.isVisible()) 
 857:                   {
 858:                     int delta = sb.getBlockIncrement(1);
 859:                     sb.setValue(sb.getValue() + delta);
 860:                   }
 861:               }
 862:             }
 863:     );
 864:     map.put("negativeUnitIncrement", 
 865:             new AbstractAction("negativeUnitIncrement") {
 866:               public void actionPerformed(ActionEvent event)
 867:               {
 868:                 JScrollBar sb = (JScrollBar) event.getSource();
 869:                 if (sb.isVisible()) 
 870:                   {
 871:                     int delta = sb.getUnitIncrement(-1);
 872:                     sb.setValue(sb.getValue() + delta);
 873:                   }
 874:               }
 875:             }
 876:     );
 877:     map.put("negativeBlockIncrement", 
 878:             new AbstractAction("negativeBlockIncrement") {
 879:               public void actionPerformed(ActionEvent event)
 880:               {
 881:                 JScrollBar sb = (JScrollBar) event.getSource();
 882:                 if (sb.isVisible()) 
 883:                   {
 884:                     int delta = sb.getBlockIncrement(-1);
 885:                     sb.setValue(sb.getValue() + delta);
 886:                   }
 887:               }
 888:             }
 889:     );
 890:     map.put("minScroll", 
 891:             new AbstractAction("minScroll") {
 892:               public void actionPerformed(ActionEvent event)
 893:               {
 894:                 JScrollBar sb = (JScrollBar) event.getSource();
 895:                 if (sb.isVisible()) 
 896:                   {
 897:                     sb.setValue(sb.getMinimum());
 898:                   }
 899:               }
 900:             }
 901:     );
 902:     map.put("maxScroll", 
 903:             new AbstractAction("maxScroll") {
 904:               public void actionPerformed(ActionEvent event)
 905:               {
 906:                 JScrollBar sb = (JScrollBar) event.getSource();
 907:                 if (sb.isVisible()) 
 908:                   {
 909:                     sb.setValue(sb.getMaximum());
 910:                   }
 911:               }
 912:             }
 913:     );
 914:     return map;
 915:   }
 916:   
 917:   /**
 918:    * This method installs any listeners for the scrollbar. This method also
 919:    * installs listeners for things such as the JButtons and the timer.
 920:    */
 921:   protected void installListeners()
 922:   {
 923:     scrollListener = createScrollListener();
 924:     trackListener = createTrackListener();
 925:     buttonListener = createArrowButtonListener();
 926:     modelListener = createModelListener();
 927:     propertyChangeListener = createPropertyChangeListener();
 928: 
 929:     scrollbar.addMouseMotionListener(trackListener);
 930:     scrollbar.addMouseListener(trackListener);
 931: 
 932:     incrButton.addMouseListener(buttonListener);
 933:     decrButton.addMouseListener(buttonListener);
 934: 
 935:     scrollbar.addPropertyChangeListener(propertyChangeListener);
 936:     scrollbar.getModel().addChangeListener(modelListener);
 937: 
 938:     scrollTimer.addActionListener(scrollListener);
 939:   }
 940: 
 941:   /**
 942:    * This method installs the UI for the component. This can include setting
 943:    * up listeners, defaults,  and components. This also includes initializing
 944:    * any data objects.
 945:    *
 946:    * @param c The JComponent to install.
 947:    */
 948:   public void installUI(JComponent c)
 949:   {
 950:     super.installUI(c);
 951:     if (c instanceof JScrollBar)
 952:       {
 953:         scrollbar = (JScrollBar) c;
 954: 
 955:         trackRect = new Rectangle();
 956:         thumbRect = new Rectangle();
 957: 
 958:         scrollTimer = new Timer(300, null);
 959: 
 960:         installDefaults();
 961:         installComponents();
 962:         configureScrollBarColors();
 963:         installListeners();
 964:         installKeyboardActions();
 965: 
 966:         calculatePreferredSize();
 967:       }
 968:   }
 969: 
 970:   /**
 971:    * This method lays out the scrollbar.
 972:    *
 973:    * @param scrollbarContainer The Container to layout.
 974:    */
 975:   public void layoutContainer(Container scrollbarContainer)
 976:   {
 977:     if (scrollbarContainer instanceof JScrollBar)
 978:       {
 979:     if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
 980:       layoutHScrollbar((JScrollBar) scrollbarContainer);
 981:     else
 982:       layoutVScrollbar((JScrollBar) scrollbarContainer);
 983:       }
 984:   }
 985: 
 986:   /**
 987:    * This method lays out the scrollbar horizontally.
 988:    *
 989:    * @param sb The JScrollBar to layout.
 990:    */
 991:   protected void layoutHScrollbar(JScrollBar sb)
 992:   {
 993:     Rectangle vr = new Rectangle();
 994:     SwingUtilities.calculateInnerArea(scrollbar, vr);
 995: 
 996:     Dimension incrDims = incrButton.getPreferredSize();
 997:     Dimension decrDims = decrButton.getPreferredSize();
 998:     
 999:     // calculate and update the track bounds
1000:     SwingUtilities.calculateInnerArea(scrollbar, trackRect);
1001:     trackRect.width -= incrDims.getWidth();
1002:     trackRect.width -= decrDims.getWidth();
1003:     trackRect.x += decrDims.getWidth();
1004: 
1005:     updateThumbRect();
1006:     
1007:     decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height);
1008:     incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width,
1009:                          trackRect.height);
1010:   }
1011: 
1012:   /**
1013:    * This method lays out the scrollbar vertically.
1014:    *
1015:    * @param sb The JScrollBar to layout.
1016:    */
1017:   protected void layoutVScrollbar(JScrollBar sb)
1018:   {
1019:     Rectangle vr = new Rectangle();
1020:     SwingUtilities.calculateInnerArea(scrollbar, vr);
1021: 
1022:     Dimension incrDims = incrButton.getPreferredSize();
1023:     Dimension decrDims = decrButton.getPreferredSize();
1024:     
1025:     // Update rectangles
1026:     SwingUtilities.calculateInnerArea(scrollbar, trackRect);
1027:     trackRect.height -= incrDims.getHeight();
1028:     trackRect.height -= decrDims.getHeight();
1029:     trackRect.y += decrDims.getHeight();
1030:     
1031:     updateThumbRect();
1032: 
1033:     decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height);
1034:     incrButton.setBounds(vr.x, trackRect.y + trackRect.height,
1035:                          trackRect.width, incrDims.height);
1036:   }
1037: 
1038:   /**
1039:    * Updates the thumb rect.
1040:    */
1041:   void updateThumbRect()
1042:   {
1043:     int max = scrollbar.getMaximum();
1044:     int min = scrollbar.getMinimum();
1045:     int value = scrollbar.getValue();
1046:     int extent = scrollbar.getVisibleAmount();
1047:     if (max - extent <= min)
1048:       {
1049:         if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
1050:           {
1051:             thumbRect.x = trackRect.x;
1052:             thumbRect.y = trackRect.y;
1053:             thumbRect.width = getMinimumThumbSize().width;
1054:             thumbRect.height = trackRect.height;
1055:           }
1056:         else
1057:           {
1058:             thumbRect.x = trackRect.x;
1059:             thumbRect.y = trackRect.y;
1060:             thumbRect.width = trackRect.width;
1061:             thumbRect.height = getMinimumThumbSize().height;
1062:           }
1063:       }
1064:     else
1065:       {
1066:         if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
1067:           {
1068:             thumbRect.x = trackRect.x;
1069:             thumbRect.width = Math.max(extent * trackRect.width / (max - min),
1070:                 getMinimumThumbSize().width);
1071:             int availableWidth = trackRect.width - thumbRect.width;
1072:             thumbRect.x += (value - min) * availableWidth / (max - min - extent);
1073:             thumbRect.y = trackRect.y;
1074:             thumbRect.height = trackRect.height;
1075:           }
1076:         else
1077:           {
1078:             thumbRect.x = trackRect.x;
1079:             thumbRect.height = Math.max(extent * trackRect.height / (max - min),
1080:                     getMinimumThumbSize().height);
1081:             int availableHeight = trackRect.height - thumbRect.height;
1082:             thumbRect.y = trackRect.y 
1083:               + (value - min) * availableHeight / (max - min - extent);
1084:             thumbRect.width = trackRect.width;
1085:           }
1086:       }
1087: 
1088:   }
1089:   
1090:   /**
1091:    * This method returns the minimum size required for the layout.
1092:    *
1093:    * @param scrollbarContainer The Container that is laid out.
1094:    *
1095:    * @return The minimum size.
1096:    */
1097:   public Dimension minimumLayoutSize(Container scrollbarContainer)
1098:   {
1099:     return preferredLayoutSize(scrollbarContainer);
1100:   }
1101: 
1102:   /**
1103:    * This method is called when the component is painted.
1104:    *
1105:    * @param g The Graphics object to paint with.
1106:    * @param c The JComponent to paint.
1107:    */
1108:   public void paint(Graphics g, JComponent c)
1109:   {
1110:     paintTrack(g, c, getTrackBounds());
1111:     paintThumb(g, c, getThumbBounds());
1112: 
1113:     if (trackHighlight == INCREASE_HIGHLIGHT)
1114:       paintIncreaseHighlight(g);
1115:     else if (trackHighlight == DECREASE_HIGHLIGHT)
1116:       paintDecreaseHighlight(g);
1117:   }
1118: 
1119:   /**
1120:    * This method is called when repainting and the mouse is  pressed in the
1121:    * track. It paints the track below the thumb with the trackHighlight
1122:    * color.
1123:    *
1124:    * @param g The Graphics object to paint with.
1125:    */
1126:   protected void paintDecreaseHighlight(Graphics g)
1127:   {
1128:     Color saved = g.getColor();
1129: 
1130:     g.setColor(trackHighlightColor);
1131:     if (scrollbar.getOrientation() == HORIZONTAL)
1132:       g.fillRect(trackRect.x, trackRect.y, thumbRect.x - trackRect.x,
1133:                  trackRect.height);
1134:     else
1135:       g.fillRect(trackRect.x, trackRect.y, trackRect.width,
1136:                  thumbRect.y - trackRect.y);
1137:     g.setColor(saved);
1138:   }
1139: 
1140:   /**
1141:    * This method is called when repainting and the mouse is  pressed in the
1142:    * track. It paints the track above the thumb with the trackHighlight
1143:    * color.
1144:    *
1145:    * @param g The Graphics objet to paint with.
1146:    */
1147:   protected void paintIncreaseHighlight(Graphics g)
1148:   {
1149:     Color saved = g.getColor();
1150: 
1151:     g.setColor(trackHighlightColor);
1152:     if (scrollbar.getOrientation() == HORIZONTAL)
1153:       g.fillRect(thumbRect.x + thumbRect.width, trackRect.y,
1154:                  trackRect.x + trackRect.width - thumbRect.x - thumbRect.width,
1155:                  trackRect.height);
1156:     else
1157:       g.fillRect(trackRect.x, thumbRect.y + thumbRect.height, trackRect.width,
1158:                  trackRect.y + trackRect.height - thumbRect.y
1159:                  - thumbRect.height);
1160:     g.setColor(saved);
1161:   }
1162: 
1163:   /**
1164:    * This method paints the thumb.
1165:    *
1166:    * @param g The Graphics object to paint with.
1167:    * @param c The Component that is being painted.
1168:    * @param thumbBounds The thumb bounds.
1169:    */
1170:   protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
1171:   {
1172:     g.setColor(thumbColor);
1173:     g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width,
1174:                thumbBounds.height);
1175: 
1176:     BasicGraphicsUtils.drawBezel(g, thumbBounds.x, thumbBounds.y,
1177:                                  thumbBounds.width, thumbBounds.height,
1178:                                  false, false, thumbDarkShadowColor,
1179:                                  thumbDarkShadowColor, thumbHighlightColor,
1180:                                  thumbHighlightColor);
1181:   }
1182: 
1183:   /**
1184:    * This method paints the track.
1185:    *
1186:    * @param g The Graphics object to paint with.
1187:    * @param c The JComponent being painted.
1188:    * @param trackBounds The track's bounds.
1189:    */
1190:   protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
1191:   {
1192:     Color saved = g.getColor();
1193:     g.setColor(trackColor);
1194:     g.fill3DRect(trackBounds.x, trackBounds.y, trackBounds.width,
1195:                  trackBounds.height, false);
1196:     g.setColor(saved);
1197:   }
1198: 
1199:   /**
1200:    * This method returns the preferred size for the layout.
1201:    *
1202:    * @param scrollbarContainer The Container to find a size for.
1203:    *
1204:    * @return The preferred size for the layout.
1205:    */
1206:   public Dimension preferredLayoutSize(Container scrollbarContainer)
1207:   {
1208:     if (scrollbarContainer instanceof JComponent)
1209:       return getPreferredSize((JComponent) scrollbarContainer);
1210:     else
1211:       return null;
1212:   }
1213: 
1214:   /**
1215:    * This method removes a child component from the layout.
1216:    *
1217:    * @param child The child to remove.
1218:    */
1219:   public void removeLayoutComponent(Component child)
1220:   {
1221:     // You should not be removing stuff from this component.
1222:   }
1223: 
1224:   /**
1225:    * The method scrolls the thumb by a block in the  direction specified.
1226:    *
1227:    * @param direction The direction to scroll.
1228:    */
1229:   protected void scrollByBlock(int direction)
1230:   {
1231:     scrollbar.setValue(scrollbar.getValue()
1232:                        + scrollbar.getBlockIncrement(direction));
1233:   }
1234: 
1235:   /**
1236:    * The method scrolls the thumb by a unit in the direction specified.
1237:    *
1238:    * @param direction The direction to scroll.
1239:    */
1240:   protected void scrollByUnit(int direction)
1241:   {
1242:     scrollbar.setValue(scrollbar.getValue()
1243:                        + scrollbar.getUnitIncrement(direction));
1244:   }
1245: 
1246:   /**
1247:    * This method sets the thumb's bounds.
1248:    *
1249:    * @param x The X position of the thumb.
1250:    * @param y The Y position of the thumb.
1251:    * @param width The width of the thumb.
1252:    * @param height The height of the thumb.
1253:    */
1254:   protected void setThumbBounds(int x, int y, int width, int height)
1255:   {
1256:     thumbRect.x = x;
1257:     thumbRect.y = y;
1258:     thumbRect.width = width;
1259:     thumbRect.height = height;
1260:   }
1261: 
1262:   /**
1263:    * This method uninstalls any components that  are a part of or related to
1264:    * this scrollbar.
1265:    */
1266:   protected void uninstallComponents()
1267:   {
1268:     if (incrButton != null)
1269:       scrollbar.remove(incrButton);
1270:     if (decrButton != null)
1271:       scrollbar.remove(decrButton);
1272:   }
1273: 
1274:   /**
1275:    * This method uninstalls any defaults that this scrollbar acquired from the
1276:    * Basic Look and Feel defaults.
1277:    */
1278:   protected void uninstallDefaults()
1279:   {
1280:     scrollbar.setForeground(null);
1281:     scrollbar.setBackground(null);
1282:     LookAndFeel.uninstallBorder(scrollbar);
1283:     incrButton = null;
1284:     decrButton = null;
1285:   }
1286: 
1287:   /**
1288:    * This method uninstalls any listeners that were registered during install.
1289:    */
1290:   protected void uninstallListeners()
1291:   {
1292:     if (scrollTimer != null)
1293:       scrollTimer.removeActionListener(scrollListener);
1294: 
1295:     if (scrollbar != null)
1296:       {
1297:         scrollbar.getModel().removeChangeListener(modelListener);
1298:         scrollbar.removePropertyChangeListener(propertyChangeListener);
1299:         scrollbar.removeMouseListener(trackListener);
1300:         scrollbar.removeMouseMotionListener(trackListener);
1301:       }
1302: 
1303:     if (decrButton != null)
1304:       decrButton.removeMouseListener(buttonListener);
1305:     if (incrButton != null)
1306:       incrButton.removeMouseListener(buttonListener);
1307:     
1308:     propertyChangeListener = null;
1309:     modelListener = null;
1310:     buttonListener = null;
1311:     trackListener = null;
1312:     scrollListener = null;
1313:   }
1314: 
1315:   /**
1316:    * This method uninstalls the UI. This includes removing any defaults,
1317:    * listeners, and components that this UI may have initialized. It also
1318:    * nulls any instance data.
1319:    *
1320:    * @param c The Component to uninstall for.
1321:    */
1322:   public void uninstallUI(JComponent c)
1323:   {
1324:     uninstallKeyboardActions();
1325:     uninstallListeners();
1326:     uninstallDefaults();
1327:     uninstallComponents();
1328: 
1329:     scrollTimer = null;
1330: 
1331:     thumbRect = null;
1332:     trackRect = null;
1333: 
1334:     trackColor = null;
1335:     trackHighlightColor = null;
1336:     thumbColor = null;
1337:     thumbHighlightColor = null;
1338:     thumbDarkShadowColor = null;
1339:     thumbLightShadowColor = null;
1340: 
1341:     scrollbar = null;
1342:   }
1343: 
1344:   /**
1345:    * This method returns the value in the scrollbar's range given the y
1346:    * coordinate. If the value is out of range, it will return the closest
1347:    * legal value.
1348:    * This is package-private to avoid an accessor method.
1349:    *
1350:    * @param yPos The y coordinate to calculate a value for.
1351:    *
1352:    * @return The value for the y coordinate.
1353:    */
1354:   int valueForYPosition(int yPos)
1355:   {
1356:     int min = scrollbar.getMinimum();
1357:     int max = scrollbar.getMaximum();
1358:     int len = trackRect.height;
1359: 
1360:     int value;
1361: 
1362:     // If the length is 0, you shouldn't be able to even see where the thumb is.
1363:     // This really shouldn't ever happen, but just in case, we'll return the middle.
1364:     if (len == 0)
1365:       return (max - min) / 2;
1366: 
1367:     value = (yPos - trackRect.y) * (max - min) / len + min;
1368: 
1369:     // If this isn't a legal value, then we'll have to move to one now.
1370:     if (value > max)
1371:       value = max;
1372:     else if (value < min)
1373:       value = min;
1374:     return value;
1375:   }
1376: 
1377:   /**
1378:    * This method returns the value in the scrollbar's range given the x
1379:    * coordinate. If the value is out of range, it will return the closest
1380:    * legal value.
1381:    * This is package-private to avoid an accessor method.
1382:    *
1383:    * @param xPos The x coordinate to calculate a value for.
1384:    *
1385:    * @return The value for the x coordinate.
1386:    */
1387:   int valueForXPosition(int xPos)
1388:   {
1389:     int min = scrollbar.getMinimum();
1390:     int max = scrollbar.getMaximum();
1391:     int len = trackRect.width;
1392: 
1393:     int value;
1394: 
1395:     // If the length is 0, you shouldn't be able to even see where the slider is.
1396:     // This really shouldn't ever happen, but just in case, we'll return the middle.
1397:     if (len == 0)
1398:       return (max - min) / 2;
1399: 
1400:     value = (xPos - trackRect.x) * (max - min) / len + min;
1401: 
1402:     // If this isn't a legal value, then we'll have to move to one now.
1403:     if (value > max)
1404:       value = max;
1405:     else if (value < min)
1406:       value = min;
1407:     return value;
1408:   }
1409:   
1410:   /**
1411:    * Returns true if the mouse is over the thumb.
1412:    * 
1413:    * @return true if the mouse is over the thumb.
1414:    * 
1415:    * @since 1.5
1416:    */
1417:   public boolean isThumbRollover()
1418:   {
1419:    return thumbRollover; 
1420:   }
1421:   
1422:   /**
1423:    * Set thumbRollover to active. This indicates
1424:    * whether or not the mouse is over the thumb.
1425:    * 
1426:    * @param active - true if the mouse is over the thumb.
1427:    * 
1428:    * @since 1.5
1429:    */
1430:   protected void setThumbRollover(boolean active)
1431:   {
1432:     thumbRollover = active;
1433:   }
1434:   
1435:   /**
1436:    * Indicates whether the user can position the thumb with 
1437:    * a mouse click (i.e. middle button).
1438:    * 
1439:    * @return true if the user can position the thumb with a mouse
1440:    * click.
1441:    * 
1442:    * @since 1.5
1443:    */
1444:   public boolean getSupportsAbsolutePositioning()
1445:   {
1446:     // The positioning feature has not been implemented.
1447:     // So, false is always returned.
1448:     return false;
1449:   }
1450: }