2
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
10
* o Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
14
* o Neither the name of Substance Kirill Grouchnikov nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
package org.pushingpixels.substance.internal.utils.icon;
32
import java.awt.AlphaComposite;
33
import java.awt.Component;
34
import java.awt.Graphics;
35
import java.awt.Graphics2D;
36
import java.awt.image.BufferedImage;
39
import javax.swing.AbstractButton;
40
import javax.swing.Icon;
41
import javax.swing.ImageIcon;
42
import javax.swing.JComponent;
43
import javax.swing.JMenu;
44
import javax.swing.SwingConstants;
46
import org.pushingpixels.substance.api.ColorSchemeAssociationKind;
47
import org.pushingpixels.substance.api.ComponentState;
48
import org.pushingpixels.substance.api.ComponentStateFacet;
49
import org.pushingpixels.substance.api.SubstanceColorScheme;
50
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
51
import org.pushingpixels.substance.internal.animation.TransitionAwareUI;
52
import org.pushingpixels.substance.internal.utils.HashMapKey;
53
import org.pushingpixels.substance.internal.utils.LazyResettableHashMap;
54
import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
55
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
56
import org.pushingpixels.substance.internal.utils.SubstanceImageCreator;
57
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
60
* Transition aware implementation of arrow button icons. Used for implementing
61
* icons of scroll bar buttons, combobox buttons, menus and more.
63
* @author Kirill Grouchnikov
66
public class ArrowButtonTransitionAwareIcon implements Icon {
68
* Icon cache to speed up the subsequent icon painting. The basic assumption
69
* is that the {@link #delegate} returns an icon that paints the same for
70
* the same parameters.
72
private static LazyResettableHashMap<Icon> iconMap = new LazyResettableHashMap<Icon>(
73
"ButtonArrowTransitionAwareIcon");
76
* Arrow icon orientation. Must be one of {@link SwingConstants#NORTH},
77
* {@link SwingConstants#SOUTH}, {@link SwingConstants#EAST} or
78
* {@link SwingConstants#WEST}.
80
private int orientation;
85
protected int iconWidth;
90
protected int iconHeight;
93
* Delegate to compute the actual icons.
95
protected TransitionAwareIcon.Delegate delegate;
97
protected JComponent component;
99
private TransitionAwareIcon.TransitionAwareUIDelegate transitionAwareUIDelegate;
101
public ArrowButtonTransitionAwareIcon(final AbstractButton button,
103
this(button, new TransitionAwareIcon.TransitionAwareUIDelegate() {
105
public TransitionAwareUI getTransitionAwareUI() {
106
return (TransitionAwareUI) button.getUI();
112
* Creates an arrow icon.
117
* Arrow icon orientation.
119
public ArrowButtonTransitionAwareIcon(
120
final JComponent component,
121
TransitionAwareIcon.TransitionAwareUIDelegate transitionAwareUIDelegate,
122
final int orientation) {
123
this.component = component;
124
this.transitionAwareUIDelegate = transitionAwareUIDelegate;
125
this.orientation = orientation;
126
this.delegate = new TransitionAwareIcon.Delegate() {
128
public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
129
int fontSize = SubstanceSizeUtils
130
.getComponentFontSize(component);
131
return SubstanceImageCreator.getArrowIcon(fontSize,
132
orientation, scheme);
136
this.iconWidth = this.delegate.getColorSchemeIcon(
137
SubstanceColorSchemeUtilities.getColorScheme(component,
138
ComponentState.ENABLED)).getIconWidth();
139
this.iconHeight = this.delegate.getColorSchemeIcon(
140
SubstanceColorSchemeUtilities.getColorScheme(component,
141
ComponentState.ENABLED)).getIconHeight();
145
public void paintIcon(Component c, Graphics g, int x, int y) {
146
this.getIconToPaint().paintIcon(c, g, x, y);
150
* Returns the icon to be painted for the current state of the button.
152
* @return Icon to be painted.
154
private Icon getIconToPaint() {
155
boolean isMenu = (this.component instanceof JMenu);
156
StateTransitionTracker stateTransitionTracker = this.transitionAwareUIDelegate
157
.getTransitionAwareUI().getTransitionTracker();
158
StateTransitionTracker.ModelStateInfo modelStateInfo = stateTransitionTracker
159
.getModelStateInfo();
160
Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = isMenu ? modelStateInfo
161
.getStateNoSelectionContributionMap()
162
: modelStateInfo.getStateContributionMap();
163
ComponentState currState = isMenu ? modelStateInfo
164
.getCurrModelStateNoSelection() : modelStateInfo
165
.getCurrModelState();
167
// Use HIGHLIGHT for rollover menus (arrow icons)
168
// and MARK for the rest
169
ColorSchemeAssociationKind baseAssociationKind = isMenu
170
&& currState.isFacetActive(ComponentStateFacet.ROLLOVER) ? ColorSchemeAssociationKind.HIGHLIGHT
171
: ColorSchemeAssociationKind.MARK;
172
SubstanceColorScheme baseScheme = SubstanceColorSchemeUtilities
173
.getColorScheme(this.component, baseAssociationKind, currState);
174
float baseAlpha = SubstanceColorSchemeUtilities.getAlpha(
175
this.component, currState);
177
HashMapKey keyBase = SubstanceCoreUtilities.getHashKey(this.component
178
.getClass().getName(), this.orientation, SubstanceSizeUtils
179
.getComponentFontSize(this.component), baseScheme
180
.getDisplayName(), baseAlpha);
181
Icon layerBase = iconMap.get(keyBase);
182
if (layerBase == null) {
183
Icon baseFullOpacity = this.delegate.getColorSchemeIcon(baseScheme);
184
if (baseAlpha == 1.0f) {
185
layerBase = baseFullOpacity;
186
iconMap.put(keyBase, layerBase);
188
BufferedImage baseImage = SubstanceCoreUtilities.getBlankImage(
189
baseFullOpacity.getIconWidth(), baseFullOpacity
191
Graphics2D g2base = baseImage.createGraphics();
192
g2base.setComposite(AlphaComposite.SrcOver.derive(baseAlpha));
193
baseFullOpacity.paintIcon(this.component, g2base, 0, 0);
195
layerBase = new ImageIcon(baseImage);
196
iconMap.put(keyBase, layerBase);
199
// System.out.println("Contribution map in painting");
200
// for (Map.Entry<ComponentState,
201
// StateTransitionTracker.StateContributionInfo> existing : activeStates
203
// System.out.println("\t" + existing.getKey() + " in ["
204
// + existing.getValue().start + ":" + existing.getValue().end
205
// + "] : " + existing.getValue().curr);
208
if (currState.isDisabled() || (activeStates.size() == 1)) {
212
BufferedImage result = SubstanceCoreUtilities.getBlankImage(layerBase
213
.getIconWidth(), layerBase.getIconHeight());
214
Graphics2D g2d = result.createGraphics();
215
// draw the base layer
216
// System.out.println("Painting currState " + currState + ":" +
218
layerBase.paintIcon(this.component, g2d, 0, 0);
220
// draw the other active layers
221
for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : activeStates
223
ComponentState activeState = activeEntry.getKey();
224
// System.out.println("Painting state " + activeState + "[curr is "
225
// + currState + "] with " + activeEntry.getValue());
226
if (activeState == currState)
229
float stateContribution = activeEntry.getValue().getContribution();
230
// System.out.println("Painting activeState "
233
// + stateContribution
235
// + SubstanceColorSchemeUtilities.getAlpha(this.component,
237
if (stateContribution > 0.0f) {
238
g2d.setComposite(AlphaComposite.SrcOver
239
.derive(stateContribution));
241
ColorSchemeAssociationKind associationKind = isMenu
243
.isFacetActive(ComponentStateFacet.ROLLOVER) ? ColorSchemeAssociationKind.HIGHLIGHT
244
: ColorSchemeAssociationKind.MARK;
245
SubstanceColorScheme scheme = SubstanceColorSchemeUtilities
246
.getColorScheme(this.component, associationKind,
248
float alpha = SubstanceColorSchemeUtilities.getAlpha(
249
this.component, activeState);
251
HashMapKey key = SubstanceCoreUtilities
252
.getHashKey(this.component.getClass().getName(),
253
this.orientation, SubstanceSizeUtils
254
.getComponentFontSize(this.component),
255
scheme.getDisplayName(), alpha);
256
Icon layer = iconMap.get(key);
258
Icon fullOpacity = this.delegate.getColorSchemeIcon(scheme);
261
iconMap.put(key, layer);
263
BufferedImage image = SubstanceCoreUtilities
264
.getBlankImage(fullOpacity.getIconWidth(),
265
fullOpacity.getIconHeight());
266
Graphics2D g2layer = image.createGraphics();
267
g2layer.setComposite(AlphaComposite.SrcOver
269
fullOpacity.paintIcon(this.component, g2layer, 0, 0);
271
layer = new ImageIcon(image);
272
iconMap.put(key, layer);
275
layer.paintIcon(this.component, g2d, 0, 0);
279
return new ImageIcon(result);
282
// SubstanceColorScheme currScheme = SubstanceColorSchemeUtilities
283
// .getColorScheme(this.component, currAssociationKind, currState);
285
// SubstanceColorScheme prevScheme = currScheme;
287
// // Use HIGHLIGHT for rollover menus (arrow icons)
288
// // and MARK for the rest
289
// if (prevState != currState) {
290
// ColorSchemeAssociationKind prevAssociationKind = (this.component
292
// && prevState.isFacetActive(AnimationFacet.ROLLOVER) ?
293
// ColorSchemeAssociationKind.HIGHLIGHT
294
// : ColorSchemeAssociationKind.MARK;
295
// prevScheme = SubstanceColorSchemeUtilities.getColorScheme(
296
// this.component, prevAssociationKind, prevState);
298
// cyclePos = stateTransitionTracker.getModelTransitionPosition();
299
// float currAlpha = SubstanceColorSchemeUtilities.getAlpha(
300
// this.component, currState);
301
// float prevAlpha = SubstanceColorSchemeUtilities.getAlpha(
302
// this.component, prevState);
304
// HashMapKey key = SubstanceCoreUtilities.getHashKey(this.component
305
// .getClass().getName(), this.orientation, SubstanceSizeUtils
306
// .getComponentFontSize(this.component), currScheme
307
// .getDisplayName(), prevScheme.getDisplayName(), currAlpha,
308
// prevAlpha, cyclePos);
309
// Icon result = iconMap.get(key);
310
// if (result == null) {
311
// Icon icon = this.delegate.getColorSchemeIcon(currScheme);
312
// Icon prevIcon = this.delegate.getColorSchemeIcon(prevScheme);
314
// BufferedImage temp = SubstanceCoreUtilities.getBlankImage(icon
315
// .getIconWidth(), icon.getIconHeight());
316
// Graphics2D g2d = temp.createGraphics();
318
// if (currScheme == prevScheme) {
319
// // same scheme - can paint just the current icon, no matter
320
// // what the cycle position is.
321
// g2d.setComposite(AlphaComposite.getInstance(
322
// AlphaComposite.SRC_OVER, currAlpha));
323
// icon.paintIcon(this.component, g2d, 0, 0);
325
// // make optimizations for limit values of the cycle position.
326
// if (cyclePos < 1.0f) {
327
// g2d.setComposite(AlphaComposite.SrcOver.derive(prevAlpha));
328
// prevIcon.paintIcon(this.component, g2d, 0, 0);
330
// if (cyclePos > 0.0f) {
331
// g2d.setComposite(AlphaComposite.SrcOver.derive(currAlpha
333
// icon.paintIcon(this.component, g2d, 0, 0);
337
// result = new ImageIcon(temp);
338
// iconMap.put(key, result);
348
* @see javax.swing.Icon#getIconHeight()
351
public int getIconHeight() {
352
return this.iconHeight;
358
* @see javax.swing.Icon#getIconWidth()
361
public int getIconWidth() {
362
return this.iconWidth;