1. /*
  2. * @(#)MetalSliderUI.java 1.34 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.metal;
  8. import javax.swing.plaf.basic.BasicSliderUI;
  9. import java.awt.Component;
  10. import java.awt.Container;
  11. import java.awt.Graphics;
  12. import java.awt.Dimension;
  13. import java.awt.Rectangle;
  14. import java.awt.Point;
  15. import java.awt.Insets;
  16. import java.awt.Color;
  17. import java.io.Serializable;
  18. import java.awt.IllegalComponentStateException;
  19. import java.awt.Polygon;
  20. import java.beans.*;
  21. import javax.swing.border.AbstractBorder;
  22. import javax.swing.*;
  23. import javax.swing.event.*;
  24. import javax.swing.plaf.*;
  25. /**
  26. * A Java L&F implementation of SliderUI.
  27. * <p>
  28. * <strong>Warning:</strong>
  29. * Serialized objects of this class will not be compatible with
  30. * future Swing releases. The current serialization support is
  31. * appropriate for short term storage or RMI between applications running
  32. * the same version of Swing. As of 1.4, support for long term storage
  33. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  34. * has been added to the <code>java.beans</code> package.
  35. * Please see {@link java.beans.XMLEncoder}.
  36. *
  37. * @version 1.34 12/19/03
  38. * @author Tom Santos
  39. */
  40. public class MetalSliderUI extends BasicSliderUI {
  41. protected final int TICK_BUFFER = 4;
  42. protected boolean filledSlider = false;
  43. protected static Color thumbColor;
  44. protected static Color highlightColor;
  45. protected static Color darkShadowColor;
  46. protected static int trackWidth;
  47. protected static int tickLength;
  48. protected static Icon horizThumbIcon;
  49. protected static Icon vertThumbIcon;
  50. protected final String SLIDER_FILL = "JSlider.isFilled";
  51. public static ComponentUI createUI(JComponent c) {
  52. return new MetalSliderUI();
  53. }
  54. public MetalSliderUI() {
  55. super( null );
  56. }
  57. public void installUI( JComponent c ) {
  58. trackWidth = ((Integer)UIManager.get( "Slider.trackWidth" )).intValue();
  59. tickLength = ((Integer)UIManager.get( "Slider.majorTickLength" )).intValue();
  60. horizThumbIcon = UIManager.getIcon( "Slider.horizontalThumbIcon" );
  61. vertThumbIcon = UIManager.getIcon( "Slider.verticalThumbIcon" );
  62. super.installUI( c );
  63. thumbColor = UIManager.getColor("Slider.thumb");
  64. highlightColor = UIManager.getColor("Slider.highlight");
  65. darkShadowColor = UIManager.getColor("Slider.darkShadow");
  66. scrollListener.setScrollByBlock( false );
  67. Object sliderFillProp = c.getClientProperty( SLIDER_FILL );
  68. if ( sliderFillProp != null ) {
  69. filledSlider = ((Boolean)sliderFillProp).booleanValue();
  70. }
  71. }
  72. protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
  73. return new MetalPropertyListener();
  74. }
  75. protected class MetalPropertyListener extends BasicSliderUI.PropertyChangeHandler {
  76. public void propertyChange( PropertyChangeEvent e ) { // listen for slider fill
  77. super.propertyChange( e );
  78. String name = e.getPropertyName();
  79. if ( name.equals( SLIDER_FILL ) ) {
  80. if ( e.getNewValue() != null ) {
  81. filledSlider = ((Boolean)e.getNewValue()).booleanValue();
  82. }
  83. else {
  84. filledSlider = false;
  85. }
  86. }
  87. }
  88. }
  89. public void paintThumb(Graphics g) {
  90. Rectangle knobBounds = thumbRect;
  91. g.translate( knobBounds.x, knobBounds.y );
  92. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  93. horizThumbIcon.paintIcon( slider, g, 0, 0 );
  94. }
  95. else {
  96. vertThumbIcon.paintIcon( slider, g, 0, 0 );
  97. }
  98. g.translate( -knobBounds.x, -knobBounds.y );
  99. }
  100. /**
  101. * If <code>chooseFirst</code>is true, <code>c1</code> is returned,
  102. * otherwise <code>c2</code>.
  103. */
  104. private Color chooseColor(boolean chooseFirst, Color c1, Color c2) {
  105. if (chooseFirst) {
  106. return c2;
  107. }
  108. return c1;
  109. }
  110. /**
  111. * Returns a rectangle enclosing the track that will be painted.
  112. */
  113. private Rectangle getPaintTrackRect() {
  114. int trackLeft = 0, trackRight = 0, trackTop = 0, trackBottom = 0;
  115. if (slider.getOrientation() == JSlider.HORIZONTAL) {
  116. trackBottom = (trackRect.height - 1) - getThumbOverhang();
  117. trackTop = trackBottom - (getTrackWidth() - 1);
  118. trackRight = trackRect.width - 1;
  119. }
  120. else {
  121. if (MetalUtils.isLeftToRight(slider)) {
  122. trackLeft = (trackRect.width - getThumbOverhang()) -
  123. getTrackWidth();
  124. trackRight = (trackRect.width - getThumbOverhang()) - 1;
  125. }
  126. else {
  127. trackLeft = getThumbOverhang();
  128. trackRight = getThumbOverhang() + getTrackWidth() - 1;
  129. }
  130. trackBottom = trackRect.height - 1;
  131. }
  132. return new Rectangle(trackRect.x + trackLeft, trackRect.y + trackTop,
  133. trackRight - trackLeft, trackBottom - trackTop);
  134. }
  135. public void paintTrack(Graphics g) {
  136. if (MetalLookAndFeel.usingOcean()) {
  137. oceanPaintTrack(g);
  138. return;
  139. }
  140. Color trackColor = !slider.isEnabled() ? MetalLookAndFeel.getControlShadow() :
  141. slider.getForeground();
  142. boolean leftToRight = MetalUtils.isLeftToRight(slider);
  143. g.translate( trackRect.x, trackRect.y );
  144. int trackLeft = 0;
  145. int trackTop = 0;
  146. int trackRight = 0;
  147. int trackBottom = 0;
  148. // Draw the track
  149. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  150. trackBottom = (trackRect.height - 1) - getThumbOverhang();
  151. trackTop = trackBottom - (getTrackWidth() - 1);
  152. trackRight = trackRect.width - 1;
  153. }
  154. else {
  155. if (leftToRight) {
  156. trackLeft = (trackRect.width - getThumbOverhang()) -
  157. getTrackWidth();
  158. trackRight = (trackRect.width - getThumbOverhang()) - 1;
  159. }
  160. else {
  161. trackLeft = getThumbOverhang();
  162. trackRight = getThumbOverhang() + getTrackWidth() - 1;
  163. }
  164. trackBottom = trackRect.height - 1;
  165. }
  166. if ( slider.isEnabled() ) {
  167. g.setColor( MetalLookAndFeel.getControlDarkShadow() );
  168. g.drawRect( trackLeft, trackTop,
  169. (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1 );
  170. g.setColor( MetalLookAndFeel.getControlHighlight() );
  171. g.drawLine( trackLeft + 1, trackBottom, trackRight, trackBottom );
  172. g.drawLine( trackRight, trackTop + 1, trackRight, trackBottom );
  173. g.setColor( MetalLookAndFeel.getControlShadow() );
  174. g.drawLine( trackLeft + 1, trackTop + 1, trackRight - 2, trackTop + 1 );
  175. g.drawLine( trackLeft + 1, trackTop + 1, trackLeft + 1, trackBottom - 2 );
  176. }
  177. else {
  178. g.setColor( MetalLookAndFeel.getControlShadow() );
  179. g.drawRect( trackLeft, trackTop,
  180. (trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1 );
  181. }
  182. // Draw the fill
  183. if ( filledSlider ) {
  184. int middleOfThumb = 0;
  185. int fillTop = 0;
  186. int fillLeft = 0;
  187. int fillBottom = 0;
  188. int fillRight = 0;
  189. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  190. middleOfThumb = thumbRect.x + (thumbRect.width / 2);
  191. middleOfThumb -= trackRect.x; // To compensate for the g.translate()
  192. fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
  193. fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;
  194. if ( !drawInverted() ) {
  195. fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
  196. fillRight = middleOfThumb;
  197. }
  198. else {
  199. fillLeft = middleOfThumb;
  200. fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;
  201. }
  202. }
  203. else {
  204. middleOfThumb = thumbRect.y + (thumbRect.height / 2);
  205. middleOfThumb -= trackRect.y; // To compensate for the g.translate()
  206. fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
  207. fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;
  208. if ( !drawInverted() ) {
  209. fillTop = middleOfThumb;
  210. fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;
  211. }
  212. else {
  213. fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
  214. fillBottom = middleOfThumb;
  215. }
  216. }
  217. if ( slider.isEnabled() ) {
  218. g.setColor( slider.getBackground() );
  219. g.drawLine( fillLeft, fillTop, fillRight, fillTop );
  220. g.drawLine( fillLeft, fillTop, fillLeft, fillBottom );
  221. g.setColor( MetalLookAndFeel.getControlShadow() );
  222. g.fillRect( fillLeft + 1, fillTop + 1,
  223. fillRight - fillLeft, fillBottom - fillTop );
  224. }
  225. else {
  226. g.setColor( MetalLookAndFeel.getControlShadow() );
  227. g.fillRect( fillLeft, fillTop,
  228. fillRight - fillLeft, trackBottom - trackTop );
  229. }
  230. }
  231. g.translate( -trackRect.x, -trackRect.y );
  232. }
  233. private void oceanPaintTrack(Graphics g) {
  234. boolean leftToRight = MetalUtils.isLeftToRight(slider);
  235. boolean drawInverted = drawInverted();
  236. Color sliderAltTrackColor = (Color)UIManager.get(
  237. "Slider.altTrackColor");
  238. // Translate to the origin of the painting rectangle
  239. Rectangle paintRect = getPaintTrackRect();
  240. g.translate(paintRect.x, paintRect.y);
  241. // Width and height of the painting rectangle.
  242. int w = paintRect.width;
  243. int h = paintRect.height;
  244. if (!slider.isEnabled()) {
  245. g.setColor(MetalLookAndFeel.getControlShadow());
  246. g.drawRect(0, 0, w - 1, h - 1);
  247. }
  248. else if (slider.getOrientation() == JSlider.HORIZONTAL) {
  249. int middleOfThumb = thumbRect.x + (thumbRect.width / 2) -
  250. paintRect.x;
  251. int fillMinX;
  252. int fillMaxX;
  253. if (middleOfThumb > 0) {
  254. g.setColor(chooseColor(drawInverted,
  255. MetalLookAndFeel.getPrimaryControlDarkShadow(),
  256. MetalLookAndFeel.getControlDarkShadow()));
  257. g.drawRect(0, 0, middleOfThumb - 1, h - 1);
  258. }
  259. if (middleOfThumb < w) {
  260. g.setColor(chooseColor(drawInverted,
  261. MetalLookAndFeel.getControlDarkShadow(),
  262. MetalLookAndFeel.getPrimaryControlDarkShadow()));
  263. g.drawRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1);
  264. }
  265. g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
  266. if (drawInverted) {
  267. fillMinX = middleOfThumb;
  268. fillMaxX = w - 2;
  269. g.drawLine(1, 1, middleOfThumb, 1);
  270. }
  271. else {
  272. fillMinX = 1;
  273. fillMaxX = middleOfThumb;
  274. g.drawLine(middleOfThumb, 1, w - 1, 1);
  275. }
  276. if (h == 6) {
  277. g.setColor(MetalLookAndFeel.getWhite());
  278. g.drawLine(fillMinX, 1, fillMaxX, 1);
  279. g.setColor(sliderAltTrackColor);
  280. g.drawLine(fillMinX, 2, fillMaxX, 2);
  281. g.setColor(MetalLookAndFeel.getControlShadow());
  282. g.drawLine(fillMinX, 3, fillMaxX, 3);
  283. g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
  284. g.drawLine(fillMinX, 4, fillMaxX, 4);
  285. }
  286. }
  287. else {
  288. int middleOfThumb = thumbRect.y + (thumbRect.height / 2) -
  289. paintRect.y;
  290. int fillMinY;
  291. int fillMaxY;
  292. if (middleOfThumb > 0) {
  293. g.setColor(chooseColor(drawInverted,
  294. MetalLookAndFeel.getControlDarkShadow(),
  295. MetalLookAndFeel.getPrimaryControlDarkShadow()));
  296. g.drawRect(0, 0, w - 1, middleOfThumb - 1);
  297. }
  298. if (middleOfThumb < h) {
  299. g.setColor(chooseColor(drawInverted,
  300. MetalLookAndFeel.getPrimaryControlDarkShadow(),
  301. MetalLookAndFeel.getControlDarkShadow()));
  302. g.drawRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1);
  303. }
  304. g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
  305. if (drawInverted()) {
  306. fillMinY = 1;
  307. fillMaxY = middleOfThumb;
  308. if (leftToRight) {
  309. g.drawLine(1, middleOfThumb, 1, h - 1);
  310. }
  311. else {
  312. g.drawLine(w - 2, middleOfThumb, w - 2, h - 1);
  313. }
  314. }
  315. else {
  316. fillMinY = middleOfThumb;
  317. fillMaxY = h - 2;
  318. if (leftToRight) {
  319. g.drawLine(1, 1, 1, middleOfThumb);
  320. }
  321. else {
  322. g.drawLine(w - 2, 1, w - 2, middleOfThumb);
  323. }
  324. }
  325. if (w == 6) {
  326. g.setColor(chooseColor(!leftToRight,
  327. MetalLookAndFeel.getWhite(),
  328. MetalLookAndFeel.getPrimaryControlShadow()));
  329. g.drawLine(1, fillMinY, 1, fillMaxY);
  330. g.setColor(chooseColor(!leftToRight, sliderAltTrackColor,
  331. MetalLookAndFeel.getControlShadow()));
  332. g.drawLine(2, fillMinY, 2, fillMaxY);
  333. g.setColor(chooseColor(!leftToRight,
  334. MetalLookAndFeel.getControlShadow(),
  335. sliderAltTrackColor));
  336. g.drawLine(3, fillMinY, 3, fillMaxY);
  337. g.setColor(chooseColor(!leftToRight,
  338. MetalLookAndFeel.getPrimaryControlShadow(),
  339. MetalLookAndFeel.getWhite()));
  340. g.drawLine(4, fillMinY, 4, fillMaxY);
  341. }
  342. }
  343. g.translate(-paintRect.x, -paintRect.y);
  344. }
  345. public void paintFocus(Graphics g) {
  346. }
  347. protected Dimension getThumbSize() {
  348. Dimension size = new Dimension();
  349. if ( slider.getOrientation() == JSlider.VERTICAL ) {
  350. size.width = vertThumbIcon.getIconWidth();
  351. size.height = vertThumbIcon.getIconHeight();
  352. }
  353. else {
  354. size.width = horizThumbIcon.getIconWidth();
  355. size.height = horizThumbIcon.getIconHeight();
  356. }
  357. return size;
  358. }
  359. /**
  360. * Gets the height of the tick area for horizontal sliders and the width of the
  361. * tick area for vertical sliders. BasicSliderUI uses the returned value to
  362. * determine the tick area rectangle.
  363. */
  364. public int getTickLength() {
  365. return slider.getOrientation() == JSlider.HORIZONTAL ? tickLength + TICK_BUFFER + 1 :
  366. tickLength + TICK_BUFFER + 3;
  367. }
  368. /**
  369. * Returns the shorter dimension of the track.
  370. */
  371. protected int getTrackWidth() {
  372. // This strange calculation is here to keep the
  373. // track in proportion to the thumb.
  374. final double kIdealTrackWidth = 7.0;
  375. final double kIdealThumbHeight = 16.0;
  376. final double kWidthScalar = kIdealTrackWidth / kIdealThumbHeight;
  377. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  378. return (int)(kWidthScalar * thumbRect.height);
  379. }
  380. else {
  381. return (int)(kWidthScalar * thumbRect.width);
  382. }
  383. }
  384. /**
  385. * Returns the longer dimension of the slide bar. (The slide bar is only the
  386. * part that runs directly under the thumb)
  387. */
  388. protected int getTrackLength() {
  389. if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
  390. return trackRect.width;
  391. }
  392. return trackRect.height;
  393. }
  394. /**
  395. * Returns the amount that the thumb goes past the slide bar.
  396. */
  397. protected int getThumbOverhang() {
  398. return (int)(getThumbSize().getHeight()-getTrackWidth())/2;
  399. }
  400. protected void scrollDueToClickInTrack( int dir ) {
  401. scrollByUnit( dir );
  402. }
  403. protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
  404. g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
  405. g.drawLine( x, TICK_BUFFER, x, TICK_BUFFER + (tickLength / 2) );
  406. }
  407. protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
  408. g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
  409. g.drawLine( x, TICK_BUFFER , x, TICK_BUFFER + (tickLength - 1) );
  410. }
  411. protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
  412. g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
  413. if (MetalUtils.isLeftToRight(slider)) {
  414. g.drawLine( TICK_BUFFER, y, TICK_BUFFER + (tickLength / 2), y );
  415. }
  416. else {
  417. g.drawLine( 0, y, tickLength2, y );
  418. }
  419. }
  420. protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
  421. g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
  422. if (MetalUtils.isLeftToRight(slider)) {
  423. g.drawLine( TICK_BUFFER, y, TICK_BUFFER + tickLength, y );
  424. }
  425. else {
  426. g.drawLine( 0, y, tickLength, y );
  427. }
  428. }
  429. }