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.painter.decoration;
031:
032: import java.awt.*;
033: import java.awt.image.BufferedImage;
034: import java.util.LinkedHashMap;
035: import java.util.Map.Entry;
036:
037: import javax.swing.*;
038:
039: import org.jvnet.lafwidget.layout.TransitionLayout;
040: import org.jvnet.substance.SubstanceImageCreator;
041: import org.jvnet.substance.theme.SubstanceTheme;
042: import org.jvnet.substance.utils.SubstanceCoreUtilities;
043:
044: /**
045: * Implementation of {@link SubstanceDecorationPainter} that uses brushed metal
046: * painting on decoration areas.
047: *
048: * @author Kirill Grouchnikov
049: * @since version 4.3
050: */
051: public abstract class ImageWrapperDecorationPainter implements
052: SubstanceDecorationPainter {
053: /**
054: * Contains the original (not colorized) image of this painter.
055: */
056: protected Image originalTile = null;
057:
058: /**
059: * The base decoration painter - the colorized image tiles are painted over
060: * the painting of this painter. Can be <code>null</code>.
061: */
062: protected SubstanceDecorationPainter baseDecorationPainter;
063:
064: /**
065: * Map of colorized tiles.
066: */
067: protected LinkedHashMap<String, Image> colorizedTileMap;
068:
069: /**
070: * Alpha channel for the texture image (colorized tiles applied on top of
071: * the {@link #baseDecorationPainter} painting).
072: */
073: protected float textureAlpha;
074:
075: /**
076: * Indication whether this painter paints horizontal separators on the
077: * bottom of each component passed to
078: * {@link #paintDecorationArea(Graphics2D, Component, DecorationAreaType, int, int, SubstanceTheme, float)}.
079: */
080: protected boolean isPaintingSeparators;
081:
082: /**
083: * Creates a new image wrapper decoration painter.
084: */
085: public ImageWrapperDecorationPainter() {
086: this .isPaintingSeparators = false;
087: this .textureAlpha = 0.3f;
088:
089: this .colorizedTileMap = new LinkedHashMap<String, Image>() {
090: @Override
091: protected boolean removeEldestEntry(
092: Entry<String, Image> eldest) {
093: return this .size() > 10;
094: }
095: };
096: }
097:
098: /*
099: * (non-Javadoc)
100: *
101: * @see org.jvnet.substance.painter.decoration.SubstanceDecorationPainter#paintDecorationArea(java.awt.Graphics2D,
102: * java.awt.Component,
103: * org.jvnet.substance.painter.decoration.DecorationAreaType, int, int,
104: * org.jvnet.substance.theme.SubstanceTheme)
105: */
106: public void paintDecorationArea(Graphics2D graphics,
107: Component comp, DecorationAreaType decorationAreaType,
108: int width, int height, SubstanceTheme theme) {
109: switch (decorationAreaType) {
110: case PRIMARY_TITLE_PANE:
111: case SECONDARY_TITLE_PANE:
112: this .paintTitleBackground(graphics, comp,
113: decorationAreaType, width, height, theme);
114: break;
115: default:
116: this .paintExtraBackground(graphics, SubstanceCoreUtilities
117: .getHeaderParent(comp), comp, decorationAreaType,
118: width, height, theme);
119: }
120: }
121:
122: private void paintTitleBackground(Graphics2D graphics,
123: Component comp, DecorationAreaType decorationAreaType,
124: int width, int height, SubstanceTheme theme) {
125:
126: SubstanceTheme tileTheme = theme;
127: if (this .baseDecorationPainter == null) {
128: tileTheme = theme.getActiveTitlePaneTheme();
129: graphics.setColor(tileTheme.getColorScheme().getMidColor());
130: graphics.fillRect(0, 0, width, height);
131: } else {
132: this .baseDecorationPainter.paintDecorationArea(graphics,
133: comp, decorationAreaType, width, height, theme);
134: }
135:
136: Graphics2D temp = (Graphics2D) graphics.create();
137: this .tileArea(temp, comp, tileTheme, 0, 0, 0, 0, width, height);
138:
139: if (this .isPaintingSeparators) {
140: temp.translate(0, height - 1);
141: SubstanceCoreUtilities.paintSeparator(comp, temp, theme
142: .getActiveTitlePaneTheme().getColorScheme(),
143: SubstanceCoreUtilities.isThemeDark(theme
144: .getActiveTitlePaneTheme()), width, 1,
145: JSeparator.HORIZONTAL, false, 0);
146: temp.dispose();
147: }
148: }
149:
150: private void paintExtraBackground(Graphics2D graphics,
151: Container parent, Component comp,
152: DecorationAreaType decorationAreaType, int width,
153: int height, SubstanceTheme theme) {
154:
155: JRootPane rootPane = SwingUtilities.getRootPane(parent);
156: // fix for bug 234 - Window doesn't have a root pane.
157: int dx = 0;
158: int dy = 0;
159: JComponent titlePane = null;
160: if (rootPane != null) {
161: titlePane = SubstanceCoreUtilities.getTitlePane(rootPane);
162:
163: if (titlePane != null) {
164: if (comp.isShowing() && titlePane.isShowing()) {
165: dx += (comp.getLocationOnScreen().x - titlePane
166: .getLocationOnScreen().x);
167: dy += (comp.getLocationOnScreen().y - titlePane
168: .getLocationOnScreen().y);
169: } else {
170: // have to traverse the hierarchy
171: Component c = comp;
172: dx = 0;
173: dy = 0;
174: while (c != rootPane) {
175: dx += c.getX();
176: dy += c.getY();
177: c = c.getParent();
178: }
179: c = titlePane;
180: while (c != rootPane) {
181: dx -= c.getX();
182: dy -= c.getY();
183: c = c.getParent();
184: }
185: }
186: }
187: }
188:
189: SubstanceTheme tileTheme = theme;
190: if (this .baseDecorationPainter != null) {
191: this .baseDecorationPainter.paintDecorationArea(graphics,
192: comp, decorationAreaType, width, height, theme);
193: } else {
194: tileTheme = theme.getActiveTitlePaneTheme();
195: graphics.setColor(tileTheme.getColorScheme().getMidColor());
196: graphics.fillRect(0, 0, width, height);
197: }
198: Graphics2D temp = (Graphics2D) graphics.create();
199: this .tileArea(temp, comp, tileTheme, dx, dy, 0, 0, width,
200: height);
201:
202: // paint separators only on top-level toolbars (not on toolbars
203: // embedded in other toolbars).
204: // if (this.isPaintingSeparators
205: // && (SwingUtilities.getAncestorOfClass(JToolBar.class, comp) == null)
206: // && (SubstanceDecorationUtilities
207: // .getImmediateDecorationType(comp) != null)) {
208: // int pWidth = (titlePane == null) ? parent.getWidth() : titlePane
209: // .getWidth();
210: //
211: // temp.translate(-dx, height - 1);
212: // SubstanceCoreUtilities.paintSeparator(comp, temp, theme
213: // .getActiveTitlePaneTheme().getColorScheme(),
214: // SubstanceCoreUtilities.isThemeDark(theme
215: // .getActiveTitlePaneTheme()), pWidth, 1,
216: // JSeparator.HORIZONTAL, false, 0);
217: // }
218:
219: if (this .isPaintingSeparators) {
220: int totalOffsetY = height - 1;
221: // Component c = comp;
222:
223: int deltaY = 0;
224: // int totalOffsetY = 0;
225: Component c = comp;
226: while (c.getParent() != null) {
227: Component cParent = c.getParent();
228: deltaY += (cParent.getHeight() - (c.getHeight() + c
229: .getY()));
230: if (SubstanceDecorationUtilities
231: .getImmediateDecorationType(cParent) != null) {
232: totalOffsetY += deltaY;
233: deltaY = 0;
234: }
235: c = cParent;
236: }
237:
238: // while (c != null) {
239: // if (SubstanceDecorationUtilities.getImmediateDecorationType(c) !=
240: // null) {
241: // break;
242: // }
243: // Component cParent = c.getParent();
244: // if (cParent != null) {
245: // totalOffsetY += (cParent.getHeight() - (c.getHeight() + c
246: // .getY()));
247: // }
248: // c = cParent;
249: // }
250: int pWidth = (titlePane == null) ? parent.getWidth()
251: : titlePane.getWidth();
252:
253: temp.translate(-dx, totalOffsetY);
254: SubstanceCoreUtilities.paintSeparator(comp, temp, theme
255: .getActiveTitlePaneTheme().getColorScheme(),
256: SubstanceCoreUtilities.isThemeDark(theme
257: .getActiveTitlePaneTheme()), pWidth, 1,
258: JSeparator.HORIZONTAL, false, 0);
259: }
260:
261: temp.dispose();
262: }
263:
264: /**
265: * Tiles the specified area with colorized version of the image tile. This
266: * is called after the {@link #baseDecorationPainter} has painted the area.
267: * This method should respect the current {@link #textureAlpha} value.
268: *
269: * @param g
270: * Graphic context.
271: * @param comp
272: * Component.
273: * @param tileTheme
274: * Theme for the tile colorization.
275: * @param offsetTextureX
276: * X offset for the tiling.
277: * @param offsetTextureY
278: * Y offset for the tiling.
279: * @param x
280: * X coordinate of the tiling region.
281: * @param y
282: * Y coordinate of the tiling region.
283: * @param width
284: * Width of the tiling region.
285: * @param height
286: * Height of the tiling region.
287: */
288: protected void tileArea(Graphics2D g, Component comp,
289: SubstanceTheme tileTheme, int offsetTextureX,
290: int offsetTextureY, int x, int y, int width, int height) {
291:
292: Graphics2D graphics = (Graphics2D) g.create();
293: graphics.setComposite(TransitionLayout.getAlphaComposite(comp,
294: this .textureAlpha, g));
295:
296: Image colorizedTile = this .getColorizedTile(tileTheme);
297: int tileWidth = colorizedTile.getWidth(null);
298: int tileHeight = colorizedTile.getHeight(null);
299:
300: offsetTextureX = offsetTextureX % tileWidth;
301: offsetTextureY = offsetTextureY % tileHeight;
302: int currTileTop = -offsetTextureY;
303: do {
304: int currTileLeft = -offsetTextureX;
305: do {
306: graphics.drawImage(colorizedTile, currTileLeft,
307: currTileTop, null);
308: currTileLeft += tileWidth;
309: } while (currTileLeft < width);
310: currTileTop += tileHeight;
311: } while (currTileTop < height);
312:
313: graphics.dispose();
314: }
315:
316: /**
317: * Sets the base decoration painter.
318: *
319: * @param baseDecorationPainter
320: * Base decoration painter.
321: */
322: public void setBaseDecorationPainter(
323: SubstanceDecorationPainter baseDecorationPainter) {
324: this .baseDecorationPainter = baseDecorationPainter;
325: }
326:
327: /**
328: * Sets the alpha channel for the image texture.
329: *
330: * @param textureAlpha
331: * Alpha channel for the image texture.
332: */
333: public void setTextureAlpha(float textureAlpha) {
334: this .textureAlpha = textureAlpha;
335: }
336:
337: /**
338: * Sets indication whether this painter paints separators.
339: *
340: * @param isPaintingSeparators
341: * If <code>true</code>, this painter will paint horizontal
342: * separators on the bottom of each component passed to either
343: * {@link #paintTitleBackground(Graphics2D, Component, int, int, int, int, SubstanceTheme, float)}
344: * or
345: * {@link #paintExtraBackground(Graphics2D, Container, Component, int, int, SubstanceTheme, boolean)}.
346: */
347: public void setPaintingSeparators(boolean isPaintingSeparators) {
348: this .isPaintingSeparators = isPaintingSeparators;
349: }
350:
351: /**
352: * Returns a colorized image tile.
353: *
354: * @param theme
355: * Theme for the colorization.
356: * @return Colorized tile.
357: */
358: protected synchronized Image getColorizedTile(SubstanceTheme theme) {
359: Image result = this .colorizedTileMap
360: .get(theme.getDisplayName());
361: if (result == null) {
362: BufferedImage tileBi = new BufferedImage(this .originalTile
363: .getWidth(null), this .originalTile.getHeight(null),
364: BufferedImage.TYPE_INT_ARGB);
365: tileBi.getGraphics().drawImage(this .originalTile, 0, 0,
366: null);
367: result = SubstanceImageCreator.getThemeImage(tileBi, theme,
368: false);
369: this.colorizedTileMap.put(theme.getDisplayName(), result);
370: }
371: return result;
372: }
373: }
|