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;
031:
032: import java.awt.*;
033: import java.awt.geom.GeneralPath;
034: import java.awt.image.BufferedImage;
035: import java.util.*;
036:
037: import javax.swing.AbstractButton;
038:
039: import org.jvnet.lafwidget.animation.*;
040: import org.jvnet.substance.SubstanceImageCreator;
041: import org.jvnet.substance.SubstanceLookAndFeel;
042: import org.jvnet.substance.border.SubstanceBorderPainter;
043: import org.jvnet.substance.button.BaseButtonShaper;
044: import org.jvnet.substance.color.ColorScheme;
045: import org.jvnet.substance.painter.*;
046: import org.jvnet.substance.theme.SubstanceTheme;
047: import org.jvnet.substance.utils.ComponentState.ColorSchemeKind;
048: import org.jvnet.substance.utils.SubstanceConstants.Side;
049:
050: /**
051: * Delegate class for painting backgrounds of buttons in <b>Substance </b> look
052: * and feel. This class is <b>for internal use only</b>.
053: *
054: * @author Kirill Grouchnikov
055: */
056: public class PairwiseButtonBackgroundDelegate {
057: /**
058: * Cache for background images for pairwise backgrounds. Each time
059: * {@link #getPairwiseBackground(AbstractButton, int, int, Side)} is called,
060: * it checks <code>this</code> map to see if it already contains such
061: * background. If so, the background from the map is returned.
062: */
063: private static Map<String, BufferedImage> pairwiseBackgrounds = new HashMap<String, BufferedImage>();
064:
065: /**
066: * Contains information on a button background.
067: *
068: * @author Kirill Grouchnikov
069: */
070: public static class ButtonBackground {
071: /**
072: * Indicates whether the button is painted in active visual state.
073: */
074: public boolean isPaintedActive;
075:
076: /**
077: * The button background image.
078: */
079: public BufferedImage backgroundImage;
080:
081: /**
082: * Button background info object.
083: *
084: * @param isPaintedActive
085: * Indicates whether the button is painted in active visual
086: * state.
087: * @param backgroundImage
088: * The button background image.
089: */
090: public ButtonBackground(boolean isPaintedActive,
091: BufferedImage backgroundImage) {
092: this .isPaintedActive = isPaintedActive;
093: this .backgroundImage = backgroundImage;
094: }
095:
096: }
097:
098: /**
099: * Resets image maps (used when setting new theme).
100: *
101: * @see SubstanceLookAndFeel#setCurrentTheme(String)
102: * @see SubstanceLookAndFeel#setCurrentTheme(SubstanceTheme)
103: */
104: public static synchronized void reset() {
105: pairwiseBackgrounds.clear();
106: }
107:
108: /**
109: * Retrieves background image for the specified button in button pair (such
110: * as scrollbar arrows, for example).
111: *
112: * @param button
113: * Button.
114: * @param painter
115: * Gradient painter.
116: * @param width
117: * Button width.
118: * @param height
119: * Button height.
120: * @param side
121: * Button orientation.
122: * @param toIgnoreOpenSides
123: * If <code>true</code>, the open side setting (controlled by
124: * the {@link SubstanceLookAndFeel#BUTTON_OPEN_SIDE_PROPERTY} is
125: * ignored.
126: * @return Button background image.
127: */
128: public static BufferedImage getPairwiseBackground(
129: AbstractButton button, SubstanceGradientPainter painter,
130: int width, int height, SubstanceConstants.Side side,
131: boolean toIgnoreOpenSides) {
132: if (SubstanceCoreUtilities.isButtonNeverPainted(button))
133: return null;
134:
135: BufferedImage resultNonFlat = null;
136: ComponentState state = ComponentState.getState(button
137: .getModel(), button);
138: ComponentState prevState = SubstanceCoreUtilities
139: .getPrevComponentState(button);
140:
141: float cyclePos = state.getCycleCount();
142: ControlBackgroundComposite controlComposite = SubstanceCoreUtilities
143: .getControlBackgroundComposite(button);
144:
145: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(button,
146: state);
147: ColorScheme colorScheme = theme.getColorScheme();
148: ColorScheme colorScheme2 = colorScheme;
149: ColorScheme borderScheme = theme.getBorderTheme()
150: .getColorScheme();
151: ColorScheme borderScheme2 = borderScheme;
152:
153: FadeTracker fadeTracker = FadeTracker.getInstance();
154: FadeState fadeState = fadeTracker.getFadeState(button,
155: FadeKind.ROLLOVER);
156: if (fadeState != null) {
157: Composite defaultComposite = controlComposite
158: .getBackgroundComposite(button, button.getParent(),
159: -1, false);
160: Composite activeComposite = controlComposite
161: .getBackgroundComposite(button, button.getParent(),
162: -1, true);
163:
164: colorScheme = SubstanceThemeUtilities.getTheme(button,
165: prevState).getColorScheme();
166: colorScheme2 = SubstanceThemeUtilities.getTheme(button,
167: state).getColorScheme();
168: borderScheme = SubstanceThemeUtilities.getTheme(button,
169: state).getBorderTheme().getColorScheme();
170: borderScheme2 = SubstanceThemeUtilities.getTheme(button,
171: prevState).getBorderTheme().getColorScheme();
172: cyclePos = fadeState.getFadePosition();
173: if (!fadeState.isFadingIn())
174: cyclePos = 10 - cyclePos;
175:
176: // System.out.println("\t" + width + ":" + prevState.name() + ":"
177: // + colorScheme.getClass().getSimpleName() + " -> "
178: // + state.name() + ":"
179: // + colorScheme2.getClass().getSimpleName() + " at "
180: // + cyclePos);
181:
182: BufferedImage imageDefault = getPairwiseBackground(button,
183: painter, width, height, side, cyclePos,
184: colorScheme, colorScheme2, borderScheme,
185: borderScheme2, defaultComposite, toIgnoreOpenSides);
186: BufferedImage imageActive = getPairwiseBackground(button,
187: painter, width, height, side, cyclePos,
188: colorScheme, colorScheme2, borderScheme,
189: borderScheme2, activeComposite, toIgnoreOpenSides);
190: if (imageDefault == null)
191: return null;
192: Graphics2D graphics = imageDefault.createGraphics();
193: graphics.setComposite(AlphaComposite.getInstance(
194: AlphaComposite.SRC_OVER, cyclePos / 10.0f));
195: // System.out.println("Painting [" + colorScheme + "-->"
196: // + colorScheme2 + "]" + cyclePos / 10.0f);
197: graphics.drawImage(imageActive, 0, 0, null);
198: resultNonFlat = imageDefault;
199: } else {
200: Composite graphicsComposite = controlComposite
201: .getBackgroundComposite(
202: button,
203: button.getParent(),
204: 0,
205: (ComponentState.getState(button.getModel(),
206: null).getColorSchemeKind() == ColorSchemeKind.CURRENT));
207: // System.out.println("\t" + width + ":" +
208: // colorScheme.getClass().getSimpleName() + ":"
209: // + colorScheme2.getClass().getSimpleName());
210: resultNonFlat = getPairwiseBackground(button, painter,
211: width, height, side, 0, colorScheme, colorScheme2,
212: borderScheme, borderScheme2, graphicsComposite,
213: toIgnoreOpenSides);
214: }
215:
216: BufferedImage result = SubstanceCoreUtilities.getBlankImage(
217: width, height);
218: Graphics2D resultGr = result.createGraphics();
219: if (SubstanceCoreUtilities.hasFlatAppearance(
220: button.getParent(), false)) {
221: // Special handling of flat buttons
222: BufferedImage temp = SubstanceCoreUtilities
223: .getBlankImage(resultNonFlat.getWidth(),
224: resultNonFlat.getHeight());
225: Graphics2D tempGr = temp.createGraphics();
226: try {
227: if (FadeTracker.getInstance().isTracked(button,
228: FadeKind.ROLLOVER)
229: && !state.isKindActive(FadeKind.SELECTION)
230: && state.isKindActive(FadeKind.ENABLE)) {
231: float fadeCoef = FadeTracker.getInstance()
232: .getFade10(button, FadeKind.ROLLOVER);
233: tempGr.setComposite(AlphaComposite.getInstance(
234: AlphaComposite.SRC_OVER, fadeCoef / 10.0f));
235: tempGr.drawImage(resultNonFlat, 0, 0, null);
236: } else {
237: if ((state != ComponentState.DISABLED_UNSELECTED)
238: && (state != ComponentState.DEFAULT)) {
239: tempGr.drawImage(resultNonFlat, 0, 0, null);
240: }
241: }
242: resultGr.drawImage(temp, 0, 0, null);
243: } finally {
244: tempGr.dispose();
245: }
246: } else {
247: resultGr.setComposite(AlphaComposite.getInstance(
248: AlphaComposite.SRC_OVER, SubstanceThemeUtilities
249: .getTheme(button).getThemeAlpha(button,
250: state)));
251: resultGr.drawImage(resultNonFlat, 0, 0, null);
252: }
253: return result;
254: }
255:
256: /**
257: * Retrieves background image for the specified button in button pair (such
258: * as scrollbar arrows, for example).
259: *
260: * @param button
261: * Button.
262: * @param kind
263: * Color scheme kind.
264: * @param painter
265: * Gradient painter.
266: * @param width
267: * Button width.
268: * @param height
269: * Button height.
270: * @param side
271: * Button orientation.
272: * @param cyclePos
273: * Cycle position.
274: * @param colorScheme
275: * The first color scheme.
276: * @param colorScheme2
277: * The second color scheme.
278: * @param borderScheme
279: * The first border color scheme.
280: * @param borderScheme2
281: * The second border color scheme.
282: * @param graphicsComposite
283: * Composite to apply before painting the button.
284: * @param toIgnoreOpenSides
285: * If <code>true</code>, the open side setting (controlled by
286: * the {@link SubstanceLookAndFeel#BUTTON_OPEN_SIDE_PROPERTY} is
287: * ignored.
288: * @return Button background image.
289: */
290: private synchronized static BufferedImage getPairwiseBackground(
291: AbstractButton button, SubstanceGradientPainter painter,
292: int width, int height, SubstanceConstants.Side side,
293: float cyclePos, ColorScheme colorScheme,
294: ColorScheme colorScheme2, ColorScheme borderScheme,
295: ColorScheme borderScheme2, Composite graphicsComposite,
296: boolean toIgnoreOpenSides) {
297: if (SubstanceCoreUtilities.isButtonNeverPainted(button))
298: return null;
299: Set<Side> openSides = toIgnoreOpenSides ? new HashSet<Side>()
300: : SubstanceCoreUtilities.getSides(button,
301: SubstanceLookAndFeel.BUTTON_OPEN_SIDE_PROPERTY);
302: String openKey = "";
303: for (Side oSide : openSides) {
304: openKey += oSide.name() + "-";
305: }
306: boolean noBorder = SubstanceCoreUtilities
307: .isSpinnerButton(button)
308: && !button.getParent().isEnabled();
309: String sideKey = (side == null) ? "null" : side.toString();
310: String key = width + ":" + height + ":" + sideKey + ":"
311: + cyclePos + ":" + openKey + ":"
312: + SubstanceCoreUtilities.getSchemeId(colorScheme) + ":"
313: + SubstanceCoreUtilities.getSchemeId(colorScheme2)
314: + ":"
315: + SubstanceCoreUtilities.getSchemeId(borderScheme)
316: + ":"
317: + SubstanceCoreUtilities.getSchemeId(borderScheme2)
318: + ":" + button.getClass().getName() + ":"
319: + painter.getDisplayName() + ":" + noBorder;
320: // System.out.println("\tKey " + key);
321: if (!pairwiseBackgrounds.containsKey(key)) {
322: // System.out.println("\tNot found");
323: BufferedImage newBackground = null;
324:
325: // buttons will be rectangular to make two scrolls (horizontal
326: // and vertical) touch the corners.
327: int radius = 0;
328:
329: int deltaLeft = openSides.contains(Side.LEFT) ? 3 : 0;
330: int deltaRight = openSides.contains(Side.RIGHT) ? 3 : 0;
331: int deltaTop = openSides.contains(Side.TOP) ? 3 : 0;
332: int deltaBottom = openSides.contains(Side.BOTTOM) ? 3 : 0;
333:
334: GeneralPath contour = null;
335:
336: // SubstanceGradientPainter painter = new StandardGradientPainter();
337:
338: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
339: .getBorderPainter(button);
340:
341: int borderDelta = (int) Math.floor(SubstanceSizeUtils
342: .getBorderStrokeWidth(SubstanceSizeUtils
343: .getComponentFontSize(button)) / 2.0);
344: if (side != null) {
345: switch (side) {
346: case TOP:
347: case BOTTOM:
348: // arrow in vertical bar
349: contour = BaseButtonShaper.getBaseOutline(height
350: + deltaTop + deltaBottom, width + deltaLeft
351: + deltaRight, radius, null, borderDelta);
352:
353: newBackground = painter.getContourBackground(height
354: + deltaTop + deltaBottom, width + deltaLeft
355: + deltaRight, contour, false, colorScheme,
356: colorScheme2, cyclePos, true,
357: colorScheme != colorScheme2);
358: if (!noBorder) {
359: borderPainter
360: .paintBorder(newBackground
361: .getGraphics(), button, height
362: + deltaTop + deltaBottom, width
363: + deltaLeft + deltaRight,
364: contour, null, borderScheme,
365: borderScheme2, cyclePos,
366: borderScheme != borderScheme2);
367: }
368: newBackground = SubstanceImageCreator.getRotated(
369: newBackground, 3);
370: break;
371: case RIGHT:
372: case LEFT:
373: // arrow in horizontal bar
374: contour = BaseButtonShaper.getBaseOutline(width
375: + deltaLeft + deltaRight, height + deltaTop
376: + deltaBottom, radius, null, borderDelta);
377:
378: newBackground = painter.getContourBackground(width
379: + deltaLeft + deltaRight, height + deltaTop
380: + deltaBottom, contour, false, colorScheme,
381: colorScheme2, cyclePos, true,
382: colorScheme != colorScheme2);
383: if (!noBorder) {
384: borderPainter
385: .paintBorder(
386: newBackground.getGraphics(),
387: button,
388: width + deltaLeft + deltaRight,
389: height + deltaTop + deltaBottom,
390: contour, null, borderScheme,
391: borderScheme2, cyclePos,
392: borderScheme != borderScheme2);
393: }
394: break;
395: }
396: } else {
397: contour = BaseButtonShaper.getBaseOutline(width
398: + deltaLeft + deltaRight, height + deltaTop
399: + deltaBottom, radius, null, borderDelta);
400:
401: newBackground = painter.getContourBackground(width
402: + deltaLeft + deltaRight, height + deltaTop
403: + deltaBottom, contour, false, colorScheme,
404: colorScheme2, cyclePos, true,
405: colorScheme != colorScheme2);
406: if (!noBorder) {
407: borderPainter.paintBorder(newBackground
408: .getGraphics(), button, width + deltaLeft
409: + deltaRight, height + deltaTop
410: + deltaBottom, contour, null, borderScheme,
411: borderScheme2, cyclePos,
412: borderScheme != borderScheme2);
413: }
414: }
415:
416: BufferedImage finalBackground = SubstanceCoreUtilities
417: .getBlankImage(width, height);
418: Graphics2D finalGraphics = (Graphics2D) finalBackground
419: .getGraphics();
420: finalGraphics.translate(-deltaLeft, -deltaTop);
421: finalGraphics.drawImage(newBackground, 0, 0, null);
422: // borderPainter.paintBorder(finalGraphics, width, height, contour,
423: // colorScheme, colorScheme2, cyclePos,
424: // colorScheme != colorScheme2);
425:
426: // System.out.println("\tCreated new background " + width + ":" +
427: // height);
428: pairwiseBackgrounds.put(key, finalBackground);
429: }
430: BufferedImage opaque = pairwiseBackgrounds.get(key);
431: BufferedImage result = SubstanceCoreUtilities.getBlankImage(
432: opaque.getWidth(), opaque.getHeight());
433: Graphics2D resultGr = result.createGraphics();
434: resultGr.setComposite(graphicsComposite);
435: resultGr.drawImage(opaque, 0, 0, null);
436: resultGr.dispose();
437: return result;
438: }
439:
440: /**
441: * Simple constructor.
442: */
443: public PairwiseButtonBackgroundDelegate() {
444: super ();
445: }
446:
447: /**
448: * Updates background of the specified button.
449: *
450: * @param g
451: * Graphic context.
452: * @param button
453: * Button to update.
454: * @param side
455: * Button side.
456: */
457: public static void updateBackground(Graphics g,
458: AbstractButton button, SubstanceConstants.Side side) {
459: Graphics2D graphics = (Graphics2D) g.create();
460:
461: SubstanceGradientPainter painter = SubstanceCoreUtilities
462: .getGradientPainter(button);
463: int width = button.getWidth();
464: int height = button.getHeight();
465:
466: painter = new SimplisticSoftBorderReverseGradientPainter();
467: BufferedImage bgImage = getPairwiseBackground(button, painter,
468: width, height, side, false);
469:
470: graphics.drawImage(bgImage, 0, 0, null);
471: graphics.dispose();
472: }
473: }
|