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:     configureScrollBarColors();
 764: 
 765:     maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize");
 766:     minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize");
 767:   }
 768: 
 769:   /**
 770:    * Installs the input map from the look and feel defaults, and a 
 771:    * corresponding action map.  Note the the keyboard bindings will only
 772:    * work when the {@link JScrollBar} component has the focus, which is rare.
 773:    */
 774:   protected void installKeyboardActions()
 775:   {
 776:     InputMap keyMap = getInputMap(
 777:         JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 778:     SwingUtilities.replaceUIInputMap(scrollbar, 
 779:         JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
 780:     ActionMap map = getActionMap();
 781:     SwingUtilities.replaceUIActionMap(scrollbar, map);
 782:   }
 783: 
 784:   /**
 785:    * Uninstalls the input map and action map installed by
 786:    * {@link #installKeyboardActions()}.
 787:    */
 788:   protected void uninstallKeyboardActions()
 789:   {
 790:     SwingUtilities.replaceUIActionMap(scrollbar, null);
 791:     SwingUtilities.replaceUIInputMap(scrollbar, 
 792:         JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
 793:   }
 794: 
 795:   InputMap getInputMap(int condition) 
 796:   {
 797:     if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
 798:       return (InputMap) UIManager.get("ScrollBar.focusInputMap");
 799:     return null;
 800:   }
 801:   
 802:   /**
 803:    * Returns the action map for the {@link JScrollBar}.  All scroll bars 
 804:    * share a single action map which is created the first time this method is 
 805:    * called, then stored in the UIDefaults table for subsequent access.
 806:    * 
 807:    * @return The shared action map.
 808:    */
 809:   ActionMap getActionMap() 
 810:   {
 811:     ActionMap map = (ActionMap) UIManager.get("ScrollBar.actionMap");
 812: 
 813:     if (map == null) // first time here
 814:       {
 815:         map = createActionMap();
 816:         if (map != null)
 817:           UIManager.put("ScrollBar.actionMap", map);
 818:       }
 819:     return map;
 820:   }
 821: 
 822:   /**
 823:    * Creates the action map shared by all {@link JSlider} instances.
 824:    * This method is called once by {@link #getActionMap()} when it 
 825:    * finds no action map in the UIDefaults table...after the map is 
 826:    * created, it gets added to the defaults table so that subsequent 
 827:    * calls to {@link #getActionMap()} will return the same shared 
 828:    * instance.
 829:    * 
 830:    * @return The action map.
 831:    */
 832:   ActionMap createActionMap()
 833:   {
 834:     ActionMap map = new ActionMapUIResource();
 835:     map.put("positiveUnitIncrement", 
 836:             new AbstractAction("positiveUnitIncrement") {
 837:               public void actionPerformed(ActionEvent event)
 838:               {
 839:                 JScrollBar sb = (JScrollBar) event.getSource();
 840:                 if (sb.isVisible()) 
 841:                   {
 842:                     int delta = sb.getUnitIncrement(1);
 843:                     sb.setValue(sb.getValue() + delta);
 844:                   }
 845:               }
 846:             }
 847:     );
 848:     map.put("positiveBlockIncrement", 
 849:             new AbstractAction("positiveBlockIncrement") {
 850:               public void actionPerformed(ActionEvent event)
 851:               {
 852:                 JScrollBar sb = (JScrollBar) event.getSource();
 853:                 if (sb.isVisible()) 
 854:                   {
 855:                     int delta = sb.getBlockIncrement(1);
 856:                     sb.setValue(sb.getValue() + delta);
 857:                   }
 858:               }
 859:             }
 860:     );
 861:     map.put("negativeUnitIncrement", 
 862:             new AbstractAction("negativeUnitIncrement") {
 863:               public void actionPerformed(ActionEvent event)
 864:               {
 865:                 JScrollBar sb = (JScrollBar) event.getSource();
 866:                 if (sb.isVisible()) 
 867:                   {
 868:                     int delta = sb.getUnitIncrement(-1);
 869:                     sb.setValue(sb.getValue() + delta);
 870:                   }
 871:               }
 872:             }
 873:     );
 874:     map.put("negativeBlockIncrement", 
 875:             new AbstractAction("negativeBlockIncrement") {
 876:               public void actionPerformed(ActionEvent event)
 877:               {
 878:                 JScrollBar sb = (JScrollBar) event.getSource();
 879:                 if (sb.isVisible()) 
 880:                   {
 881:                     int delta = sb.getBlockIncrement(-1);
 882:                     sb.setValue(sb.getValue() + delta);
 883:                   }
 884:               }
 885:             }
 886:     );
 887:     map.put("minScroll", 
 888:             new AbstractAction("minScroll") {
 889:               public void actionPerformed(ActionEvent event)
 890:               {
 891:                 JScrollBar sb = (JScrollBar) event.getSource();
 892:                 if (sb.isVisible()) 
 893:                   {
 894:                     sb.setValue(sb.getMinimum());
 895:                   }
 896:               }
 897:             }
 898:     );
 899:     map.put("maxScroll", 
 900:             new AbstractAction("maxScroll") {
 901:               public void actionPerformed(ActionEvent event)
 902:               {
 903:                 JScrollBar sb = (JScrollBar) event.getSource();
 904:                 if (sb.isVisible()) 
 905:                   {
 906:                     sb.setValue(sb.getMaximum());
 907:                   }
 908:               }
 909:             }
 910:     );
 911:     return map;
 912:   }
 913:   
 914:   /**
 915:    * This method installs any listeners for the scrollbar. This method also
 916:    * installs listeners for things such as the JButtons and the timer.
 917:    */
 918:   protected void installListeners()
 919:   {
 920:     scrollListener = createScrollListener();
 921:     trackListener = createTrackListener();
 92