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