Source for javax.swing.plaf.basic.BasicScrollBarUI

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