001: /*
002: * Copyright (c) 2001-2007 JGoodies Karsten Lentzsch. 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 JGoodies Karsten Lentzsch 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:
031: package com.jgoodies.looks.plastic;
032:
033: import java.awt.*;
034: import java.io.Serializable;
035:
036: import javax.swing.AbstractButton;
037: import javax.swing.ButtonModel;
038: import javax.swing.Icon;
039: import javax.swing.JCheckBox;
040: import javax.swing.UIManager;
041: import javax.swing.plaf.UIResource;
042: import javax.swing.plaf.metal.MetalLookAndFeel;
043:
044: import com.jgoodies.looks.LookUtils;
045:
046: /**
047: * Factory class that vends <code>Icon</code>s for the JGoodies
048: * Plastic XP look&feel.
049: * These icons are used extensively in PlasticXP via the defaults mechanism.
050: * While other look and feels often use GIFs for icons, creating icons
051: * in code facilitates switching to other themes.
052: * <p>
053: * Each method in this class returns either an <code>Icon</code> or
054: * <code>null</code>, where <code>null</code> implies that there is
055: * no default icon.
056: *
057: * @author Karsten Lentzsch
058: * @version $Revision: 1.6 $
059: */
060: public final class PlasticXPIconFactory {
061:
062: private static CheckBoxIcon checkBoxIcon;
063: private static RadioButtonIcon radioButtonIcon;
064:
065: private PlasticXPIconFactory() {
066: // Overrides default constructor; prevents instantiation.
067: }
068:
069: /**
070: * Lazily creates and returns the check box icon.
071: *
072: * @return the check box icon
073: */
074: static Icon getCheckBoxIcon() {
075: if (checkBoxIcon == null) {
076: checkBoxIcon = new CheckBoxIcon();
077: }
078: return checkBoxIcon;
079: }
080:
081: /**
082: * Lazily creates and returns the radio button icon.
083: *
084: * @return the check box icon
085: */
086: static Icon getRadioButtonIcon() {
087: if (radioButtonIcon == null) {
088: radioButtonIcon = new RadioButtonIcon();
089: }
090: return radioButtonIcon;
091: }
092:
093: /**
094: * Paints the the icon and focus border for Plastic XP check boxes.
095: */
096: private static final class CheckBoxIcon implements Icon,
097: UIResource, Serializable {
098:
099: private static final int SIZE = LookUtils.IS_LOW_RESOLUTION ? 13
100: : 15;
101:
102: public int getIconWidth() {
103: return SIZE;
104: }
105:
106: public int getIconHeight() {
107: return SIZE;
108: }
109:
110: public void paintIcon(Component c, Graphics g, int x, int y) {
111: JCheckBox cb = (JCheckBox) c;
112: ButtonModel model = cb.getModel();
113: Graphics2D g2 = (Graphics2D) g;
114: boolean paintFocus = (model.isArmed() && !model.isPressed())
115: || (cb.hasFocus() && isBlank(cb.getText()));
116:
117: final RenderingHints.Key key = RenderingHints.KEY_ANTIALIASING;
118: Object newAAHint = RenderingHints.VALUE_ANTIALIAS_ON;
119: Object oldAAHint = g2.getRenderingHint(key);
120: if (newAAHint != oldAAHint) {
121: g2.setRenderingHint(key, newAAHint);
122: } else {
123: oldAAHint = null;
124: }
125:
126: drawBorder(g2, model.isEnabled(), x, y, SIZE - 1, SIZE - 1);
127: drawFill(g2, model.isPressed(), x + 1, y + 1, SIZE - 2,
128: SIZE - 2);
129: if (paintFocus) {
130: drawFocus(g2, x + 1, y + 1, SIZE - 3, SIZE - 3);
131: }
132: if (model.isSelected()) {
133: drawCheck(g2, model.isEnabled(), x + 3, y + 3,
134: SIZE - 7, SIZE - 7);
135: }
136:
137: if (oldAAHint != null) {
138: g2.setRenderingHint(key, oldAAHint);
139: }
140: }
141:
142: private void drawBorder(Graphics2D g2, boolean enabled, int x,
143: int y, int width, int height) {
144: g2.setColor(enabled ? PlasticLookAndFeel
145: .getControlDarkShadow() : MetalLookAndFeel
146: .getControlDisabled());
147: g2.drawRect(x, y, width, height);
148: }
149:
150: private void drawCheck(Graphics2D g2, boolean enabled, int x,
151: int y, int width, int height) {
152: g2.setColor(enabled ? UIManager.getColor("CheckBox.check")
153: : MetalLookAndFeel.getControlDisabled());
154: int right = x + width;
155: int bottom = y + height;
156: int startY = y + height / 3;
157: int turnX = x + width / 2 - 2;
158: g2.drawLine(x, startY, turnX, bottom - 3);
159: g2.drawLine(x, startY + 1, turnX, bottom - 2);
160: g2.drawLine(x, startY + 2, turnX, bottom - 1);
161: g2.drawLine(turnX + 1, bottom - 2, right, y);
162: g2.drawLine(turnX + 1, bottom - 1, right, y + 1);
163: g2.drawLine(turnX + 1, bottom, right, y + 2);
164: }
165:
166: private void drawFill(Graphics2D g2, boolean pressed, int x,
167: int y, int w, int h) {
168: Color upperLeft;
169: Color lowerRight;
170: if (pressed) {
171: upperLeft = MetalLookAndFeel.getControlShadow();
172: lowerRight = PlasticLookAndFeel.getControlHighlight();
173: } else {
174: upperLeft = PlasticLookAndFeel.getControl();
175: lowerRight = PlasticLookAndFeel.getControlHighlight()
176: .brighter();
177: }
178: g2.setPaint(new GradientPaint(x, y, upperLeft, x + w,
179: y + h, lowerRight));
180: g2.fillRect(x, y, w, h);
181: }
182:
183: private void drawFocus(Graphics2D g2, int x, int y, int width,
184: int height) {
185: g2.setPaint(new GradientPaint(x, y, PlasticLookAndFeel
186: .getFocusColor().brighter(), width, height,
187: PlasticLookAndFeel.getFocusColor() /*.darker()*/
188: ));
189: g2.drawRect(x, y, width, height);
190: g2.drawRect(x + 1, y + 1, width - 2, height - 2);
191: }
192:
193: }
194:
195: /**
196: * Paints the the icon and focus border for Plastic XP check boxes.
197: */
198: private static final class RadioButtonIcon implements Icon,
199: UIResource, Serializable {
200:
201: private static final int SIZE = LookUtils.IS_LOW_RESOLUTION ? 13
202: : 15;
203:
204: private static final Stroke FOCUS_STROKE = new BasicStroke(2);
205:
206: public int getIconWidth() {
207: return SIZE;
208: }
209:
210: public int getIconHeight() {
211: return SIZE;
212: }
213:
214: public void paintIcon(Component c, Graphics g, int x, int y) {
215: Graphics2D g2 = (Graphics2D) g;
216: AbstractButton rb = (AbstractButton) c;
217: ButtonModel model = rb.getModel();
218: boolean paintFocus = (model.isArmed() && !model.isPressed())
219: || (rb.hasFocus() && isBlank(rb.getText()));
220:
221: final RenderingHints.Key key = RenderingHints.KEY_ANTIALIASING;
222: Object newAAHint = RenderingHints.VALUE_ANTIALIAS_ON;
223: Object oldAAHint = g2.getRenderingHint(key);
224: if (newAAHint != oldAAHint) {
225: g2.setRenderingHint(key, newAAHint);
226: } else {
227: oldAAHint = null;
228: }
229:
230: drawFill(g2, model.isPressed(), x, y, SIZE - 1, SIZE - 1);
231: if (paintFocus) {
232: drawFocus(g2, x + 1, y + 1, SIZE - 3, SIZE - 3);
233: }
234: if (model.isSelected()) {
235: drawCheck(g2, c, model.isEnabled(), x + 4, y + 4,
236: SIZE - 8, SIZE - 8);
237: }
238: drawBorder(g2, model.isEnabled(), x, y, SIZE - 1, SIZE - 1);
239:
240: if (oldAAHint != null) {
241: g2.setRenderingHint(key, oldAAHint);
242: }
243: }
244:
245: private void drawBorder(Graphics2D g2, boolean enabled, int x,
246: int y, int w, int h) {
247: g2.setColor(enabled ? PlasticLookAndFeel
248: .getControlDarkShadow() : MetalLookAndFeel
249: .getControlDisabled());
250: g2.drawOval(x, y, w, h);
251: }
252:
253: private void drawCheck(Graphics2D g2, Component c,
254: boolean enabled, int x, int y, int w, int h) {
255: g2.translate(x, y);
256: if (enabled) {
257: g2.setColor(UIManager.getColor("RadioButton.check"));
258: g2.fillOval(0, 0, w, h);
259: UIManager.getIcon("RadioButton.checkIcon").paintIcon(c,
260: g2, 0, 0);
261: } else {
262: g2.setColor(MetalLookAndFeel.getControlDisabled());
263: g2.fillOval(0, 0, w, h);
264: }
265: g2.translate(-x, -y);
266: }
267:
268: private void drawFill(Graphics2D g2, boolean pressed, int x,
269: int y, int w, int h) {
270: Color upperLeft;
271: Color lowerRight;
272: if (pressed) {
273: upperLeft = MetalLookAndFeel.getControlShadow();
274: lowerRight = PlasticLookAndFeel.getControlHighlight();
275: } else {
276: upperLeft = PlasticLookAndFeel.getControl();
277: lowerRight = PlasticLookAndFeel.getControlHighlight()
278: .brighter();
279: }
280: g2.setPaint(new GradientPaint(x, y, upperLeft, x + w,
281: y + h, lowerRight));
282: g2.fillOval(x, y, w, h);
283: }
284:
285: private void drawFocus(Graphics2D g2, int x, int y, int w, int h) {
286: g2.setPaint(new GradientPaint(x, y, PlasticLookAndFeel
287: .getFocusColor().brighter(), w, h,
288: PlasticLookAndFeel.getFocusColor()));
289: Stroke stroke = g2.getStroke();
290: g2.setStroke(FOCUS_STROKE);
291: g2.drawOval(x, y, w, h);
292: g2.setStroke(stroke);
293: }
294:
295: }
296:
297: // Helper Code ************************************************************
298:
299: /**
300: * Checks and answers if the given string is whitespace,
301: * empty ("") or <code>null</code>.
302: *
303: * <pre>
304: * isBlank(null) == true
305: * isBlank("") == true
306: * isBlank(" ") == true
307: * isBlank(" abc") == false
308: * isBlank("abc ") == false
309: * isBlank(" abc ") == false
310: * </pre>
311: *
312: * @param str the string to check, may be <code>null</code>
313: * @return <code>true</code> if the string is whitespace, empty
314: * or <code>null</code>
315: */
316: private static boolean isBlank(String str) {
317: int length;
318: if ((str == null) || ((length = str.length()) == 0))
319: return true;
320: for (int i = length - 1; i >= 0; i--) {
321: if (!Character.isWhitespace(str.charAt(i)))
322: return false;
323: }
324: return true;
325: }
326:
327: }
|