Source for javax.swing.plaf.basic.BasicScrollBarUI

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