0001: /*
0002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030: package org.jvnet.substance;
0031:
0032: import java.awt.*;
0033: import java.awt.event.MouseEvent;
0034: import java.awt.geom.GeneralPath;
0035: import java.awt.image.BufferedImage;
0036: import java.beans.PropertyChangeEvent;
0037: import java.beans.PropertyChangeListener;
0038: import java.util.Dictionary;
0039: import java.util.Enumeration;
0040:
0041: import javax.swing.*;
0042: import javax.swing.plaf.*;
0043: import javax.swing.plaf.basic.BasicHTML;
0044: import javax.swing.plaf.basic.BasicSliderUI;
0045: import javax.swing.text.View;
0046:
0047: import org.jvnet.lafwidget.animation.*;
0048: import org.jvnet.lafwidget.layout.TransitionLayout;
0049: import org.jvnet.substance.border.SubstanceBorderPainter;
0050: import org.jvnet.substance.button.BaseButtonShaper;
0051: import org.jvnet.substance.color.ColorScheme;
0052: import org.jvnet.substance.painter.ClassicGradientPainter;
0053: import org.jvnet.substance.painter.SubstanceGradientPainter;
0054: import org.jvnet.substance.painter.text.SubstanceTextPainter;
0055: import org.jvnet.substance.theme.SubstanceTheme;
0056: import org.jvnet.substance.utils.*;
0057: import org.jvnet.substance.utils.icon.SubstanceIconFactory;
0058:
0059: /**
0060: * UI for sliders in <b>Substance</b> look and feel.
0061: *
0062: * @author Kirill Grouchnikov
0063: */
0064: public class SubstanceSliderUI extends BasicSliderUI implements
0065: Trackable {
0066: /**
0067: * Background delegate.
0068: */
0069: private static SubstanceFillBackgroundDelegate bgDelegate = new SubstanceFillBackgroundDelegate();
0070:
0071: /**
0072: * Surrogate button model for tracking the thumb transitions.
0073: */
0074: private ButtonModel thumbModel;
0075:
0076: /**
0077: * Listener for fade animations.
0078: */
0079: private RolloverControlListener substanceRolloverListener;
0080:
0081: /**
0082: * Listener on property change events.
0083: */
0084: private PropertyChangeListener substancePropertyChangeListener;
0085:
0086: /**
0087: * Listener for fade animations.
0088: */
0089: protected FadeStateListener substanceFadeStateListener;
0090:
0091: /**
0092: * Icon for horizontal sliders.
0093: */
0094: protected Icon horizontalIcon;
0095:
0096: /**
0097: * Icon for sliders without labels and ticks.
0098: */
0099: protected Icon roundIcon;
0100:
0101: /**
0102: * Icon for vertical sliders.
0103: */
0104: protected Icon verticalIcon;
0105:
0106: /*
0107: * (non-Javadoc)
0108: *
0109: * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
0110: */
0111: public static ComponentUI createUI(JComponent c) {
0112: return new SubstanceSliderUI((JSlider) c);
0113: }
0114:
0115: /**
0116: * Simple constructor.
0117: *
0118: * @param slider
0119: * Slider.
0120: */
0121: public SubstanceSliderUI(JSlider slider) {
0122: super (null);
0123: this .thumbModel = new DefaultButtonModel();
0124: this .thumbModel.setArmed(false);
0125: this .thumbModel.setSelected(false);
0126: this .thumbModel.setPressed(false);
0127: this .thumbModel.setRollover(false);
0128: this .thumbModel.setEnabled(slider.isEnabled());
0129: }
0130:
0131: /*
0132: * (non-Javadoc)
0133: *
0134: * @see javax.swing.plaf.basic.BasicSliderUI#calculateTrackRect()
0135: */
0136: @Override
0137: protected void calculateTrackRect() {
0138: super .calculateTrackRect();
0139: if (this .slider.getOrientation() == SwingConstants.HORIZONTAL) {
0140: this .trackRect.y = 3
0141: + (int) Math
0142: .ceil(SubstanceSizeUtils
0143: .getFocusStrokeWidth(SubstanceSizeUtils
0144: .getComponentFontSize(this .slider)))
0145: + this .insetCache.top;
0146: }
0147: }
0148:
0149: /**
0150: * Returns the rectangle of track for painting.
0151: *
0152: * @return The rectangle of track for painting.
0153: */
0154: private Rectangle getPaintTrackRect() {
0155: int trackLeft = 0, trackRight = 0, trackTop = 0, trackBottom = 0;
0156: if (this .slider.getOrientation() == SwingConstants.HORIZONTAL) {
0157: trackTop = 3 + this .insetCache.top + 2
0158: * this .focusInsets.top;
0159: trackBottom = trackTop + this .getTrackWidth() - 1;
0160: trackRight = this .trackRect.width;
0161: return new Rectangle(this .trackRect.x + trackLeft,
0162: trackTop, trackRight - trackLeft, trackBottom
0163: - trackTop);
0164: } else {
0165: if (this .slider.getComponentOrientation().isLeftToRight()) {
0166: trackLeft = 6 + this .insetCache.left
0167: + this .focusInsets.left;
0168: trackRight = trackLeft + this .getTrackWidth() - 1;
0169: } else {
0170: trackRight = this .slider.getWidth() - 8
0171: - this .insetCache.right;
0172: trackLeft = trackRight - this .getTrackWidth() + 1;
0173: }
0174: trackBottom = this .trackRect.height - 1;
0175: return new Rectangle(trackLeft,
0176: this .trackRect.y + trackTop,
0177: trackRight - trackLeft, trackBottom - trackTop);
0178: }
0179: }
0180:
0181: /*
0182: * (non-Javadoc)
0183: *
0184: * @see javax.swing.plaf.basic.BasicSliderUI#paintTrack(java.awt.Graphics)
0185: */
0186: @Override
0187: public void paintTrack(Graphics g) {
0188: Graphics2D graphics = (Graphics2D) g.create();
0189: graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0190: RenderingHints.VALUE_ANTIALIAS_ON);
0191:
0192: boolean drawInverted = this .drawInverted();
0193:
0194: // Translate to the origin of the painting rectangle
0195: Rectangle paintRect = this .getPaintTrackRect();
0196: // System.out.println(paintRect);
0197: graphics.translate(paintRect.x, paintRect.y);
0198:
0199: // Width and height of the painting rectangle.
0200: int width = paintRect.width;
0201: int height = paintRect.height;
0202:
0203: ComponentState currState = ComponentState.getState(
0204: this .thumbModel, this .slider);
0205: ComponentState prevState = SubstanceCoreUtilities
0206: .getPrevComponentState(this .slider);
0207:
0208: SubstanceTheme trackTheme = SubstanceThemeUtilities
0209: .getTheme(this .slider);
0210: if (this .slider.isEnabled())
0211: trackTheme = trackTheme.getDefaultTheme();
0212: else
0213: trackTheme = trackTheme.getDisabledTheme();
0214:
0215: this .paintSliderTrack(graphics, drawInverted, trackTheme,
0216: width, height,
0217: (this .slider.getOrientation() == JSlider.VERTICAL));
0218:
0219: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0220: this .slider, currState);
0221: FadeState fadeState = SubstanceFadeUtilities.getFadeState(
0222: this .slider, FadeKind.ROLLOVER, FadeKind.SELECTION,
0223: FadeKind.PRESS);
0224: if (fadeState != null) {
0225: SubstanceTheme prevTheme = SubstanceThemeUtilities
0226: .getTheme(this .slider, prevState);
0227: float cyclePos = fadeState.getFadePosition();
0228: if (!fadeState.isFadingIn())
0229: cyclePos = 10.0f - cyclePos;
0230:
0231: if (prevState != ComponentState.DEFAULT) {
0232: graphics.setComposite(TransitionLayout
0233: .getAlphaComposite(this .slider,
0234: 1.0f - cyclePos / 10.0f, g));
0235: this .paintSliderTrackSelected(graphics, drawInverted,
0236: paintRect, prevTheme, width, height);
0237: }
0238: if (currState != ComponentState.DEFAULT) {
0239: graphics.setComposite(TransitionLayout
0240: .getAlphaComposite(this .slider,
0241: cyclePos / 10.0f, g));
0242: this .paintSliderTrackSelected(graphics, drawInverted,
0243: paintRect, theme, width, height);
0244: }
0245: } else {
0246: boolean hasFill = currState.isKindActive(FadeKind.ROLLOVER)
0247: || currState.isKindActive(FadeKind.PRESS)
0248: || SubstanceCoreUtilities
0249: .isControlAlwaysPaintedActive(this .slider,
0250: true);
0251: if (hasFill) {
0252: this .paintSliderTrackSelected(graphics, drawInverted,
0253: paintRect, theme, width, height);
0254: }
0255: }
0256:
0257: graphics.dispose();
0258: }
0259:
0260: /**
0261: * Paints the slider track.
0262: *
0263: * @param graphics
0264: * Graphics.
0265: * @param drawInverted
0266: * Indicates whether the value-range shown for the slider is
0267: * reversed.
0268: * @param theme
0269: * Theme.
0270: * @param width
0271: * Track width.
0272: * @param height
0273: * Track height.
0274: * @param toRotate
0275: * Indicates whether the slider track image should be rotated (<code>true</code>
0276: * if the slider is vertical).
0277: */
0278: private void paintSliderTrack(Graphics2D graphics,
0279: boolean drawInverted, SubstanceTheme theme, int width,
0280: int height, boolean toRotate) {
0281: // ColorScheme colorScheme = theme.getColorScheme();
0282:
0283: SubstanceGradientPainter gradientPainter = new ClassicGradientPainter();
0284: ColorScheme fillColorScheme = theme.getColorScheme();
0285: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
0286: .getBorderPainter(this .slider);
0287:
0288: // some strange bug in clipping a rotated Graphics removes the
0289: // last pixel row during the border painting. So, for vertical sliders
0290: // we create a temporary image, paint the slider there and then rotate
0291: // the image
0292: if (toRotate) {
0293: BufferedImage image = SubstanceCoreUtilities.getBlankImage(
0294: height + 1, width + 1);
0295: this .paintSliderTrack((Graphics2D) image.getGraphics(),
0296: drawInverted, theme, height, width, false);
0297: BufferedImage track = SubstanceImageCreator.getRotated(
0298: image, 3);
0299: graphics.drawImage(track, 0, 0, null);
0300: return;
0301: }
0302:
0303: int borderDelta = (int) Math.floor(SubstanceSizeUtils
0304: .getBorderStrokeWidth(SubstanceSizeUtils
0305: .getComponentFontSize(slider)) / 2.0);
0306: float radius = SubstanceSizeUtils
0307: .getClassicButtonCornerRadius(SubstanceSizeUtils
0308: .getComponentFontSize(slider)) / 2.0f;
0309: Shape contour = BaseButtonShaper.getBaseOutline(width + 1,
0310: height + 1, radius, null, borderDelta);
0311:
0312: BufferedImage fillTrackImage = gradientPainter
0313: .getContourBackground(width, height, contour, false,
0314: fillColorScheme, fillColorScheme, 0, false,
0315: false);
0316: graphics.drawImage(fillTrackImage, 0, 0, null);
0317:
0318: ColorScheme borderScheme = theme.getBorderTheme()
0319: .getColorScheme();
0320: int borderThickness = (int) SubstanceSizeUtils
0321: .getBorderStrokeWidth(SubstanceSizeUtils
0322: .getComponentFontSize(this .slider));
0323: GeneralPath contourInner = BaseButtonShaper.getBaseOutline(
0324: width + 1, height + 1, radius, null, borderThickness
0325: + borderDelta);
0326: borderPainter.paintBorder(graphics, slider, width + 1,
0327: height + 1, contour, contourInner, borderScheme,
0328: borderScheme, 0, false);
0329: }
0330:
0331: /**
0332: * Paints the selected part of the slider track.
0333: *
0334: * @param graphics
0335: * Graphics.
0336: * @param drawInverted
0337: * Indicates whether the value-range shown for the slider is
0338: * reversed.
0339: * @param paintRect
0340: * Selected portion.
0341: * @param theme
0342: * Theme.
0343: * @param width
0344: * Track width.
0345: * @param height
0346: * Track height.
0347: */
0348: private void paintSliderTrackSelected(Graphics2D graphics,
0349: boolean drawInverted, Rectangle paintRect,
0350: SubstanceTheme theme, int width, int height) {
0351: Insets insets = this .slider.getInsets();
0352: insets.top /= 2;
0353: insets.left /= 2;
0354: insets.bottom /= 2;
0355: insets.right /= 2;
0356:
0357: SubstanceGradientPainter gp = SubstanceCoreUtilities
0358: .getGradientPainter(this .slider);
0359: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
0360: .getBorderPainter(this .slider);
0361: float radius = SubstanceSizeUtils
0362: .getClassicButtonCornerRadius(SubstanceSizeUtils
0363: .getComponentFontSize(slider)) / 2.0f;
0364: int borderDelta = (int) Math.floor(SubstanceSizeUtils
0365: .getBorderStrokeWidth(SubstanceSizeUtils
0366: .getComponentFontSize(slider)) / 2.0);
0367:
0368: // fill selected portion
0369: if (this .slider.isEnabled()) {
0370: if (this .slider.getOrientation() == SwingConstants.HORIZONTAL) {
0371: int middleOfThumb = this .thumbRect.x
0372: + (this .thumbRect.width / 2) - paintRect.x;
0373: int fillMinX;
0374: int fillMaxX;
0375:
0376: // graphics.setPaint(new GradientPaint(0, 1, fillColor1, 0,
0377: // height - 1, fillColor2));
0378:
0379: if (drawInverted) {
0380: fillMinX = middleOfThumb;
0381: fillMaxX = width;
0382: } else {
0383: fillMinX = 0;
0384: fillMaxX = middleOfThumb;
0385: }
0386:
0387: int fillWidth = fillMaxX - fillMinX;
0388: int fillHeight = height + 1;
0389: if ((fillWidth > 0) && (fillHeight > 0)) {
0390: Shape contour = BaseButtonShaper.getBaseOutline(
0391: fillWidth, fillHeight, radius, null,
0392: borderDelta);
0393: BufferedImage im = gp.getContourBackground(
0394: fillWidth, fillHeight, contour, false,
0395: theme.getColorScheme(), theme
0396: .getColorScheme(), 0.0f, false,
0397: false);
0398: borderPainter.paintBorder(im.getGraphics(),
0399: this .slider, fillWidth, fillHeight,
0400: contour, null, theme.getColorScheme(),
0401: theme.getColorScheme(), 0.0f, false);
0402: graphics.drawImage(im, fillMinX, 0, null);
0403: }
0404: } else {
0405: int middleOfThumb = this .thumbRect.y
0406: + (this .thumbRect.height / 2) - paintRect.y;
0407: int fillMinY;
0408: int fillMaxY;
0409:
0410: if (this .drawInverted()) {
0411: fillMinY = 0;
0412: fillMaxY = middleOfThumb;
0413: } else {
0414: fillMinY = middleOfThumb;
0415: fillMaxY = height + 1;
0416: }
0417:
0418: int fillWidth = fillMaxY - fillMinY;
0419: int fillHeight = width + 1;
0420: if ((fillWidth > 0) && (fillHeight > 0)) {
0421: Shape contour = BaseButtonShaper.getBaseOutline(
0422: fillWidth, fillHeight, radius, null,
0423: borderDelta);
0424: BufferedImage im = gp.getContourBackground(
0425: fillWidth, fillHeight, contour, false,
0426: theme.getColorScheme(), theme
0427: .getColorScheme(), 0.0f, false,
0428: false);
0429: borderPainter.paintBorder(im.getGraphics(),
0430: this .slider, fillWidth, fillHeight,
0431: contour, null, theme.getColorScheme(),
0432: theme.getColorScheme(), 0.0f, false);
0433: im = SubstanceImageCreator.getRotated(im, 1);
0434: graphics.drawImage(im, 0, fillMinY, null);
0435: }
0436: }
0437: }
0438: }
0439:
0440: /*
0441: * (non-Javadoc)
0442: *
0443: * @see javax.swing.plaf.basic.BasicSliderUI#getThumbSize()
0444: */
0445: @Override
0446: protected Dimension getThumbSize() {
0447: Icon thumbIcon = this .getIcon();
0448: return new Dimension(thumbIcon.getIconWidth(), thumbIcon
0449: .getIconHeight());
0450: }
0451:
0452: /**
0453: * Returns the thumb icon for the associated slider.
0454: *
0455: * @return The thumb icon for the associated slider.
0456: */
0457: protected Icon getIcon() {
0458: if (this .slider.getOrientation() == JSlider.HORIZONTAL) {
0459: if (this .slider.getPaintTicks()
0460: || this .slider.getPaintLabels())
0461: return this .horizontalIcon;
0462: else
0463: return this .roundIcon;
0464: } else {
0465: if (this .slider.getPaintTicks()
0466: || this .slider.getPaintLabels())
0467: return this .verticalIcon;
0468: else
0469: return this .roundIcon;
0470: }
0471: }
0472:
0473: /*
0474: * (non-Javadoc)
0475: *
0476: * @see javax.swing.plaf.basic.BasicSliderUI#paintThumb(java.awt.Graphics)
0477: */
0478: @Override
0479: public void paintThumb(Graphics g) {
0480: Graphics2D graphics = (Graphics2D) g.create();
0481: // graphics.setComposite(TransitionLayout.getAlphaComposite(slider));
0482: Rectangle knobBounds = this .thumbRect;
0483: // System.out.println(thumbRect);
0484:
0485: graphics.translate(knobBounds.x, knobBounds.y);
0486:
0487: Icon icon = this .getIcon();
0488: if (this .slider.getOrientation() == JSlider.HORIZONTAL) {
0489: if (icon != null)
0490: icon.paintIcon(this .slider, graphics, -1, 0);
0491: } else {
0492: if (this .slider.getComponentOrientation().isLeftToRight()) {
0493: if (icon != null)
0494: icon.paintIcon(this .slider, graphics, 0, -1);
0495: } else {
0496: if (icon != null)
0497: icon.paintIcon(this .slider, graphics, 0, 1);
0498: }
0499: }
0500:
0501: // graphics.translate(-knobBounds.x, -knobBounds.y);
0502: graphics.dispose();
0503: }
0504:
0505: /*
0506: * (non-Javadoc)
0507: *
0508: * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics,
0509: * javax.swing.JComponent)
0510: */
0511: @Override
0512: public synchronized void paint(Graphics g, final JComponent c) {
0513: Graphics2D graphics = (Graphics2D) g.create();
0514:
0515: ComponentState currState = ComponentState.getState(
0516: this .thumbModel, this .slider);
0517: float alpha = SubstanceThemeUtilities.getTheme(this .slider)
0518: .getThemeAlpha(this .slider, currState);
0519:
0520: SubstanceTextPainter textPainter = SubstanceLookAndFeel
0521: .getCurrentTextPainter();
0522: textPainter.init(c, null, true);
0523: if (textPainter.needsBackgroundImage()) {
0524: textPainter.setBackgroundFill(c, c.getBackground(), true,
0525: 0, 0);
0526: textPainter
0527: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
0528: public void paintBackground(Graphics g) {
0529: // need for the non-showing labels
0530: bgDelegate.updateIfOpaque(g, c);
0531: }
0532: });
0533: } else {
0534: bgDelegate.updateIfOpaque(graphics, c);
0535: // graphics.setColor(c.getBackground());
0536: // graphics.fillRect(0, 0, c.getWidth(), c.getHeight());
0537: // System.out.println(c.isEnabled() + ":" + c.getBackground());
0538: }
0539: // // remove opacity
0540: // boolean isOpaque = c.isOpaque();
0541: // c.setOpaque(false);
0542: // // super.paint(graphics, c);
0543:
0544: recalculateIfInsetsChanged();
0545: recalculateIfOrientationChanged();
0546: final Rectangle clip = graphics.getClipBounds();
0547:
0548: if (!clip.intersects(trackRect) && slider.getPaintTrack())
0549: calculateGeometry();
0550:
0551: graphics.setComposite(TransitionLayout.getAlphaComposite(
0552: this .slider, alpha, g));
0553: SubstanceTextPainter.BackgroundPaintingCallback callback = new SubstanceTextPainter.BackgroundPaintingCallback() {
0554: public void paintBackground(Graphics g) {
0555: if (slider.getPaintTrack()
0556: && clip.intersects(trackRect)) {
0557: paintTrack(g);
0558: }
0559: if (slider.getPaintTicks() && clip.intersects(tickRect)) {
0560: paintTicks(g);
0561: }
0562: if (slider.hasFocus() && clip.intersects(focusRect)) {
0563: paintFocus(g);
0564: }
0565: if (clip.intersects(thumbRect)) {
0566: paintThumb(g);
0567: }
0568: }
0569: };
0570: if (textPainter.needsBackgroundImage()) {
0571: textPainter.attachCallback(callback);
0572: } else {
0573: callback.paintBackground(graphics);
0574: }
0575: if (slider.getPaintLabels() && clip.intersects(labelRect)) {
0576: paintLabels(graphics);
0577: }
0578:
0579: textPainter.renderSurface(graphics);
0580:
0581: // // restore opacity
0582: // c.setOpaque(isOpaque);
0583:
0584: if (!this .slider.hasFocus()) {
0585: if (FadeTracker.getInstance().isTracked(c, FadeKind.FOCUS))
0586: this .paintFocus(graphics);
0587: }
0588:
0589: graphics.dispose();
0590: }
0591:
0592: /**
0593: * Returns the button model for tracking the thumb transitions.
0594: *
0595: * @return Button model for tracking the thumb transitions.
0596: */
0597: public ButtonModel getButtonModel() {
0598: return this .thumbModel;
0599: }
0600:
0601: /*
0602: * (non-Javadoc)
0603: *
0604: * @see org.jvnet.substance.Trackable#isInside(java.awt.event.MouseEvent)
0605: */
0606: public boolean isInside(MouseEvent me) {
0607: Rectangle thumbB = this .thumbRect;
0608: if (thumbB == null)
0609: return false;
0610: return thumbB.contains(me.getX(), me.getY());
0611: }
0612:
0613: /*
0614: * (non-Javadoc)
0615: *
0616: * @see javax.swing.plaf.basic.BasicSliderUI#installDefaults(javax.swing.JSlider)
0617: */
0618: @Override
0619: protected void installDefaults(JSlider slider) {
0620: super .installDefaults(slider);
0621: Font f = slider.getFont();
0622: if (f == null || f instanceof UIResource) {
0623: slider.setFont(new FontUIResource(SubstanceLookAndFeel
0624: .getFontPolicy().getFontSet("Substance", null)
0625: .getControlFont()));
0626: }
0627: int size = SubstanceSizeUtils
0628: .getSliderIconSize(SubstanceSizeUtils
0629: .getComponentFontSize(slider));
0630: // System.out.println("Slider size : " + size);
0631: this .horizontalIcon = SubstanceIconFactory
0632: .getSliderHorizontalIcon(size, false);
0633: this .roundIcon = SubstanceIconFactory.getSliderRoundIcon(size);
0634: this .verticalIcon = SubstanceIconFactory.getSliderVerticalIcon(
0635: size, false);
0636:
0637: int focusIns = (int) Math.ceil(2.0 * SubstanceSizeUtils
0638: .getFocusStrokeWidth(SubstanceSizeUtils
0639: .getComponentFontSize(slider)));
0640: this .focusInsets = new Insets(focusIns, focusIns, focusIns,
0641: focusIns);
0642: }
0643:
0644: /*
0645: * (non-Javadoc)
0646: *
0647: * @see javax.swing.plaf.basic.BasicSliderUI#installListeners(javax.swing.JSlider)
0648: */
0649: @Override
0650: protected void installListeners(final JSlider slider) {
0651: super .installListeners(slider);
0652:
0653: // fix for defect 109 - memory leak on changing theme
0654: this .substanceRolloverListener = new RolloverControlListener(
0655: this , this .thumbModel);
0656: slider.addMouseListener(this .substanceRolloverListener);
0657: slider.addMouseMotionListener(this .substanceRolloverListener);
0658:
0659: this .substancePropertyChangeListener = new PropertyChangeListener() {
0660: public void propertyChange(PropertyChangeEvent evt) {
0661: if ("enabled".equals(evt.getPropertyName())) {
0662: SubstanceSliderUI.this .thumbModel.setEnabled(slider
0663: .isEnabled());
0664: }
0665: if ("font".equals(evt.getPropertyName())) {
0666: SwingUtilities.invokeLater(new Runnable() {
0667: public void run() {
0668: slider.updateUI();
0669: }
0670: });
0671: }
0672: }
0673: };
0674: this .slider
0675: .addPropertyChangeListener(this .substancePropertyChangeListener);
0676:
0677: this .substanceFadeStateListener = new FadeStateListener(
0678: this .slider, this .thumbModel, SubstanceCoreUtilities
0679: .getFadeCallback(this .slider, this .thumbModel,
0680: false, false, this .slider));
0681: this .substanceFadeStateListener.registerListeners(false);
0682: }
0683:
0684: /*
0685: * (non-Javadoc)
0686: *
0687: * @see javax.swing.plaf.basic.BasicSliderUI#uninstallListeners(javax.swing.JSlider)
0688: */
0689: @Override
0690: protected void uninstallListeners(JSlider slider) {
0691: super .uninstallListeners(slider);
0692:
0693: // fix for defect 109 - memory leak on changing theme
0694: slider.removeMouseListener(this .substanceRolloverListener);
0695: slider
0696: .removeMouseMotionListener(this .substanceRolloverListener);
0697: this .substanceRolloverListener = null;
0698:
0699: slider
0700: .removePropertyChangeListener(this .substancePropertyChangeListener);
0701: this .substancePropertyChangeListener = null;
0702:
0703: this .substanceFadeStateListener.unregisterListeners();
0704: this .substanceFadeStateListener = null;
0705: }
0706:
0707: /*
0708: * (non-Javadoc)
0709: *
0710: * @see javax.swing.plaf.basic.BasicSliderUI#paintFocus(java.awt.Graphics)
0711: */
0712: @Override
0713: public void paintFocus(Graphics g) {
0714: SubstanceCoreUtilities
0715: .paintFocus(
0716: g,
0717: this .slider,
0718: this .slider,
0719: null,
0720: null,
0721: 1.0f,
0722: (int) Math
0723: .ceil(SubstanceSizeUtils
0724: .getFocusStrokeWidth(SubstanceSizeUtils
0725: .getComponentFontSize(this .slider))) / 2);
0726: }
0727:
0728: /**
0729: * Returns the amount that the thumb goes past the slide bar.
0730: *
0731: * @return Amount that the thumb goes past the slide bar.
0732: */
0733: protected int getThumbOverhang() {
0734: return (int) (this .getThumbSize().getHeight() - this
0735: .getTrackWidth()) / 2;
0736: }
0737:
0738: /**
0739: * Returns the shorter dimension of the track.
0740: *
0741: * @return Shorter dimension of the track.
0742: */
0743: protected int getTrackWidth() {
0744: return SubstanceSizeUtils.getSliderTrackSize(SubstanceSizeUtils
0745: .getComponentFontSize(this .slider));
0746: }
0747:
0748: /*
0749: * (non-Javadoc)
0750: *
0751: * @see javax.swing.plaf.basic.BasicSliderUI#getTickLength()
0752: */
0753: @Override
0754: protected int getTickLength() {
0755: return SubstanceSizeUtils.getSliderTickSize(SubstanceSizeUtils
0756: .getComponentFontSize(this .slider));
0757: }
0758:
0759: /*
0760: * (non-Javadoc)
0761: *
0762: * @see javax.swing.plaf.basic.BasicSliderUI#paintTicks(java.awt.Graphics)
0763: */
0764: @Override
0765: public void paintTicks(Graphics g) {
0766: Rectangle tickBounds = this .tickRect;
0767: if (this .slider.getOrientation() == JSlider.HORIZONTAL) {
0768: g.translate(0, tickBounds.y);
0769:
0770: int value = this .slider.getMinimum()
0771: + this .slider.getMinorTickSpacing();
0772: int xPos = 0;
0773:
0774: if ((this .slider.getMinorTickSpacing() > 0)
0775: && (this .slider.getMajorTickSpacing() > 0)) {
0776: while (value < this .slider.getMaximum()) {
0777: int delta = value - this .slider.getMinimum();
0778: if (delta % this .slider.getMajorTickSpacing() != 0) {
0779: xPos = this .xPositionForValue(value);
0780: this .paintMinorTickForHorizSlider(g,
0781: tickBounds, xPos);
0782: }
0783: value += this .slider.getMinorTickSpacing();
0784: }
0785: }
0786:
0787: if (this .slider.getMajorTickSpacing() > 0) {
0788: value = this .slider.getMinimum()
0789: + this .slider.getMajorTickSpacing();
0790:
0791: while (value < this .slider.getMaximum()) {
0792: xPos = this .xPositionForValue(value);
0793: this .paintMajorTickForHorizSlider(g, tickBounds,
0794: xPos);
0795: value += this .slider.getMajorTickSpacing();
0796: }
0797: }
0798:
0799: g.translate(0, -tickBounds.y);
0800: } else {
0801: g.translate(tickBounds.x, 0);
0802:
0803: int value = this .slider.getMinimum()
0804: + this .slider.getMinorTickSpacing();
0805: int yPos = 0;
0806:
0807: boolean ltr = this .slider.getComponentOrientation()
0808: .isLeftToRight();
0809: if (this .slider.getMinorTickSpacing() > 0) {
0810: int offset = 0;
0811: if (!ltr) {
0812: offset = tickBounds.width - tickBounds.width / 2;
0813: g.translate(offset, 0);
0814: }
0815:
0816: while (value < this .slider.getMaximum()) {
0817: yPos = this .yPositionForValue(value);
0818: this .paintMinorTickForVertSlider(g, tickBounds,
0819: yPos);
0820: value += this .slider.getMinorTickSpacing();
0821: }
0822:
0823: if (!ltr) {
0824: g.translate(-offset, 0);
0825: }
0826: }
0827:
0828: if (this .slider.getMajorTickSpacing() > 0) {
0829: value = this .slider.getMinimum()
0830: + this .slider.getMajorTickSpacing();
0831: if (!ltr) {
0832: g.translate(2, 0);
0833: }
0834:
0835: while (value < this .slider.getMaximum()) {
0836: yPos = this .yPositionForValue(value);
0837: this .paintMajorTickForVertSlider(g, tickBounds,
0838: yPos);
0839: value += this .slider.getMajorTickSpacing();
0840: }
0841:
0842: if (!ltr) {
0843: g.translate(-2, 0);
0844: }
0845: }
0846: g.translate(-tickBounds.x, 0);
0847: }
0848: }
0849:
0850: /*
0851: * (non-Javadoc)
0852: *
0853: * @see javax.swing.plaf.basic.BasicSliderUI#paintMajorTickForHorizSlider(java.awt.Graphics,
0854: * java.awt.Rectangle, int)
0855: */
0856: @Override
0857: protected void paintMajorTickForHorizSlider(Graphics g,
0858: Rectangle tickBounds, int x) {
0859: boolean isDark = SubstanceCoreUtilities
0860: .isThemeDark(SubstanceLookAndFeel.getTheme());
0861:
0862: ColorScheme colorScheme = SubstanceThemeUtilities.getTheme(
0863: this .slider,
0864: this .slider.isEnabled() ? ComponentState.DEFAULT
0865: : ComponentState.DISABLED_UNSELECTED)
0866: .getColorScheme();
0867:
0868: Graphics2D graphics = (Graphics2D) g.create();
0869: graphics.translate(x - 1, 0);
0870: SubstanceCoreUtilities.paintSeparator(this .slider, graphics,
0871: colorScheme, isDark, 0, tickBounds.height,
0872: JSeparator.VERTICAL, true, 0, 4);
0873: graphics.dispose();
0874: }
0875:
0876: /*
0877: * (non-Javadoc)
0878: *
0879: * @see javax.swing.plaf.basic.BasicSliderUI#paintMajorTickForVertSlider(java.awt.Graphics,
0880: * java.awt.Rectangle, int)
0881: */
0882: @Override
0883: protected void paintMajorTickForVertSlider(Graphics g,
0884: Rectangle tickBounds, int y) {
0885: boolean isDark = SubstanceCoreUtilities
0886: .isThemeDark(SubstanceLookAndFeel.getTheme());
0887: ColorScheme colorScheme = SubstanceThemeUtilities.getTheme(
0888: this .slider,
0889: this .slider.isEnabled() ? ComponentState.DEFAULT
0890: : ComponentState.DISABLED_UNSELECTED)
0891: .getColorScheme();
0892:
0893: Graphics2D graphics = (Graphics2D) g.create();
0894: graphics.translate(0, y);
0895: SubstanceCoreUtilities.paintSeparator(this .slider, graphics,
0896: colorScheme, isDark, tickBounds.width - 1, 0,
0897: JSeparator.HORIZONTAL, true, this .slider
0898: .getComponentOrientation().isLeftToRight() ? 0
0899: : 4, this .slider.getComponentOrientation()
0900: .isLeftToRight() ? 4 : 0);
0901: graphics.dispose();
0902: }
0903:
0904: /*
0905: * (non-Javadoc)
0906: *
0907: * @see javax.swing.plaf.basic.BasicSliderUI#paintMinorTickForHorizSlider(java.awt.Graphics,
0908: * java.awt.Rectangle, int)
0909: */
0910: @Override
0911: protected void paintMinorTickForHorizSlider(Graphics g,
0912: Rectangle tickBounds, int x) {
0913: boolean isDark = SubstanceCoreUtilities
0914: .isThemeDark(SubstanceLookAndFeel.getTheme());
0915: ColorScheme colorScheme = SubstanceThemeUtilities.getTheme(
0916: this .slider,
0917: this .slider.isEnabled() ? ComponentState.DEFAULT
0918: : ComponentState.DISABLED_UNSELECTED)
0919: .getColorScheme();
0920:
0921: Graphics2D graphics = (Graphics2D) g.create();
0922: graphics.translate(x - 1, 0);
0923: SubstanceCoreUtilities.paintSeparator(this .slider, graphics,
0924: colorScheme, isDark, 0, tickBounds.height / 2,
0925: JSeparator.VERTICAL, true, 0, 4);
0926: graphics.dispose();
0927: }
0928:
0929: /*
0930: * (non-Javadoc)
0931: *
0932: * @see javax.swing.plaf.basic.BasicSliderUI#paintMinorTickForVertSlider(java.awt.Graphics,
0933: * java.awt.Rectangle, int)
0934: */
0935: @Override
0936: protected void paintMinorTickForVertSlider(Graphics g,
0937: Rectangle tickBounds, int y) {
0938: boolean isDark = SubstanceCoreUtilities
0939: .isThemeDark(SubstanceLookAndFeel.getTheme());
0940: ColorScheme colorScheme = SubstanceThemeUtilities.getTheme(
0941: this .slider,
0942: this .slider.isEnabled() ? ComponentState.DEFAULT
0943: : ComponentState.DISABLED_UNSELECTED)
0944: .getColorScheme();
0945:
0946: Graphics2D graphics = (Graphics2D) g.create();
0947: graphics.translate(0, y);
0948: SubstanceCoreUtilities.paintSeparator(this .slider, graphics,
0949: colorScheme, isDark, tickBounds.width / 2, 0,
0950: JSeparator.HORIZONTAL, true, this .slider
0951: .getComponentOrientation().isLeftToRight() ? 0
0952: : 4, this .slider.getComponentOrientation()
0953: .isLeftToRight() ? 4 : 0);
0954: graphics.dispose();
0955: }
0956:
0957: /*
0958: * (non-Javadoc)
0959: *
0960: * @see javax.swing.plaf.basic.BasicSliderUI#calculateTickRect()
0961: */
0962: @Override
0963: protected void calculateTickRect() {
0964: if (this .slider.getOrientation() == JSlider.HORIZONTAL) {
0965: this .tickRect.x = this .trackRect.x;
0966: this .tickRect.y = this .trackRect.y + this .trackRect.height;
0967: this .tickRect.width = this .trackRect.width;
0968: this .tickRect.height = (this .slider.getPaintTicks()) ? this
0969: .getTickLength() : 0;
0970: } else {
0971: this .tickRect.width = (this .slider.getPaintTicks()) ? this
0972: .getTickLength() : 0;
0973: if (this .slider.getComponentOrientation().isLeftToRight()) {
0974: this .tickRect.x = this .trackRect.x
0975: + this .trackRect.width;
0976: } else {
0977: this .tickRect.x = this .trackRect.x
0978: - this .tickRect.width;
0979: }
0980: this .tickRect.y = this .trackRect.y;
0981: this .tickRect.height = this .trackRect.height;
0982: }
0983:
0984: if (this .slider.getPaintTicks()) {
0985: if (this .slider.getOrientation() == JSlider.HORIZONTAL) {
0986: this .tickRect.y -= 3;
0987: } else {
0988: if (this .slider.getComponentOrientation()
0989: .isLeftToRight()) {
0990: this .tickRect.x -= 2;
0991: } else {
0992: this .tickRect.x += 2;
0993: }
0994: }
0995: }
0996: }
0997:
0998: /*
0999: * (non-Javadoc)
1000: *
1001: * @see javax.swing.plaf.basic.BasicSliderUI#calculateLabelRect()
1002: */
1003: @Override
1004: protected void calculateLabelRect() {
1005: super .calculateLabelRect();
1006: if ((this .slider.getOrientation() == JSlider.VERTICAL)
1007: && !this .slider.getPaintTicks()
1008: && this .slider.getComponentOrientation()
1009: .isLeftToRight()) {
1010: this .labelRect.x += 3;
1011: }
1012: if (this .slider.getOrientation() == JSlider.VERTICAL) {
1013: this .labelRect.width = getHeightOfTallestLabel();
1014: }
1015: }
1016:
1017: /*
1018: * (non-Javadoc)
1019: *
1020: * @see javax.swing.plaf.basic.BasicSliderUI#calculateThumbLocation()
1021: */
1022: @Override
1023: protected void calculateThumbLocation() {
1024: super .calculateThumbLocation();
1025: Rectangle trackRect = this .getPaintTrackRect();
1026: if (slider.getOrientation() == JSlider.HORIZONTAL) {
1027: int valuePosition = xPositionForValue(slider.getValue());
1028:
1029: double centerY = trackRect.y + trackRect.height / 2.0;
1030: thumbRect.y = (int) (centerY - thumbRect.height / 2.0) + 1;
1031:
1032: thumbRect.x = valuePosition - thumbRect.width / 2;
1033: } else {
1034: int valuePosition = yPositionForValue(slider.getValue());
1035:
1036: double centerX = trackRect.x + trackRect.width / 2.0;
1037: thumbRect.x = (int) (centerX - thumbRect.width / 2.0) + 1;
1038:
1039: thumbRect.y = valuePosition - (thumbRect.height / 2);
1040: }
1041: }
1042:
1043: /*
1044: * (non-Javadoc)
1045: *
1046: * @see javax.swing.plaf.basic.BasicSliderUI#getPreferredSize(javax.swing.JComponent)
1047: */
1048: @Override
1049: public Dimension getPreferredSize(JComponent c) {
1050: this .recalculateIfInsetsChanged();
1051: Dimension d;
1052: if (this .slider.getOrientation() == JSlider.VERTICAL) {
1053: d = new Dimension(this .getPreferredVerticalSize());
1054: d.width = this .insetCache.left + this .insetCache.right;
1055: d.width += this .focusInsets.left + this .focusInsets.right;
1056: d.width += this .trackRect.width;
1057: if (this .slider.getPaintTicks())
1058: d.width += getTickLength();
1059: if (this .slider.getPaintLabels())
1060: d.width += getWidthOfWidestLabel();
1061: d.width += 3;
1062: } else {
1063: d = new Dimension(this .getPreferredHorizontalSize());
1064: d.height = this .insetCache.top + this .insetCache.bottom;
1065: d.height += this .focusInsets.top + this .focusInsets.bottom;
1066: d.height += this .trackRect.height;
1067: if (this .slider.getPaintTicks())
1068: d.height += getTickLength();
1069: if (this .slider.getPaintLabels())
1070: d.height += getHeightOfTallestLabel();
1071: d.height += 3;
1072: }
1073:
1074: return d;
1075: }
1076:
1077: /*
1078: * (non-Javadoc)
1079: *
1080: * @see javax.swing.plaf.basic.BasicSliderUI#setThumbLocation(int, int)
1081: */
1082: @Override
1083: public void setThumbLocation(int x, int y) {
1084: super .setThumbLocation(x, y);
1085: this .slider.repaint();
1086: }
1087:
1088: /*
1089: * (non-Javadoc)
1090: *
1091: * @see javax.swing.plaf.basic.BasicSliderUI#getPreferredHorizontalSize()
1092: */
1093: @Override
1094: public Dimension getPreferredHorizontalSize() {
1095: return new Dimension(SubstanceSizeUtils.getAdjustedSize(
1096: SubstanceSizeUtils.getComponentFontSize(this .slider),
1097: 200, 1, 20, false), 21);
1098: }
1099:
1100: /*
1101: * (non-Javadoc)
1102: *
1103: * @see javax.swing.plaf.basic.BasicSliderUI#getPreferredVerticalSize()
1104: */
1105: @Override
1106: public Dimension getPreferredVerticalSize() {
1107: return new Dimension(21, SubstanceSizeUtils.getAdjustedSize(
1108: SubstanceSizeUtils.getComponentFontSize(this .slider),
1109: 200, 1, 20, false));
1110: }
1111:
1112: /*
1113: * (non-Javadoc)
1114: *
1115: * @see javax.swing.plaf.basic.BasicSliderUI#paintLabels(java.awt.Graphics)
1116: */
1117: @Override
1118: public void paintLabels(Graphics g) {
1119: final Rectangle labelBounds = labelRect;
1120:
1121: SubstanceTextPainter textPainter = SubstanceLookAndFeel
1122: .getCurrentTextPainter();
1123: Dictionary<?, ?> dictionary = slider.getLabelTable();
1124: if (dictionary != null) {
1125: Enumeration<?> keys = dictionary.keys();
1126: int minValue = slider.getMinimum();
1127: int maxValue = slider.getMaximum();
1128: while (keys.hasMoreElements()) {
1129: Integer key = (Integer) keys.nextElement();
1130: int value = key.intValue();
1131: if (value >= minValue && value <= maxValue) {
1132: Component label = (Component) dictionary.get(key);
1133: Dimension prefSize = label.getPreferredSize();
1134: JLabel jlabel = (JLabel) label;
1135: final View v = (View) jlabel
1136: .getClientProperty(BasicHTML.propertyKey);
1137: String text = jlabel.getText();
1138:
1139: // fix for issue 316 - respecting custom foreground
1140: // colors on slider labels.
1141: ComponentState state = slider.isEnabled() ? ComponentState.DEFAULT
1142: : ComponentState.DISABLED_UNSELECTED;
1143: Color labelColor = SubstanceCoreUtilities
1144: .getForegroundColor(jlabel, state, state);
1145:
1146: if (slider.getOrientation() == JSlider.HORIZONTAL) {
1147: final Rectangle textRect = new Rectangle(
1148: xPositionForValue(value)
1149: - prefSize.width / 2,
1150: labelBounds.y, prefSize.width,
1151: prefSize.height);
1152: if (v != null) {
1153: // fix for issue 327 - support for custom HTML
1154: // labels
1155: if (textPainter.needsBackgroundImage()) {
1156: textPainter
1157: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
1158: public void paintBackground(
1159: Graphics g) {
1160: v.paint(g, textRect);
1161: }
1162: });
1163: } else {
1164: v.paint(g, textRect);
1165: }
1166: } else {
1167: textPainter.attachText(this .slider,
1168: textRect, text, -1, g.getFont(),
1169: labelColor, g.getClipBounds());
1170: }
1171: } else {
1172: int offset = 0;
1173: if (slider.getComponentOrientation()
1174: .isLeftToRight()) {
1175: offset = labelBounds.width
1176: - label.getPreferredSize().height;
1177: }
1178: final Rectangle labelRect = new Rectangle(
1179: labelBounds.x + offset,
1180: yPositionForValue(value)
1181: - prefSize.width / 2,
1182: prefSize.height, prefSize.width);
1183: // System.out.println(labelRect + ":" + text);
1184: // textPainter
1185: // .attachCallback(new
1186: // SubstanceTextPainter.BackgroundPaintingCallback() {
1187: // public void paintBackground(Graphics g) {
1188: // g.setColor(Color.blue);
1189: // g.drawRect(labelRect.x, labelRect.y,
1190: // labelRect.width - 1,
1191: // labelRect.height - 1);
1192: // }
1193: // });
1194: textPainter.attachVerticalText(this .slider,
1195: labelRect, text, -1, g.getFont(),
1196: labelColor, null, slider
1197: .getComponentOrientation()
1198: .isLeftToRight());
1199: }
1200: }
1201: }
1202: }
1203: // textPainter.setBackgroundFill(slider, slider.getBackground(), true,
1204: // 0,
1205: // 0);
1206: }
1207: }
|