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