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.min(getMaximumThumbSize().height, height);
 663:       }
 664:     else
 665:       {
 666:     height += incrButton.getPreferredSize().getHeight();
 667:     height += decrButton.getPreferredSize().getHeight();
 668: 
 669:     height += (scrollbar.getMaximum() - scrollbar.getMinimum());
 670: 
 671:     width = Math.max(incrButton.getPreferredSize().width,
 672:                      decrButton.getPreferredSize().width);
 673:     width = Math.max(getMinimumThumbSize().width, width);
 674:     width = Math.min(getMaximumThumbSize().width, width);
 675:       }
 676: 
 677:     Insets insets = scrollbar.getInsets();
 678: 
 679:     height += insets.top + insets.bottom;
 680:     width += insets.left + insets.right;
 681: 
 682:     preferredSize = new Dimension(width, height);
 683:   }
 684: 
 685:   /**
 686:    * This method returns a cached value of the preferredSize. The only
 687:    * restrictions are: If the scrollbar is horizontal, the height should be
 688:    * the maximum of the height of the JButtons and  the minimum width of the
 689:    * thumb. For vertical scrollbars, the  calculation is similar (swap width
 690:    * for height and vice versa).
 691:    *
 692:    * @param c The JComponent to measure.
 693:    *
 694:    * @return The preferredSize.
 695:    */
 696:   public Dimension getPreferredSize(JComponent c)
 697:   {
 698:     calculatePreferredSize();
 699:     return preferredSize;
 700:   }
 701: 
 702:   /**
 703:    * This method returns the thumb's bounds based on the  current value of the
 704:    * scrollbar. This method updates the cached value and returns that.
 705:    *
 706:    * @return The thumb bounds.
 707:    */
 708:   protected Rectangle getThumbBounds()
 709:   {
 710:     int max = scrollbar.getMaximum();
 711:     int min = scrollbar.getMinimum();
 712:     int value = scrollbar.getValue();
 713:     int extent = scrollbar.getVisibleAmount();
 714: 
 715:     // System.err.println(this + ".getThumbBounds()");
 716:     if (max == min)
 717:       {
 718:     thumbRect.x = trackRect.x;
 719:     thumbRect.y = trackRect.y;
 720:     if (scrollbar.getOrientation() == HORIZONTAL)
 721:       {
 722:         thumbRect.width = getMinimumThumbSize().width;
 723:         thumbRect.height = trackRect.height;
 724:       }
 725:     else
 726:       {
 727:         thumbRect.width = trackRect.width;
 728:         thumbRect.height = getMinimumThumbSize().height;
 729:       }
 730:     return thumbRect;
 731:       }
 732: 
 733:     if (scrollbar.getOrientation() == HORIZONTAL)
 734:       {
 735:     thumbRect.x = trackRect.x;
 736:     thumbRect.x += (value - min) * trackRect.width / (max - min);
 737:     thumbRect.y = trackRect.y;
 738: 
 739:     thumbRect.width = Math.max(extent * trackRect.width / (max - min),
 740:                                    getMinimumThumbSize().width);
 741:     thumbRect.height = trackRect.height;
 742:       }
 743:     else
 744:       {
 745:     thumbRect.x = trackRect.x;
 746:     thumbRect.y = trackRect.y + value * trackRect.height / (max - min);
 747: 
 748:     thumbRect.width = trackRect.width;
 749:     thumbRect.height = Math.max(extent * trackRect.height / (max - min),
 750:                                     getMinimumThumbSize().height);
 751:       }
 752:     return thumbRect;
 753:   }
 754: 
 755:   /**
 756:    * This method calculates the bounds of the track. This method updates the
 757:    * cached value and returns it.
 758:    *
 759:    * @return The track's bounds.
 760:    */
 761:   protected Rectangle getTrackBounds()
 762:   {
 763:     SwingUtilities.calculateInnerArea(scrollbar, trackRect);
 764: 
 765:     if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
 766:       {
 767:     trackRect.width -= incrButton.getPreferredSize().getWidth();
 768:     trackRect.width -= decrButton.getPreferredSize().getWidth();
 769: 
 770:     trackRect.x += decrButton.getPreferredSize().getWidth();
 771:       }
 772:     else
 773:       {
 774:     trackRect.height -= incrButton.getPreferredSize().getHeight();
 775:     trackRect.height -= decrButton.getPreferredSize().getHeight();
 776: 
 777:     trackRect.y += incrButton.getPreferredSize().getHeight();
 778:       }
 779:     return trackRect;
 780:   }
 781: 
 782:   /**
 783:    * This method installs any addition Components that  are a part of or
 784:    * related to this scrollbar.
 785:    */
 786:   protected void installComponents()
 787:   {
 788:     int orientation = scrollbar.getOrientation();
 789:     switch (orientation)
 790:       {
 791:       case (JScrollBar.HORIZONTAL):
 792:         incrButton = createIncreaseButton(EAST);
 793:         decrButton = createDecreaseButton(WEST);
 794:         break;
 795:       default:
 796:         incrButton = createIncreaseButton(SOUTH);
 797:         decrButton = createDecreaseButton(NORTH);
 798:         break;
 799:       }
 800:     scrollbar.add(incrButton);
 801:     scrollbar.add(decrButton);
 802:   }
 803: 
 804:   /**
 805:    * This method installs the defaults for the scrollbar specified by the
 806:    * Basic Look and Feel.
 807:    */
 808:   protected void installDefaults()
 809:   {
 810:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 811: 
 812:     scrollbar.setForeground(defaults.getColor("ScrollBar.foreground"));
 813:     scrollbar.setBackground(defaults.getColor("ScrollBar.background"));
 814:     scrollbar.setBorder(defaults.getBorder("ScrollBar.border"));
 815:     scrollbar.setOpaque(true);
 816:     scrollbar.setLayout(this);
 817: 
 818:     thumbColor = defaults.getColor("ScrollBar.thumb");
 819:     thumbDarkShadowColor = defaults.getColor("ScrollBar.thumbDarkShadow");
 820:     thumbHighlightColor = defaults.getColor("ScrollBar.thumbHighlight");
 821:     thumbLightShadowColor = defaults.getColor("ScrollBar.thumbShadow");
 822: 
 823:     maximumThumbSize = defaults.getDimension("ScrollBar.maximumThumbSize");
 824:     minimumThumbSize = defaults.getDimension("ScrollBar.minimumThumbSize");
 825:   }
 826: 
 827:   /**
 828:    * This method installs the keyboard actions for the scrollbar.
 829:    */
 830:   protected void installKeyboardActions()
 831:   {
 832:     // FIXME: implement.
 833:   }
 834: 
 835:   /**
 836:    * This method installs any listeners for the scrollbar. This method also
 837:    * installs listeners for things such as the JButtons and the timer.
 838:    */
 839:   protected void installListeners()
 840:   {
 841:     scrollListener = createScrollListener();
 842:     trackListener = createTrackListener();
 843:     buttonListener = createArrowButtonListener();
 844:     modelListener = createModelListener();
 845:     propertyChangeListener = createPropertyChangeListener();
 846: 
 847:     scrollbar.addMouseMotionListener(trackListener);
 848:     scrollbar.addMouseListener(trackListener);
 849: 
 850:     incrButton.addMouseListener(buttonListener);
 851:     decrButton.addMouseListener(buttonListener);
 852: 
 853:     scrollbar.addPropertyChangeListener(propertyChangeListener);
 854:     scrollbar.getModel().addChangeListener(modelListener);
 855: 
 856:     scrollTimer.addActionListener(scrollListener);
 857:   }
 858: 
 859:   /**
 860:    * This method installs the UI for the component. This can include setting
 861:    * up listeners, defaults,  and components. This also includes initializing
 862:    * any data objects.
 863:    *
 864:    * @param c The JComponent to install.
 865:    */
 866:   public void installUI(JComponent c)
 867:   {
 868:     super.installUI(c);
 869:     if (c instanceof JScrollBar)
 870:       {
 871:     scrollbar = (JScrollBar) c;
 872: 
 873:     trackRect = new Rectangle();
 874:     thumbRect = new Rectangle();
 875: 
 876:     scrollTimer = new Timer(200, null);
 877:     scrollTimer.setRepeats(true);
 878: 
 879:     installComponents();
 880:     installDefaults();
 881:     configureScrollBarColors();
 882:     installListeners();
 883: 
 884:     calculatePreferredSize();
 885:       }
 886:   }
 887: 
 888:   /**
 889:    * This method lays out the scrollbar.
 890:    *
 891:    * @param scrollbarContainer The Container to layout.
 892:    */
 893:   public void layoutContainer(Container scrollbarContainer)
 894:   {
 895:     if (scrollbarContainer instanceof JScrollBar)
 896:       {
 897:     if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
 898:       layoutHScrollbar((JScrollBar) scrollbarContainer);
 899:     else
 900:       layoutVScrollbar((JScrollBar) scrollbarContainer);
 901:       }
 902:   }
 903: 
 904:   /**
 905:    * This method lays out the scrollbar horizontally.
 906:    *
 907:    * @param sb The JScrollBar to layout.
 908:    */
 909:   protected void layoutHScrollbar(JScrollBar sb)
 910:   {
 911:     // All we have to do is layout the 2 buttons?
 912:     Rectangle vr = new Rectangle();
 913:     SwingUtilities.calculateInnerArea(scrollbar, vr);
 914: 
 915:     // Update the rectangles.
 916:     getTrackBounds();
 917:     getThumbBounds();
 918: 
 919:     Dimension incrDims = incrButton.getPreferredSize();
 920:     Dimension decrDims = decrButton.getPreferredSize();
 921: 
 922:     decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height);
 923:     incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width,
 924:                          trackRect.height);
 925:   }
 926: 
 927:   /**
 928:    * This method lays out the scrollbar vertically.
 929:    *
 930:    * @param sb The JScrollBar to layout.
 931:    */
 932:   protected void layoutVScrollbar(JScrollBar sb)
 933:   {
 934:     Rectangle vr = new Rectangle();
 935:     SwingUtilities.calculateInnerArea(scrollbar, vr);
 936: 
 937:     // Update rectangles
 938:     getTrackBounds();
 939:     getThumbBounds();
 940: 
 941:     Dimension incrDims = incrButton.getPreferredSize();
 942:     Dimension decrDims = decrButton.getPreferredSize();
 943: 
 944:     decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height);
 945:     incrButton.setBounds(vr.x, trackRect.y + trackRect.height,
 946:                          trackRect.width, incrDims.height);
 947:   }
 948: 
 949:   /**
 950:    * This method returns the minimum size required for the layout.
 951:    *
 952:    * @param scrollbarContainer The Container that is laid out.
 953:    *
 954:    * @return The minimum size.
 955:    */
 956:   public Dimension minimumLayoutSize(Container scrollbarContainer)
 957:   {
 958:     return preferredLayoutSize(scrollbarContainer);
 959:   }
 960: 
 961:   /**
 962:    * This method is called when the component is painted.
 963:    *
 964:    * @param g The Graphics object to paint with.
 965:    * @param c The JComponent to paint.
 966:    */
 967:   public void paint(Graphics g, JComponent c)
 968:   {
 969:     paintTrack(g, c, getTrackBounds());
 970:     paintThumb(g, c, getThumbBounds());
 971: 
 972:     if (trackHighlight == INCREASE_HIGHLIGHT)
 973:       paintIncreaseHighlight(g);
 974:     else if (trackHighlight == DECREASE_HIGHLIGHT)
 975:       paintDecreaseHighlight(g);
 976:   }
 977: 
 978:   /**
 979:    * This method is called when repainting and the mouse is  pressed in the
 980:    * track. It paints the track below the thumb with the trackHighlight
 981:    * color.
 982:    *
 983:    * @param g The Graphics object to paint with.
 984:    */
 985:   protected void paintDecreaseHighlight(Graphics g)
 986:   {
 987:     Color saved = g.getColor();
 988: 
 989:     g.setColor(trackHighlightColor);
 990:     if (scrollbar.getOrientation() == HORIZONTAL)
 991:       g.fillRect(trackRect.x, trackRect.y, thumbRect.x - trackRect.x,
 992:                  trackRect.height);
 993:     else
 994:       g.fillRect(trackRect.x, trackRect.y, trackRect.width,
 995:                  thumbRect.y - trackRect.y);
 996:     g.setColor(saved);
 997:   }
 998: 
 999:   /**
1000:    * This method is called when repainting and the mouse is  pressed in the
1001:    * track. It paints the track above the thumb with the trackHighlight
1002:    * color.
1003:    *
1004:    * @param g The Graphics objet to paint with.
1005:    */
1006:   protected void paintIncreaseHighlight(Graphics g)
1007:   {
1008:     Color saved = g.getColor();
1009: 
1010:     g.setColor(trackHighlightColor);
1011:     if (scrollbar.getOrientation() == HORIZONTAL)
1012:       g.fillRect(thumbRect.x + thumbRect.width, trackRect.y,
1013:                  trackRect.x + trackRect.width - thumbRect.x - thumbRect.width,
1014:                  trackRect.height);
1015:     else
1016:       g.fillRect(trackRect.x, thumbRect.y + thumbRect.height, trackRect.width,
1017:                  trackRect.y + trackRect.height - thumbRect.y
1018:                  - thumbRect.height);
1019:     g.setColor(saved);
1020:   }
1021: 
1022:   /**
1023:    * This method paints the thumb.
1024:    *
1025:    * @param g The Graphics object to paint with.
1026:    * @param c The Component that is being painted.
1027:    * @param thumbBounds The thumb bounds.
1028:    */
1029:   protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
1030:   {
1031:     g.setColor(thumbColor);
1032:     g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width,
1033:                thumbBounds.height);
1034: 
1035:     BasicGraphicsUtils.drawBezel(g, thumbBounds.x, thumbBounds.y,
1036:                                  thumbBounds.width, thumbBounds.height,
1037:                                  false, false, thumbDarkShadowColor,
1038:                                  thumbDarkShadowColor, thumbHighlightColor,
1039:                                  thumbHighlightColor);
1040:   }
1041: 
1042:   /**
1043:    * This method paints the track.
1044:    *
1045:    * @param g The Graphics object to paint with.
1046:    * @param c The JComponent being painted.
1047:    * @param trackBounds The track's bounds.
1048:    */
1049:   protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
1050:   {
1051:     Color saved = g.getColor();
1052:     g.setColor(trackColor);
1053:     g.fill3DRect(trackBounds.x, trackBounds.y, trackBounds.width,
1054:                  trackBounds.height, false);
1055:     g.setColor(saved);
1056:   }
1057: 
1058:   /**
1059:    * This method returns the preferred size for the layout.
1060:    *
1061:    * @param scrollbarContainer The Container to find a size for.
1062:    *
1063:    * @return The preferred size for the layout.
1064:    */
1065:   public Dimension preferredLayoutSize(Container scrollbarContainer)
1066:   {
1067:     if (scrollbarContainer instanceof JComponent)
1068:       return getPreferredSize((JComponent) scrollbarContainer);
1069:     else
1070:       return null;
1071:   }
1072: 
1073:   /**
1074:    * This method removes a child component from the layout.
1075:    *
1076:    * @param child The child to remove.
1077:    */
1078:   public void removeLayoutComponent(Component child)
1079:   {
1080:     // You should not be removing stuff from this component.
1081:   }
1082: 
1083:   /**
1084:    * The method scrolls the thumb by a block in the  direction specified.
1085:    *
1086:    * @param direction The direction to scroll.
1087:    */
1088:   protected void scrollByBlock(int direction)
1089:   {
1090:     scrollbar.setValue(scrollbar.getValue()
1091:                        + scrollbar.getBlockIncrement(direction));
1092:   }
1093: 
1094:   /**
1095:    * The method scrolls the thumb by a unit in the direction specified.
1096:    *
1097:    * @param direction The direction to scroll.
1098:    */
1099:   protected void scrollByUnit(int direction)
1100:   {
1101:     scrollbar.setValue(scrollbar.getValue()
1102:                        + scrollbar.getUnitIncrement(direction));
1103:   }
1104: 
1105:   /**
1106:    * This method sets the thumb's bounds.
1107:    *
1108:    * @param x The X position of the thumb.
1109:    * @param y The Y position of the thumb.
1110:    * @param width The width of the thumb.
1111:    * @param height The height of the thumb.
1112:    */
1113:   protected void setThumbBounds(int x, int y, int width, int height)
1114:   {
1115:     thumbRect.x = x;
1116:     thumbRect.y = y;
1117:     thumbRect.width = width;
1118:     thumbRect.height = height;
1119:   }
1120: 
1121:   /**
1122:    * This method uninstalls any components that  are a part of or related to
1123:    * this scrollbar.
1124:    */
1125:   protected void uninstallComponents()
1126:   {
1127:     scrollbar.remove(incrButton);
1128:     scrollbar.remove(decrButton);
1129:     incrButton = null;
1130:     decrButton = null;
1131:   }
1132: 
1133:   /**
1134:    * This method uninstalls any defaults that this scrollbar acquired from the
1135:    * Basic Look and Feel defaults.
1136:    */
1137:   protected void uninstallDefaults()
1138:   {
1139:     scrollbar.setForeground(null);
1140:     scrollbar.setBackground(null);
1141:     scrollbar.setBorder(null);
1142:   }
1143: 
1144:   /**
1145:    * This method uninstalls any keyboard actions this scrollbar acquired
1146:    * during install.
1147:    */
1148:   protected void uninstallKeyboardActions()
1149:   {
1150:     // FIXME: implement.
1151:   }
1152: 
1153:   /**
1154:    * This method uninstalls any listeners that were registered during install.
1155:    */
1156:   protected void uninstallListeners()
1157:   {
1158:     scrollTimer.removeActionListener(scrollListener);
1159: 
1160:     scrollbar.getModel().removeChangeListener(modelListener);
1161:     scrollbar.removePropertyChangeListener(propertyChangeListener);
1162: 
1163:     decrButton.removeMouseListener(buttonListener);
1164:     incrButton.removeMouseListener(buttonListener);
1165: 
1166:     scrollbar.removeMouseListener(trackListener);
1167:     scrollbar.removeMouseMotionListener(trackListener);
1168: 
1169:     propertyChangeListener = null;
1170:     modelListener = null;
1171:     buttonListener = null;
1172:     trackListener = null;
1173:     scrollListener = null;
1174:   }
1175: 
1176:   /**
1177:    * This method uninstalls the UI. This includes removing any defaults,
1178:    * listeners, and components that this UI may have initialized. It also
1179:    * nulls any instance data.
1180:    *
1181:    * @param c The Component to uninstall for.
1182:    */
1183:   public void uninstallUI(JComponent c)
1184:   {
1185:     uninstallDefaults();
1186:     uninstallListeners();
1187:     uninstallComponents();
1188: 
1189:     scrollTimer = null;
1190: 
1191:     thumbRect = null;
1192:     trackRect = null;
1193: 
1194:     trackColor = null;
1195:     trackHighlightColor = null;
1196:     thumbColor = null;
1197:     thumbHighlightColor = null;
1198:     thumbDarkShadowColor = null;
1199:     thumbLightShadowColor = null;
1200: 
1201:     scrollbar = null;
1202:   }
1203: 
1204:   /**
1205:    * This method returns the value in the scrollbar's range given the y
1206:    * coordinate. If the value is out of range, it will return the closest
1207:    * legal value.
1208:    * This is package-private to avoid an accessor method.
1209:    *
1210:    * @param yPos The y coordinate to calculate a value for.
1211:    *
1212:    * @return The value for the y coordinate.
1213:    */
1214:   int valueForYPosition(int yPos)
1215:   {
1216:     int min = scrollbar.getMinimum();
1217:     int max = scrollbar.getMaximum();
1218:     int len = trackRect.height;
1219: 
1220:     int value;
1221: 
1222:     // If the length is 0, you shouldn't be able to even see where the thumb is.
1223:     // This really shouldn't ever happen, but just in case, we'll return the middle.
1224:     if (len == 0)
1225:       return ((max - min) / 2);
1226: 
1227:     value = ((yPos - trackRect.y) * (max - min) / len + min);
1228: 
1229:     // If this isn't a legal value, then we'll have to move to one now.
1230:     if (value > max)
1231:       value = max;
1232:     else if (value < min)
1233:       value = min;
1234:     return value;
1235:   }
1236: 
1237:   /**
1238:    * This method returns the value in the scrollbar's range given the x
1239:    * coordinate. If the value is out of range, it will return the closest
1240:    * legal value.
1241:    * This is package-private to avoid an accessor method.
1242:    *
1243:    * @param xPos The x coordinate to calculate a value for.
1244:    *
1245:    * @return The value for the x coordinate.
1246:    */
1247:   int valueForXPosition(int xPos)
1248:   {
1249:     int min = scrollbar.getMinimum();
1250:     int max = scrollbar.getMaximum();
1251:     int len = trackRect.width;
1252: 
1253:     int value;
1254: 
1255:     // If the length is 0, you shouldn't be able to even see where the slider is.
1256:     // This really shouldn't ever happen, but just in case, we'll return the middle.
1257:     if (len == 0)
1258:       return ((max - min) / 2);
1259: 
1260:     value = ((xPos - trackRect.x) * (max - min) / len + min);
1261: 
1262:     // If this isn't a legal value, then we'll have to move to one now.
1263:     if (value > max)
1264:       value = max;
1265:     else if (value < min)
1266:       value = min;
1267:     return value;
1268:   }
1269: }