Source for javax.swing.plaf.basic.BasicScrollBarUI

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