1. /*
  2. * @(#)BasicSliderUI.java 1.95 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import java.awt.Component;
  9. import java.awt.Container;
  10. import java.awt.Adjustable;
  11. import java.awt.event.*;
  12. import java.awt.Graphics;
  13. import java.awt.Dimension;
  14. import java.awt.Rectangle;
  15. import java.awt.Point;
  16. import java.awt.Insets;
  17. import java.awt.Color;
  18. import java.awt.IllegalComponentStateException;
  19. import java.awt.Polygon;
  20. import java.beans.*;
  21. import java.util.Dictionary;
  22. import java.util.Enumeration;
  23. import javax.swing.border.AbstractBorder;
  24. import javax.swing.*;
  25. import javax.swing.event.*;
  26. import javax.swing.plaf.*;
  27. /**
  28. * A Basic L&F implementation of SliderUI.
  29. *
  30. * @version 1.95 01/23/03
  31. * @author Tom Santos
  32. */
  33. public class BasicSliderUI extends SliderUI{
  34. public static final int POSITIVE_SCROLL = +1;
  35. public static final int NEGATIVE_SCROLL = -1;
  36. public static final int MIN_SCROLL = -2;
  37. public static final int MAX_SCROLL = +2;
  38. protected Timer scrollTimer;
  39. protected JSlider slider;
  40. protected Insets focusInsets = null;
  41. protected Insets insetCache = null;
  42. protected boolean leftToRightCache = true;
  43. protected Rectangle focusRect = null;
  44. protected Rectangle contentRect = null;
  45. protected Rectangle labelRect = null;
  46. protected Rectangle tickRect = null;
  47. protected Rectangle trackRect = null;
  48. protected Rectangle thumbRect = null;
  49. protected int trackBuffer = 0; // The distance that the track is from the side of the control
  50. private static final Dimension PREFERRED_HORIZONTAL_SIZE = new Dimension(200, 21);
  51. private static final Dimension PREFERRED_VERTICAL_SIZE = new Dimension(21, 200);
  52. private static final Dimension MINIMUM_HORIZONTAL_SIZE = new Dimension(36, 21);
  53. private static final Dimension MINIMUM_VERTICAL_SIZE = new Dimension(21, 36);
  54. private transient boolean isDragging;
  55. protected TrackListener trackListener;
  56. protected ChangeListener changeListener;
  57. protected ComponentListener componentListener;
  58. protected FocusListener focusListener;
  59. protected ScrollListener scrollListener;
  60. protected PropertyChangeListener propertyChangeListener;
  61. // Colors
  62. private Color shadowColor;
  63. private Color highlightColor;
  64. private Color focusColor;
  65. protected Color getShadowColor() {
  66. return shadowColor;
  67. }
  68. protected Color getHighlightColor() {
  69. return highlightColor;
  70. }
  71. protected Color getFocusColor() {
  72. return focusColor;
  73. }
  74. /////////////////////////////////////////////////////////////////////////////
  75. // ComponentUI Interface Implementation methods
  76. /////////////////////////////////////////////////////////////////////////////
  77. public static ComponentUI createUI(JComponent b) {
  78. return new BasicSliderUI((JSlider)b);
  79. }
  80. public BasicSliderUI(JSlider b) {
  81. }
  82. public void installUI(JComponent c) {
  83. slider = (JSlider) c;
  84. slider.setEnabled(slider.isEnabled());
  85. slider.setOpaque(true);
  86. isDragging = false;
  87. trackListener = createTrackListener( slider );
  88. changeListener = createChangeListener( slider );
  89. componentListener = createComponentListener( slider );
  90. focusListener = createFocusListener( slider );
  91. scrollListener = createScrollListener( slider );
  92. propertyChangeListener = createPropertyChangeListener( slider );
  93. installDefaults( slider );
  94. installListeners( slider );
  95. installKeyboardActions( slider );
  96. scrollTimer = new Timer( 100, scrollListener );
  97. scrollTimer.setInitialDelay( 300 );
  98. insetCache = slider.getInsets();
  99. leftToRightCache = BasicGraphicsUtils.isLeftToRight(slider);
  100. focusRect = new Rectangle();
  101. contentRect = new Rectangle();
  102. labelRect = new Rectangle();
  103. tickRect = new Rectangle();
  104. trackRect = new Rectangle();
  105. thumbRect = new Rectangle();
  106. calculateGeometry(); // This figures out where the labels, ticks, track, and thumb are.
  107. }
  108. public void uninstallUI(JComponent c) {
  109. if ( c != slider )
  110. throw new IllegalComponentStateException(
  111. this + " was asked to deinstall() "
  112. + c + " when it only knows about "
  113. + slider + ".");
  114. LookAndFeel.uninstallBorder(slider);
  115. scrollTimer.stop();
  116. scrollTimer = null;
  117. uninstallListeners( slider );
  118. uninstallKeyboardActions(slider);
  119. focusInsets = null;
  120. insetCache = null;
  121. leftToRightCache = true;
  122. focusRect = null;
  123. contentRect = null;
  124. labelRect = null;
  125. tickRect = null;
  126. trackRect = null;
  127. thumbRect = null;
  128. trackListener = null;
  129. changeListener = null;
  130. componentListener = null;
  131. focusListener = null;
  132. scrollListener = null;
  133. propertyChangeListener = null;
  134. slider = null;
  135. }
  136. protected void installDefaults( JSlider slider ) {
  137. LookAndFeel.installBorder(slider, "Slider.border");
  138. LookAndFeel.installColors(slider, "Slider.background", "Slider.foreground");
  139. highlightColor = UIManager.getColor("Slider.highlight");
  140. shadowColor = UIManager.getColor("Slider.shadow");
  141. focusColor = UIManager.getColor("Slider.focus");
  142. focusInsets = (Insets)UIManager.get( "Slider.focusInsets" );
  143. }
  144. protected TrackListener createTrackListener( JSlider slider ) {
  145. return new TrackListener();
  146. }
  147. protected ChangeListener createChangeListener( JSlider slider ) {
  148. return new ChangeHandler();
  149. }
  150. protected ComponentListener createComponentListener( JSlider slider ) {
  151. return new ComponentHandler();
  152. }
  153. protected FocusListener createFocusListener( JSlider slider ) {
  154. return new FocusHandler();
  155. }
  156. protected ScrollListener createScrollListener( JSlider slider ) {
  157. return new ScrollListener();
  158. }
  159. protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
  160. return new PropertyChangeHandler();
  161. }
  162. protected void installListeners( JSlider slider ) {
  163. slider.addMouseListener(trackListener);
  164. slider.addMouseMotionListener(trackListener);
  165. slider.addFocusListener(focusListener);
  166. slider.addComponentListener(componentListener);
  167. slider.addPropertyChangeListener( propertyChangeListener );
  168. slider.getModel().addChangeListener(changeListener);
  169. }
  170. protected void uninstallListeners( JSlider slider ) {
  171. slider.removeMouseListener(trackListener);
  172. slider.removeMouseMotionListener(trackListener);
  173. slider.removeFocusListener(focusListener);
  174. slider.removeComponentListener(componentListener);
  175. slider.removePropertyChangeListener( propertyChangeListener );
  176. slider.getModel().removeChangeListener(changeListener);
  177. }
  178. protected void installKeyboardActions( JSlider slider ) {
  179. InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
  180. SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED,
  181. km);
  182. ActionMap am = getActionMap();
  183. SwingUtilities.replaceUIActionMap(slider, am);
  184. }
  185. InputMap getInputMap(int condition) {
  186. if (condition == JComponent.WHEN_FOCUSED) {
  187. InputMap keyMap = (InputMap)UIManager.get("Slider.focusInputMap");
  188. InputMap rtlKeyMap;
  189. if (slider.getComponentOrientation().isLeftToRight() ||
  190. ((rtlKeyMap = (InputMap)UIManager.get("Slider.focusInputMap.RightToLeft")) == null)) {
  191. return keyMap;
  192. } else {
  193. rtlKeyMap.setParent(keyMap);
  194. return rtlKeyMap;
  195. }
  196. }
  197. return null;
  198. }
  199. ActionMap getActionMap() {
  200. ActionMap map = (ActionMap)UIManager.get("Slider.actionMap");
  201. if (map == null) {
  202. map = createActionMap();
  203. if (map != null) {
  204. UIManager.getLookAndFeelDefaults().put
  205. ("Slider.actionMap", map);
  206. }
  207. }
  208. return map;
  209. }
  210. ActionMap createActionMap() {
  211. ActionMap map = new ActionMapUIResource();
  212. map.put("positiveUnitIncrement", new SharedActionScroller
  213. (POSITIVE_SCROLL, false));
  214. map.put("positiveBlockIncrement", new SharedActionScroller
  215. (POSITIVE_SCROLL, true));
  216. map.put("negativeUnitIncrement", new SharedActionScroller
  217. (NEGATIVE_SCROLL, false));
  218. map.put("negativeBlockIncrement", new SharedActionScroller
  219. (NEGATIVE_SCROLL, true));
  220. map.put("minScroll", new SharedActionScroller(MIN_SCROLL, true));
  221. map.put("maxScroll", new SharedActionScroller(MAX_SCROLL, true));
  222. return map;
  223. }
  224. protected void uninstallKeyboardActions( JSlider slider ) {
  225. SwingUtilities.replaceUIActionMap(slider, null);
  226. SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED,
  227. null);
  228. }
  229. public Dimension getPreferredHorizontalSize() {
  230. return PREFERRED_HORIZONTAL_SIZE;
  231. }
  232. public Dimension getPreferredVerticalSize() {
  233. return PREFERRED_VERTICAL_SIZE;
  234. }
  235. public Dimension getMinimumHorizontalSize() {
  236. return MINIMUM_HORIZONTAL_SIZE;
  237. }
  238. public Dimension getMinimumVerticalSize() {
  239. return MINIMUM_VERTICAL_SIZE;
  240. }
  241. public Dimension getPreferredSize(JComponent c) {
  242. recalculateIfInsetsChanged();
  243. Dimension d;
  244. if ( slider.getOrientation() == JSlider.VERTICAL ) {
  245. d = new Dimension(getPreferredVerticalSize());
  246. d.width = insetCache.left + insetCache.right;
  247. d.width += focusInsets.left + focusInsets.right;
  248. d.width += trackRect.width + tickRect.width + labelRect.width;
  249. }
  250. else {
  251. d = new Dimension(getPreferredHorizontalSize());
  252. d.height = insetCache.top + insetCache.bottom;
  253. d.height += focusInsets.top + focusInsets.bottom;
  254. d.height += trackRect.height + tickRect.height + labelRect.height;
  255. }
  256. return d;
  257. }
  258. public Dimension getMinimumSize(JComponent c) {
  259. recalculateIfInsetsChanged();
  260. Dimension d;
  261. if ( slider.getOrientation() == JSlider.VERTICAL ) {
  262. d = new Dimension(getMinimumVerticalSize());
  263. d.width = insetCache.left + insetCache.right;
  264. d.width += focusInsets.left + focusInsets.right;
  265. d.width += trackRect.width + tickRect.width + labelRect.width;
  266. }
  267. else {
  268. d = new Dimension(getMinimumHorizontalSize());
  269. d.height = insetCache.top + insetCache.bottom;
  270. d.height += focusInsets.top + focusInsets.bottom;
  271. d.height += trackRect.height + tickRect.height + labelRect.height;
  272. }
  273. return d;
  274. }
  275. public Dimension getMaximumSize(JComponent c) {
  276. Dimension d = getPreferredSize(c);
  277. if ( slider.getOrientation() == JSlider.VERTICAL ) {
  278. d.height = Short.MAX_VALUE;
  279. }
  280. else {
  281. d.width = Short.MAX_VALUE;
  282. }
  283. return d;
  284. }
  285. protected void calculateGeometry() {
  286. calculateFocusRect();
  287. calculateContentRect();
  288. calculateThumbSize();
  289. calculateTrackBuffer();
  290. calculateTrackRect();
  291. calculateTickRect();
  292. calculateLabelRect();
  293. calculateThumbLocation();
  294. }
  295. protected void calculateFocusRect() {
  296. focusRect.x = insetCache.left;
  297. focusRect.y = insetCache.top;
  298. focusRect.width = slider.getWidth() - (insetCache.left + insetCache.right);
  299. focusRect.height = slider.getHeight() - (insetCache.top + insetCache.bottom);
  300. }
  301. protected void calculateThumbSize() {
  302. Dimension size = getThumbSize();
  303. thumbRect.setSize( size.width, size.height );
  304. }
  305. protected void calculateContentRect() {
  306. contentRect.x = focusRect.x + focusInsets.left;
  307. contentRect.y = focusRect.y + focusInsets.top;
  308. contentRect.width = focusRect.width - (focusInsets.left + focusInsets.right);
  309. contentRect.height = focusRect.height - (focusInsets.top + focusInsets.bottom);
  310. }
  311. protected void calculateThumbLocation() {
  312. if ( slider.getSnapToTicks() ) {
  313. int sliderValue = slider.getValue();
  314. int snappedValue = sliderValue;
  315. int majorTickSpacing = slider.getMajorTickSpacing();
  316. int minorTickSpacing = slider.getMinorTickSpacing();
  317. int tickSpacing = 0;
  318. if ( minorTickSpacing > 0 ) {
  319. tickSpacing = minorTickSpacing;
  320. }
  321. else if ( majorTickSpacing > 0 ) {
  322. tickSpacing = majorTickSpacing;
  323. }
  324. if ( tickSpacing != 0 ) {
  325. // If it's not on a tick, change the value
  326. if ( (sliderValue - slider.getMinimum()) % tickSpacing != 0 ) {
  327. float temp = (float)(sliderValue - slider.getMinimum()) / (float)tickSpacing;
  328. int whichTick = Math.round( temp );
  329. snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
  330. }
  331. if( snappedValue != sliderValue ) {
  332. slider.setValue( snappedValue );
  333. }
  334. }
  335. }
  336. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  337. int valuePosition = xPositionForValue(slider.getValue());
  338. thumbRect.x = valuePosition - (thumbRect.width / 2);
  339. thumbRect.y = trackRect.y;
  340. }
  341. else {
  342. int valuePosition = yPositionForValue(slider.getValue());
  343. thumbRect.x = trackRect.x;
  344. thumbRect.y = valuePosition - (thumbRect.height / 2);
  345. }
  346. }
  347. protected void calculateTrackBuffer() {
  348. if ( slider.getPaintLabels() && slider.getLabelTable() != null ) {
  349. Component highLabel = getHighestValueLabel();
  350. Component lowLabel = getLowestValueLabel();
  351. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  352. trackBuffer = Math.max( highLabel.getBounds().width, lowLabel.getBounds().width ) / 2;
  353. trackBuffer = Math.max( trackBuffer, thumbRect.width / 2 );
  354. }
  355. else {
  356. trackBuffer = Math.max( highLabel.getBounds().height, lowLabel.getBounds().height ) / 2;
  357. trackBuffer = Math.max( trackBuffer, thumbRect.height / 2 );
  358. }
  359. }
  360. else {
  361. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  362. trackBuffer = thumbRect.width / 2;
  363. }
  364. else {
  365. trackBuffer = thumbRect.height / 2;
  366. }
  367. }
  368. }
  369. protected void calculateTrackRect() {
  370. int centerSpacing = 0; // used to center sliders added using BorderLayout.CENTER (bug 4275631)
  371. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  372. centerSpacing = thumbRect.height;
  373. if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
  374. if ( slider.getPaintLabels() ) centerSpacing += getHeightOfTallestLabel();
  375. trackRect.x = contentRect.x + trackBuffer;
  376. trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1)/2;
  377. trackRect.width = contentRect.width - (trackBuffer * 2);
  378. trackRect.height = thumbRect.height;
  379. }
  380. else {
  381. centerSpacing = thumbRect.width;
  382. if (BasicGraphicsUtils.isLeftToRight(slider)) {
  383. if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
  384. if ( slider.getPaintLabels() ) centerSpacing += getWidthOfWidestLabel();
  385. } else {
  386. if ( slider.getPaintTicks() ) centerSpacing -= getTickLength();
  387. if ( slider.getPaintLabels() ) centerSpacing -= getWidthOfWidestLabel();
  388. }
  389. trackRect.x = contentRect.x + (contentRect.width - centerSpacing - 1)/2;
  390. trackRect.y = contentRect.y + trackBuffer;
  391. trackRect.width = thumbRect.width;
  392. trackRect.height = contentRect.height - (trackBuffer * 2);
  393. }
  394. }
  395. /**
  396. * Gets the height of the tick area for horizontal sliders and the width of the
  397. * tick area for vertical sliders. BasicSliderUI uses the returned value to
  398. * determine the tick area rectangle. If you want to give your ticks some room,
  399. * make this larger than you need and paint your ticks away from the sides in paintTicks().
  400. */
  401. protected int getTickLength() {
  402. return 8;
  403. }
  404. protected void calculateTickRect() {
  405. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  406. tickRect.x = trackRect.x;
  407. tickRect.y = trackRect.y + trackRect.height;
  408. tickRect.width = trackRect.width;
  409. tickRect.height = getTickLength();
  410. if ( !slider.getPaintTicks() ) {
  411. --tickRect.y;
  412. tickRect.height = 0;
  413. }
  414. }
  415. else {
  416. if(BasicGraphicsUtils.isLeftToRight(slider)) {
  417. tickRect.x = trackRect.x + trackRect.width;
  418. tickRect.width = getTickLength();
  419. }
  420. else {
  421. tickRect.width = getTickLength();
  422. tickRect.x = trackRect.x - tickRect.width;
  423. }
  424. tickRect.y = trackRect.y;
  425. tickRect.height = trackRect.height;
  426. if ( !slider.getPaintTicks() ) {
  427. --tickRect.x;
  428. tickRect.width = 0;
  429. }
  430. }
  431. }
  432. protected void calculateLabelRect() {
  433. if ( slider.getPaintLabels() ) {
  434. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  435. labelRect.x = tickRect.x - trackBuffer;
  436. labelRect.y = tickRect.y + tickRect.height;
  437. labelRect.width = tickRect.width + (trackBuffer * 2);
  438. labelRect.height = getHeightOfTallestLabel();
  439. }
  440. else {
  441. if(BasicGraphicsUtils.isLeftToRight(slider)) {
  442. labelRect.x = tickRect.x + tickRect.width;
  443. labelRect.width = getWidthOfWidestLabel();
  444. }
  445. else {
  446. labelRect.width = getWidthOfWidestLabel();
  447. labelRect.x = tickRect.x - labelRect.width;
  448. }
  449. labelRect.y = tickRect.y - trackBuffer;
  450. labelRect.height = tickRect.height + (trackBuffer * 2);
  451. }
  452. }
  453. else {
  454. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  455. labelRect.x = tickRect.x;
  456. labelRect.y = tickRect.y + tickRect.height;
  457. labelRect.width = tickRect.width;
  458. labelRect.height = 0;
  459. }
  460. else {
  461. if(BasicGraphicsUtils.isLeftToRight(slider)) {
  462. labelRect.x = tickRect.x + tickRect.width;
  463. }
  464. else {
  465. labelRect.x = tickRect.x;
  466. }
  467. labelRect.y = tickRect.y;
  468. labelRect.width = 0;
  469. labelRect.height = tickRect.height;
  470. }
  471. }
  472. }
  473. protected Dimension getThumbSize() {
  474. Dimension size = new Dimension();
  475. if ( slider.getOrientation() == JSlider.VERTICAL ) {
  476. size.width = 20;
  477. size.height = 11;
  478. }
  479. else {
  480. size.width = 11;
  481. size.height = 20;
  482. }
  483. return size;
  484. }
  485. public class PropertyChangeHandler implements PropertyChangeListener {
  486. public void propertyChange( PropertyChangeEvent e ) {
  487. String propertyName = e.getPropertyName();
  488. if (propertyName.equals( "orientation" ) ||
  489. propertyName.equals( "inverted" ) ||
  490. propertyName.equals( "labelTable" ) ||
  491. propertyName.equals( "majorTickSpacing" ) ||
  492. propertyName.equals( "minorTickSpacing" ) ||
  493. propertyName.equals( "paintTicks" ) ||
  494. propertyName.equals( "paintTrack" ) ||
  495. propertyName.equals( "paintLabels" ) ) {
  496. calculateGeometry();
  497. slider.repaint();
  498. }
  499. else if (propertyName.equals( "componentOrientation" )) {
  500. calculateGeometry();
  501. slider.repaint();
  502. InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
  503. SwingUtilities.replaceUIInputMap(slider,
  504. JComponent.WHEN_FOCUSED, km);
  505. }
  506. else if ( propertyName.equals( "model" ) ) {
  507. ((BoundedRangeModel)e.getOldValue()).removeChangeListener( changeListener );
  508. ((BoundedRangeModel)e.getNewValue()).addChangeListener( changeListener );
  509. calculateThumbLocation();
  510. slider.repaint();
  511. }
  512. }
  513. }
  514. protected int getWidthOfWidestLabel() {
  515. Dictionary dictionary = slider.getLabelTable();
  516. int widest = 0;
  517. if ( dictionary != null ) {
  518. Enumeration keys = dictionary.keys();
  519. while ( keys.hasMoreElements() ) {
  520. Component label = (Component)dictionary.get( keys.nextElement() );
  521. widest = Math.max( label.getPreferredSize().width, widest );
  522. }
  523. }
  524. return widest;
  525. }
  526. protected int getHeightOfTallestLabel() {
  527. Dictionary dictionary = slider.getLabelTable();
  528. int tallest = 0;
  529. if ( dictionary != null ) {
  530. Enumeration keys = dictionary.keys();
  531. while ( keys.hasMoreElements() ) {
  532. Component label = (Component)dictionary.get( keys.nextElement() );
  533. tallest = Math.max( label.getPreferredSize().height, tallest );
  534. }
  535. }
  536. return tallest;
  537. }
  538. protected int getWidthOfHighValueLabel() {
  539. Component label = getHighestValueLabel();
  540. int width = 0;
  541. if ( label != null ) {
  542. width = label.getPreferredSize().width;
  543. }
  544. return width;
  545. }
  546. protected int getWidthOfLowValueLabel() {
  547. Component label = getLowestValueLabel();
  548. int width = 0;
  549. if ( label != null ) {
  550. width = label.getPreferredSize().width;
  551. }
  552. return width;
  553. }
  554. protected int getHeightOfHighValueLabel() {
  555. Component label = getHighestValueLabel();
  556. int height = 0;
  557. if ( label != null ) {
  558. height = label.getPreferredSize().height;
  559. }
  560. return height;
  561. }
  562. protected int getHeightOfLowValueLabel() {
  563. Component label = getLowestValueLabel();
  564. int height = 0;
  565. if ( label != null ) {
  566. height = label.getPreferredSize().height;
  567. }
  568. return height;
  569. }
  570. protected boolean drawInverted() {
  571. if (slider.getOrientation()==JSlider.HORIZONTAL) {
  572. if(BasicGraphicsUtils.isLeftToRight(slider)) {
  573. return slider.getInverted();
  574. } else {
  575. return !slider.getInverted();
  576. }
  577. } else {
  578. return slider.getInverted();
  579. }
  580. }
  581. /**
  582. * Returns the label that corresponds to the highest slider value in the label table.
  583. * @see JSlider#setLabelTable
  584. */
  585. protected Component getLowestValueLabel() {
  586. Dictionary dictionary = slider.getLabelTable();
  587. Component label = null;
  588. if ( dictionary != null ) {
  589. Enumeration keys = dictionary.keys();
  590. if ( keys.hasMoreElements() ) {
  591. int lowestValue = ((Integer)keys.nextElement()).intValue();
  592. while ( keys.hasMoreElements() ) {
  593. int value = ((Integer)keys.nextElement()).intValue();
  594. lowestValue = Math.min( value, lowestValue );
  595. }
  596. label = (Component)dictionary.get( new Integer( lowestValue ) );
  597. }
  598. }
  599. return label;
  600. }
  601. /**
  602. * Returns the label that corresponds to the lowest slider value in the label table.
  603. * @see JSlider#setLabelTable
  604. */
  605. protected Component getHighestValueLabel() {
  606. Dictionary dictionary = slider.getLabelTable();
  607. Component label = null;
  608. if ( dictionary != null ) {
  609. Enumeration keys = dictionary.keys();
  610. if ( keys.hasMoreElements() ) {
  611. int highestValue = ((Integer)keys.nextElement()).intValue();
  612. while ( keys.hasMoreElements() ) {
  613. int value = ((Integer)keys.nextElement()).intValue();
  614. highestValue = Math.max( value, highestValue );
  615. }
  616. label = (Component)dictionary.get( new Integer( highestValue ) );
  617. }
  618. }
  619. return label;
  620. }
  621. public void paint( Graphics g, JComponent c ) {
  622. recalculateIfInsetsChanged();
  623. recalculateIfOrientationChanged();
  624. Rectangle clip = g.getClipBounds();
  625. if ( !clip.intersects(trackRect) && slider.getPaintTrack())
  626. calculateGeometry();
  627. if ( slider.getPaintTrack() && clip.intersects( trackRect ) ) {
  628. paintTrack( g );
  629. }
  630. if ( slider.getPaintTicks() && clip.intersects( tickRect ) ) {
  631. paintTicks( g );
  632. }
  633. if ( slider.getPaintLabels() && clip.intersects( labelRect ) ) {
  634. paintLabels( g );
  635. }
  636. if ( slider.hasFocus() && clip.intersects( focusRect ) ) {
  637. paintFocus( g );
  638. }
  639. if ( clip.intersects( thumbRect ) ) {
  640. paintThumb( g );
  641. }
  642. }
  643. protected void recalculateIfInsetsChanged() {
  644. Insets newInsets = slider.getInsets();
  645. if ( !newInsets.equals( insetCache ) ) {
  646. insetCache = newInsets;
  647. calculateGeometry();
  648. }
  649. }
  650. protected void recalculateIfOrientationChanged() {
  651. boolean ltr = BasicGraphicsUtils.isLeftToRight(slider);
  652. if ( ltr!=leftToRightCache ) {
  653. leftToRightCache = ltr;
  654. calculateGeometry();
  655. }
  656. }
  657. public void paintFocus(Graphics g) {
  658. g.setColor( getFocusColor() );
  659. BasicGraphicsUtils.drawDashedRect( g, focusRect.x, focusRect.y,
  660. focusRect.width, focusRect.height );
  661. }
  662. public void paintTrack(Graphics g) {
  663. int cx, cy, cw, ch;
  664. int pad;
  665. Rectangle trackBounds = trackRect;
  666. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  667. pad = trackBuffer;
  668. cx = pad;
  669. cy = (trackBounds.height / 2) - 2;
  670. cw = trackBounds.width;
  671. g.translate(trackBounds.x, trackBounds.y + cy);
  672. g.setColor(getShadowColor());
  673. g.drawLine(0, 0, cw - 1, 0);
  674. g.drawLine(0, 1, 0, 2);
  675. g.setColor(getHighlightColor());
  676. g.drawLine(0, 3, cw, 3);
  677. g.drawLine(cw, 0, cw, 3);
  678. g.setColor(Color.black);
  679. g.drawLine(1, 1, cw-2, 1);
  680. g.translate(-trackBounds.x, -(trackBounds.y + cy));
  681. }
  682. else {
  683. pad = trackBuffer;
  684. cx = (trackBounds.width / 2) - 2;
  685. cy = pad;
  686. ch = trackBounds.height;
  687. g.translate(trackBounds.x + cx, trackBounds.y);
  688. g.setColor(getShadowColor());
  689. g.drawLine(0, 0, 0, ch - 1);
  690. g.drawLine(1, 0, 2, 0);
  691. g.setColor(getHighlightColor());
  692. g.drawLine(3, 0, 3, ch);
  693. g.drawLine(0, ch, 3, ch);
  694. g.setColor(Color.black);
  695. g.drawLine(1, 1, 1, ch-2);
  696. g.translate(-(trackBounds.x + cx), -trackBounds.y);
  697. }
  698. }
  699. public void paintTicks(Graphics g) {
  700. Rectangle tickBounds = tickRect;
  701. int i;
  702. int maj, min, max;
  703. int w = tickBounds.width;
  704. int h = tickBounds.height;
  705. int centerEffect, tickHeight;
  706. g.setColor(slider.getBackground());
  707. g.fillRect(tickBounds.x, tickBounds.y, tickBounds.width, tickBounds.height);
  708. g.setColor(Color.black);
  709. maj = slider.getMajorTickSpacing();
  710. min = slider.getMinorTickSpacing();
  711. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  712. g.translate( 0, tickBounds.y);
  713. int value = slider.getMinimum();
  714. int xPos = 0;
  715. if ( slider.getMinorTickSpacing() > 0 ) {
  716. while ( value <= slider.getMaximum() ) {
  717. xPos = xPositionForValue( value );
  718. paintMinorTickForHorizSlider( g, tickBounds, xPos );
  719. value += slider.getMinorTickSpacing();
  720. }
  721. }
  722. if ( slider.getMajorTickSpacing() > 0 ) {
  723. value = slider.getMinimum();
  724. while ( value <= slider.getMaximum() ) {
  725. xPos = xPositionForValue( value );
  726. paintMajorTickForHorizSlider( g, tickBounds, xPos );
  727. value += slider.getMajorTickSpacing();
  728. }
  729. }
  730. g.translate( 0, -tickBounds.y);
  731. }
  732. else {
  733. g.translate(tickBounds.x, 0);
  734. int value = slider.getMinimum();
  735. int yPos = 0;
  736. if ( slider.getMinorTickSpacing() > 0 ) {
  737. int offset = 0;
  738. if(!BasicGraphicsUtils.isLeftToRight(slider)) {
  739. offset = tickBounds.width - tickBounds.width / 2;
  740. g.translate(offset, 0);
  741. }
  742. while ( value <= slider.getMaximum() ) {
  743. yPos = yPositionForValue( value );
  744. paintMinorTickForVertSlider( g, tickBounds, yPos );
  745. value += slider.getMinorTickSpacing();
  746. }
  747. if(!BasicGraphicsUtils.isLeftToRight(slider)) {
  748. g.translate(-offset, 0);
  749. }
  750. }
  751. if ( slider.getMajorTickSpacing() > 0 ) {
  752. value = slider.getMinimum();
  753. if(!BasicGraphicsUtils.isLeftToRight(slider)) {
  754. g.translate(2, 0);
  755. }
  756. while ( value <= slider.getMaximum() ) {
  757. yPos = yPositionForValue( value );
  758. paintMajorTickForVertSlider( g, tickBounds, yPos );
  759. value += slider.getMajorTickSpacing();
  760. }
  761. if(!BasicGraphicsUtils.isLeftToRight(slider)) {
  762. g.translate(-2, 0);
  763. }
  764. }
  765. g.translate(-tickBounds.x, 0);
  766. }
  767. }
  768. protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
  769. g.drawLine( x, 0, x, tickBounds.height / 2 - 1 );
  770. }
  771. protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
  772. g.drawLine( x, 0, x, tickBounds.height - 2 );
  773. }
  774. protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
  775. g.drawLine( 0, y, tickBounds.width / 2 - 1, y );
  776. }
  777. protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
  778. g.drawLine( 0, y, tickBounds.width - 2, y );
  779. }
  780. public void paintLabels( Graphics g ) {
  781. Rectangle labelBounds = labelRect;
  782. Dictionary dictionary = slider.getLabelTable();
  783. if ( dictionary != null ) {
  784. Enumeration keys = dictionary.keys();
  785. int minValue = slider.getMinimum();
  786. int maxValue = slider.getMaximum();
  787. while ( keys.hasMoreElements() ) {
  788. Integer key = (Integer)keys.nextElement();
  789. int value = key.intValue();
  790. if (value >= minValue && value <= maxValue) {
  791. Component label = (Component)dictionary.get( key );
  792. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  793. g.translate( 0, labelBounds.y );
  794. paintHorizontalLabel( g, value, label );
  795. g.translate( 0, -labelBounds.y );
  796. }
  797. else {
  798. int offset = 0;
  799. if (!BasicGraphicsUtils.isLeftToRight(slider)) {
  800. offset = labelBounds.width -
  801. label.getPreferredSize().width;
  802. }
  803. g.translate( labelBounds.x + offset, 0 );
  804. paintVerticalLabel( g, value, label );
  805. g.translate( -labelBounds.x - offset, 0 );
  806. }
  807. }
  808. }
  809. }
  810. }
  811. /**
  812. * Called for every label in the label table. Used to draw the labels for horizontal sliders.
  813. * The graphics have been translated to labelRect.y already.
  814. * @see JSlider#setLabelTable
  815. */
  816. protected void paintHorizontalLabel( Graphics g, int value, Component label ) {
  817. int labelCenter = xPositionForValue( value );
  818. int labelLeft = labelCenter - (label.getPreferredSize().width / 2);
  819. g.translate( labelLeft, 0 );
  820. label.paint( g );
  821. g.translate( -labelLeft, 0 );
  822. }
  823. /**
  824. * Called for every label in the label table. Used to draw the labels for vertical sliders.
  825. * The graphics have been translated to labelRect.x already.
  826. * @see JSlider#setLabelTable
  827. */
  828. protected void paintVerticalLabel( Graphics g, int value, Component label ) {
  829. int labelCenter = yPositionForValue( value );
  830. int labelTop = labelCenter - (label.getPreferredSize().height / 2);
  831. g.translate( 0, labelTop );
  832. label.paint( g );
  833. g.translate( 0, -labelTop );
  834. }
  835. public void paintThumb(Graphics g) {
  836. Rectangle knobBounds = thumbRect;
  837. int w = knobBounds.width;
  838. int h = knobBounds.height;
  839. g.translate(knobBounds.x, knobBounds.y);
  840. if ( slider.isEnabled() ) {
  841. g.setColor(slider.getBackground());
  842. }
  843. else {
  844. g.setColor(slider.getBackground().darker());
  845. }
  846. if ( !slider.getPaintTicks() ) {
  847. // "plain" version
  848. g.fillRect(0, 0, w, h);
  849. g.setColor(Color.black);
  850. g.drawLine(0, h-1, w-1, h-1);
  851. g.drawLine(w-1, 0, w-1, h-1);
  852. g.setColor(highlightColor);
  853. g.drawLine(0, 0, 0, h-2);
  854. g.drawLine(1, 0, w-2, 0);
  855. g.setColor(shadowColor);
  856. g.drawLine(1, h-2, w-2, h-2);
  857. g.drawLine(w-2, 1, w-2, h-3);
  858. }
  859. else if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  860. int cw = w / 2;
  861. g.fillRect(1, 1, w-3, h-1-cw);
  862. Polygon p = new Polygon();
  863. p.addPoint(1, h-cw);
  864. p.addPoint(cw-1, h-1);
  865. p.addPoint(w-2, h-1-cw);
  866. g.fillPolygon(p);
  867. g.setColor(highlightColor);
  868. g.drawLine(0, 0, w-2, 0);
  869. g.drawLine(0, 1, 0, h-1-cw);
  870. g.drawLine(0, h-cw, cw-1, h-1);
  871. g.setColor(Color.black);
  872. g.drawLine(w-1, 0, w-1, h-2-cw);
  873. g.drawLine(w-1, h-1-cw, w-1-cw, h-1);
  874. g.setColor(shadowColor);
  875. g.drawLine(w-2, 1, w-2, h-2-cw);
  876. g.drawLine(w-2, h-1-cw, w-1-cw, h-2);
  877. }
  878. else { // vertical
  879. int cw = h / 2;
  880. if(BasicGraphicsUtils.isLeftToRight(slider)) {
  881. g.fillRect(1, 1, w-1-cw, h-3);
  882. Polygon p = new Polygon();
  883. p.addPoint(w-cw-1, 0);
  884. p.addPoint(w-1, cw);
  885. p.addPoint(w-1-cw, h-2);
  886. g.fillPolygon(p);
  887. g.setColor(highlightColor);
  888. g.drawLine(0, 0, 0, h - 2); // left
  889. g.drawLine(1, 0, w-1-cw, 0); // top
  890. g.drawLine(w-cw-1, 0, w-1, cw); // top slant
  891. g.setColor(Color.black);
  892. g.drawLine(0, h-1, w-2-cw, h-1); // bottom
  893. g.drawLine(w-1-cw, h-1, w-1, h-1-cw); // bottom slant
  894. g.setColor(shadowColor);
  895. g.drawLine(1, h-2, w-2-cw, h-2 ); // bottom
  896. g.drawLine(w-1-cw, h-2, w-2, h-cw-1 ); // bottom slant
  897. }
  898. else {
  899. g.fillRect(5, 1, w-1-cw, h-3);
  900. Polygon p = new Polygon();
  901. p.addPoint(cw, 0);
  902. p.addPoint(0, cw);
  903. p.addPoint(cw, h-2);
  904. g.fillPolygon(p);
  905. g.setColor(highlightColor);
  906. g.drawLine(cw-1, 0, w-2, 0); // top
  907. g.drawLine(0, cw, cw, 0); // top slant
  908. g.setColor(Color.black);
  909. g.drawLine(0, h-1-cw, cw, h-1 ); // bottom slant
  910. g.drawLine(cw, h-1, w-1, h-1); // bottom
  911. g.setColor(shadowColor);
  912. g.drawLine(cw, h-2, w-2, h-2 ); // bottom
  913. g.drawLine(w-1, 1, w-1, h-2 ); // right
  914. }
  915. }
  916. g.translate(-knobBounds.x, -knobBounds.y);
  917. }
  918. // Used exclusively by setThumbLocation()
  919. private static Rectangle unionRect = new Rectangle();
  920. public void setThumbLocation(int x, int y) {
  921. unionRect.setBounds( thumbRect );
  922. thumbRect.setLocation( x, y );
  923. SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, unionRect );
  924. slider.repaint( unionRect.x, unionRect.y, unionRect.width, unionRect.height );
  925. }
  926. public void scrollByBlock(int direction) {
  927. synchronized(slider) {
  928. int oldValue = slider.getValue();
  929. int blockIncrement =
  930. (slider.getMaximum() - slider.getMinimum()) / 10;
  931. if (blockIncrement <= 0 &&
  932. slider.getMaximum() > slider.getMinimum()) {
  933. blockIncrement = 1;
  934. }
  935. int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
  936. slider.setValue(oldValue + delta);
  937. }
  938. }
  939. public void scrollByUnit(int direction) {
  940. synchronized(slider) {
  941. int oldValue = slider.getValue();
  942. int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
  943. slider.setValue(oldValue + delta);
  944. }
  945. }
  946. /**
  947. * This function is called when a mousePressed was detected in the track, not
  948. * in the thumb. The default behavior is to scroll by block. You can
  949. * override this method to stop it from scrolling or to add additional behavior.
  950. */
  951. protected void scrollDueToClickInTrack( int dir ) {
  952. scrollByBlock( dir );
  953. }
  954. protected int xPositionForValue( int value ) {
  955. int min = slider.getMinimum();
  956. int max = slider.getMaximum();
  957. int trackLength = trackRect.width;
  958. double valueRange = (double)max - (double)min;
  959. double pixelsPerValue = (double)trackLength / valueRange;
  960. int trackLeft = trackRect.x;
  961. int trackRight = trackRect.x + (trackRect.width - 1);
  962. int xPosition;
  963. if ( !drawInverted() ) {
  964. xPosition = trackLeft;
  965. xPosition += Math.round( pixelsPerValue * ((double)value - min) );
  966. }
  967. else {
  968. xPosition = trackRight;
  969. xPosition -= Math.round( pixelsPerValue * ((double)value - min) );
  970. }
  971. xPosition = Math.max( trackLeft, xPosition );
  972. xPosition = Math.min( trackRight, xPosition );
  973. return xPosition;
  974. }
  975. protected int yPositionForValue( int value ) {
  976. int min = slider.getMinimum();
  977. int max = slider.getMaximum();
  978. int trackLength = trackRect.height;
  979. double valueRange = (double)max - (double)min;
  980. double pixelsPerValue = (double)trackLength / (double)valueRange;
  981. int trackTop = trackRect.y;
  982. int trackBottom = trackRect.y + (trackRect.height - 1);
  983. int yPosition;
  984. if ( !drawInverted() ) {
  985. yPosition = trackTop;
  986. yPosition += Math.round( pixelsPerValue * ((double)max - value ) );
  987. }
  988. else {
  989. yPosition = trackTop;
  990. yPosition += Math.round( pixelsPerValue * ((double)value - min) );
  991. }
  992. yPosition = Math.max( trackTop, yPosition );
  993. yPosition = Math.min( trackBottom, yPosition );
  994. return yPosition;
  995. }
  996. /**
  997. * Returns a value give a y position. If yPos is past the track at the top or the
  998. * bottom it will set the value to the min or max of the slider, depending if the
  999. * slider is inverted or not.
  1000. */
  1001. public int valueForYPosition( int yPos ) {
  1002. int value;
  1003. final int minValue = slider.getMinimum();
  1004. final int maxValue = slider.getMaximum();
  1005. final int trackLength = trackRect.height;
  1006. final int trackTop = trackRect.y;
  1007. final int trackBottom = trackRect.y + (trackRect.height - 1);
  1008. if ( yPos <= trackTop ) {
  1009. value = drawInverted() ? minValue : maxValue;
  1010. }
  1011. else if ( yPos >= trackBottom ) {
  1012. value = drawInverted() ? maxValue : minValue;
  1013. }
  1014. else {
  1015. int distanceFromTrackTop = yPos - trackTop;
  1016. double valueRange = (double)maxValue - (double)minValue;
  1017. double valuePerPixel = valueRange / (double)trackLength;
  1018. int valueFromTrackTop = (int)Math.round( distanceFromTrackTop * valuePerPixel );
  1019. value = drawInverted() ? minValue + valueFromTrackTop : maxValue - valueFromTrackTop;
  1020. }
  1021. return value;
  1022. }
  1023. /**
  1024. * Returns a value give an x position. If xPos is past the track at the left or the
  1025. * right it will set the value to the min or max of the slider, depending if the
  1026. * slider is inverted or not.
  1027. */
  1028. public int valueForXPosition( int xPos ) {
  1029. int value;
  1030. final int minValue = slider.getMinimum();
  1031. final int maxValue = slider.getMaximum();
  1032. final int trackLength = trackRect.width;
  1033. final int trackLeft = trackRect.x;
  1034. final int trackRight = trackRect.x + (trackRect.width - 1);
  1035. if ( xPos <= trackLeft ) {
  1036. value = drawInverted() ? maxValue : minValue;
  1037. }
  1038. else if ( xPos >= trackRight ) {
  1039. value = drawInverted() ? minValue : maxValue;
  1040. }
  1041. else {
  1042. int distanceFromTrackLeft = xPos - trackLeft;
  1043. double valueRange = (double)maxValue - (double)minValue;
  1044. double valuePerPixel = valueRange / (double)trackLength;
  1045. int valueFromTrackLeft = (int)Math.round( distanceFromTrackLeft * valuePerPixel );
  1046. value = drawInverted() ? maxValue - valueFromTrackLeft :
  1047. minValue + valueFromTrackLeft;
  1048. }
  1049. return value;
  1050. }
  1051. /////////////////////////////////////////////////////////////////////////
  1052. /// Model Listener Class
  1053. /////////////////////////////////////////////////////////////////////////
  1054. /**
  1055. * Data model listener.
  1056. *
  1057. * This inner class is marked "public" due to a compiler bug.
  1058. * This class should be treated as a "protected" inner class.
  1059. * Instantiate it only within subclasses of <Foo>.
  1060. */
  1061. public class ChangeHandler implements ChangeListener {
  1062. public void stateChanged(ChangeEvent e) {
  1063. if ( !isDragging ) {
  1064. calculateThumbLocation();
  1065. slider.repaint();
  1066. }
  1067. }
  1068. }
  1069. /////////////////////////////////////////////////////////////////////////
  1070. /// Track Listener Class
  1071. /////////////////////////////////////////////////////////////////////////
  1072. /**
  1073. * Track mouse movements.
  1074. *
  1075. * This inner class is marked "public" due to a compiler bug.
  1076. * This class should be treated as a "protected" inner class.
  1077. * Instantiate it only within subclasses of <Foo>.
  1078. */
  1079. public class TrackListener extends MouseInputAdapter {
  1080. protected transient int offset;
  1081. protected transient int currentMouseX, currentMouseY;
  1082. public void mouseReleased(MouseEvent e) {
  1083. if ( !slider.isEnabled() )
  1084. return;
  1085. offset = 0;
  1086. scrollTimer.stop();
  1087. // This is the way we have to determine snap-to-ticks. It's hard to explain
  1088. // but since ChangeEvents don't give us any idea what has changed we don't
  1089. // have a way to stop the thumb bounds from being recalculated. Recalculating
  1090. // the thumb bounds moves the thumb over the current value (i.e., snapping
  1091. // to the ticks).
  1092. if ( slider.getSnapToTicks() /*|| slider.getSnapToValue()*/ ) {
  1093. isDragging = false;
  1094. slider.setValueIsAdjusting(false);
  1095. }
  1096. else {
  1097. slider.setValueIsAdjusting(false);
  1098. isDragging = false;
  1099. }
  1100. slider.repaint();
  1101. }
  1102. /**
  1103. * If the mouse is pressed above the "thumb" component
  1104. * then reduce the scrollbars value by one page ("page up"),
  1105. * otherwise increase it by one page. If there is no
  1106. * thumb then page up if the mouse is in the upper half
  1107. * of the track.
  1108. */
  1109. public void mousePressed(MouseEvent e) {
  1110. if ( !slider.isEnabled() )
  1111. return;
  1112. currentMouseX = e.getX();
  1113. currentMouseY = e.getY();
  1114. if (slider.isRequestFocusEnabled()) {
  1115. slider.requestFocus();
  1116. }
  1117. // Clicked in the Thumb area?
  1118. if ( thumbRect.contains(currentMouseX, currentMouseY) ) {
  1119. switch ( slider.getOrientation() ) {
  1120. case JSlider.VERTICAL:
  1121. offset = currentMouseY - thumbRect.y;
  1122. break;
  1123. case JSlider.HORIZONTAL:
  1124. offset = currentMouseX - thumbRect.x;
  1125. break;
  1126. }
  1127. isDragging = true;
  1128. return;
  1129. }
  1130. isDragging = false;
  1131. slider.setValueIsAdjusting(true);
  1132. Dimension sbSize = slider.getSize();
  1133. int direction = POSITIVE_SCROLL;
  1134. switch ( slider.getOrientation() ) {
  1135. case JSlider.VERTICAL:
  1136. if ( thumbRect.isEmpty() ) {
  1137. int scrollbarCenter = sbSize.height / 2;
  1138. if ( !drawInverted() ) {
  1139. direction = (currentMouseY < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
  1140. }
  1141. else {
  1142. direction = (currentMouseY < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
  1143. }
  1144. }
  1145. else {
  1146. int thumbY = thumbRect.y;
  1147. if ( !drawInverted() ) {
  1148. direction = (currentMouseY < thumbY) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
  1149. }
  1150. else {
  1151. direction = (currentMouseY < thumbY) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
  1152. }
  1153. }
  1154. break;
  1155. case JSlider.HORIZONTAL:
  1156. if ( thumbRect.isEmpty() ) {
  1157. int scrollbarCenter = sbSize.width / 2;
  1158. if ( !drawInverted() ) {
  1159. direction = (currentMouseX < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
  1160. }
  1161. else {
  1162. direction = (currentMouseX < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
  1163. }
  1164. }
  1165. else {
  1166. int thumbX = thumbRect.x;
  1167. if ( !drawInverted() ) {
  1168. direction = (currentMouseX < thumbX) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
  1169. }
  1170. else {
  1171. direction = (currentMouseX < thumbX) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
  1172. }
  1173. }
  1174. break;
  1175. }
  1176. scrollDueToClickInTrack(direction);
  1177. Rectangle r = thumbRect;
  1178. if ( !r.contains(currentMouseX, currentMouseY) ) {
  1179. if ( shouldScroll(direction) ) {
  1180. scrollTimer.stop();
  1181. scrollListener.setDirection(direction);
  1182. scrollTimer.start();
  1183. }
  1184. }
  1185. }
  1186. public boolean shouldScroll(int direction) {
  1187. Rectangle r = thumbRect;
  1188. if ( slider.getOrientation() == JSlider.VERTICAL ) {
  1189. if ( drawInverted() ? direction < 0 : direction > 0 ) {
  1190. if ( r.y + r.height <= currentMouseY ) {
  1191. return false;
  1192. }
  1193. }
  1194. else if ( r.y >= currentMouseY ) {
  1195. return false;
  1196. }
  1197. }
  1198. else {
  1199. if ( drawInverted() ? direction < 0 : direction > 0 ) {
  1200. if ( r.x + r.width >= currentMouseX ) {
  1201. return false;
  1202. }
  1203. }
  1204. else if ( r.x <= currentMouseX ) {
  1205. return false;
  1206. }
  1207. }
  1208. if ( direction > 0 && slider.getValue() + slider.getExtent() >= slider.getMaximum() ) {
  1209. return false;
  1210. }
  1211. else if ( direction < 0 && slider.getValue() <= slider.getMinimum() ) {
  1212. return false;
  1213. }
  1214. return true;
  1215. }
  1216. /**
  1217. * Set the models value to the position of the top/left
  1218. * of the thumb relative to the origin of the track.
  1219. */
  1220. public void mouseDragged( MouseEvent e ) {
  1221. BasicScrollBarUI ui;
  1222. int thumbMiddle = 0;
  1223. if ( !slider.isEnabled() )
  1224. return;
  1225. currentMouseX = e.getX();
  1226. currentMouseY = e.getY();
  1227. if ( !isDragging )
  1228. return;
  1229. slider.setValueIsAdjusting(true);
  1230. switch ( slider.getOrientation() ) {
  1231. case JSlider.VERTICAL:
  1232. int halfThumbHeight = thumbRect.height / 2;
  1233. int thumbTop = e.getY() - offset;
  1234. int trackTop = trackRect.y;
  1235. int trackBottom = trackRect.y + (trackRect.height - 1);
  1236. int vMax = yPositionForValue(slider.getMaximum() -
  1237. slider.getExtent());
  1238. if (drawInverted()) {
  1239. trackBottom = vMax;
  1240. }
  1241. else {
  1242. trackTop = vMax;
  1243. }
  1244. thumbTop = Math.max( thumbTop, trackTop - halfThumbHeight );
  1245. thumbTop = Math.min( thumbTop, trackBottom - halfThumbHeight );
  1246. setThumbLocation(thumbRect.x, thumbTop);
  1247. thumbMiddle = thumbTop + halfThumbHeight;
  1248. slider.setValue( valueForYPosition( thumbMiddle ) );
  1249. break;
  1250. case JSlider.HORIZONTAL:
  1251. int halfThumbWidth = thumbRect.width / 2;
  1252. int thumbLeft = e.getX() - offset;
  1253. int trackLeft = trackRect.x;
  1254. int trackRight = trackRect.x + (trackRect.width - 1);
  1255. int hMax = xPositionForValue(slider.getMaximum() -
  1256. slider.getExtent());
  1257. if (drawInverted()) {
  1258. trackLeft = hMax;
  1259. }
  1260. else {
  1261. trackRight = hMax;
  1262. }
  1263. thumbLeft = Math.max( thumbLeft, trackLeft - halfThumbWidth );
  1264. thumbLeft = Math.min( thumbLeft, trackRight - halfThumbWidth );
  1265. setThumbLocation( thumbLeft, thumbRect.y);
  1266. thumbMiddle = thumbLeft + halfThumbWidth;
  1267. slider.setValue( valueForXPosition( thumbMiddle ) );
  1268. break;
  1269. default:
  1270. return;
  1271. }
  1272. }
  1273. public void mouseMoved(MouseEvent e) {}
  1274. }
  1275. /**
  1276. * Scroll-event listener.
  1277. *
  1278. * This inner class is marked "public" due to a compiler bug.
  1279. * This class should be treated as a "protected" inner class.
  1280. * Instantiate it only within subclasses of <Foo>.
  1281. */
  1282. public class ScrollListener implements ActionListener {
  1283. // changed this class to public to avoid bogus IllegalAccessException bug i
  1284. // InternetExplorer browser. It was protected. Work around for 4109432
  1285. int direction = POSITIVE_SCROLL;
  1286. boolean useBlockIncrement;
  1287. public ScrollListener() {
  1288. direction = POSITIVE_SCROLL;
  1289. useBlockIncrement = true;
  1290. }
  1291. public ScrollListener(int dir, boolean block) {
  1292. direction = dir;
  1293. useBlockIncrement = block;
  1294. }
  1295. public void setDirection(int direction) { this.direction = direction;}
  1296. public void setScrollByBlock(boolean block) { this.useBlockIncrement = block;}
  1297. public void actionPerformed(ActionEvent e) {
  1298. if ( useBlockIncrement ) {
  1299. scrollByBlock(direction);
  1300. }
  1301. else {
  1302. scrollByUnit(direction);
  1303. }
  1304. if ( !trackListener.shouldScroll(direction) ) {
  1305. ((Timer)e.getSource()).stop();
  1306. }
  1307. }
  1308. };
  1309. /**
  1310. * Listener for resizing events.
  1311. * <p>
  1312. * This inner class is marked "public" due to a compiler bug.
  1313. * This class should be treated as a "protected" inner class.
  1314. * Instantiate it only within subclasses of <Foo>.
  1315. */
  1316. public class ComponentHandler extends ComponentAdapter {
  1317. public void componentResized(ComponentEvent e) {
  1318. calculateGeometry();
  1319. slider.repaint();
  1320. }
  1321. };
  1322. /**
  1323. * Focus-change listener.
  1324. * <p>
  1325. * This inner class is marked "public" due to a compiler bug.
  1326. * This class should be treated as a "protected" inner class.
  1327. * Instantiate it only within subclasses of <Foo>.
  1328. */
  1329. public class FocusHandler implements FocusListener {
  1330. public void focusGained(FocusEvent e) { slider.repaint();}
  1331. public void focusLost(FocusEvent e) { slider.repaint();}
  1332. };
  1333. /**
  1334. * As of Java 2 platform v1.3 this undocumented class is no longer used.
  1335. * The recommended approach to creating bindings is to use a
  1336. * combination of an <code>ActionMap</code>, to contain the action,
  1337. * and an <code>InputMap</code> to contain the mapping from KeyStroke
  1338. * to action description. The InputMap is is usually described in the
  1339. * LookAndFeel tables.
  1340. * <p>
  1341. * Please refer to the key bindings specification for further details.
  1342. * <p>
  1343. * This inner class is marked "public" due to a compiler bug.
  1344. * This class should be treated as a "protected" inner class.
  1345. * Instantiate it only within subclasses of <Foo>.
  1346. */
  1347. public class ActionScroller extends AbstractAction {
  1348. int dir;
  1349. boolean block;
  1350. JSlider slider;
  1351. public ActionScroller( JSlider slider, int dir, boolean block) {
  1352. this.dir = dir;
  1353. this.block = block;
  1354. this.slider = slider;
  1355. }
  1356. public void actionPerformed(ActionEvent e) {
  1357. if ( dir == NEGATIVE_SCROLL || dir == POSITIVE_SCROLL ) {
  1358. int realDir = dir;
  1359. if ( drawInverted() ) {
  1360. realDir = dir == NEGATIVE_SCROLL ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
  1361. }
  1362. if ( block )
  1363. scrollByBlock(realDir);
  1364. else
  1365. scrollByUnit(realDir);
  1366. }
  1367. else {
  1368. if ( drawInverted() ) {
  1369. if ( dir == MIN_SCROLL )
  1370. slider.setValue(slider.getMaximum());
  1371. else if ( dir == MAX_SCROLL )
  1372. slider.setValue(slider.getMinimum());
  1373. }
  1374. else {
  1375. if ( dir == MIN_SCROLL )
  1376. slider.setValue(slider.getMinimum());
  1377. else if ( dir == MAX_SCROLL )
  1378. slider.setValue(slider.getMaximum());
  1379. }
  1380. }
  1381. }
  1382. public boolean isEnabled() {
  1383. boolean b = true;
  1384. if (slider != null) {
  1385. b = slider.isEnabled();
  1386. }
  1387. return b;
  1388. }
  1389. };
  1390. /**
  1391. * A static version of the above.
  1392. */
  1393. static class SharedActionScroller extends AbstractAction {
  1394. int dir;
  1395. boolean block;
  1396. public SharedActionScroller(int dir, boolean block) {
  1397. this.dir = dir;
  1398. this.block = block;
  1399. }
  1400. public void actionPerformed(ActionEvent e) {
  1401. JSlider slider = (JSlider)e.getSource();
  1402. if ( dir == NEGATIVE_SCROLL || dir == POSITIVE_SCROLL ) {
  1403. int realDir = dir;
  1404. BasicSliderUI ui = (BasicSliderUI)slider.getUI();
  1405. if ( slider.getInverted() ) {
  1406. realDir = dir == NEGATIVE_SCROLL ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
  1407. }
  1408. if ( block )
  1409. ui.scrollByBlock(realDir);
  1410. else
  1411. ui.scrollByUnit(realDir);
  1412. }
  1413. else {
  1414. if ( slider.getInverted() ) {
  1415. if ( dir == MIN_SCROLL )
  1416. slider.setValue(slider.getMaximum());
  1417. else if ( dir == MAX_SCROLL )
  1418. slider.setValue(slider.getMinimum());
  1419. }
  1420. else {
  1421. if ( dir == MIN_SCROLL )
  1422. slider.setValue(slider.getMinimum());
  1423. else if ( dir == MAX_SCROLL )
  1424. slider.setValue(slider.getMaximum());
  1425. }
  1426. }
  1427. }
  1428. }
  1429. }