001: /*
002: #IFNDEF ALT_LICENSE
003: ThinWire(R) RIA Ajax Framework
004: Copyright (C) 2003-2007 Custom Credit Systems
005:
006: This library is free software; you can redistribute it and/or modify it under
007: the terms of the GNU Lesser General Public License as published by the Free
008: Software Foundation; either version 2.1 of the License, or (at your option) any
009: later version.
010:
011: This library is distributed in the hope that it will be useful, but WITHOUT ANY
012: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013: PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014:
015: You should have received a copy of the GNU Lesser General Public License along
016: with this library; if not, write to the Free Software Foundation, Inc., 59
017: Temple Place, Suite 330, Boston, MA 02111-1307 USA
018:
019: Users who would rather have a commercial license, warranty or support should
020: contact the following company who invented, built and supports the technology:
021:
022: Custom Credit Systems, Richardson, TX 75081, USA.
023: email: info@thinwire.com ph: +1 (888) 644-6405
024: http://www.thinwire.com
025: #ENDIF
026: [ v1.2_RC2 ]
027: */
028: package thinwire.ui.style;
029:
030: import java.util.ArrayList;
031: import java.util.Collections;
032: import java.util.Comparator;
033: import java.util.HashMap;
034: import java.util.List;
035: import java.util.Map;
036:
037: import thinwire.render.web.WebApplication;
038: import thinwire.ui.Application;
039:
040: /**
041: * @author Joshua J. Gertzen
042: */
043: public class Font {
044: public static final String PROPERTY_FONT_FAMILY = "fontFamily";
045: public static final String PROPERTY_FONT_COLOR = "fontColor";
046: public static final String PROPERTY_FONT_SIZE = "fontSize";
047: public static final String PROPERTY_FONT_BOLD = "fontBold";
048: public static final String PROPERTY_FONT_ITALIC = "fontItalic";
049: public static final String PROPERTY_FONT_UNDERLINE = "fontUnderline";
050: public static final String PROPERTY_FONT_STRIKE = "fontStrike";
051:
052: public static final class Family {
053: private static List<Family> VALUES = new ArrayList<Family>();
054:
055: public static final Family SERIF = new Family("serif", null);
056: public static final Family SANS_SERIF = new Family(
057: "sans-serif", null);
058: public static final Family CURSIVE = new Family("cursive", null);
059: public static final Family FANTASY = new Family("fantasy", null);
060: public static final Family MONOSPACE = new Family("monospace",
061: null);
062: static {
063: Collections.sort(VALUES, new Comparator<Family>() {
064: public int compare(Family c1, Family c2) {
065: int o1 = c1.ordinal();
066: int o2 = c2.ordinal();
067:
068: if (o1 == o2) {
069: return 0;
070: } else if (o1 > o2) {
071: return 1;
072: } else {
073: return -1;
074: }
075: }
076: });
077: }
078:
079: private static int nextOrdinal = 0;
080:
081: public static final Family valueOf(String name) {
082: if (name == null)
083: throw new IllegalArgumentException("name == null");
084: name = name.trim();
085: if (name.equals(""))
086: throw new IllegalArgumentException("name.equals(\"\")");
087: int index = name.indexOf(',');
088:
089: if (index >= 0) {
090: Family parent = valueOf(name.substring(index + 1));
091: name = name.substring(0, index).trim();
092: if (name.equals(""))
093: throw new IllegalArgumentException(
094: "name.equals(\"\")");
095: return new Family(name, parent);
096: } else {
097: name = name.toUpperCase().replace('-', '_');
098:
099: for (Family f : VALUES) {
100: if (f.name.equals(name)) {
101: return f;
102: }
103: }
104:
105: throw new IllegalArgumentException("name=" + name
106: + " is not a valid base font family");
107: }
108: }
109:
110: public static final Family[] values() {
111: return VALUES.toArray(new Family[VALUES.size()]);
112: }
113:
114: private int ordinal;
115: private String name;
116: private String qualifiedName;
117: private Family parent;
118:
119: private Family(String name, Family parent) {
120: if (name == null)
121: throw new IllegalArgumentException("name == null");
122: name = name.trim();
123: if (name.equals(""))
124: throw new IllegalArgumentException("name.equals(\"\")");
125: if (!name.matches("[-\\w ]+"))
126: throw new IllegalArgumentException(
127: "!name.matches(\"[-\\w ]+\")");
128: this .ordinal = parent == null ? nextOrdinal++ : -1;
129: StringBuilder sb = new StringBuilder();
130: sb.append(name);
131: this .name = name.toUpperCase().replaceAll("[- ]", "_");
132: this .parent = parent;
133:
134: if (parent == null) {
135: VALUES.add(this );
136: } else {
137: sb.append(", ").append(parent.toString());
138: }
139:
140: qualifiedName = sb.toString();
141: }
142:
143: public Family getParent() {
144: return parent;
145: }
146:
147: public Family sub(String name) {
148: return new Family(name, this );
149: }
150:
151: public String name() {
152: return name;
153: }
154:
155: public int ordinal() {
156: return ordinal;
157: }
158:
159: public int hashCode() {
160: return toString().hashCode();
161: }
162:
163: public boolean equals(Object o) {
164: if (o == null || !(o instanceof Family)) {
165: return false;
166: } else {
167: Family f = (Family) o;
168: return this .toString().equals(f.toString());
169: }
170: }
171:
172: public String toString() {
173: return qualifiedName;
174: }
175: }
176:
177: private static Application.Local<Map<String, Metrics>> fontMetrics = new Application.Local<Map<String, Metrics>>() {
178: protected Map<String, Metrics> initialValue() {
179: return new HashMap<String, Metrics>();
180: }
181: };
182:
183: private static class Metrics {
184: private int[] widths;
185: private int height;
186: }
187:
188: private Style parent;
189: private Family family;
190: private Color color;
191: private double size;
192: private int bold = -1;
193: private int italic = -1;
194: private int underline = -1;
195: private int strike = -1;
196: private String stringValue;
197:
198: Font(Style parent) {
199: this .parent = parent;
200: if (parent.defaultStyle != null)
201: copy(parent.defaultStyle.getFont());
202: }
203:
204: private void clearStringValue() {
205: this .stringValue = null;
206: if (parent != null)
207: parent.stringValue = null;
208: }
209:
210: public String toString() {
211: if (stringValue == null)
212: stringValue = "Font{family:" + getFamily() + ",size:"
213: + getSize() + ",color:" + getColor() + ",bold:"
214: + isBold() + ",italic:" + isItalic()
215: + ",underline:" + isUnderline() + ",strike:"
216: + isStrike() + "}";
217:
218: return stringValue;
219: }
220:
221: public int hashCode() {
222: return toString().hashCode();
223: }
224:
225: public boolean equals(Object o) {
226: if (!(o instanceof Font))
227: return false;
228: if (this == o)
229: return true;
230: return this .toString().equals(o.toString());
231: }
232:
233: private Metrics getMetrics() {
234: String computedState = toString();
235: Map<String, Metrics> map = fontMetrics.get();
236: Metrics fm = map.get(computedState);
237:
238: if (fm == null) {
239: fm = new Metrics();
240: String[] ary = ((WebApplication) WebApplication.current())
241: .clientSideFunctionCallWaitForReturn(
242: "tw_getFontMetrics", getFamily(),
243: getSize(), isBold(), isItalic(),
244: isUnderline()).split(",");
245: fm.widths = new int[ary.length - 1];
246: fm.height = Integer.parseInt(ary[0]);
247:
248: for (int i = 1, cnt = ary.length; i < cnt; i++) {
249: fm.widths[i - 1] = Integer.parseInt(ary[i]);
250: }
251:
252: map.put(computedState, fm);
253: }
254:
255: return fm;
256: }
257:
258: public int getStringWidth(String s) {
259: Metrics fm = getMetrics();
260: int[] widths = fm.widths;
261: int width = 0;
262:
263: for (int i = 0, cnt = s.length(); i < cnt; i++) {
264: char c = s.charAt(i);
265: byte b = (byte) c;
266: int w = widths[b - 32];
267: width += w;
268: }
269:
270: return width;
271: }
272:
273: public int getStringHeight(String s) {
274: return getMetrics().height;
275: }
276:
277: public void copy(Font font) {
278: copy(font, false);
279: }
280:
281: public void copy(Font font, boolean onlyIfDefault) {
282: if (font == null)
283: throw new IllegalArgumentException("font == null");
284:
285: if (onlyIfDefault) {
286: Font df = parent.defaultStyle.getFont();
287: if (getFamily().equals(df.getFamily()))
288: setFamily(font.getFamily());
289: if (getColor().equals(df.getColor()))
290: setColor(font.getColor());
291: if (getSize() == df.getSize())
292: setSize(font.getSize());
293: if (isBold() == df.isBold())
294: setBold(font.isBold());
295: if (isItalic() == df.isItalic())
296: setItalic(font.isItalic());
297: if (isUnderline() == df.isUnderline())
298: setUnderline(font.isUnderline());
299: if (isStrike() == df.isStrike())
300: setStrike(font.isStrike());
301: } else {
302: setFamily(font.getFamily());
303: setColor(font.getColor());
304: setSize(font.getSize());
305: setBold(font.isBold());
306: setItalic(font.isItalic());
307: setUnderline(font.isUnderline());
308: setStrike(font.isStrike());
309: }
310: }
311:
312: public void setProperty(String name, Object value) {
313: if (name.equals(Font.PROPERTY_FONT_FAMILY)) {
314: setFamily((Font.Family) value);
315: } else if (name.equals(Font.PROPERTY_FONT_SIZE)) {
316: setSize((Double) value);
317: } else if (name.equals(Font.PROPERTY_FONT_COLOR)) {
318: setColor((Color) value);
319: } else if (name.equals(Font.PROPERTY_FONT_BOLD)) {
320: setBold((Boolean) value);
321: } else if (name.equals(Font.PROPERTY_FONT_ITALIC)) {
322: setItalic((Boolean) value);
323: } else if (name.equals(Font.PROPERTY_FONT_UNDERLINE)) {
324: setUnderline((Boolean) value);
325: } else if (name.equals(Font.PROPERTY_FONT_STRIKE)) {
326: setStrike((Boolean) value);
327: } else {
328: throw new IllegalArgumentException(
329: "unknown style property '" + name + "'");
330: }
331: }
332:
333: public Object getProperty(String name) {
334: Object ret;
335:
336: if (name.equals(Font.PROPERTY_FONT_FAMILY)) {
337: ret = getFamily();
338: } else if (name.equals(Font.PROPERTY_FONT_SIZE)) {
339: ret = getSize();
340: } else if (name.equals(Font.PROPERTY_FONT_COLOR)) {
341: ret = getColor();
342: } else if (name.equals(Font.PROPERTY_FONT_BOLD)) {
343: ret = isBold();
344: } else if (name.equals(Font.PROPERTY_FONT_ITALIC)) {
345: ret = isItalic();
346: } else if (name.equals(Font.PROPERTY_FONT_UNDERLINE)) {
347: ret = isUnderline();
348: } else if (name.equals(Font.PROPERTY_FONT_STRIKE)) {
349: ret = isStrike();
350: } else {
351: throw new IllegalArgumentException(
352: "unknown style property '" + name + "'");
353: }
354:
355: return ret;
356: }
357:
358: public Style getParent() {
359: return parent;
360: }
361:
362: public Family getFamily() {
363: return family;
364: }
365:
366: public void setFamily(Family family) {
367: if (family == null && parent.defaultStyle != null)
368: family = parent.defaultStyle.getFont().getFamily();
369: if (family == null)
370: throw new IllegalArgumentException(
371: "family == null && defaultStyle.getFont().getFamily() == null");
372: Family oldFamily = this .family;
373: this .clearStringValue();
374: this .family = family;
375: if (parent != null)
376: parent.firePropertyChange(this , PROPERTY_FONT_FAMILY,
377: oldFamily, family);
378: }
379:
380: public Color getColor() {
381: if (color == null)
382: throw new IllegalStateException("color not initialized");
383: return color;
384: }
385:
386: public void setColor(Color color) {
387: if (color == null && parent.defaultStyle != null)
388: color = parent.defaultStyle.getFont().getColor();
389: if (color == null)
390: throw new IllegalArgumentException(
391: "color == null && defaultStyle.getFont().getColor() == null");
392: Color oldColor = this .color;
393: this .clearStringValue();
394: this .color = color;
395: if (parent != null)
396: parent.firePropertyChange(this , PROPERTY_FONT_COLOR,
397: oldColor, this .color);
398: }
399:
400: public double getSize() {
401: if (size <= 0)
402: throw new IllegalStateException("size <= 0");
403: return size;
404: }
405:
406: public void setSize(double size) {
407: if (size <= 0 && parent.defaultStyle != null)
408: size = parent.defaultStyle.getFont().getSize();
409: if (size <= 0 || size > 128)
410: throw new IllegalArgumentException(
411: "size <= 0 || size > 128");
412: size = Math.floor(size * 10) / 10;
413: double oldSize = this .size;
414: this .clearStringValue();
415: this .size = size;
416: if (parent != null)
417: parent.firePropertyChange(this , PROPERTY_FONT_SIZE,
418: oldSize, size);
419: }
420:
421: public boolean isBold() {
422: if (bold == -1)
423: throw new IllegalStateException("bold not initialized");
424: return bold == 1;
425: }
426:
427: public void setBold(boolean bold) {
428: boolean oldBold = this .bold == 1;
429: this .clearStringValue();
430: this .bold = bold == true ? 1 : 0;
431: if (parent != null)
432: parent.firePropertyChange(this , PROPERTY_FONT_BOLD,
433: oldBold, bold);
434: }
435:
436: public boolean isItalic() {
437: if (italic == -1)
438: throw new IllegalStateException("italic not initialized");
439: return italic == 1;
440: }
441:
442: public void setItalic(boolean italic) {
443: boolean oldItalic = this .italic == 1;
444: this .clearStringValue();
445: this .italic = italic == true ? 1 : 0;
446: if (parent != null)
447: parent.firePropertyChange(this , PROPERTY_FONT_ITALIC,
448: oldItalic, italic);
449: }
450:
451: public boolean isUnderline() {
452: if (underline == -1)
453: throw new IllegalStateException("underline not initialized");
454: return underline == 1;
455: }
456:
457: public void setUnderline(boolean underline) {
458: boolean oldUnderline = this .underline == 1;
459: this .clearStringValue();
460: this .underline = underline == true ? 1 : 0;
461: if (parent != null)
462: parent.firePropertyChange(this , PROPERTY_FONT_UNDERLINE,
463: oldUnderline, underline);
464: }
465:
466: public boolean isStrike() {
467: if (strike == -1)
468: throw new IllegalStateException("strike not initialized");
469: return strike == 1;
470: }
471:
472: public void setStrike(boolean strike) {
473: boolean oldStrike = this .strike == 1;
474: this .clearStringValue();
475: this .strike = strike == true ? 1 : 0;
476: if (parent != null)
477: parent.firePropertyChange(this, PROPERTY_FONT_STRIKE,
478: oldStrike, strike);
479: }
480: }
|