OSDN Git Service

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