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.utils.icon;
0031:
0032: import java.awt.*;
0033: import java.awt.event.MouseEvent;
0034: import java.awt.geom.Ellipse2D;
0035: import java.awt.geom.GeneralPath;
0036: import java.awt.image.BufferedImage;
0037: import java.lang.reflect.Field;
0038: import java.util.*;
0039:
0040: import javax.swing.*;
0041: import javax.swing.plaf.SliderUI;
0042: import javax.swing.plaf.UIResource;
0043: import javax.swing.plaf.basic.BasicSliderUI;
0044:
0045: import org.jvnet.lafwidget.animation.*;
0046: import org.jvnet.substance.*;
0047: import org.jvnet.substance.border.SubstanceBorderPainter;
0048: import org.jvnet.substance.button.BaseButtonShaper;
0049: import org.jvnet.substance.color.ColorScheme;
0050: import org.jvnet.substance.painter.SubstanceGradientPainter;
0051: import org.jvnet.substance.theme.SubstanceTheme;
0052: import org.jvnet.substance.utils.*;
0053:
0054: /**
0055: * Icon factory for dynamically-changing icons. This class is <b>for internal
0056: * use only</b>.
0057: *
0058: * @author Kirill Grouchnikov
0059: */
0060: public class SubstanceIconFactory {
0061: /**
0062: * Icons for check box menu item in {@link SubstanceCheckBoxMenuItemUI}.
0063: */
0064: private static Map<Integer, Icon> checkBoxMenuItemIcons = new HashMap<Integer, Icon>();
0065:
0066: /**
0067: * Icons for radio button menu item in
0068: * {@link SubstanceRadioButtonMenuItemUI}.
0069: */
0070: private static Map<Integer, Icon> radioButtonMenuItemIcons = new HashMap<Integer, Icon>();
0071:
0072: /**
0073: * Icons for horizontal slider in {@link SubstanceSliderUI}.
0074: */
0075: private static Map<String, Icon> sliderHorizontalIcons = new HashMap<String, Icon>();
0076:
0077: /**
0078: * Icons for horizontal slider in {@link SubstanceSliderUI}.
0079: */
0080: private static Map<String, Icon> sliderRoundIcons = new HashMap<String, Icon>();
0081:
0082: /**
0083: * Icons for vertical slider in {@link SubstanceSliderUI}.
0084: */
0085: private static Map<String, Icon> sliderVerticalIcons = new HashMap<String, Icon>();
0086:
0087: /**
0088: * Icons for tree collapse / expand in {@link SubstanceTreeUI}.
0089: */
0090: private static Map<String, Icon> treeIcons = new HashMap<String, Icon>();
0091:
0092: /**
0093: * Resets image maps (used when setting new theme).
0094: *
0095: * @see SubstanceLookAndFeel#setCurrentTheme(String)
0096: * @see SubstanceLookAndFeel#setCurrentTheme(SubstanceTheme)
0097: */
0098: public static synchronized void reset() {
0099: SubstanceIconFactory.checkBoxMenuItemIcons.clear();
0100: SubstanceIconFactory.radioButtonMenuItemIcons.clear();
0101: SubstanceIconFactory.sliderHorizontalIcons.clear();
0102: SubstanceIconFactory.sliderRoundIcons.clear();
0103: SubstanceIconFactory.sliderVerticalIcons.clear();
0104: SubstanceIconFactory.treeIcons.clear();
0105:
0106: SubstanceIconFactory.titlePaneIcons.get(IconKind.CLOSE).clear();
0107: SubstanceIconFactory.titlePaneIcons.get(IconKind.MINIMIZE)
0108: .clear();
0109: SubstanceIconFactory.titlePaneIcons.get(IconKind.MAXIMIZE)
0110: .clear();
0111: SubstanceIconFactory.titlePaneIcons.get(IconKind.RESTORE)
0112: .clear();
0113: }
0114:
0115: /**
0116: * Retrieves icon for horizontal slider in {@link SubstanceSliderUI}.
0117: *
0118: * @param size
0119: * The size of the icon to retrieve.
0120: * @param isMirrorred
0121: * Indication whether the icon should be mirrored.
0122: * @return Icon for horizontal slider in {@link SubstanceSliderUI}.
0123: */
0124: public static Icon getSliderHorizontalIcon(int size,
0125: boolean isMirrorred) {
0126: String key = size + ":" + isMirrorred;
0127: if (SubstanceIconFactory.sliderHorizontalIcons.get(key) == null) {
0128: Icon icon = new SliderHorizontalIcon(size, isMirrorred);
0129: SubstanceIconFactory.sliderHorizontalIcons.put(key, icon);
0130: }
0131: return SubstanceIconFactory.sliderHorizontalIcons.get(key);
0132: }
0133:
0134: /**
0135: * Retrieves round icon for slider in {@link SubstanceSliderUI}.
0136: *
0137: * @param size
0138: * The size of the icon to retrieve.
0139: * @return Round icon for slider in {@link SubstanceSliderUI}.
0140: */
0141: public static Icon getSliderRoundIcon(int size) {
0142: String key = "" + size;
0143: if (SubstanceIconFactory.sliderRoundIcons.get(key) == null) {
0144: Icon icon = new SliderRoundIcon(size);
0145: SubstanceIconFactory.sliderRoundIcons.put(key, icon);
0146: }
0147: return SubstanceIconFactory.sliderRoundIcons.get(key);
0148: }
0149:
0150: /**
0151: * Retrieves icon for vertical slider in {@link SubstanceSliderUI}.
0152: *
0153: * @param size
0154: * The size of the icon to retrieve.
0155: * @param isMirrorred
0156: * Indication whether the icon should be mirrored.
0157: * @return Icon for vertical slider in {@link SubstanceSliderUI}.
0158: */
0159: public static Icon getSliderVerticalIcon(int size,
0160: boolean isMirrorred) {
0161: String key = size + ":" + isMirrorred;
0162: if (SubstanceIconFactory.sliderVerticalIcons.get(key) == null) {
0163: Icon icon = new SliderVerticalIcon(size, isMirrorred);
0164: SubstanceIconFactory.sliderVerticalIcons.put(key, icon);
0165: }
0166: return SubstanceIconFactory.sliderVerticalIcons.get(key);
0167: }
0168:
0169: public static Icon getTreeIcon(JTree tree, boolean isCollapsed) {
0170: int fontSize = SubstanceSizeUtils.getComponentFontSize(tree);
0171: int size = SubstanceSizeUtils.getTreeIconSize(fontSize);
0172:
0173: String key = size + ":" + isCollapsed;
0174: if (SubstanceIconFactory.treeIcons.get(key) == null) {
0175: Icon icon = new TreeIcon(size, isCollapsed);
0176: SubstanceIconFactory.treeIcons.put(key, icon);
0177: }
0178: return SubstanceIconFactory.treeIcons.get(key);
0179: }
0180:
0181: /**
0182: * Trackable slider (for sliders that do not use {@link SubstanceSliderUI}
0183: * as their UI (such as
0184: * {@link contrib.ch.randelshofer.quaqua.colorchooser.ColorSliderUI}from
0185: * Quaqua). Uses reflection to implement the {@link Trackable}interface,
0186: * fetching the value of {@link BasicSliderUI#thumbRect}field.
0187: *
0188: * @author Kirill Grouchnikov
0189: */
0190: private static class TrackableSlider implements Trackable {
0191: /**
0192: * The associated slider.
0193: */
0194: private JSlider slider;
0195:
0196: /**
0197: * Reflection reference to {@link BasicSliderUI#thumbRect}field. If
0198: * reflection failed, or no such field (for example the custom UI
0199: * implements {@link SliderUI}directly, <code>this</code> field is
0200: * <code>null</code>.
0201: */
0202: private Field thumbRectField;
0203:
0204: /**
0205: * Simple constructor.
0206: *
0207: * @param slider
0208: * The associated slider.
0209: */
0210: public TrackableSlider(JSlider slider) {
0211: this .slider = slider;
0212:
0213: SliderUI sliderUI = slider.getUI();
0214: if (sliderUI instanceof BasicSliderUI) {
0215: try {
0216: this .thumbRectField = BasicSliderUI.class
0217: .getDeclaredField("thumbRect");
0218: this .thumbRectField.setAccessible(true);
0219: } catch (Exception exc) {
0220: this .thumbRectField = null;
0221: }
0222: }
0223: }
0224:
0225: /*
0226: * (non-Javadoc)
0227: *
0228: * @see org.jvnet.substance.Trackable#isInside(java.awt.event.MouseEvent)
0229: */
0230: public boolean isInside(MouseEvent me) {
0231: try {
0232: Rectangle thumbB = (Rectangle) this .thumbRectField
0233: .get(this .slider.getUI());
0234: if (thumbB == null)
0235: return false;
0236: return thumbB.contains(me.getX(), me.getY());
0237: } catch (Exception exc) {
0238: return false;
0239: }
0240: }
0241: }
0242:
0243: /**
0244: * Icon for horizontal slider in {@link SubstanceSliderUI}.
0245: *
0246: * @author Kirill Grouchnikov
0247: */
0248: private static class SliderHorizontalIcon implements Icon,
0249: UIResource {
0250: /**
0251: * Icon hash.
0252: */
0253: // private Map<ColorScheme, Map<ComponentState, Icon>> icons;
0254: private static Map<String, Icon> icons = new HashMap<String, Icon>();
0255:
0256: /**
0257: * The size of <code>this</code> icon.
0258: */
0259: private int size;
0260:
0261: /**
0262: * Indication whether the icon is mirrorred.
0263: */
0264: private boolean isMirrorred;
0265:
0266: /**
0267: * Contains models for the sliders whose UI is <b>not </b>
0268: * {@link SubstanceSliderUI}. A model is used for rollover effects (as
0269: * in {@link SubstanceSliderUI}and {@link SubstanceScrollBarUI}). Note
0270: * that this is weak map (on keys) to allow disposing of unused keys.
0271: */
0272: private WeakHashMap<JSlider, ButtonModel> models = new WeakHashMap<JSlider, ButtonModel>();
0273:
0274: /**
0275: * Simple constructor.
0276: *
0277: * @param size
0278: * The size of <code>this</code> icon.
0279: * @param isMirrorred
0280: * Indication whether the icon should be mirrored.
0281: */
0282: public SliderHorizontalIcon(int size, boolean isMirrorred) {
0283: // icons = new HashMap<ColorScheme, Map<ComponentState, Icon>>();
0284: this .size = size;
0285: this .isMirrorred = isMirrorred;
0286: }
0287:
0288: /**
0289: * Retrieves icon that matches the specified state of the slider thumb.
0290: *
0291: * @param state
0292: * Slider state.
0293: * @param prevState
0294: * The previous slider state.
0295: * @param slider
0296: * The slider itself.
0297: * @param sliderIcon
0298: * The slider icon.
0299: * @return Icon that matches the specified state of the slider thumb.
0300: */
0301: private static synchronized Icon getIcon(ComponentState state,
0302: ComponentState prevState, JSlider slider,
0303: SliderHorizontalIcon sliderIcon) {
0304:
0305: float cyclePos = state.getCycleCount();
0306:
0307: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0308: slider, state);
0309: SubstanceTheme theme2 = SubstanceThemeUtilities.getTheme(
0310: slider, prevState);
0311: ColorScheme colorScheme = theme.getColorScheme();
0312: ColorScheme colorScheme2 = theme2.getColorScheme();
0313: ColorScheme borderScheme = theme.getBorderTheme()
0314: .getColorScheme();
0315: ColorScheme borderScheme2 = theme2.getBorderTheme()
0316: .getColorScheme();
0317:
0318: FadeTracker fadeTracker = FadeTracker.getInstance();
0319: int width = 2 * sliderIcon.size / 3;
0320: FadeState rolloverFadeState = fadeTracker.getFadeState(
0321: slider, FadeKind.ROLLOVER);
0322: FadeState pressFadeState = fadeTracker.getFadeState(slider,
0323: FadeKind.PRESS);
0324: if (rolloverFadeState != null) {
0325: cyclePos = rolloverFadeState.getFadePosition();
0326: if (rolloverFadeState.isFadingIn())
0327: cyclePos = 10 - cyclePos;
0328: width = (int) (sliderIcon.size
0329: * (2.0 + rolloverFadeState.getFadePosition() / 10.0) / 3.0);
0330: } else if (pressFadeState != null) {
0331: cyclePos = pressFadeState.getFadePosition();
0332: if (pressFadeState.isFadingIn())
0333: cyclePos = 10 - cyclePos;
0334: width = sliderIcon.size;
0335: } else {
0336: switch (state) {
0337: case ROLLOVER_UNSELECTED:
0338: case ROLLOVER_SELECTED:
0339: case ACTIVE:
0340: case PRESSED_SELECTED:
0341: case PRESSED_UNSELECTED:
0342: width = sliderIcon.size;
0343: }
0344: }
0345: width = Math.min(width, sliderIcon.size - 2);
0346: int delta = (sliderIcon.size - width) / 2;
0347:
0348: String key = sliderIcon.size + ":" + width + ":"
0349: + SubstanceCoreUtilities.getSchemeId(colorScheme)
0350: + ":"
0351: + SubstanceCoreUtilities.getSchemeId(colorScheme2)
0352: + ":"
0353: + SubstanceCoreUtilities.getSchemeId(borderScheme)
0354: + ":"
0355: + SubstanceCoreUtilities.getSchemeId(borderScheme2)
0356: + ":" + cyclePos;
0357:
0358: Icon result = SliderHorizontalIcon.icons.get(key);
0359: if (result != null)
0360: return result;
0361:
0362: SubstanceGradientPainter painter = SubstanceLookAndFeel
0363: .getCurrentGradientPainter();
0364: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
0365: .getBorderPainter(slider);
0366:
0367: int borderDelta = (int) Math.floor(SubstanceSizeUtils
0368: .getBorderStrokeWidth(SubstanceSizeUtils
0369: .getComponentFontSize(slider)) / 2.0);
0370: Shape contour = BaseButtonShaper.getTriangleButtonOutline(
0371: width, sliderIcon.size - 1, 2, borderDelta);
0372:
0373: BufferedImage stateImage = painter.getContourBackground(
0374: width, sliderIcon.size - 1, contour, false,
0375: colorScheme, colorScheme2, cyclePos, true,
0376: colorScheme != colorScheme2);
0377: BufferedImage biResult = SubstanceCoreUtilities
0378: .getBlankImage(sliderIcon.size - 1,
0379: sliderIcon.size - 1);
0380: Graphics2D bg2d = (Graphics2D) biResult.getGraphics();
0381: bg2d.translate(delta, 0);
0382: bg2d.drawImage(stateImage, 0, 0, null);
0383:
0384: int borderThickness = (int) SubstanceSizeUtils
0385: .getBorderStrokeWidth(SubstanceSizeUtils
0386: .getComponentFontSize(slider));
0387: GeneralPath contourInner = BaseButtonShaper
0388: .getTriangleButtonOutline(width,
0389: sliderIcon.size - 1, 2, borderThickness
0390: + borderDelta);
0391:
0392: borderPainter.paintBorder(bg2d, slider, width,
0393: sliderIcon.size - 1, contour, contourInner,
0394: borderScheme, borderScheme2, cyclePos,
0395: borderScheme != borderScheme2);
0396: bg2d.translate(-delta, 0);
0397:
0398: if (sliderIcon.isMirrorred)
0399: biResult = SubstanceImageCreator
0400: .getRotated(biResult, 2);
0401: // Graphics g = stateImage.getGraphics();
0402: // g.setColor(Color.red);
0403: // g.drawRect(0, 0, stateImage.getWidth()-1,
0404: // stateImage.getHeight()-1);
0405:
0406: result = new ImageIcon(biResult);
0407: SliderHorizontalIcon.icons.put(key, result);
0408:
0409: return result;
0410: }
0411:
0412: /*
0413: * (non-Javadoc)
0414: *
0415: * @see javax.swing.Icon#paintIcon(java.awt.Component,
0416: * java.awt.Graphics, int, int)
0417: */
0418: public void paintIcon(Component c, Graphics g, int x, int y) {
0419: if (!(g instanceof Graphics2D)) {
0420: return;
0421: }
0422:
0423: JSlider slider = (JSlider) c;
0424: SliderUI sliderUI = slider.getUI();
0425: ComponentState state = ComponentState.ACTIVE;
0426: if (sliderUI instanceof SubstanceSliderUI) {
0427: state = ComponentState
0428: .getState(((SubstanceSliderUI) sliderUI)
0429: .getButtonModel(), null);
0430: } else {
0431: ButtonModel buttonModel = this .models.get(slider);
0432: if (buttonModel == null) {
0433: buttonModel = new DefaultButtonModel();
0434: buttonModel.setEnabled(slider.isEnabled());
0435: RolloverControlListener listener = new RolloverControlListener(
0436: new TrackableSlider(slider), buttonModel);
0437: slider.addMouseListener(listener);
0438: slider.addMouseMotionListener(listener);
0439: this .models.put(slider, buttonModel);
0440: }
0441: state = ComponentState.getState(buttonModel, null);
0442: }
0443: ComponentState prevState = SubstanceCoreUtilities
0444: .getPrevComponentState(slider);
0445:
0446: Icon iconToDraw = SliderHorizontalIcon.getIcon(state,
0447: prevState, slider, this );
0448:
0449: iconToDraw.paintIcon(c, g, x, y);
0450: }
0451:
0452: /*
0453: * (non-Javadoc)
0454: *
0455: * @see javax.swing.Icon#getIconWidth()
0456: */
0457: public int getIconWidth() {
0458: return this .size - 1;
0459: }
0460:
0461: /*
0462: * (non-Javadoc)
0463: *
0464: * @see javax.swing.Icon#getIconHeight()
0465: */
0466: public int getIconHeight() {
0467: return this .size - 1;
0468: }
0469: }
0470:
0471: /**
0472: * Round icon for sliders in {@link SubstanceSliderUI}.
0473: *
0474: * @author Kirill Grouchnikov
0475: */
0476: private static class SliderRoundIcon implements Icon, UIResource {
0477: /**
0478: * Icon hash.
0479: */
0480: // private Map<ColorScheme, Map<ComponentState, Icon>> icons;
0481: private static Map<String, Icon> icons = new HashMap<String, Icon>();
0482:
0483: /**
0484: * The size of <code>this</code> icon.
0485: */
0486: private int size;
0487:
0488: /**
0489: * Contains models for the sliders whose UI is <b>not </b>
0490: * {@link SubstanceSliderUI}. A model is used for rollover effects (as
0491: * in {@link SubstanceSliderUI}and {@link SubstanceScrollBarUI}). Note
0492: * that this is weak map (on keys) to allow disposing of unused keys.
0493: */
0494: private WeakHashMap<JSlider, ButtonModel> models = new WeakHashMap<JSlider, ButtonModel>();
0495:
0496: /**
0497: * Simple constructor.
0498: *
0499: * @param size
0500: * The size of <code>this</code> icon.
0501: */
0502: public SliderRoundIcon(int size) {
0503: // icons = new HashMap<ColorScheme, Map<ComponentState, Icon>>();
0504: this .size = size;
0505: }
0506:
0507: /**
0508: * Retrieves icon that matches the specified state of the slider thumb.
0509: *
0510: * @param state
0511: * Slider state.
0512: * @param prevState
0513: * The previous slider state.
0514: * @param slider
0515: * The slider itself.
0516: * @param sliderIcon
0517: * The slider icon.
0518: * @return Icon that matches the specified state of the slider thumb.
0519: */
0520: private static synchronized Icon getIcon(ComponentState state,
0521: ComponentState prevState, JSlider slider,
0522: SliderRoundIcon sliderIcon) {
0523:
0524: // System.out.println("Curr : " + state.name() + ", prev : " +
0525: // prevState.name());
0526:
0527: float cyclePos = state.getCycleCount();
0528:
0529: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0530: slider, state);
0531: SubstanceTheme theme2 = SubstanceThemeUtilities.getTheme(
0532: slider, prevState);
0533: ColorScheme colorScheme = theme.getColorScheme();
0534: ColorScheme colorScheme2 = theme2.getColorScheme();
0535: ColorScheme borderScheme = theme.getBorderTheme()
0536: .getColorScheme();
0537: ColorScheme borderScheme2 = theme2.getBorderTheme()
0538: .getColorScheme();
0539:
0540: FadeTracker fadeTracker = FadeTracker.getInstance();
0541: int width = 2 * sliderIcon.size / 3;
0542: FadeState rolloverFadeState = fadeTracker.getFadeState(
0543: slider, FadeKind.ROLLOVER);
0544: FadeState pressFadeState = fadeTracker.getFadeState(slider,
0545: FadeKind.PRESS);
0546: if (rolloverFadeState != null) {
0547: cyclePos = rolloverFadeState.getFadePosition();
0548: if (rolloverFadeState.isFadingIn())
0549: cyclePos = 10 - cyclePos;
0550: width = (int) (sliderIcon.size
0551: * (2.0 + rolloverFadeState.getFadePosition() / 10.0) / 3.0);
0552: } else if (pressFadeState != null) {
0553: cyclePos = pressFadeState.getFadePosition();
0554: if (pressFadeState.isFadingIn())
0555: cyclePos = 10 - cyclePos;
0556: width = sliderIcon.size;
0557: } else {
0558: switch (state) {
0559: case ROLLOVER_UNSELECTED:
0560: case ROLLOVER_SELECTED:
0561: case ACTIVE:
0562: case PRESSED_SELECTED:
0563: case PRESSED_UNSELECTED:
0564: width = sliderIcon.size;
0565: }
0566: }
0567: width = Math.min(width, sliderIcon.size - 2);
0568: if (width % 2 == 0)
0569: width--;
0570: int delta = (sliderIcon.size - width) / 2;
0571:
0572: String key = sliderIcon.size + ":" + width + ":"
0573: + SubstanceCoreUtilities.getSchemeId(colorScheme)
0574: + ":"
0575: + SubstanceCoreUtilities.getSchemeId(colorScheme2)
0576: + ":"
0577: + SubstanceCoreUtilities.getSchemeId(borderScheme)
0578: + ":"
0579: + SubstanceCoreUtilities.getSchemeId(borderScheme2)
0580: + ":" + cyclePos;
0581:
0582: Icon result = SliderRoundIcon.icons.get(key);
0583: if (result != null)
0584: return result;
0585:
0586: SubstanceGradientPainter painter = SubstanceLookAndFeel
0587: .getCurrentGradientPainter();
0588: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
0589: .getBorderPainter(slider);
0590:
0591: int borderDelta = (int) Math.floor(SubstanceSizeUtils
0592: .getBorderStrokeWidth(SubstanceSizeUtils
0593: .getComponentFontSize(slider)) / 2.0);
0594: Shape contour = new Ellipse2D.Float(borderDelta,
0595: borderDelta, width - 2 * borderDelta - 1, width - 2
0596: * borderDelta - 1);
0597:
0598: BufferedImage stateImage = painter.getContourBackground(
0599: width, sliderIcon.size - 1, contour, false,
0600: colorScheme, colorScheme2, cyclePos, true,
0601: colorScheme != colorScheme2);
0602: BufferedImage biResult = SubstanceCoreUtilities
0603: .getBlankImage(sliderIcon.size - 1,
0604: sliderIcon.size - 1);
0605: Graphics2D bg2d = (Graphics2D) biResult.getGraphics();
0606: // bg2d.setColor(Color.red);
0607: // bg2d.drawRect(0, 0, sliderIcon.size-2, sliderIcon.size-2);
0608: bg2d.translate(delta, delta);
0609: bg2d.drawImage(stateImage, 0, 0, null);
0610:
0611: int borderThickness = (int) SubstanceSizeUtils
0612: .getBorderStrokeWidth(SubstanceSizeUtils
0613: .getComponentFontSize(slider));
0614: Shape contourInner = new Ellipse2D.Float(borderDelta
0615: + borderThickness, borderDelta + borderThickness,
0616: width - 2 * borderDelta - 2 * borderThickness - 1,
0617: width - 2 * borderDelta - 2 * borderThickness - 1);
0618:
0619: borderPainter.paintBorder(bg2d, slider, width,
0620: sliderIcon.size - 1, contour, contourInner,
0621: borderScheme, borderScheme2, cyclePos,
0622: borderScheme != borderScheme2);
0623: bg2d.translate(-delta, -delta);
0624:
0625: result = new ImageIcon(biResult);
0626: SliderRoundIcon.icons.put(key, result);
0627:
0628: return result;
0629: }
0630:
0631: /*
0632: * (non-Javadoc)
0633: *
0634: * @see javax.swing.Icon#paintIcon(java.awt.Component,
0635: * java.awt.Graphics, int, int)
0636: */
0637: public void paintIcon(Component c, Graphics g, int x, int y) {
0638: if (!(g instanceof Graphics2D)) {
0639: return;
0640: }
0641:
0642: JSlider slider = (JSlider) c;
0643: SliderUI sliderUI = slider.getUI();
0644: ComponentState state = ComponentState.ACTIVE;
0645: if (sliderUI instanceof SubstanceSliderUI) {
0646: state = ComponentState
0647: .getState(((SubstanceSliderUI) sliderUI)
0648: .getButtonModel(), null);
0649: } else {
0650: ButtonModel buttonModel = this .models.get(slider);
0651: if (buttonModel == null) {
0652: buttonModel = new DefaultButtonModel();
0653: buttonModel.setEnabled(slider.isEnabled());
0654: RolloverControlListener listener = new RolloverControlListener(
0655: new TrackableSlider(slider), buttonModel);
0656: slider.addMouseListener(listener);
0657: slider.addMouseMotionListener(listener);
0658: this .models.put(slider, buttonModel);
0659: }
0660: state = ComponentState.getState(buttonModel, null);
0661: }
0662: ComponentState prevState = SubstanceCoreUtilities
0663: .getPrevComponentState(slider);
0664: Icon iconToDraw = SliderRoundIcon.getIcon(state, prevState,
0665: slider, this );
0666:
0667: iconToDraw.paintIcon(c, g, x, y);
0668: }
0669:
0670: /*
0671: * (non-Javadoc)
0672: *
0673: * @see javax.swing.Icon#getIconWidth()
0674: */
0675: public int getIconWidth() {
0676: return this .size - 1;
0677: }
0678:
0679: /*
0680: * (non-Javadoc)
0681: *
0682: * @see javax.swing.Icon#getIconHeight()
0683: */
0684: public int getIconHeight() {
0685: return this .size - 1;
0686: }
0687: }
0688:
0689: /**
0690: * Icon for vertical slider in {@link SubstanceSliderUI}.
0691: *
0692: * @author Kirill Grouchnikov
0693: */
0694: private static class SliderVerticalIcon implements Icon, UIResource {
0695: /**
0696: * Icon hash.
0697: */
0698: private static Map<String, Icon> icons = new HashMap<String, Icon>();
0699:
0700: /**
0701: * The size of <code>this</code> icon.
0702: */
0703: private int size;
0704:
0705: /**
0706: * Indication whether the icon is mirrorred.
0707: */
0708: private boolean isMirrorred;
0709:
0710: /**
0711: * Contains models for the sliders whose UI is <b>not </b>
0712: * {@link SubstanceSliderUI}. A model is used for rollover effects (as
0713: * in {@link SubstanceSliderUI}and {@link SubstanceScrollBarUI}). Note
0714: * that this is weak map (on keys) to allow disposing of unused keys.
0715: */
0716: private WeakHashMap<JSlider, ButtonModel> models = new WeakHashMap<JSlider, ButtonModel>();
0717:
0718: /**
0719: * Simple constructor.
0720: *
0721: * @param size
0722: * The size of <code>this</code> icon.
0723: * @param isMirrorred
0724: * Indication whether the icon should be mirrored.
0725: */
0726: public SliderVerticalIcon(int size, boolean isMirrorred) {
0727: this .size = size;
0728: this .isMirrorred = isMirrorred;
0729: }
0730:
0731: /**
0732: * Retrieves icon that matches the specified state of the slider thumb.
0733: *
0734: * @param state
0735: * Slider state.
0736: * @param prevState
0737: * The previous slider state.
0738: * @param slider
0739: * The slider itself.
0740: * @param sliderIcon
0741: * The slider icon.
0742: * @return Icon that matches the specified state of the slider thumb.
0743: */
0744: private static synchronized Icon getIcon(ComponentState state,
0745: ComponentState prevState, JSlider slider,
0746: SliderVerticalIcon sliderIcon) {
0747:
0748: float cyclePos = state.getCycleCount();
0749:
0750: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0751: slider, state);
0752: SubstanceTheme theme2 = SubstanceThemeUtilities.getTheme(
0753: slider, prevState);
0754: ColorScheme colorScheme = theme.getColorScheme();
0755: ColorScheme colorScheme2 = theme2.getColorScheme();
0756: ColorScheme borderScheme = theme.getBorderTheme()
0757: .getColorScheme();
0758: ColorScheme borderScheme2 = theme2.getBorderTheme()
0759: .getColorScheme();
0760:
0761: FadeTracker fadeTracker = FadeTracker.getInstance();
0762: int height = 2 * sliderIcon.size / 3;
0763:
0764: FadeState rolloverFadeState = fadeTracker.getFadeState(
0765: slider, FadeKind.ROLLOVER);
0766: FadeState pressFadeState = fadeTracker.getFadeState(slider,
0767: FadeKind.PRESS);
0768: if (rolloverFadeState != null) {
0769: cyclePos = rolloverFadeState.getFadePosition();
0770: if (rolloverFadeState.isFadingIn())
0771: cyclePos = 10 - cyclePos;
0772: height = (int) (sliderIcon.size
0773: * (2.0 + rolloverFadeState.getFadePosition() / 10.0) / 3.0);
0774: } else if (pressFadeState != null) {
0775: cyclePos = pressFadeState.getFadePosition();
0776: if (pressFadeState.isFadingIn())
0777: cyclePos = 10 - cyclePos;
0778: height = sliderIcon.size;
0779: } else {
0780: switch (state) {
0781: case ROLLOVER_UNSELECTED:
0782: case ROLLOVER_SELECTED:
0783: case ACTIVE:
0784: case PRESSED_SELECTED:
0785: case PRESSED_UNSELECTED:
0786: height = sliderIcon.size;
0787: }
0788: }
0789: height = Math.min(height, sliderIcon.size - 2);
0790: int delta = (sliderIcon.size - height) / 2 - 1;
0791:
0792: String key = sliderIcon.size + ":" + height + ":"
0793: + slider.getComponentOrientation() + ":"
0794: + SubstanceCoreUtilities.getSchemeId(colorScheme)
0795: + ":"
0796: + SubstanceCoreUtilities.getSchemeId(colorScheme2)
0797: + ":"
0798: + SubstanceCoreUtilities.getSchemeId(borderScheme)
0799: + ":"
0800: + SubstanceCoreUtilities.getSchemeId(borderScheme2)
0801: + ":" + cyclePos;
0802:
0803: Icon result = SliderVerticalIcon.icons.get(key);
0804: if (result != null)
0805: return result;
0806:
0807: SubstanceGradientPainter painter = SubstanceLookAndFeel
0808: .getCurrentGradientPainter();
0809: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
0810: .getBorderPainter(slider);
0811:
0812: int borderDelta = (int) Math.floor(SubstanceSizeUtils
0813: .getBorderStrokeWidth(SubstanceSizeUtils
0814: .getComponentFontSize(slider)) / 2.0);
0815: Shape contour = BaseButtonShaper.getTriangleButtonOutline(
0816: height, sliderIcon.size, 2, borderDelta);
0817:
0818: BufferedImage stateImage = painter.getContourBackground(
0819: height, sliderIcon.size, contour, false,
0820: colorScheme, colorScheme2, cyclePos, true,
0821: colorScheme != colorScheme2);
0822: BufferedImage biResult = SubstanceCoreUtilities
0823: .getBlankImage(sliderIcon.size - 1,
0824: sliderIcon.size - 1);
0825: Graphics2D bg2d = (Graphics2D) biResult.getGraphics();
0826: bg2d.translate(delta, 0);
0827: bg2d.drawImage(stateImage, 0, 0, null);
0828:
0829: int borderThickness = (int) SubstanceSizeUtils
0830: .getBorderStrokeWidth(SubstanceSizeUtils
0831: .getComponentFontSize(slider));
0832: GeneralPath contourInner = BaseButtonShaper
0833: .getTriangleButtonOutline(height, sliderIcon.size,
0834: 2, borderThickness + borderDelta);
0835:
0836: borderPainter.paintBorder(bg2d, slider, height,
0837: sliderIcon.size - 1, contour, contourInner,
0838: borderScheme, borderScheme2, cyclePos,
0839: borderScheme != borderScheme2);
0840: bg2d.translate(-delta, 0);
0841:
0842: if (sliderIcon.isMirrorred)
0843: biResult = SubstanceImageCreator
0844: .getRotated(biResult, 1);
0845: else
0846: biResult = SubstanceImageCreator
0847: .getRotated(biResult, 3);
0848:
0849: if (!slider.getComponentOrientation().isLeftToRight()) {
0850: biResult = SubstanceImageCreator
0851: .getRotated(biResult, 2);
0852: }
0853:
0854: // Graphics g = stateImage.getGraphics();
0855: // g.setColor(Color.red);
0856: // g.drawRect(0, 0, stateImage.getWidth()-1,
0857: // stateImage.getHeight()-1);
0858:
0859: result = new ImageIcon(biResult);
0860: SliderVerticalIcon.icons.put(key, result);
0861:
0862: return result;
0863: }
0864:
0865: /*
0866: * (non-Javadoc)
0867: *
0868: * @see javax.swing.Icon#paintIcon(java.awt.Component,
0869: * java.awt.Graphics, int, int)
0870: */
0871: public void paintIcon(Component c, Graphics g, int x, int y) {
0872: if (!(g instanceof Graphics2D)) {
0873: return;
0874: }
0875: JSlider slider = (JSlider) c;
0876: SliderUI sliderUI = slider.getUI();
0877: ComponentState state = ComponentState.ACTIVE;
0878: if (sliderUI instanceof SubstanceSliderUI) {
0879: state = ComponentState
0880: .getState(((SubstanceSliderUI) sliderUI)
0881: .getButtonModel(), null);
0882: } else {
0883: ButtonModel buttonModel = this .models.get(slider);
0884: if (buttonModel == null) {
0885: buttonModel = new DefaultButtonModel();
0886: buttonModel.setEnabled(slider.isEnabled());
0887: RolloverControlListener listener = new RolloverControlListener(
0888: new TrackableSlider(slider), buttonModel);
0889: slider.addMouseListener(listener);
0890: slider.addMouseMotionListener(listener);
0891: this .models.put(slider, buttonModel);
0892: }
0893: state = ComponentState.getState(buttonModel, null);
0894: }
0895: ComponentState prevState = SubstanceCoreUtilities
0896: .getPrevComponentState(slider);
0897:
0898: Icon iconToDraw = SliderVerticalIcon.getIcon(state,
0899: prevState, slider, this );
0900: iconToDraw.paintIcon(c, g, x, y);
0901: }
0902:
0903: /*
0904: * (non-Javadoc)
0905: *
0906: * @see javax.swing.Icon#getIconWidth()
0907: */
0908: public int getIconWidth() {
0909: return this .size - 1;
0910: }
0911:
0912: /*
0913: * (non-Javadoc)
0914: *
0915: * @see javax.swing.Icon#getIconHeight()
0916: */
0917: public int getIconHeight() {
0918: return this .size - 1;
0919: }
0920: }
0921:
0922: /**
0923: * Collapse / expand icons for JTrees in {@link SubstanceTreeUI}.
0924: *
0925: * @author Kirill Grouchnikov
0926: */
0927: private static class TreeIcon implements Icon, UIResource {
0928: /**
0929: * Icon hash.
0930: */
0931: private static Map<String, Icon> icons = new HashMap<String, Icon>();
0932:
0933: /**
0934: * The collapsed indication of this icon
0935: */
0936: private boolean isCollapsed;
0937:
0938: private int size;
0939:
0940: /**
0941: * Simple constructor.
0942: *
0943: * @param isCollapsed
0944: * The collapsed indication of <code>this</code> icon.
0945: */
0946: public TreeIcon(int size, boolean isCollapsed) {
0947: this .isCollapsed = isCollapsed;
0948: this .size = size;
0949: }
0950:
0951: /**
0952: * Retrieves icon that matches the specified state of the slider thumb.
0953: *
0954: * @param state
0955: * Slider state.
0956: * @param prevState
0957: * The previous slider state.
0958: * @param slider
0959: * The slider itself.
0960: * @param sliderIcon
0961: * The slider icon.
0962: * @return Icon that matches the specified state of the slider thumb.
0963: */
0964: private static synchronized Icon getIcon(JTree tree,
0965: boolean isCollapsed) {
0966: ComponentState state = ((tree == null) || tree.isEnabled()) ? ComponentState.DEFAULT
0967: : ComponentState.DISABLED_UNSELECTED;
0968: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0969: tree, state);
0970:
0971: // if ((tree != null) && SubstanceCoreUtilities.hasColorization(tree)) {
0972: // Color treeBackgr = tree.getBackground();
0973: // if (!(treeBackgr instanceof UIResource)) {
0974: // double colorization = SubstanceCoreUtilities
0975: // .getColorizationFactor(tree);
0976: // if (!tree.isEnabled())
0977: // colorization /= 2.0;
0978: // theme = SubstanceShiftTheme.getShiftedTheme(theme,
0979: // treeBackgr, colorization, null, 0.0);
0980: // }
0981: // }
0982: //
0983: int fontSize = SubstanceSizeUtils
0984: .getComponentFontSize(tree);
0985:
0986: String key = fontSize + ":" + theme.getDisplayName() + ":"
0987: + isCollapsed;
0988:
0989: Icon result = TreeIcon.icons.get(key);
0990: if (result != null)
0991: return result;
0992:
0993: result = new ImageIcon(SubstanceImageCreator.getTreeIcon(
0994: tree, theme, isCollapsed));
0995: TreeIcon.icons.put(key, result);
0996:
0997: return result;
0998: }
0999:
1000: /*
1001: * (non-Javadoc)
1002: *
1003: * @see javax.swing.Icon#paintIcon(java.awt.Component,
1004: * java.awt.Graphics, int, int)
1005: */
1006: public void paintIcon(Component c, Graphics g, int x, int y) {
1007: if (!(g instanceof Graphics2D)) {
1008: return;
1009: }
1010:
1011: // The following check is here because some applications
1012: // (like JIDE's ExpandablePanel) may decide to use the
1013: // "Tree.collapsedIcon" and "Tree.expandedIcon" UIManager
1014: // entries to paint on non-JTree components. Sigh.
1015: JTree tree = (c instanceof JTree) ? (JTree) c : null;
1016: Icon iconToDraw = TreeIcon.getIcon(tree, this .isCollapsed);
1017:
1018: iconToDraw.paintIcon(c, g, x, y);
1019: }
1020:
1021: /*
1022: * (non-Javadoc)
1023: *
1024: * @see javax.swing.Icon#getIconWidth()
1025: */
1026: public int getIconWidth() {
1027: return this .size;
1028: }
1029:
1030: /*
1031: * (non-Javadoc)
1032: *
1033: * @see javax.swing.Icon#getIconHeight()
1034: */
1035: public int getIconHeight() {
1036: return this .size;
1037: }
1038: }
1039:
1040: /**
1041: * Icon kind of a title pane button.
1042: *
1043: * @author Kirill Grocuhnikov.
1044: */
1045: public enum IconKind {
1046: /**
1047: * Icon of a close button.
1048: */
1049: CLOSE,
1050:
1051: /**
1052: * Icon of a minimize button.
1053: */
1054: MINIMIZE,
1055:
1056: /**
1057: * Icon of a maximize button.
1058: */
1059: MAXIMIZE,
1060:
1061: /**
1062: * Icon of a restore button.
1063: */
1064: RESTORE;
1065: }
1066:
1067: /**
1068: * Cache of title pane icons.
1069: */
1070: private static final Map<IconKind, Map<SubstanceTheme, Icon>> titlePaneIcons = SubstanceIconFactory
1071: .createTitlePaneIcons();
1072:
1073: /**
1074: * Creates an empty map of title pane icons.
1075: *
1076: * @return Empty map of title pane icons.
1077: */
1078: private static Map<IconKind, Map<SubstanceTheme, Icon>> createTitlePaneIcons() {
1079: Map<IconKind, Map<SubstanceTheme, Icon>> result = new HashMap<IconKind, Map<SubstanceTheme, Icon>>();
1080:
1081: result.put(IconKind.CLOSE, new HashMap<SubstanceTheme, Icon>());
1082: result.put(IconKind.MINIMIZE,
1083: new HashMap<SubstanceTheme, Icon>());
1084: result.put(IconKind.MAXIMIZE,
1085: new HashMap<SubstanceTheme, Icon>());
1086: result.put(IconKind.RESTORE,
1087: new HashMap<SubstanceTheme, Icon>());
1088: return result;
1089: }
1090:
1091: /**
1092: * Returns title pane icon of the specified kind.
1093: *
1094: * @param iconKind
1095: * Icon kind.
1096: * @param theme
1097: * Substance theme.
1098: * @return Title pane icon of the specified kind.
1099: */
1100: public static synchronized Icon getTitlePaneIcon(IconKind iconKind,
1101: SubstanceTheme theme) {
1102: Map<SubstanceTheme, Icon> kindMap = SubstanceIconFactory.titlePaneIcons
1103: .get(iconKind);
1104: Icon result = kindMap.get(theme);
1105: if (result != null)
1106: return result;
1107:
1108: switch (iconKind) {
1109: case CLOSE:
1110: result = SubstanceImageCreator.getCloseIcon(theme);
1111: break;
1112: case MINIMIZE:
1113: result = SubstanceImageCreator.getMinimizeIcon(theme);
1114: break;
1115: case MAXIMIZE:
1116: result = SubstanceImageCreator.getMaximizeIcon(theme);
1117: break;
1118: case RESTORE:
1119: result = SubstanceImageCreator.getRestoreIcon(theme);
1120: break;
1121: }
1122: kindMap.put(theme, result);
1123: return result;
1124: }
1125:
1126: }
|