001: /*
002: * Copyright (c) 2005-2008 Flamingo / 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 Flamingo 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.flamingo.ribbon;
031:
032: import java.awt.Graphics;
033: import java.awt.Graphics2D;
034: import java.awt.geom.GeneralPath;
035: import java.awt.image.BufferedImage;
036: import java.util.*;
037:
038: import javax.swing.AbstractButton;
039:
040: import org.jvnet.lafwidget.animation.*;
041: import org.jvnet.lafwidget.layout.TransitionLayout;
042: import org.jvnet.substance.SubstanceFillBackgroundDelegate;
043: import org.jvnet.substance.SubstanceLookAndFeel;
044: import org.jvnet.substance.border.SubstanceBorderPainter;
045: import org.jvnet.substance.button.BaseButtonShaper;
046: import org.jvnet.substance.color.ColorScheme;
047: import org.jvnet.substance.painter.SubstanceGradientPainter;
048: import org.jvnet.substance.theme.SubstanceTheme;
049: import org.jvnet.substance.utils.*;
050: import org.jvnet.substance.utils.SubstanceConstants.Side;
051:
052: /**
053: * Delegate class for painting backgrounds of buttons in Ribbon plugin.
054: *
055: * @author Kirill Grouchnikov
056: */
057: public class RibbonBackgroundDelegate {
058: /**
059: * Cache for background images of ribbon buttons. Each time
060: * {@link #getRibbonBackground(AbstractButton, int, int)} is called with
061: * <code>isRoundCorners</code> equal to <code>true</code>, it checks
062: * <code>this</code> map to see if it already contains such background. If
063: * so, the background from the map is returned.
064: */
065: private static Map<String, BufferedImage> ribbonBackgrounds = new HashMap<String, BufferedImage>();
066:
067: /**
068: * Fill background delegate.
069: */
070: private static SubstanceFillBackgroundDelegate fillDelegate = new SubstanceFillBackgroundDelegate();// 0.4f);
071:
072: /**
073: * Resets image maps (used when setting new theme).
074: *
075: * @see SubstanceLookAndFeel#setCurrentTheme(String)
076: * @see SubstanceLookAndFeel#setCurrentTheme(SubstanceTheme)
077: */
078: public static synchronized void reset() {
079: ribbonBackgrounds.clear();
080: }
081:
082: /**
083: * Retrieves background image for the specified ribbon button.
084: *
085: * @param button
086: * Button.
087: * @param width
088: * Button width.
089: * @param height
090: * Button height.
091: * @return Button background image.
092: */
093: private static synchronized BufferedImage getRibbonBackground(
094: AbstractButton button, int width, int height) {
095: ComponentState state = ComponentState.getState(button
096: .getModel(), button);
097: ComponentState prevState = SubstanceCoreUtilities
098: .getPrevComponentState(button);
099: float cyclePos = state.getCycleCount();
100:
101: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(button,
102: state);
103: ColorScheme colorScheme = theme.getColorScheme();
104: ColorScheme colorScheme2 = colorScheme;
105: ColorScheme borderScheme = theme.getBorderTheme()
106: .getColorScheme();
107: ColorScheme borderScheme2 = borderScheme;
108:
109: FadeState fadeState = SubstanceFadeUtilities.getFadeState(
110: button, FadeKind.ROLLOVER, FadeKind.SELECTION,
111: FadeKind.PRESS);
112: if (fadeState != null) {
113: SubstanceTheme prevTheme = SubstanceThemeUtilities
114: .getTheme(button, prevState);
115: colorScheme = theme.getColorScheme();
116: colorScheme2 = prevTheme.getColorScheme();
117: borderScheme = theme.getBorderTheme().getColorScheme();
118: borderScheme2 = prevTheme.getBorderTheme().getColorScheme();
119: cyclePos = fadeState.getFadePosition();
120: if (fadeState.isFadingIn())
121: cyclePos = 10 - cyclePos;
122: }
123:
124: // ColorScheme activeScheme = SubstanceCoreUtilities.getActiveTheme(
125: // button, true).getColorScheme();
126: // ColorScheme borderScheme = SubstanceCoreUtilities.getTheme(button,
127: // ComponentState.DEFAULT, true, true).getBorderTheme()
128: // .getColorScheme();
129: //
130: SubstanceGradientPainter painter = SubstanceLookAndFeel
131: .getCurrentGradientPainter();
132: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
133: .getBorderPainter(button);
134: String key = width + ":" + height + ":" + prevState.name()
135: + ":" + state.name() + ":" + cyclePos + ":"
136: + SubstanceCoreUtilities.getSchemeId(colorScheme) + ":"
137: + SubstanceCoreUtilities.getSchemeId(colorScheme2)
138: + ":"
139: + SubstanceCoreUtilities.getSchemeId(borderScheme)
140: + ":"
141: + SubstanceCoreUtilities.getSchemeId(borderScheme2)
142: + ":" + painter.getDisplayName() + ":"
143: + borderPainter.getDisplayName() + ":"
144: + button.getClass().getName() + ":" + state.name()
145: + ":" + button.getParent().getBackground().getRGB();
146: if (!ribbonBackgrounds.containsKey(key)) {
147: BufferedImage newBackground;
148:
149: Set<Side> bottom = new HashSet<Side>();
150: bottom.add(Side.BOTTOM);
151:
152: float radius = SubstanceSizeUtils
153: .getClassicButtonCornerRadius(SubstanceSizeUtils
154: .getComponentFontSize(button));
155: int borderDelta = (int) Math.ceil(2.0 * SubstanceSizeUtils
156: .getBorderStrokeWidth(SubstanceSizeUtils
157: .getComponentFontSize(button)));
158: int borderInsets = (int) Math.floor(SubstanceSizeUtils
159: .getBorderStrokeWidth(SubstanceSizeUtils
160: .getComponentFontSize(button)) / 2.0);
161: GeneralPath contour = BaseButtonShaper.getBaseOutline(
162: width, height + 2 + borderDelta, radius, bottom,
163: borderInsets);
164:
165: newBackground = painter.getContourBackground(width, height
166: + 2 + borderDelta, contour, false, colorScheme,
167: colorScheme2, cyclePos, true,
168: colorScheme != colorScheme2);
169:
170: int borderThickness = (int) SubstanceSizeUtils
171: .getBorderStrokeWidth(SubstanceSizeUtils
172: .getComponentFontSize(button));
173: GeneralPath contourInner = BaseButtonShaper.getBaseOutline(
174: width, height + 2 + borderDelta, radius, bottom,
175: borderThickness + borderInsets);
176:
177: borderPainter.paintBorder(newBackground.getGraphics(),
178: button, width, height + 2, contour, contourInner,
179: borderScheme, borderScheme2, cyclePos,
180: borderScheme != borderScheme2);
181:
182: BufferedImage finalBackground = SubstanceCoreUtilities
183: .getBlankImage(width, height);
184: Graphics2D finalGraphics = (Graphics2D) finalBackground
185: .getGraphics();
186: finalGraphics.drawImage(newBackground, 0, 0, null);
187:
188: if (button.isSelected()) {
189: int fw = finalBackground.getWidth();
190: int fh = finalBackground.getHeight();
191: BufferedImage fade = SubstanceCoreUtilities
192: .getBlankImage(fw, fh);
193: Graphics2D fadeGraphics = fade.createGraphics();
194: fadeGraphics.setColor(button.getParent()
195: .getBackground());
196: fadeGraphics.fillRect(0, 0, fw, fh);
197: SubstanceLookAndFeel.getCurrentWatermark()
198: .drawWatermarkImage(fadeGraphics, button, 0, 0,
199: fw, fh);
200:
201: borderPainter.paintBorder(fadeGraphics, button, width,
202: height + 2, contour, contourInner,
203: borderScheme, borderScheme, cyclePos, false);
204:
205: SubstanceTheme buttonTheme = SubstanceThemeUtilities
206: .getTheme(button);
207: finalBackground = SubstanceCoreUtilities
208: .blendImagesVertical(finalBackground, fade,
209: buttonTheme.getSelectedTabFadeStart(),
210: buttonTheme.getSelectedTabFadeEnd());
211: }
212:
213: ribbonBackgrounds.put(key, finalBackground);
214: }
215: return ribbonBackgrounds.get(key);
216: }
217:
218: /**
219: * Simple constructor.
220: */
221: public RibbonBackgroundDelegate() {
222: super ();
223: }
224:
225: /**
226: * Updates background of the specified button.
227: *
228: * @param g
229: * Graphic context.
230: * @param button
231: * Button to update.
232: * @param cycleCount
233: * Cycle count for transition effects.
234: */
235: public void updateBackground(Graphics g, AbstractButton button,
236: long cycleCount) {
237: button.setOpaque(false);
238: Graphics2D graphics = (Graphics2D) g.create();
239:
240: int width = button.getWidth();
241: int height = button.getHeight();
242:
243: try {
244: BufferedImage ribbonBackground = getRibbonBackground(
245: button, width, height);
246: if (ribbonBackground == null) {
247: // should call fill background
248: fillDelegate.update(graphics, button, false);
249: } else {
250: // check whether the toggle tab button is animating
251: // in a rollover sequence.
252: ComponentState state = ComponentState.getState(button
253: .getModel(), button);
254: if (FadeTracker.getInstance().isTracked(button,
255: FadeKind.ROLLOVER)
256: && !state.isKindActive(FadeKind.SELECTION)
257: && state.isKindActive(FadeKind.ENABLE)) {
258: // If the rollover fade is under way and the button
259: // is enabled and not selected, play with the background
260: // translucency (same as for flat buttons).
261: float fadeCoef = FadeTracker.getInstance()
262: .getFade10(button, FadeKind.ROLLOVER);
263: graphics.setComposite(TransitionLayout
264: .getAlphaComposite(button,
265: fadeCoef / 10.0f, g));
266: } else {
267: if (state == ComponentState.DEFAULT) {
268: return;
269: }
270: }
271: graphics.drawImage(ribbonBackground, 0, 0, null);
272: }
273: } finally {
274: graphics.dispose();
275: }
276: }
277: }
|