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