J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package javax.swing.plaf.synth; |
| 27 | |
| 28 | import java.awt.Component; |
| 29 | import java.awt.Container; |
| 30 | import java.awt.Adjustable; |
| 31 | import java.awt.event.*; |
| 32 | import java.awt.Graphics; |
| 33 | import java.awt.Dimension; |
| 34 | import java.awt.Font; |
| 35 | import java.awt.FontMetrics; |
| 36 | import java.awt.Rectangle; |
| 37 | import java.awt.Point; |
| 38 | import java.awt.Insets; |
| 39 | import java.awt.Color; |
| 40 | import java.awt.IllegalComponentStateException; |
| 41 | import java.awt.Polygon; |
| 42 | import java.beans.*; |
| 43 | import java.util.Dictionary; |
| 44 | import java.util.Enumeration; |
| 45 | import javax.swing.border.AbstractBorder; |
| 46 | import javax.swing.*; |
| 47 | import javax.swing.event.*; |
| 48 | import javax.swing.plaf.*; |
| 49 | import javax.swing.plaf.basic.BasicSliderUI; |
| 50 | import sun.swing.plaf.synth.SynthUI; |
| 51 | import sun.swing.SwingUtilities2; |
| 52 | |
| 53 | |
| 54 | /** |
| 55 | * Synth's SliderUI. |
| 56 | * |
| 57 | * @author Joshua Outwater |
| 58 | */ |
| 59 | class SynthSliderUI extends BasicSliderUI implements PropertyChangeListener, |
| 60 | SynthUI { |
| 61 | protected Dimension contentDim = new Dimension(); |
| 62 | protected Rectangle valueRect = new Rectangle(); |
| 63 | protected boolean paintValue; |
| 64 | |
| 65 | private int trackHeight; |
| 66 | private int trackBorder; |
| 67 | private int thumbWidth; |
| 68 | private int thumbHeight; |
| 69 | |
| 70 | private SynthStyle style; |
| 71 | private SynthStyle sliderTrackStyle; |
| 72 | private SynthStyle sliderThumbStyle; |
| 73 | |
| 74 | /** Used to determine the color to paint the thumb. */ |
| 75 | private transient boolean thumbActive; |
| 76 | |
| 77 | /////////////////////////////////////////////////// |
| 78 | // ComponentUI Interface Implementation methods |
| 79 | /////////////////////////////////////////////////// |
| 80 | public static ComponentUI createUI(JComponent c) { |
| 81 | return new SynthSliderUI((JSlider)c); |
| 82 | } |
| 83 | |
| 84 | public SynthSliderUI(JSlider c) { |
| 85 | super(c); |
| 86 | } |
| 87 | |
| 88 | protected void installDefaults(JSlider slider) { |
| 89 | updateStyle(slider); |
| 90 | } |
| 91 | |
| 92 | protected void uninstallDefaults() { |
| 93 | SynthContext context = getContext(slider, ENABLED); |
| 94 | style.uninstallDefaults(context); |
| 95 | context.dispose(); |
| 96 | style = null; |
| 97 | |
| 98 | context = getContext(slider, Region.SLIDER_TRACK, ENABLED); |
| 99 | sliderTrackStyle.uninstallDefaults(context); |
| 100 | context.dispose(); |
| 101 | sliderTrackStyle = null; |
| 102 | |
| 103 | context = getContext(slider, Region.SLIDER_THUMB, ENABLED); |
| 104 | sliderThumbStyle.uninstallDefaults(context); |
| 105 | context.dispose(); |
| 106 | sliderThumbStyle = null; |
| 107 | } |
| 108 | |
| 109 | protected void installListeners(JSlider slider) { |
| 110 | super.installListeners(slider); |
| 111 | slider.addPropertyChangeListener(this); |
| 112 | } |
| 113 | |
| 114 | protected void uninstallListeners(JSlider slider) { |
| 115 | slider.removePropertyChangeListener(this); |
| 116 | super.uninstallListeners(slider); |
| 117 | } |
| 118 | |
| 119 | private void updateStyle(JSlider c) { |
| 120 | SynthContext context = getContext(c, ENABLED); |
| 121 | SynthStyle oldStyle = style; |
| 122 | style = SynthLookAndFeel.updateStyle(context, this); |
| 123 | |
| 124 | if (style != oldStyle) { |
| 125 | thumbWidth = |
| 126 | style.getInt(context, "Slider.thumbWidth", 30); |
| 127 | |
| 128 | thumbHeight = |
| 129 | style.getInt(context, "Slider.thumbHeight", 14); |
| 130 | |
| 131 | trackBorder = |
| 132 | style.getInt(context, "Slider.trackBorder", 1); |
| 133 | |
| 134 | trackHeight = thumbHeight + trackBorder * 2; |
| 135 | |
| 136 | paintValue = style.getBoolean(context, |
| 137 | "Slider.paintValue", true); |
| 138 | if (oldStyle != null) { |
| 139 | uninstallKeyboardActions(c); |
| 140 | installKeyboardActions(c); |
| 141 | } |
| 142 | } |
| 143 | context.dispose(); |
| 144 | |
| 145 | context = getContext(c, Region.SLIDER_TRACK, ENABLED); |
| 146 | sliderTrackStyle = |
| 147 | SynthLookAndFeel.updateStyle(context, this); |
| 148 | context.dispose(); |
| 149 | |
| 150 | context = getContext(c, Region.SLIDER_THUMB, ENABLED); |
| 151 | sliderThumbStyle = |
| 152 | SynthLookAndFeel.updateStyle(context, this); |
| 153 | context.dispose(); |
| 154 | } |
| 155 | |
| 156 | protected TrackListener createTrackListener(JSlider s) { |
| 157 | return new SynthTrackListener(); |
| 158 | } |
| 159 | |
| 160 | private void updateThumbState(int x, int y) { |
| 161 | setThumbActive(thumbRect.contains(x, y)); |
| 162 | } |
| 163 | |
| 164 | private void setThumbActive(boolean active) { |
| 165 | if (thumbActive != active) { |
| 166 | thumbActive = active; |
| 167 | slider.repaint(thumbRect); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | public int getBaseline(JComponent c, int width, int height) { |
| 172 | if (c == null) { |
| 173 | throw new NullPointerException("Component must be non-null"); |
| 174 | } |
| 175 | if (width < 0 || height < 0) { |
| 176 | throw new IllegalArgumentException( |
| 177 | "Width and height must be >= 0"); |
| 178 | } |
| 179 | if (slider.getPaintLabels() && labelsHaveSameBaselines()) { |
| 180 | // Get the insets for the track. |
| 181 | Insets trackInsets = new Insets(0, 0, 0, 0); |
| 182 | SynthContext trackContext = getContext(slider, |
| 183 | Region.SLIDER_TRACK); |
| 184 | style.getInsets(trackContext, trackInsets); |
| 185 | trackContext.dispose(); |
| 186 | if (slider.getOrientation() == JSlider.HORIZONTAL) { |
| 187 | int valueHeight = 0; |
| 188 | if (paintValue) { |
| 189 | SynthContext context = getContext(slider); |
| 190 | valueHeight = context.getStyle().getGraphicsUtils(context). |
| 191 | getMaximumCharHeight(context); |
| 192 | context.dispose(); |
| 193 | } |
| 194 | int tickHeight = 0; |
| 195 | if (slider.getPaintTicks()) { |
| 196 | tickHeight = getTickLength(); |
| 197 | } |
| 198 | int labelHeight = getHeightOfTallestLabel(); |
| 199 | int contentHeight = valueHeight + trackHeight + |
| 200 | trackInsets.top + trackInsets.bottom + |
| 201 | tickHeight + labelHeight + 4; |
| 202 | int centerY = height / 2 - contentHeight / 2; |
| 203 | centerY += valueHeight + 2; |
| 204 | centerY += trackHeight + trackInsets.top + trackInsets.bottom; |
| 205 | centerY += tickHeight + 2; |
| 206 | Component label = (Component)slider.getLabelTable(). |
| 207 | elements().nextElement(); |
| 208 | Dimension pref = label.getPreferredSize(); |
| 209 | return centerY + label.getBaseline(pref.width, pref.height); |
| 210 | } |
| 211 | else { // VERTICAL |
| 212 | Integer value = slider.getInverted() ? getLowestValue() : |
| 213 | getHighestValue(); |
| 214 | if (value != null) { |
| 215 | int valueY = insetCache.top; |
| 216 | int valueHeight = 0; |
| 217 | if (paintValue) { |
| 218 | SynthContext context = getContext(slider); |
| 219 | valueHeight = context.getStyle().getGraphicsUtils( |
| 220 | context).getMaximumCharHeight(context); |
| 221 | context.dispose(); |
| 222 | } |
| 223 | int contentHeight = height - insetCache.top - |
| 224 | insetCache.bottom; |
| 225 | int trackY = valueY + valueHeight; |
| 226 | int trackHeight = contentHeight - valueHeight; |
| 227 | int yPosition = yPositionForValue(value.intValue(), trackY, |
| 228 | trackHeight); |
| 229 | Component label = (Component)slider.getLabelTable(). |
| 230 | get(value); |
| 231 | Dimension pref = label.getPreferredSize(); |
| 232 | return yPosition - pref.height / 2 + |
| 233 | label.getBaseline(pref.width, pref.height); |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | return -1; |
| 238 | } |
| 239 | |
| 240 | public Dimension getPreferredSize(JComponent c) { |
| 241 | recalculateIfInsetsChanged(); |
| 242 | Dimension d = new Dimension(contentDim); |
| 243 | if (slider.getOrientation() == JSlider.VERTICAL) { |
| 244 | d.height = 200; |
| 245 | } else { |
| 246 | d.width = 200; |
| 247 | } |
| 248 | Insets i = slider.getInsets(); |
| 249 | d.width += i.left + i.right; |
| 250 | d.height += i.top + i.bottom; |
| 251 | return d; |
| 252 | } |
| 253 | |
| 254 | public Dimension getMinimumSize(JComponent c) { |
| 255 | recalculateIfInsetsChanged(); |
| 256 | Dimension d = new Dimension(contentDim); |
| 257 | if (slider.getOrientation() == JSlider.VERTICAL) { |
| 258 | d.height = thumbRect.height + insetCache.top + insetCache.bottom; |
| 259 | } else { |
| 260 | d.width = thumbRect.width + insetCache.left + insetCache.right; |
| 261 | } |
| 262 | return d; |
| 263 | } |
| 264 | |
| 265 | protected void calculateGeometry() { |
| 266 | layout(); |
| 267 | calculateThumbLocation(); |
| 268 | } |
| 269 | |
| 270 | protected void layout() { |
| 271 | SynthContext context = getContext(slider); |
| 272 | SynthGraphicsUtils synthGraphics = style.getGraphicsUtils(context); |
| 273 | |
| 274 | // Set the thumb size. |
| 275 | Dimension size = getThumbSize(); |
| 276 | thumbRect.setSize(size.width, size.height); |
| 277 | |
| 278 | // Get the insets for the track. |
| 279 | Insets trackInsets = new Insets(0, 0, 0, 0); |
| 280 | SynthContext trackContext = getContext(slider, Region.SLIDER_TRACK); |
| 281 | style.getInsets(trackContext, trackInsets); |
| 282 | trackContext.dispose(); |
| 283 | |
| 284 | if (slider.getOrientation() == JSlider.HORIZONTAL) { |
| 285 | // Calculate the height of all the subcomponents so we can center |
| 286 | // them. |
| 287 | valueRect.height = 0; |
| 288 | if (paintValue) { |
| 289 | valueRect.height = |
| 290 | synthGraphics.getMaximumCharHeight(context); |
| 291 | } |
| 292 | |
| 293 | trackRect.height = trackHeight; |
| 294 | |
| 295 | tickRect.height = 0; |
| 296 | if (slider.getPaintTicks()) { |
| 297 | tickRect.height = getTickLength(); |
| 298 | } |
| 299 | |
| 300 | labelRect.height = 0; |
| 301 | if (slider.getPaintLabels()) { |
| 302 | labelRect.height = getHeightOfTallestLabel(); |
| 303 | } |
| 304 | |
| 305 | contentDim.height = valueRect.height + trackRect.height |
| 306 | + trackInsets.top + trackInsets.bottom |
| 307 | + tickRect.height + labelRect.height + 4; |
| 308 | contentDim.width = slider.getWidth() - insetCache.left |
| 309 | - insetCache.right; |
| 310 | |
| 311 | // Check if any of the labels will paint out of bounds. |
| 312 | int pad = 0; |
| 313 | if (slider.getPaintLabels()) { |
| 314 | // Calculate the track rectangle. It is necessary for |
| 315 | // xPositionForValue to return correct values. |
| 316 | trackRect.x = insetCache.left; |
| 317 | trackRect.width = contentDim.width; |
| 318 | |
| 319 | Dictionary dictionary = slider.getLabelTable(); |
| 320 | if (dictionary != null) { |
| 321 | int minValue = slider.getMinimum(); |
| 322 | int maxValue = slider.getMaximum(); |
| 323 | |
| 324 | // Iterate through the keys in the dictionary and find the |
| 325 | // first and last labels indices that fall within the |
| 326 | // slider range. |
| 327 | int firstLblIdx = Integer.MAX_VALUE; |
| 328 | int lastLblIdx = Integer.MIN_VALUE; |
| 329 | for (Enumeration keys = dictionary.keys(); |
| 330 | keys.hasMoreElements(); ) { |
| 331 | int keyInt = ((Integer)keys.nextElement()).intValue(); |
| 332 | if (keyInt >= minValue && keyInt < firstLblIdx) { |
| 333 | firstLblIdx = keyInt; |
| 334 | } |
| 335 | if (keyInt <= maxValue && keyInt > lastLblIdx) { |
| 336 | lastLblIdx = keyInt; |
| 337 | } |
| 338 | } |
| 339 | // Calculate the pad necessary for the labels at the first |
| 340 | // and last visible indices. |
| 341 | pad = getPadForLabel(firstLblIdx); |
| 342 | pad = Math.max(pad, getPadForLabel(lastLblIdx)); |
| 343 | } |
| 344 | } |
| 345 | // Calculate the painting rectangles for each of the different |
| 346 | // slider areas. |
| 347 | valueRect.x = trackRect.x = tickRect.x = labelRect.x = |
| 348 | (insetCache.left + pad); |
| 349 | valueRect.width = trackRect.width = tickRect.width = |
| 350 | labelRect.width = (contentDim.width - (pad * 2)); |
| 351 | |
| 352 | int centerY = slider.getHeight() / 2 - contentDim.height / 2; |
| 353 | |
| 354 | valueRect.y = centerY; |
| 355 | centerY += valueRect.height + 2; |
| 356 | |
| 357 | trackRect.y = centerY + trackInsets.top; |
| 358 | centerY += trackRect.height + trackInsets.top + trackInsets.bottom; |
| 359 | |
| 360 | tickRect.y = centerY; |
| 361 | centerY += tickRect.height + 2; |
| 362 | |
| 363 | labelRect.y = centerY; |
| 364 | centerY += labelRect.height; |
| 365 | } else { |
| 366 | // Calculate the width of all the subcomponents so we can center |
| 367 | // them. |
| 368 | trackRect.width = trackHeight; |
| 369 | |
| 370 | tickRect.width = 0; |
| 371 | if (slider.getPaintTicks()) { |
| 372 | tickRect.width = getTickLength(); |
| 373 | } |
| 374 | |
| 375 | labelRect.width = 0; |
| 376 | if (slider.getPaintLabels()) { |
| 377 | labelRect.width = getWidthOfWidestLabel(); |
| 378 | } |
| 379 | |
| 380 | valueRect.y = insetCache.top; |
| 381 | valueRect.height = 0; |
| 382 | if (paintValue) { |
| 383 | valueRect.height = |
| 384 | synthGraphics.getMaximumCharHeight(context); |
| 385 | } |
| 386 | |
| 387 | // Get the max width of the min or max value of the slider. |
| 388 | FontMetrics fm = slider.getFontMetrics(slider.getFont()); |
| 389 | valueRect.width = Math.max( |
| 390 | synthGraphics.computeStringWidth(context, slider.getFont(), |
| 391 | fm, "" + slider.getMaximum()), |
| 392 | synthGraphics.computeStringWidth(context, slider.getFont(), |
| 393 | fm, "" + slider.getMinimum())); |
| 394 | |
| 395 | int l = valueRect.width / 2; |
| 396 | int w1 = trackInsets.left + trackRect.width / 2; |
| 397 | int w2 = trackRect.width / 2 + trackInsets.right + |
| 398 | tickRect.width + labelRect.width; |
| 399 | contentDim.width = Math.max(w1, l) + Math.max(w2, l) + |
| 400 | 2 + insetCache.left + insetCache.right; |
| 401 | contentDim.height = slider.getHeight() - |
| 402 | insetCache.top - insetCache.bottom; |
| 403 | |
| 404 | // Layout the components. |
| 405 | trackRect.y = tickRect.y = labelRect.y = |
| 406 | valueRect.y + valueRect.height; |
| 407 | trackRect.height = tickRect.height = labelRect.height = |
| 408 | contentDim.height - valueRect.height; |
| 409 | |
| 410 | int startX = slider.getWidth() / 2 - contentDim.width / 2; |
| 411 | if (SynthLookAndFeel.isLeftToRight(slider)) { |
| 412 | if (l > w1) { |
| 413 | startX += (l - w1); |
| 414 | } |
| 415 | trackRect.x = startX + trackInsets.left; |
| 416 | |
| 417 | startX += trackInsets.left + trackRect.width + trackInsets.right; |
| 418 | tickRect.x = startX; |
| 419 | labelRect.x = startX + tickRect.width + 2; |
| 420 | } else { |
| 421 | if (l > w2) { |
| 422 | startX += (l - w2); |
| 423 | } |
| 424 | labelRect.x = startX; |
| 425 | |
| 426 | startX += labelRect.width + 2; |
| 427 | tickRect.x = startX; |
| 428 | trackRect.x = startX + tickRect.width + trackInsets.left; |
| 429 | } |
| 430 | } |
| 431 | context.dispose(); |
| 432 | } |
| 433 | |
| 434 | /** |
| 435 | * Calculates the pad for the label at the specified index. |
| 436 | * |
| 437 | * @param index index of the label to calculate pad for. |
| 438 | * @return padding required to keep label visible. |
| 439 | */ |
| 440 | private int getPadForLabel(int i) { |
| 441 | Dictionary dictionary = slider.getLabelTable(); |
| 442 | int pad = 0; |
| 443 | |
| 444 | Object o = dictionary.get(i); |
| 445 | if (o != null) { |
| 446 | Component c = (Component)o; |
| 447 | int centerX = xPositionForValue(i); |
| 448 | int cHalfWidth = c.getPreferredSize().width / 2; |
| 449 | if (centerX - cHalfWidth < insetCache.left) { |
| 450 | pad = Math.max(pad, insetCache.left - (centerX - cHalfWidth)); |
| 451 | } |
| 452 | |
| 453 | if (centerX + cHalfWidth > slider.getWidth() - insetCache.right) { |
| 454 | pad = Math.max(pad, (centerX + cHalfWidth) - |
| 455 | (slider.getWidth() - insetCache.right)); |
| 456 | } |
| 457 | } |
| 458 | return pad; |
| 459 | } |
| 460 | |
| 461 | protected void calculateThumbLocation() { |
| 462 | super.calculateThumbLocation(); |
| 463 | if (slider.getOrientation() == JSlider.HORIZONTAL) { |
| 464 | thumbRect.y += trackBorder; |
| 465 | } else { |
| 466 | thumbRect.x += trackBorder; |
| 467 | } |
| 468 | Point mousePosition = slider.getMousePosition(); |
| 469 | if(mousePosition != null) { |
| 470 | updateThumbState(mousePosition.x, mousePosition.y); |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | protected void calculateTickRect() { |
| 475 | if (slider.getOrientation() == JSlider.HORIZONTAL) { |
| 476 | tickRect.x = trackRect.x; |
| 477 | tickRect.y = trackRect.y + trackRect.height + 2 + getTickLength(); |
| 478 | tickRect.width = trackRect.width; |
| 479 | tickRect.height = getTickLength(); |
| 480 | |
| 481 | if (!slider.getPaintTicks()) { |
| 482 | --tickRect.y; |
| 483 | tickRect.height = 0; |
| 484 | } |
| 485 | } else { |
| 486 | if (SynthLookAndFeel.isLeftToRight(slider)) { |
| 487 | tickRect.x = trackRect.x + trackRect.width; |
| 488 | tickRect.width = getTickLength(); |
| 489 | } else { |
| 490 | tickRect.width = getTickLength(); |
| 491 | tickRect.x = trackRect.x - tickRect.width; |
| 492 | } |
| 493 | tickRect.y = trackRect.y; |
| 494 | tickRect.height = trackRect.height; |
| 495 | |
| 496 | if (!slider.getPaintTicks()) { |
| 497 | --tickRect.x; |
| 498 | tickRect.width = 0; |
| 499 | } |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | private static Rectangle unionRect = new Rectangle(); |
| 504 | |
| 505 | public void setThumbLocation(int x, int y) { |
| 506 | super.setThumbLocation(x, y); |
| 507 | // Value rect is tied to the thumb location. We need to repaint when |
| 508 | // the thumb repaints. |
| 509 | slider.repaint(valueRect.x, valueRect.y, |
| 510 | valueRect.width, valueRect.height); |
| 511 | setThumbActive(false); |
| 512 | } |
| 513 | |
| 514 | protected int xPositionForValue(int value) { |
| 515 | int min = slider.getMinimum(); |
| 516 | int max = slider.getMaximum(); |
| 517 | int trackLeft = trackRect.x + thumbRect.width / 2 + trackBorder; |
| 518 | int trackRight = trackRect.x + trackRect.width - thumbRect.width / 2 |
| 519 | - trackBorder; |
| 520 | int trackLength = trackRight - trackLeft; |
| 521 | double valueRange = (double)max - (double)min; |
| 522 | double pixelsPerValue = (double)trackLength / valueRange; |
| 523 | int xPosition; |
| 524 | |
| 525 | if (!drawInverted()) { |
| 526 | xPosition = trackLeft; |
| 527 | xPosition += Math.round( pixelsPerValue * ((double)value - min)); |
| 528 | } else { |
| 529 | xPosition = trackRight; |
| 530 | xPosition -= Math.round( pixelsPerValue * ((double)value - min)); |
| 531 | } |
| 532 | |
| 533 | xPosition = Math.max(trackLeft, xPosition); |
| 534 | xPosition = Math.min(trackRight, xPosition); |
| 535 | |
| 536 | return xPosition; |
| 537 | } |
| 538 | |
| 539 | protected int yPositionForValue(int value, int trackY, int trackHeight) { |
| 540 | int min = slider.getMinimum(); |
| 541 | int max = slider.getMaximum(); |
| 542 | int trackTop = trackY + thumbRect.height / 2 + trackBorder; |
| 543 | int trackBottom = trackY + trackHeight - thumbRect.height / 2 - |
| 544 | trackBorder; |
| 545 | int trackLength = trackBottom - trackTop; |
| 546 | double valueRange = (double)max - (double)min; |
| 547 | double pixelsPerValue = (double)trackLength / (double)valueRange; |
| 548 | int yPosition; |
| 549 | |
| 550 | if (!drawInverted()) { |
| 551 | yPosition = trackTop; |
| 552 | yPosition += Math.round(pixelsPerValue * ((double)max - value)); |
| 553 | } else { |
| 554 | yPosition = trackTop; |
| 555 | yPosition += Math.round(pixelsPerValue * ((double)value - min)); |
| 556 | } |
| 557 | |
| 558 | yPosition = Math.max(trackTop, yPosition); |
| 559 | yPosition = Math.min(trackBottom, yPosition); |
| 560 | |
| 561 | return yPosition; |
| 562 | } |
| 563 | |
| 564 | /** |
| 565 | * Returns a value give a y position. If yPos is past the track at the |
| 566 | * top or the bottom it will set the value to the min or max of the |
| 567 | * slider, depending if the slider is inverted or not. |
| 568 | */ |
| 569 | public int valueForYPosition(int yPos) { |
| 570 | int value; |
| 571 | int minValue = slider.getMinimum(); |
| 572 | int maxValue = slider.getMaximum(); |
| 573 | int trackTop = trackRect.y + thumbRect.height / 2 + trackBorder; |
| 574 | int trackBottom = trackRect.y + trackRect.height |
| 575 | - thumbRect.height / 2 - trackBorder; |
| 576 | int trackLength = trackBottom - trackTop; |
| 577 | |
| 578 | if (yPos <= trackTop) { |
| 579 | value = drawInverted() ? minValue : maxValue; |
| 580 | } else if (yPos >= trackBottom) { |
| 581 | value = drawInverted() ? maxValue : minValue; |
| 582 | } else { |
| 583 | int distanceFromTrackTop = yPos - trackTop; |
| 584 | double valueRange = (double)maxValue - (double)minValue; |
| 585 | double valuePerPixel = valueRange / (double)trackLength; |
| 586 | int valueFromTrackTop = |
| 587 | (int)Math.round(distanceFromTrackTop * valuePerPixel); |
| 588 | value = drawInverted() ? |
| 589 | minValue + valueFromTrackTop : maxValue - valueFromTrackTop; |
| 590 | } |
| 591 | return value; |
| 592 | } |
| 593 | |
| 594 | /** |
| 595 | * Returns a value give an x position. If xPos is past the track at the |
| 596 | * left or the right it will set the value to the min or max of the |
| 597 | * slider, depending if the slider is inverted or not. |
| 598 | */ |
| 599 | public int valueForXPosition(int xPos) { |
| 600 | int value; |
| 601 | int minValue = slider.getMinimum(); |
| 602 | int maxValue = slider.getMaximum(); |
| 603 | int trackLeft = trackRect.x + thumbRect.width / 2 + trackBorder; |
| 604 | int trackRight = trackRect.x + trackRect.width |
| 605 | - thumbRect.width / 2 - trackBorder; |
| 606 | int trackLength = trackRight - trackLeft; |
| 607 | |
| 608 | if (xPos <= trackLeft) { |
| 609 | value = drawInverted() ? maxValue : minValue; |
| 610 | } else if (xPos >= trackRight) { |
| 611 | value = drawInverted() ? minValue : maxValue; |
| 612 | } else { |
| 613 | int distanceFromTrackLeft = xPos - trackLeft; |
| 614 | double valueRange = (double)maxValue - (double)minValue; |
| 615 | double valuePerPixel = valueRange / (double)trackLength; |
| 616 | int valueFromTrackLeft = |
| 617 | (int)Math.round(distanceFromTrackLeft * valuePerPixel); |
| 618 | value = drawInverted() ? |
| 619 | maxValue - valueFromTrackLeft : minValue + valueFromTrackLeft; |
| 620 | } |
| 621 | return value; |
| 622 | } |
| 623 | |
| 624 | protected Dimension getThumbSize() { |
| 625 | Dimension size = new Dimension(); |
| 626 | |
| 627 | if (slider.getOrientation() == JSlider.VERTICAL) { |
| 628 | size.width = thumbHeight; |
| 629 | size.height = thumbWidth; |
| 630 | } else { |
| 631 | size.width = thumbWidth; |
| 632 | size.height = thumbHeight; |
| 633 | } |
| 634 | return size; |
| 635 | } |
| 636 | |
| 637 | protected void recalculateIfInsetsChanged() { |
| 638 | SynthContext context = getContext(slider); |
| 639 | Insets newInsets = style.getInsets(context, null); |
| 640 | Insets compInsets = slider.getInsets(); |
| 641 | newInsets.left += compInsets.left; newInsets.right += compInsets.right; |
| 642 | newInsets.top += compInsets.top; newInsets.bottom += compInsets.bottom; |
| 643 | if (!newInsets.equals(insetCache)) { |
| 644 | insetCache = newInsets; |
| 645 | calculateGeometry(); |
| 646 | } |
| 647 | context.dispose(); |
| 648 | } |
| 649 | |
| 650 | public Region getRegion(JComponent c) { |
| 651 | return SynthLookAndFeel.getRegion(c); |
| 652 | } |
| 653 | |
| 654 | public SynthContext getContext(JComponent c) { |
| 655 | return getContext(c, getComponentState(c)); |
| 656 | } |
| 657 | |
| 658 | public SynthContext getContext(JComponent c, int state) { |
| 659 | return SynthContext.getContext(SynthContext.class, c, |
| 660 | SynthLookAndFeel.getRegion(c), style, state); |
| 661 | } |
| 662 | |
| 663 | public SynthContext getContext(JComponent c, Region subregion) { |
| 664 | return getContext(c, subregion, getComponentState(c, subregion)); |
| 665 | } |
| 666 | |
| 667 | private SynthContext getContext(JComponent c, Region subregion, int state) { |
| 668 | SynthStyle style = null; |
| 669 | Class klass = SynthContext.class; |
| 670 | |
| 671 | if (subregion == Region.SLIDER_TRACK) { |
| 672 | style = sliderTrackStyle; |
| 673 | } else if (subregion == Region.SLIDER_THUMB) { |
| 674 | style = sliderThumbStyle; |
| 675 | } |
| 676 | return SynthContext.getContext(klass, c, subregion, style, state); |
| 677 | } |
| 678 | |
| 679 | public int getComponentState(JComponent c) { |
| 680 | return SynthLookAndFeel.getComponentState(c); |
| 681 | } |
| 682 | |
| 683 | private int getComponentState(JComponent c, Region region) { |
| 684 | if (region == Region.SLIDER_THUMB && thumbActive &&c.isEnabled()) { |
| 685 | return MOUSE_OVER; |
| 686 | } |
| 687 | return SynthLookAndFeel.getComponentState(c); |
| 688 | } |
| 689 | |
| 690 | public void update(Graphics g, JComponent c) { |
| 691 | SynthContext context = getContext(c); |
| 692 | SynthLookAndFeel.update(context, g); |
| 693 | context.getPainter().paintSliderBackground(context, |
| 694 | g, 0, 0, c.getWidth(), c.getHeight(), |
| 695 | slider.getOrientation()); |
| 696 | paint(context, g); |
| 697 | context.dispose(); |
| 698 | } |
| 699 | |
| 700 | public void paint(Graphics g, JComponent c) { |
| 701 | SynthContext context = getContext(c); |
| 702 | paint(context, g); |
| 703 | context.dispose(); |
| 704 | } |
| 705 | |
| 706 | public void paint(SynthContext context, Graphics g) { |
| 707 | recalculateIfInsetsChanged(); |
| 708 | recalculateIfOrientationChanged(); |
| 709 | Rectangle clip = g.getClipBounds(); |
| 710 | |
| 711 | if (paintValue) { |
| 712 | FontMetrics fm = SwingUtilities2.getFontMetrics(slider, g); |
| 713 | int labelWidth = context.getStyle().getGraphicsUtils(context). |
| 714 | computeStringWidth(context, g.getFont(), fm, |
| 715 | "" + slider.getValue()); |
| 716 | valueRect.x = thumbRect.x + (thumbRect.width - labelWidth) / 2; |
| 717 | |
| 718 | // For horizontal sliders, make sure value is not painted |
| 719 | // outside slider bounds. |
| 720 | if (slider.getOrientation() == JSlider.HORIZONTAL) { |
| 721 | if (valueRect.x + labelWidth > insetCache.left + contentDim.width) { |
| 722 | valueRect.x = (insetCache.left + contentDim.width) - labelWidth; |
| 723 | } |
| 724 | valueRect.x = Math.max(valueRect.x, 0); |
| 725 | } |
| 726 | |
| 727 | g.setColor(context.getStyle().getColor( |
| 728 | context, ColorType.TEXT_FOREGROUND)); |
| 729 | context.getStyle().getGraphicsUtils(context).paintText( |
| 730 | context, g, "" + slider.getValue(), valueRect.x, |
| 731 | valueRect.y, -1); |
| 732 | } |
| 733 | |
| 734 | SynthContext subcontext = getContext(slider, Region.SLIDER_TRACK); |
| 735 | paintTrack(subcontext, g, trackRect); |
| 736 | subcontext.dispose(); |
| 737 | |
| 738 | subcontext = getContext(slider, Region.SLIDER_THUMB); |
| 739 | paintThumb(subcontext, g, thumbRect); |
| 740 | subcontext.dispose(); |
| 741 | |
| 742 | if (slider.getPaintTicks() && clip.intersects(tickRect)) { |
| 743 | paintTicks(g); |
| 744 | } |
| 745 | |
| 746 | if (slider.getPaintLabels() && clip.intersects(labelRect)) { |
| 747 | paintLabels(g); |
| 748 | } |
| 749 | } |
| 750 | |
| 751 | public void paintBorder(SynthContext context, Graphics g, int x, |
| 752 | int y, int w, int h) { |
| 753 | context.getPainter().paintSliderBorder(context, g, x, y, w, h, |
| 754 | slider.getOrientation()); |
| 755 | } |
| 756 | |
| 757 | public void paintThumb(SynthContext context, Graphics g, |
| 758 | Rectangle thumbBounds) { |
| 759 | int orientation = slider.getOrientation(); |
| 760 | SynthLookAndFeel.updateSubregion(context, g, thumbBounds); |
| 761 | context.getPainter().paintSliderThumbBackground(context, g, |
| 762 | thumbBounds.x, thumbBounds.y, thumbBounds.width, |
| 763 | thumbBounds.height, orientation); |
| 764 | context.getPainter().paintSliderThumbBorder(context, g, |
| 765 | thumbBounds.x, thumbBounds.y, thumbBounds.width, |
| 766 | thumbBounds.height, orientation); |
| 767 | } |
| 768 | |
| 769 | public void paintTrack(SynthContext context, Graphics g, |
| 770 | Rectangle trackBounds) { |
| 771 | int orientation = slider.getOrientation(); |
| 772 | SynthLookAndFeel.updateSubregion(context, g, trackBounds); |
| 773 | context.getPainter().paintSliderTrackBackground(context, g, |
| 774 | trackBounds.x, trackBounds.y, trackBounds.width, |
| 775 | trackBounds.height, orientation); |
| 776 | context.getPainter().paintSliderTrackBorder(context, g, |
| 777 | trackBounds.x, trackBounds.y, trackBounds.width, |
| 778 | trackBounds.height, orientation); |
| 779 | } |
| 780 | |
| 781 | public void propertyChange(PropertyChangeEvent e) { |
| 782 | if (SynthLookAndFeel.shouldUpdateStyle(e)) { |
| 783 | updateStyle((JSlider)e.getSource()); |
| 784 | } |
| 785 | } |
| 786 | |
| 787 | ////////////////////////////////////////////////// |
| 788 | /// Track Listener Class |
| 789 | ////////////////////////////////////////////////// |
| 790 | /** |
| 791 | * Track mouse movements. |
| 792 | */ |
| 793 | protected class SynthTrackListener extends TrackListener { |
| 794 | |
| 795 | public void mouseExited(MouseEvent e) { |
| 796 | setThumbActive(false); |
| 797 | } |
| 798 | |
| 799 | public void mouseReleased(MouseEvent e) { |
| 800 | super.mouseReleased(e); |
| 801 | updateThumbState(e.getX(), e.getY()); |
| 802 | } |
| 803 | |
| 804 | public void mouseDragged(MouseEvent e) { |
| 805 | SynthScrollBarUI ui; |
| 806 | int thumbMiddle = 0; |
| 807 | |
| 808 | if (!slider.isEnabled()) { |
| 809 | return; |
| 810 | } |
| 811 | |
| 812 | currentMouseX = e.getX(); |
| 813 | currentMouseY = e.getY(); |
| 814 | |
| 815 | if (!isDragging()) { |
| 816 | return; |
| 817 | } |
| 818 | |
| 819 | slider.setValueIsAdjusting(true); |
| 820 | |
| 821 | switch (slider.getOrientation()) { |
| 822 | case JSlider.VERTICAL: |
| 823 | int halfThumbHeight = thumbRect.height / 2; |
| 824 | int thumbTop = e.getY() - offset; |
| 825 | int trackTop = trackRect.y; |
| 826 | int trackBottom = trackRect.y + trackRect.height |
| 827 | - halfThumbHeight - trackBorder; |
| 828 | int vMax = yPositionForValue(slider.getMaximum() - |
| 829 | slider.getExtent()); |
| 830 | |
| 831 | if (drawInverted()) { |
| 832 | trackBottom = vMax; |
| 833 | } else { |
| 834 | trackTop = vMax; |
| 835 | } |
| 836 | thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight); |
| 837 | thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight); |
| 838 | |
| 839 | setThumbLocation(thumbRect.x, thumbTop); |
| 840 | |
| 841 | thumbMiddle = thumbTop + halfThumbHeight; |
| 842 | slider.setValue(valueForYPosition(thumbMiddle)); |
| 843 | break; |
| 844 | case JSlider.HORIZONTAL: |
| 845 | int halfThumbWidth = thumbRect.width / 2; |
| 846 | int thumbLeft = e.getX() - offset; |
| 847 | int trackLeft = trackRect.x + halfThumbWidth + trackBorder; |
| 848 | int trackRight = trackRect.x + trackRect.width |
| 849 | - halfThumbWidth - trackBorder; |
| 850 | int hMax = xPositionForValue(slider.getMaximum() - |
| 851 | slider.getExtent()); |
| 852 | |
| 853 | if (drawInverted()) { |
| 854 | trackLeft = hMax; |
| 855 | } else { |
| 856 | trackRight = hMax; |
| 857 | } |
| 858 | thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth); |
| 859 | thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth); |
| 860 | |
| 861 | setThumbLocation(thumbLeft, thumbRect.y); |
| 862 | |
| 863 | thumbMiddle = thumbLeft + halfThumbWidth; |
| 864 | slider.setValue(valueForXPosition(thumbMiddle)); |
| 865 | break; |
| 866 | default: |
| 867 | return; |
| 868 | } |
| 869 | |
| 870 | if (slider.getValueIsAdjusting()) { |
| 871 | setThumbActive(true); |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | public void mouseMoved(MouseEvent e) { |
| 876 | updateThumbState(e.getX(), e.getY()); |
| 877 | } |
| 878 | } |
| 879 | } |