1 /* BasicScrollBarUI.java --
2 Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package javax.swing.plaf.basic;
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;
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;
77 * The Basic Look and Feel UI delegate for JScrollBar.
79 public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
83 * A helper class that listens to the two JButtons on each end of the
86 protected class ArrowButtonListener extends MouseAdapter
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.
93 * @param e The MouseEvent fired by the JButton.
95 public void mousePressed(MouseEvent e)
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);
108 * Stops the thumb when the JButton is released.
110 * @param e The MouseEvent fired by the JButton.
112 public void mouseReleased(MouseEvent e)
115 scrollTimer.setDelay(300);
116 if (e.getSource() == incrButton)
117 scrollByUnit(POSITIVE_SCROLL);
118 else if (e.getSource() == decrButton)
119 scrollByUnit(NEGATIVE_SCROLL);
124 * A helper class that listens to the ScrollBar's model for ChangeEvents.
126 protected class ModelListener implements ChangeListener
129 * Called when the model changes.
131 * @param e The ChangeEvent fired by the model.
133 public void stateChanged(ChangeEvent e)
135 calculatePreferredSize();
142 * A helper class that listens to the ScrollBar's properties.
144 public class PropertyChangeHandler implements PropertyChangeListener
147 * Called when one of the ScrollBar's properties change.
149 * @param e The PropertyChangeEvent fired by the ScrollBar.
151 public void propertyChange(PropertyChangeEvent e)
153 if (e.getPropertyName().equals("model"))
155 ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener);
156 scrollbar.getModel().addChangeListener(modelListener);
159 else if (e.getPropertyName().equals("orientation"))
161 uninstallListeners();
162 uninstallComponents();
168 else if (e.getPropertyName().equals("enabled"))
170 Boolean b = (Boolean) e.getNewValue();
171 if (incrButton != null)
172 incrButton.setEnabled(b.booleanValue());
173 if (decrButton != null)
174 decrButton.setEnabled(b.booleanValue());
180 * A helper class that listens for events from the timer that is used to
183 protected class ScrollListener implements ActionListener
185 /** The direction the thumb moves in. */
186 private transient int direction;
188 /** Whether movement will be in blocks. */
189 private transient boolean block;
192 * Creates a new ScrollListener object. The default is scrolling
193 * positively with block movement.
195 public ScrollListener()
197 direction = POSITIVE_SCROLL;
202 * Creates a new ScrollListener object using the given direction and
205 * @param dir The direction to move in.
206 * @param block Whether movement will be in blocks.
208 public ScrollListener(int dir, boolean block)
215 * Sets the direction to scroll in.
217 * @param direction The direction to scroll in.
219 public void setDirection(int direction)
221 this.direction = direction;
225 * Sets whether scrolling will be done in blocks.
227 * @param block Whether scrolling will be in blocks.
229 public void setScrollByBlock(boolean block)
235 * Called every time the timer reaches its interval.
237 * @param e The ActionEvent fired by the timer.
239 public void actionPerformed(ActionEvent e)
243 // Only need to check it if it's block scrolling
244 // We only block scroll if the click occurs
246 if (!trackListener.shouldScroll(direction))
248 trackHighlight = NO_HIGHLIGHT;
252 scrollByBlock(direction);
255 scrollByUnit(direction);
260 * Helper class that listens for movement on the track.
262 protected class TrackListener extends MouseAdapter
263 implements MouseMotionListener
265 /** The current X coordinate of the mouse. */
266 protected int currentMouseX;
268 /** The current Y coordinate of the mouse. */
269 protected int currentMouseY;
272 * The offset between the current mouse cursor and the current value of
275 protected int offset;
278 * This method is called when the mouse is being dragged.
280 * @param e The MouseEvent given.
282 public void mouseDragged(MouseEvent e)
284 currentMouseX = e.getX();
285 currentMouseY = e.getY();
286 if (scrollbar.getValueIsAdjusting())
289 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
290 value = valueForXPosition(currentMouseX) - offset;
292 value = valueForYPosition(currentMouseY) - offset;
294 scrollbar.setValue(value);
299 * This method is called when the mouse is moved.
301 * @param e The MouseEvent given.
303 public void mouseMoved(MouseEvent e)
305 if (thumbRect.contains(e.getPoint()))
306 thumbRollover = true;
308 thumbRollover = false;
312 * This method is called when the mouse is pressed. When it is pressed,
313 * the thumb should move in blocks towards the cursor.
315 * @param e The MouseEvent given.
317 public void mousePressed(MouseEvent e)
319 currentMouseX = e.getX();
320 currentMouseY = e.getY();
323 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
324 value = valueForXPosition(currentMouseX);
326 value = valueForYPosition(currentMouseY);
328 if (! thumbRect.contains(e.getPoint()))
331 scrollListener.setScrollByBlock(true);
332 if (value > scrollbar.getValue())
334 trackHighlight = INCREASE_HIGHLIGHT;
335 scrollListener.setDirection(POSITIVE_SCROLL);
339 trackHighlight = DECREASE_HIGHLIGHT;
340 scrollListener.setDirection(NEGATIVE_SCROLL);
342 scrollTimer.setDelay(100);
347 // We'd like to keep track of where the cursor
348 // is inside the thumb.
349 // This works because the scrollbar's value represents
350 // "lower" edge of the thumb. The value at which
351 // the cursor is at must be greater or equal
354 scrollListener.setScrollByBlock(false);
355 scrollbar.setValueIsAdjusting(true);
356 offset = value - scrollbar.getValue();
362 * This method is called when the mouse is released. It should stop
363 * movement on the thumb
365 * @param e The MouseEvent given.
367 public void mouseReleased(MouseEvent e)
370 scrollTimer.setDelay(300);
371 currentMouseX = e.getX();
372 currentMouseY = e.getY();
374 if (shouldScroll(POSITIVE_SCROLL))
375 scrollByBlock(POSITIVE_SCROLL);
376 else if (shouldScroll(NEGATIVE_SCROLL))
377 scrollByBlock(NEGATIVE_SCROLL);
379 trackHighlight = NO_HIGHLIGHT;
380 scrollListener.setScrollByBlock(false);
381 scrollbar.setValueIsAdjusting(true);
386 * A helper method that decides whether we should keep scrolling in the
389 * @param direction The direction to check for.
391 * @return Whether the thumb should keep scrolling.
393 boolean shouldScroll(int direction)
396 if (scrollbar.getOrientation() == HORIZONTAL)
397 value = valueForXPosition(currentMouseX);
399 value = valueForYPosition(currentMouseY);
401 if (thumbRect.contains(currentMouseX, currentMouseY))
404 if (direction == POSITIVE_SCROLL)
405 return value > scrollbar.getValue();
407 return value < scrollbar.getValue();
411 /** The listener that listens to the JButtons. */
412 protected ArrowButtonListener buttonListener;
414 /** The listener that listens to the model. */
415 protected ModelListener modelListener;
417 /** The listener that listens to the scrollbar for property changes. */
418 protected PropertyChangeListener propertyChangeListener;
420 /** The listener that listens to the timer. */
421 protected ScrollListener scrollListener;
423 /** The listener that listens for MouseEvents on the track. */
424 protected TrackListener trackListener;
426 /** The JButton that decrements the scrollbar's value. */
427 protected JButton decrButton;
429 /** The JButton that increments the scrollbar's value. */
430 protected JButton incrButton;
432 /** The dimensions of the maximum thumb size. */
433 protected Dimension maximumThumbSize;
435 /** The dimensions of the minimum thumb size. */
436 protected Dimension minimumThumbSize;
438 /** The color of the thumb. */
439 protected Color thumbColor;
441 /** The outer shadow of the thumb. */
442 protected Color thumbDarkShadowColor;
444 /** The top and left edge color for the thumb. */
445 protected Color thumbHighlightColor;
447 /** The outer light shadow for the thumb. */
448 protected Color thumbLightShadowColor;
450 /** The color that is used when the mouse press occurs in the track. */
451 protected Color trackHighlightColor;
453 /** The color of the track. */
454 protected Color trackColor;
456 /** The size and position of the track. */
457 protected Rectangle trackRect;
459 /** The size and position of the thumb. */
460 protected Rectangle thumbRect;
462 /** Indicates that the decrease highlight should be painted. */
463 protected static final int DECREASE_HIGHLIGHT = 1;
465 /** Indicates that the increase highlight should be painted. */
466 protected static final int INCREASE_HIGHLIGHT = 2;
468 /** Indicates that no highlight should be painted. */
469 protected static final int NO_HIGHLIGHT = 0;
471 /** Indicates that the scrolling direction is positive. */
472 private static final int POSITIVE_SCROLL = 1;
474 /** Indicates that the scrolling direction is negative. */
475 private static final int NEGATIVE_SCROLL = -1;
477 /** The cached preferred size for the scrollbar. */
478 private transient Dimension preferredSize;
480 /** The current highlight status. */
481 protected int trackHighlight;
483 /** FIXME: Use this for something (presumably mouseDragged) */
484 protected boolean isDragging;
486 /** The timer used to move the thumb when the mouse is held. */
487 protected Timer scrollTimer;
489 /** The scrollbar this UI is acting for. */
490 protected JScrollBar scrollbar;
492 /** True if the mouse is over the thumb. */
493 boolean thumbRollover;
496 * This method adds a component to the layout.
498 * @param name The name to associate with the component that is added.
499 * @param child The Component to add.
501 public void addLayoutComponent(String name, Component child)
503 // You should not be adding stuff to this component.
504 // The contents are fixed.
508 * This method configures the scrollbar's colors. This can be done by
509 * looking up the standard colors from the Look and Feel defaults.
511 protected void configureScrollBarColors()
513 trackColor = UIManager.getColor("ScrollBar.track");
514 trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
515 thumbColor = UIManager.getColor("ScrollBar.thumb");
516 thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
517 thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
518 thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
522 * This method creates an ArrowButtonListener.
524 * @return A new ArrowButtonListener.
526 protected ArrowButtonListener createArrowButtonListener()
528 return new ArrowButtonListener();
532 * This method creates a new JButton with the appropriate icon for the
535 * @param orientation The orientation this JButton uses.
537 * @return The increase JButton.
539 protected JButton createIncreaseButton(int orientation)
541 return new BasicArrowButton(orientation);
545 * This method creates a new JButton with the appropriate icon for the
548 * @param orientation The orientation this JButton uses.
550 * @return The decrease JButton.
552 protected JButton createDecreaseButton(int orientation)
554 return new BasicArrowButton(orientation);
558 * This method creates a new ModelListener.
560 * @return A new ModelListener.
562 protected ModelListener createModelListener()
564 return new ModelListener();
568 * This method creates a new PropertyChangeListener.
570 * @return A new PropertyChangeListener.
572 protected PropertyChangeListener createPropertyChangeListener()
574 return new PropertyChangeHandler();
578 * This method creates a new ScrollListener.
580 * @return A new ScrollListener.
582 protected ScrollListener createScrollListener()
584 return new ScrollListener();
588 * This method creates a new TrackListener.
590 * @return A new TrackListener.
592 protected TrackListener createTrackListener()
594 return new TrackListener();
598 * This method returns a new BasicScrollBarUI.
600 * @param c The JComponent to create a UI for.
602 * @return A new BasicScrollBarUI.
604 public static ComponentUI createUI(JComponent c)
606 return new BasicScrollBarUI();
610 * This method returns the maximum size for this JComponent.
612 * @param c The JComponent to measure the maximum size for.
614 * @return The maximum size for the component.
616 public Dimension getMaximumSize(JComponent c)
618 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
622 * This method returns the maximum thumb size.
624 * @return The maximum thumb size.
626 protected Dimension getMaximumThumbSize()
628 return maximumThumbSize;
632 * This method returns the minimum size for this JComponent.
634 * @param c The JComponent to measure the minimum size for.
636 * @return The minimum size for the component.
638 public Dimension getMinimumSize(JComponent c)
640 return getPreferredSize(c);
644 * This method returns the minimum thumb size.
646 * @return The minimum thumb size.
648 protected Dimension getMinimumThumbSize()
650 return minimumThumbSize;
654 * This method calculates the preferred size since calling
655 * getPreferredSize() returns a cached value.
656 * This is package-private to avoid an accessor method.
658 void calculatePreferredSize()
664 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
666 width += incrButton.getPreferredSize().getWidth();
667 width += decrButton.getPreferredSize().getWidth();
669 height = UIManager.getInt("ScrollBar.width");
673 height += incrButton.getPreferredSize().getHeight();
674 height += decrButton.getPreferredSize().getHeight();
676 width = UIManager.getInt("ScrollBar.width");
679 Insets insets = scrollbar.getInsets();
681 height += insets.top + insets.bottom;
682 width += insets.left + insets.right;
684 preferredSize = new Dimension(width, height);
688 * This method returns a cached value of the preferredSize. The only
689 * restrictions are: If the scrollbar is horizontal, the height should be
690 * the maximum of the height of the JButtons and the minimum width of the
691 * thumb. For vertical scrollbars, the calculation is similar (swap width
692 * for height and vice versa).
694 * @param c The JComponent to measure.
696 * @return The preferredSize.
698 public Dimension getPreferredSize(JComponent c)
700 calculatePreferredSize();
701 return preferredSize;
705 * This method returns the thumb's bounds based on the current value of the
706 * scrollbar. This method updates the cached value and returns that.
708 * @return The thumb bounds.
710 protected Rectangle getThumbBounds()
716 * This method calculates the bounds of the track. This method updates the
717 * cached value and returns it.
719 * @return The track's bounds.
721 protected Rectangle getTrackBounds()
727 * This method installs any addition Components that are a part of or
728 * related to this scrollbar.
730 protected void installComponents()
732 int orientation = scrollbar.getOrientation();
735 case JScrollBar.HORIZONTAL:
736 incrButton = createIncreaseButton(EAST);
737 decrButton = createDecreaseButton(WEST);
740 incrButton = createIncreaseButton(SOUTH);
741 decrButton = createDecreaseButton(NORTH);
745 if (incrButton != null)
746 scrollbar.add(incrButton);
747 if (decrButton != null)
748 scrollbar.add(decrButton);
752 * This method installs the defaults for the scrollbar specified by the
753 * Basic Look and Feel.
755 protected void installDefaults()
757 LookAndFeel.installColors(scrollbar, "ScrollBar.background",
758 "ScrollBar.foreground");
759 LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
760 scrollbar.setOpaque(true);
761 scrollbar.setLayout(this);
763 configureScrollBarColors();
765 maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize");
766 minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize");
770 * Installs the input map from the look and feel defaults, and a
771 * corresponding action map. Note the the keyboard bindings will only
772 * work when the {@link JScrollBar} component has the focus, which is rare.
774 protected void installKeyboardActions()
776 InputMap keyMap = getInputMap(
777 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
778 SwingUtilities.replaceUIInputMap(scrollbar,
779 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
780 ActionMap map = getActionMap();
781 SwingUtilities.replaceUIActionMap(scrollbar, map);
785 * Uninstalls the input map and action map installed by
786 * {@link #installKeyboardActions()}.
788 protected void uninstallKeyboardActions()
790 SwingUtilities.replaceUIActionMap(scrollbar, null);
791 SwingUtilities.replaceUIInputMap(scrollbar,
792 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
795 InputMap getInputMap(int condition)
797 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
798 return (InputMap) UIManager.get("ScrollBar.focusInputMap");
803 * Returns the action map for the {@link JScrollBar}. All scroll bars
804 * share a single action map which is created the first time this method is
805 * called, then stored in the UIDefaults table for subsequent access.
807 * @return The shared action map.
809 ActionMap getActionMap()
811 ActionMap map = (ActionMap) UIManager.get("ScrollBar.actionMap");
813 if (map == null) // first time here
815 map = createActionMap();
817 UIManager.put("ScrollBar.actionMap", map);
823 * Creates the action map shared by all {@link JSlider} instances.
824 * This method is called once by {@link #getActionMap()} when it
825 * finds no action map in the UIDefaults table...after the map is
826 * created, it gets added to the defaults table so that subsequent
827 * calls to {@link #getActionMap()} will return the same shared
830 * @return The action map.
832 ActionMap createActionMap()
834 ActionMap map = new ActionMapUIResource();
835 map.put("positiveUnitIncrement",
836 new AbstractAction("positiveUnitIncrement") {
837 public void actionPerformed(ActionEvent event)
839 JScrollBar sb = (JScrollBar) event.getSource();
842 int delta = sb.getUnitIncrement(1);
843 sb.setValue(sb.getValue() + delta);
848 map.put("positiveBlockIncrement",
849 new AbstractAction("positiveBlockIncrement") {
850 public void actionPerformed(ActionEvent event)
852 JScrollBar sb = (JScrollBar) event.getSource();
855 int delta = sb.getBlockIncrement(1);
856 sb.setValue(sb.getValue() + delta);
861 map.put("negativeUnitIncrement",
862 new AbstractAction("negativeUnitIncrement") {
863 public void actionPerformed(ActionEvent event)
865 JScrollBar sb = (JScrollBar) event.getSource();
868 int delta = sb.getUnitIncrement(-1);
869 sb.setValue(sb.getValue() + delta);
874 map.put("negativeBlockIncrement",
875 new AbstractAction("negativeBlockIncrement") {
876 public void actionPerformed(ActionEvent event)
878 JScrollBar sb = (JScrollBar) event.getSource();
881 int delta = sb.getBlockIncrement(-1);
882 sb.setValue(sb.getValue() + delta);
888 new AbstractAction("minScroll") {
889 public void actionPerformed(ActionEvent event)
891 JScrollBar sb = (JScrollBar) event.getSource();
894 sb.setValue(sb.getMinimum());
900 new AbstractAction("maxScroll") {
901 public void actionPerformed(ActionEvent event)
903 JScrollBar sb = (JScrollBar) event.getSource();
906 sb.setValue(sb.getMaximum());
915 * This method installs any listeners for the scrollbar. This method also
916 * installs listeners for things such as the JButtons and the timer.
918 protected void installListeners()
920 scrollListener = createScrollListener();
921 trackListener = createTrackListener();
922 buttonListener = createArrowButtonListener();
923 modelListener = createModelListener();
924 propertyChangeListener = createPropertyChangeListener();
926 scrollbar.addMouseMotionListener(trackListener);
927 scrollbar.addMouseListener(trackListener);
929 incrButton.addMouseListener(buttonListener);
930 decrButton.addMouseListener(buttonListener);
932 scrollbar.addPropertyChangeListener(propertyChangeListener);
933 scrollbar.getModel().addChangeListener(modelListener);
935 scrollTimer.addActionListener(scrollListener);
939 * This method installs the UI for the component. This can include setting
940 * up listeners, defaults, and components. This also includes initializing
943 * @param c The JComponent to install.
945 public void installUI(JComponent c)
948 if (c instanceof JScrollBar)
950 scrollbar = (JScrollBar) c;
952 trackRect = new Rectangle();
953 thumbRect = new Rectangle();
955 scrollTimer = new Timer(300, null);
959 configureScrollBarColors();
961 installKeyboardActions();
963 calculatePreferredSize();
968 * This method lays out the scrollbar.
970 * @param scrollbarContainer The Container to layout.
972 public void layoutContainer(Container scrollbarContainer)
974 if (scrollbarContainer instanceof JScrollBar)
976 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
977 layoutHScrollbar((JScrollBar) scrollbarContainer);
979 layoutVScrollbar((JScrollBar) scrollbarContainer);
984 * This method lays out the scrollbar horizontally.
986 * @param sb The JScrollBar to layout.
988 protected void layoutHScrollbar(JScrollBar sb)
990 Rectangle vr = new Rectangle();
991 SwingUtilities.calculateInnerArea(scrollbar, vr);
993 Dimension incrDims = incrButton.getPreferredSize();
994 Dimension decrDims = decrButton.getPreferredSize();
996 // calculate and update the track bounds
997 SwingUtilities.calculateInnerArea(scrollbar, trackRect);
998 trackRect.width -= incrDims.getWidth();
999 trackRect.width -= decrDims.getWidth();
1000 trackRect.x += decrDims.getWidth();
1004 decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height);
1005 incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width,
1010 * This method lays out the scrollbar vertically.
1012 * @param sb The JScrollBar to layout.
1014 protected void layoutVScrollbar(JScrollBar sb)
1016 Rectangle vr = new Rectangle();
1017 SwingUtilities.calculateInnerArea(scrollbar, vr);
1019 Dimension incrDims = incrButton.getPreferredSize();
1020 Dimension decrDims = decrButton.getPreferredSize();
1022 // Update rectangles
1023 SwingUtilities.calculateInnerArea(scrollbar, trackRect);
1024 trackRect.height -= incrDims.getHeight();
1025 trackRect.height -= decrDims.getHeight();
1026 trackRect.y += decrDims.getHeight();
1030 decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height);
1031 incrButton.setBounds(vr.x, trackRect.y + trackRect.height,
1032 trackRect.width, incrDims.height);
1036 * Updates the thumb rect.
1038 void updateThumbRect()
1040 int max = scrollbar.getMaximum();
1041 int min = scrollbar.getMinimum();
1042 int value = scrollbar.getValue();
1043 int extent = scrollbar.getVisibleAmount();
1044 if (max - extent <= min)
1046 if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
1048 thumbRect.x = trackRect.x;
1049 thumbRect.y = trackRect.y;
1050 thumbRect.width = getMinimumThumbSize().width;
1051 thumbRect.height = trackRect.height;
1055 thumbRect.x = trackRect.x;
1056 thumbRect.y = trackRect.y;
1057 thumbRect.width = trackRect.width;
1058 thumbRect.height = getMinimumThumbSize().height;
1063 if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
1065 thumbRect.x = trackRect.x;
1066 thumbRect.width = Math.max(extent * trackRect.width / (max - min),
1067 getMinimumThumbSize().width);
1068 int availableWidth = trackRect.width - thumbRect.width;
1069 thumbRect.x += (value - min) * availableWidth / (max - min - extent);
1070 thumbRect.y = trackRect.y;
1071 thumbRect.height = trackRect.height;
1075 thumbRect.x = trackRect.x;
1076 thumbRect.height = Math.max(extent * trackRect.height / (max - min),
1077 getMinimumThumbSize().height);
1078 int availableHeight = trackRect.height - thumbRect.height;
1079 thumbRect.y = trackRect.y
1080 + (value - min) * availableHeight / (max - min - extent);
1081 thumbRect.width = trackRect.width;
1088 * This method returns the minimum size required for the layout.
1090 * @param scrollbarContainer The Container that is laid out.
1092 * @return The minimum size.
1094 public Dimension minimumLayoutSize(Container scrollbarContainer)
1096 return preferredLayoutSize(scrollbarContainer);
1100 * This method is called when the component is painted.
1102 * @param g The Graphics object to paint with.
1103 * @param c The JComponent to paint.
1105 public void paint(Graphics g, JComponent c)
1107 paintTrack(g, c, getTrackBounds());
1108 paintThumb(g, c, getThumbBounds());
1110 if (trackHighlight == INCREASE_HIGHLIGHT)
1111 paintIncreaseHighlight(g);
1112 else if (trackHighlight == DECREASE_HIGHLIGHT)
1113 paintDecreaseHighlight(g);
1117 * This method is called when repainting and the mouse is pressed in the
1118 * track. It paints the track below the thumb with the trackHighlight
1121 * @param g The Graphics object to paint with.
1123 protected void paintDecreaseHighlight(Graphics g)
1125 Color saved = g.getColor();
1127 g.setColor(trackHighlightColor);
1128 if (scrollbar.getOrientation() == HORIZONTAL)
1129 g.fillRect(trackRect.x, trackRect.y, thumbRect.x - trackRect.x,
1132 g.fillRect(trackRect.x, trackRect.y, trackRect.width,
1133 thumbRect.y - trackRect.y);
1138 * This method is called when repainting and the mouse is pressed in the
1139 * track. It paints the track above the thumb with the trackHighlight
1142 * @param g The Graphics objet to paint with.
1144 protected void paintIncreaseHighlight(Graphics g)
1146 Color saved = g.getColor();
1148 g.setColor(trackHighlightColor);
1149 if (scrollbar.getOrientation() == HORIZONTAL)
1150 g.fillRect(thumbRect.x + thumbRect.width, trackRect.y,
1151 trackRect.x + trackRect.width - thumbRect.x - thumbRect.width,
1154 g.fillRect(trackRect.x, thumbRect.y + thumbRect.height, trackRect.width,
1155 trackRect.y + trackRect.height - thumbRect.y
1156 - thumbRect.height);
1161 * This method paints the thumb.
1163 * @param g The Graphics object to paint with.
1164 * @param c The Component that is being painted.
1165 * @param thumbBounds The thumb bounds.
1167 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
1169 g.setColor(thumbColor);
1170 g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width,
1171 thumbBounds.height);
1173 BasicGraphicsUtils.drawBezel(g, thumbBounds.x, thumbBounds.y,
1174 thumbBounds.width, thumbBounds.height,
1175 false, false, thumbDarkShadowColor,
1176 thumbDarkShadowColor, thumbHighlightColor,
1177 thumbHighlightColor);
1181 * This method paints the track.
1183 * @param g The Graphics object to paint with.
1184 * @param c The JComponent being painted.
1185 * @param trackBounds The track's bounds.
1187 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
1189 Color saved = g.getColor();
1190 g.setColor(trackColor);
1191 g.fill3DRect(trackBounds.x, trackBounds.y, trackBounds.width,
1192 trackBounds.height, false);
1197 * This method returns the preferred size for the layout.
1199 * @param scrollbarContainer The Container to find a size for.
1201 * @return The preferred size for the layout.
1203 public Dimension preferredLayoutSize(Container scrollbarContainer)
1205 if (scrollbarContainer instanceof JComponent)
1206 return getPreferredSize((JComponent) scrollbarContainer);
1212 * This method removes a child component from the layout.
1214 * @param child The child to remove.
1216 public void removeLayoutComponent(Component child)
1218 // You should not be removing stuff from this component.
1222 * The method scrolls the thumb by a block in the direction specified.
1224 * @param direction The direction to scroll.
1226 protected void scrollByBlock(int direction)
1228 scrollByBlock(scrollbar, direction);
1232 * Scrolls the specified <code>scrollBar</code> by one block (according
1233 * to the scrollable protocol) in the specified <code>direction</code>.
1235 * This method is here statically to support wheel scrolling from the
1236 * BasicScrollPaneUI without code duplication.
1238 * @param scrollBar the scrollbar to scroll
1239 * @param direction the scroll direction
1241 static final void scrollByBlock(JScrollBar scrollBar, int direction)
1245 delta = scrollBar.getBlockIncrement(direction);
1247 delta = - scrollBar.getBlockIncrement(direction);
1248 int oldValue = scrollBar.getValue();
1249 int newValue = oldValue + delta;
1252 if (delta > 0 && newValue < oldValue)
1253 newValue = scrollBar.getMaximum();
1254 else if (delta < 0 && newValue > oldValue)
1255 newValue = scrollBar.getMinimum();
1257 scrollBar.setValue(newValue);
1261 * The method scrolls the thumb by a unit in the direction specified.
1263 * @param direction The direction to scroll.
1265 protected void scrollByUnit(int direction)
1267 scrollByUnits(scrollbar, direction, 1);
1271 * Scrolls the specified <code>scrollbac/code> by <code>units</code> units
1272 * in the specified <code>direction</code>.
1274 * This method is here statically to support wheel scrolling from the
1275 * BasicScrollPaneUI without code duplication.
1277 * @param scrollBar the scrollbar to scroll
1278 * @param direction the direction
1279 * @param units the number of units to scroll
1281 static final void scrollByUnits(JScrollBar scrollBar, int direction,
1284 // Do this inside a loop so that we don't clash with the scrollable
1285 // interface, which can return different units at times. For instance,
1286 // a Scrollable could return a unit of 2 pixels only to adjust the
1287 // visibility of an item. If we would simply multiply this by units,
1288 // then we would only get 6 pixels, which is complete crap.
1289 for (int i = 0; i < units; i++)
1293 delta = scrollBar.getUnitIncrement(direction);
1295 delta = - scrollBar.getUnitIncrement(direction);
1296 int oldValue = scrollBar.getValue();
1297 int newValue = oldValue + delta;
1300 if (delta > 0 && newValue < oldValue)
1301 newValue = scrollBar.getMaximum();
1302 else if (delta < 0 && newValue > oldValue)
1303 newValue = scrollBar.getMinimum();
1305 scrollBar.setValue(newValue);
1310 * This method sets the thumb's bounds.
1312 * @param x The X position of the thumb.
1313 * @param y The Y position of the thumb.
1314 * @param width The width of the thumb.
1315 * @param height The height of the thumb.
1317 protected void setThumbBounds(int x, int y, int width, int height)
1321 thumbRect.width = width;
1322 thumbRect.height = height;
1326 * This method uninstalls any components that are a part of or related to
1329 protected void uninstallComponents()
1331 if (incrButton != null)
1332 scrollbar.remove(incrButton);
1333 if (decrButton != null)
1334 scrollbar.remove(decrButton);
1338 * This method uninstalls any defaults that this scrollbar acquired from the
1339 * Basic Look and Feel defaults.
1341 protected void uninstallDefaults()
1343 scrollbar.setForeground(null);
1344 scrollbar.setBackground(null);
1345 LookAndFeel.uninstallBorder(scrollbar);
1351 * This method uninstalls any listeners that were registered during install.
1353 protected void uninstallListeners()
1355 if (scrollTimer != null)
1356 scrollTimer.removeActionListener(scrollListener);
1358 if (scrollbar != null)
1360 scrollbar.getModel().removeChangeListener(modelListener);
1361 scrollbar.removePropertyChangeListener(propertyChangeListener);
1362 scrollbar.removeMouseListener(trackListener);
1363 scrollbar.removeMouseMotionListener(trackListener);
1366 if (decrButton != null)
1367 decrButton.removeMouseListener(buttonListener);
1368 if (incrButton != null)
1369 incrButton.removeMouseListener(buttonListener);
1371 propertyChangeListener = null;
1372 modelListener = null;
1373 buttonListener = null;
1374 trackListener = null;
1375 scrollListener = null;
1379 * This method uninstalls the UI. This includes removing any defaults,
1380 * listeners, and components that this UI may have initialized. It also
1381 * nulls any instance data.
1383 * @param c The Component to uninstall for.
1385 public void uninstallUI(JComponent c)
1387 uninstallKeyboardActions();
1388 uninstallListeners();
1389 uninstallDefaults();
1390 uninstallComponents();
1398 trackHighlightColor = null;
1400 thumbHighlightColor = null;
1401 thumbDarkShadowColor = null;
1402 thumbLightShadowColor = null;
1408 * This method returns the value in the scrollbar's range given the y
1409 * coordinate. If the value is out of range, it will return the closest
1411 * This is package-private to avoid an accessor method.
1413 * @param yPos The y coordinate to calculate a value for.
1415 * @return The value for the y coordinate.
1417 int valueForYPosition(int yPos)
1419 int min = scrollbar.getMinimum();
1420 int max = scrollbar.getMaximum();
1421 int len = trackRect.height;
1425 // If the length is 0, you shouldn't be able to even see where the thumb is.
1426 // This really shouldn't ever happen, but just in case, we'll return the middle.
1428 return (max - min) / 2;
1430 value = (yPos - trackRect.y) * (max - min) / len + min;
1432 // If this isn't a legal value, then we'll have to move to one now.
1435 else if (value < min)
1441 * This method returns the value in the scrollbar's range given the x
1442 * coordinate. If the value is out of range, it will return the closest
1444 * This is package-private to avoid an accessor method.
1446 * @param xPos The x coordinate to calculate a value for.
1448 * @return The value for the x coordinate.
1450 int valueForXPosition(int xPos)
1452 int min = scrollbar.getMinimum();
1453 int max = scrollbar.getMaximum();
1454 int len = trackRect.width;
1458 // If the length is 0, you shouldn't be able to even see where the slider is.
1459 // This really shouldn't ever happen, but just in case, we'll return the middle.
1461 return (max - min) / 2;
1463 value = (xPos - trackRect.x) * (max - min) / len + min;
1465 // If this isn't a legal value, then we'll have to move to one now.
1468 else if (value < min)
1474 * Returns true if the mouse is over the thumb.
1476 * @return true if the mouse is over the thumb.
1480 public boolean isThumbRollover()
1482 return thumbRollover;
1486 * Set thumbRollover to active. This indicates
1487 * whether or not the mouse is over the thumb.
1489 * @param active - true if the mouse is over the thumb.
1493 protected void setThumbRollover(boolean active)
1495 thumbRollover = active;
1499 * Indicates whether the user can position the thumb with
1500 * a mouse click (i.e. middle button).
1502 * @return true if the user can position the thumb with a mouse
1507 public boolean getSupportsAbsolutePositioning()
1509 // The positioning feature has not been implemented.
1510 // So, false is always returned.