001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance.utils.icon;
031:
032: import java.awt.*;
033: import java.awt.image.BufferedImage;
034: import java.util.Map;
035:
036: import javax.swing.*;
037:
038: import org.jvnet.lafwidget.animation.FadeKind;
039: import org.jvnet.lafwidget.animation.FadeState;
040: import org.jvnet.substance.theme.SubstanceTheme;
041: import org.jvnet.substance.utils.*;
042:
043: /**
044: * Icon with transition-aware capabilities. Has a delegate that does the actual
045: * painting based on the transition themes. This class is used heavily on
046: * Substance-provided icons, such as title pane button icons, arrow icons on
047: * scroll bars and combos etc.
048: *
049: * @author Kirill Grouchnikov
050: */
051: public class TransitionAwareIcon implements Icon {
052: /**
053: * The delegate needs to implement the method in this interface based on the
054: * provided theme. The theme is computed based on the transitions that are
055: * happening on the associated button.
056: *
057: * @author Kirill Grouchnikov
058: */
059: public static interface Delegate {
060: /**
061: * Returns the icon that matches the specified theme.
062: *
063: * @param theme
064: * Theme.
065: * @return Icon that matches the specified theme.
066: */
067: public Icon getThemeIcon(SubstanceTheme theme);
068: }
069:
070: /**
071: * The associated component.
072: */
073: private JComponent comp;
074:
075: /**
076: * The associated model.
077: */
078: private ButtonModel model;
079:
080: /**
081: * Delegate to compute the actual icons.
082: */
083: private Delegate delegate;
084:
085: /**
086: * Icon cache to speed up the subsequent icon painting. The basic assumption
087: * is that the {@link #delegate} returns an icon that paints the same for
088: * the same parameters.
089: */
090: private Map<String, Icon> iconMap;
091:
092: /**
093: * Creates a new transition-aware icon.
094: *
095: * @param button
096: * Associated button.
097: * @param delegate
098: * Delegate to compute the actual icons.
099: */
100: public TransitionAwareIcon(AbstractButton button, Delegate delegate) {
101: this (button, (button == null) ? null : button.getModel(),
102: delegate);
103: }
104:
105: /**
106: * Creates a new transition-aware icon.
107: *
108: * @param comp
109: * Associated component.
110: * @param model
111: * Associated model.
112: * @param delegate
113: * Delegate to compute the actual icons.
114: */
115: public TransitionAwareIcon(JComponent comp, ButtonModel model,
116: Delegate delegate) {
117: this .comp = comp;
118: this .model = model;
119: this .delegate = delegate;
120: this .iconMap = new SoftHashMap<String, Icon>();
121: }
122:
123: /**
124: * Returns the current icon to paint.
125: *
126: * @return Icon to paint.
127: */
128: private Icon getIconToPaint() {
129: ComponentState currState = ComponentState.getState(this .model,
130: this .comp);
131: ComponentState prevState = SubstanceCoreUtilities
132: .getPrevComponentState(this .comp);
133: if (!currState.isKindActive(FadeKind.ENABLE))
134: prevState = currState;
135: float cyclePos = currState.getCycleCount();
136: SubstanceTheme mainTheme = SubstanceThemeUtilities
137: .getTheme(this .comp);
138: SubstanceTheme currTheme = SubstanceThemeUtilities.getTheme(
139: this .comp, currState);
140: // if ((this.comp instanceof SubstanceTitleButton)
141: // && (currState == ComponentState.DEFAULT)) {
142: // currTheme = mainTheme.getActiveTitlePaneTheme();
143: // // currTheme = SubstanceLookAndFeel.getTheme()
144: // // .getActiveTitlePaneTheme();
145: // }
146:
147: SubstanceTheme prevTheme = currTheme;
148:
149: FadeState fadeState = SubstanceFadeUtilities.getFadeState(
150: this .comp, FadeKind.ROLLOVER, FadeKind.SELECTION,
151: FadeKind.PRESS, FadeKind.ARM);
152: if (fadeState != null) {
153: prevTheme = SubstanceThemeUtilities.getTheme(this .comp,
154: prevState);
155: // if ((this.comp instanceof SubstanceTitleButton)
156: // && (prevState == ComponentState.DEFAULT)) {
157: // prevTheme = mainTheme.getActiveTitlePaneTheme();
158: // }
159: cyclePos = fadeState.getFadePosition();
160: if (!fadeState.isFadingIn())
161: cyclePos = 10 - cyclePos;
162: }
163: float currAlpha = mainTheme.getThemeAlpha(this .comp, currState);
164: float prevAlpha = mainTheme.getThemeAlpha(this .comp, prevState);
165:
166: String key = currTheme.getDisplayName() + ":"
167: + prevTheme.getDisplayName() + ":" + currAlpha + ":"
168: + prevAlpha + ":" + cyclePos;
169: // System.out.println(key);
170: if (!this .iconMap.containsKey(key)) {
171: Icon icon = this .delegate.getThemeIcon(currTheme);
172: Icon prevIcon = this .delegate.getThemeIcon(prevTheme);
173:
174: BufferedImage temp = SubstanceCoreUtilities.getBlankImage(
175: icon.getIconWidth(), icon.getIconHeight());
176: Graphics2D g2d = temp.createGraphics();
177:
178: if (currTheme == prevTheme) {
179: // same theme - can paint just the current icon, no matter
180: // what the cycle position is.
181: g2d.setComposite(AlphaComposite.getInstance(
182: AlphaComposite.SRC_OVER, currAlpha));
183: icon.paintIcon(this .comp, g2d, 0, 0);
184: } else {
185: // make optimizations for limit values of the cycle position.
186: if (cyclePos < 10.0f) {
187: g2d.setComposite(AlphaComposite.getInstance(
188: AlphaComposite.SRC_OVER, prevAlpha));
189: prevIcon.paintIcon(this .comp, g2d, 0, 0);
190: }
191: if (cyclePos > 0.0f) {
192: g2d.setComposite(AlphaComposite.getInstance(
193: AlphaComposite.SRC_OVER, currAlpha
194: * cyclePos / 10.0f));
195: icon.paintIcon(this .comp, g2d, 0, 0);
196: }
197: }
198:
199: this .iconMap.put(key, new ImageIcon(temp));
200: g2d.dispose();
201: }
202:
203: return this .iconMap.get(key);
204: }
205:
206: /*
207: * (non-Javadoc)
208: *
209: * @see javax.swing.Icon#getIconHeight()
210: */
211: public int getIconHeight() {
212: return this .getIconToPaint().getIconHeight();
213: }
214:
215: /*
216: * (non-Javadoc)
217: *
218: * @see javax.swing.Icon#getIconWidth()
219: */
220: public int getIconWidth() {
221: return this .getIconToPaint().getIconWidth();
222: }
223:
224: /*
225: * (non-Javadoc)
226: *
227: * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics,
228: * int, int)
229: */
230: public void paintIcon(Component c, Graphics g, int x, int y) {
231: this.getIconToPaint().paintIcon(c, g, x, y);
232: }
233: }
|