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