001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.swing.plaf.util;
043:
044: import org.xml.sax.InputSource;
045: import org.xml.sax.Locator;
046: import org.xml.sax.Parser;
047: import org.xml.sax.SAXException;
048: import org.xml.sax.helpers.XMLReaderAdapter;
049:
050: import javax.swing.*;
051: import javax.swing.border.BevelBorder;
052: import javax.swing.border.EtchedBorder;
053: import javax.swing.plaf.*;
054: import javax.swing.plaf.metal.MetalTheme;
055: import javax.xml.parsers.ParserConfigurationException;
056: import javax.xml.parsers.SAXParserFactory;
057: import java.awt.*;
058: import java.net.URL;
059: import java.util.HashSet;
060: import java.util.StringTokenizer;
061:
062: /** An extension of javax.swing.plaf.metal.DefaultMetalTheme which can read a an xml
063: * file named <code>themes.xml</code> and apply the theme defined in XML. NbTheme
064: * stores its data in the <code>UIDefaults</code> instance available from the
065: * look-and-feel manager (<code>UiManager.getLookAndFeel.getDefaults()</code>).
066: * These means that theme files may also override per-component-type settings
067: * stored there, so changes to look and feel deeper than those afforded by the
068: * <code>MetalTheme</code> parent class are possible.
069: * <P>
070: * <code>NbTheme</code> supports seven kinds of data: Colors, Fonts, Integers,
071: * Strings, Borders, Insets and Booleans, which are the major interesting data types typical
072: * stored in <code>UIDefaults</code>. For usage instructions and details,
073: * see <A HREF="http://ui.netbeans.org/docs/ui/themes/themes.html">the themes
074: * documentation</a> on the NetBeans web site.
075: *
076: * @author Jiri Mzourek, Tim Boudreau
077: * @see http://ui.netbeans.org/docs/ui/themes/themes.html
078: */
079: public class NbTheme extends MetalTheme implements
080: org.xml.sax.DocumentHandler {
081:
082: /** The unqualified name for the theme file to use. */
083: public static final String THEMEFILE_NAME = "themes.xml"; // NOI18N
084: // legal xml tags for theme files
085: private static final String THEMESET_TAG = "themeset"; // NOI18N
086: private static final String ACTIVE_ATTR = "active"; // NOI18N
087: private static final String THEME_TAG = "theme"; // NOI18N
088: private static final String BOOL_TAG = "boolean"; // NOI18N
089: private static final String DIM_TAG = "dimension"; // NOI18N
090: private static final String FONT_TAG = "font"; // NOI18N
091: private static final String INSETS_TAG = "insets"; // NOI18N
092: private static final String ETCHEDBORDER_TAG = "etchedborder";
093: private static final String EMPTYBORDER_TAG = "emptyborder";
094: private static final String BEVELBORDER_TAG = "bevelborder";
095: private static final String LINEBORDER_TAG = "lineborder";
096: // attributes recognized in various tags
097: private static final String COLOR_ATTR = "color"; // NOI18N
098: private static final String KEY_ATTR = "key"; // NOI18N
099: private static final String METRIC_TAG = "metric"; // NOI18N
100: private static final String STRING_TAG = "string"; // NOI18N
101: private static final String NAME_ATTR = "name"; // NOI18N
102: private static final String FONTSTYLE_ATTR = "style"; // NOI18N
103: private static final String FONTSIZE_ATTR = "size"; // NOI18N
104: private static final String VALUE_ATTR = "value"; //NOI18N
105: private static final String WIDTH_ATTR = "width"; //NOI18N
106: private static final String HEIGHT_ATTR = "height"; //NOI18N
107: private static final String RED_ATTR = "r"; // NOI18N
108: private static final String GREEN_ATTR = "g"; // NOI18N
109: private static final String BLUE_ATTR = "b"; // NOI18N
110: private static final String LEFT_ATTR = "left"; // NOI18N
111: private static final String TOP_ATTR = "top"; // NOI18N
112: private static final String RIGHT_ATTR = "right"; // NOI18N
113: private static final String BOTTOM_ATTR = "bottom"; // NOI18N
114: private static final String TYPE_ATTR = "type"; // NOI18N
115: private static final String REFERENCE_ATTR = "reference"; // NOI18N
116: // font styles
117: private static final String FONTSTYLE_BOLD = "bold"; // NOI18N
118: private static final String FONTSTYLE_ITALIC = "italic"; // NOI18N
119: //border types
120: private static final String TYPE_LOWERED = "lowered"; // NOI18N
121:
122: // keys used to store theme values in UIDefaults
123: private static final String CONTROLFONT = "controlFont"; // NOI18N
124: private static final String SYSTEMFONT = "systemFont"; // NOI18N
125: private static final String USERFONT = "userFont"; // NOI18N
126: private static final String MENUFONT = "menuFont"; // NOI18N
127: private static final String WINDOWTITLEFONT = "windowTitleFont"; // NOI18N
128: private static final String SUBFONT = "subFont"; // NOI18N
129:
130: //Keys below here are MetalTheme-specific and have no meaning on
131: //non-Metal look and feels
132: private static final String PRIMARY1 = "primary1"; // NOI18N
133: private static final String PRIMARY2 = "primary2"; // NOI18N
134: private static final String PRIMARY3 = "primary3"; // NOI18N
135: private static final String SECONDARY1 = "secondary1"; // NOI18N
136: private static final String SECONDARY2 = "secondary2"; // NOI18N
137: private static final String SECONDARY3 = "secondary3"; // NOI18N
138: private static final String WHITE = "white"; // NOI18N
139: private static final String BLACK = "black"; // NOI18N
140:
141: private HashSet<String> activeThemes = null;
142: private boolean inActiveTheme = false;
143: private URL themeURL = null;
144:
145: private UIDefaults defaults;
146:
147: public String getName() {
148: return "NetBeans XML Theme";
149: } // NOI18N
150:
151: /** Create a new instance of NBTheme */
152: public NbTheme(URL themeURL, LookAndFeel lf) {
153: this .themeURL = themeURL;
154: defaults = lf.getDefaults();
155: initThemeDefaults();
156: parseTheme();
157: UIManager.getDefaults().putAll(defaults);
158: }
159:
160: /** Add any custom UIDefault values for Metal L&F here */
161: void initThemeDefaults() {
162: defaults.put(PRIMARY1, new ColorUIResource(102, 102, 153));
163: defaults.put(PRIMARY2, new ColorUIResource(153, 153, 204));
164: defaults.put(PRIMARY3, new ColorUIResource(204, 204, 255));
165:
166: defaults.put(SECONDARY1, new ColorUIResource(102, 102, 102));
167: defaults.put(SECONDARY2, new ColorUIResource(153, 153, 153));
168: defaults.put(SECONDARY3, new ColorUIResource(204, 204, 204));
169:
170: defaults.put(WHITE, new ColorUIResource(255, 255, 255));
171: defaults.put(BLACK, new ColorUIResource(0, 0, 0));
172: }
173:
174: private void parseTheme() {
175: try {
176: SAXParserFactory factory = SAXParserFactory.newInstance();
177: factory.setValidating(false);
178: factory.setNamespaceAware(false);
179:
180: Parser p = new XMLReaderAdapter(factory.newSAXParser()
181: .getXMLReader());
182: p.setDocumentHandler(this );
183: String externalForm = themeURL.toExternalForm();
184: InputSource is = new InputSource(externalForm);
185: p.parse(is);
186: activeThemes = null; //dispose of now useless hashtable
187: locator = null;
188: } catch (java.io.IOException ie) {
189: System.err.println("IO exception reading theme file"); //NOI18N
190: } catch (org.xml.sax.SAXException se) {
191: System.err.println("Error parsing theme file "
192: + (locator != null ? "line "
193: + locator.getLineNumber() : "")); //NOI18N
194: } catch (ParserConfigurationException e) {
195: System.err
196: .println("Couldn't create XML parser for theme file"); //NOI18N
197: }
198: }
199:
200: public void endDocument() throws org.xml.sax.SAXException {
201: }
202:
203: public void startDocument() throws org.xml.sax.SAXException {
204: }
205:
206: public void startElement(java.lang.String p1,
207: org.xml.sax.AttributeList atts)
208: throws org.xml.sax.SAXException {
209: //found the themeset?
210: if (p1.equals(THEMESET_TAG)) {
211: //break out the comma delimited list of active themes
212: //and stores them in activeThemes hashset
213: String themes = atts.getValue(ACTIVE_ATTR);
214: if (themes != null) {
215: StringTokenizer tok = new StringTokenizer(themes, ","); //NOI18N
216: activeThemes = new HashSet<String>(
217: tok.countTokens() + 1);
218: while (tok.hasMoreTokens()) {
219: activeThemes.add(tok.nextToken().trim());
220: }
221: }
222: } else {
223: if (p1.equals(THEME_TAG) && (activeThemes != null)) {
224: //see if the current theme is one of the active ones
225: String themeName = atts.getValue(NAME_ATTR);
226: inActiveTheme = activeThemes.contains(themeName);
227: } else {
228: if (inActiveTheme) {
229: if (handleReference(atts)) {
230: return;
231: }
232: if (p1.equals(COLOR_ATTR)) {
233: handleColor(atts);
234: return;
235: }
236: if (p1.equals(FONT_TAG)) {
237: handleFont(atts);
238: return;
239: }
240: if (p1.equals(EMPTYBORDER_TAG)) {
241: handleEmptyBorder(atts);
242: return;
243: }
244: if (p1.equals(METRIC_TAG)) {
245: handleMetric(atts);
246: return;
247: }
248: if (p1.equals(STRING_TAG)) {
249: handleString(atts);
250: return;
251: }
252: if (p1.equals(INSETS_TAG)) {
253: handleInsets(atts);
254: return;
255: }
256: if (p1.equals(BOOL_TAG)) {
257: handleBool(atts);
258: return;
259: }
260: if (p1.equals(DIM_TAG)) {
261: handleDim(atts);
262: return;
263: }
264: if (p1.equals(ETCHEDBORDER_TAG)) {
265: handleEtchedBorder(atts);
266: return;
267: }
268: if (p1.equals(LINEBORDER_TAG)) {
269: handleLineBorder(atts);
270: return;
271: }
272: if (p1.equals(BEVELBORDER_TAG)) {
273: handleBevelBorder(atts);
274: return;
275: }
276: System.err.println("UNRECOGNIZED " + "THEME ENTRY "
277: + p1 + "\" " + atts.toString()); //NOI18N
278: }
279: }
280: }
281: }
282:
283: private boolean handleReference(org.xml.sax.AttributeList atts)
284: throws SAXException {
285: String key = atts.getValue(KEY_ATTR);
286: String reference = atts.getValue(REFERENCE_ATTR);
287: if (reference != null) {
288: Object res = defaults.get(reference);
289: if (res != null) {
290: defaults.put(key, res);
291: return true;
292: }
293: }
294: return false;
295: }
296:
297: private final void handleFont(org.xml.sax.AttributeList atts)
298: throws SAXException {
299: String key = atts.getValue(KEY_ATTR);
300: String fontname = atts.getValue(NAME_ATTR);
301: String fontstylename = atts.getValue(FONTSTYLE_ATTR);
302: int fontsize = intFromAttr(atts, FONTSIZE_ATTR);
303: int fontstyle = Font.PLAIN;
304: if (fontstylename.equals(FONTSTYLE_BOLD)) {
305: fontstyle = Font.BOLD;
306: } else {
307: if (fontstylename.equals(FONTSTYLE_ITALIC))
308: fontstyle = Font.ITALIC;
309: }
310:
311: FontUIResource resource = new FontUIResource(fontname,
312: fontstyle, fontsize);
313: defaults.put(key, resource);
314: }
315:
316: private final void handleColor(org.xml.sax.AttributeList atts)
317: throws SAXException {
318: int r = intFromAttr(atts, RED_ATTR);
319: int g = intFromAttr(atts, GREEN_ATTR);
320: int b = intFromAttr(atts, BLUE_ATTR);
321: String key = atts.getValue(KEY_ATTR);
322: ColorUIResource resource = new ColorUIResource(r, g, b);
323: defaults.put(key, resource);
324: }
325:
326: private final void handleMetric(org.xml.sax.AttributeList atts)
327: throws SAXException {
328: String key = atts.getValue(KEY_ATTR);
329: Integer resource = Integer.valueOf(atts.getValue(VALUE_ATTR));
330: defaults.put(key, resource);
331: }
332:
333: private final void handleString(org.xml.sax.AttributeList atts)
334: throws SAXException {
335: String key = atts.getValue(KEY_ATTR);
336: String resource = atts.getValue(VALUE_ATTR);
337: defaults.put(key, resource);
338: }
339:
340: private final void handleBool(org.xml.sax.AttributeList atts)
341: throws SAXException {
342: String key = atts.getValue(KEY_ATTR);
343: Boolean resource = Boolean.valueOf(key);
344: defaults.put(key, resource);
345: }
346:
347: private final void handleDim(org.xml.sax.AttributeList atts)
348: throws SAXException {
349: String key = atts.getValue(KEY_ATTR);
350: int width = intFromAttr(atts, WIDTH_ATTR);
351: int height = intFromAttr(atts, HEIGHT_ATTR);
352: DimensionUIResource resource = new DimensionUIResource(width,
353: height);
354: defaults.put(key, resource);
355: }
356:
357: private final void handleInsets(org.xml.sax.AttributeList atts)
358: throws SAXException {
359: String key = atts.getValue(KEY_ATTR);
360: int top = intFromAttr(atts, TOP_ATTR);
361: int left = intFromAttr(atts, LEFT_ATTR);
362: int bottom = intFromAttr(atts, BOTTOM_ATTR);
363: int right = intFromAttr(atts, RIGHT_ATTR);
364: InsetsUIResource resource = new InsetsUIResource(top, left,
365: bottom, right);
366: defaults.put(key, resource);
367: }
368:
369: private final void handleEtchedBorder(org.xml.sax.AttributeList atts) {
370: String key = atts.getValue(KEY_ATTR);
371: int i = EtchedBorder.LOWERED;
372: String type = atts.getValue(TYPE_ATTR);
373: if (type != null)
374: i = type.equals(TYPE_LOWERED) ? EtchedBorder.LOWERED
375: : EtchedBorder.RAISED;
376: BorderUIResource.EtchedBorderUIResource resource = new BorderUIResource.EtchedBorderUIResource(
377: i);
378: defaults.put(key, resource);
379: }
380:
381: private final void handleBevelBorder(org.xml.sax.AttributeList atts) {
382: String key = atts.getValue(KEY_ATTR);
383: int i = BevelBorder.LOWERED;
384: String type = atts.getValue(TYPE_ATTR);
385: if (type != null)
386: i = type.equals(TYPE_LOWERED) ? BevelBorder.LOWERED
387: : BevelBorder.RAISED;
388: BorderUIResource.BevelBorderUIResource resource = new BorderUIResource.BevelBorderUIResource(
389: i);
390: defaults.put(key, resource);
391: }
392:
393: private final void handleEmptyBorder(org.xml.sax.AttributeList atts)
394: throws SAXException {
395: String key = atts.getValue(KEY_ATTR);
396: int top = intFromAttr(atts, TOP_ATTR);
397: int left = intFromAttr(atts, LEFT_ATTR);
398: int bottom = intFromAttr(atts, BOTTOM_ATTR);
399: int right = intFromAttr(atts, RIGHT_ATTR);
400: BorderUIResource.EmptyBorderUIResource resource = new BorderUIResource.EmptyBorderUIResource(
401: top, left, bottom, right);
402: defaults.put(key, resource);
403: }
404:
405: private final void handleLineBorder(org.xml.sax.AttributeList atts)
406: throws SAXException {
407: String key = atts.getValue(KEY_ATTR);
408: int r = intFromAttr(atts, RED_ATTR);
409: int g = intFromAttr(atts, GREEN_ATTR);
410: int b = intFromAttr(atts, BLUE_ATTR);
411: int width = 1;
412: if (atts.getValue(WIDTH_ATTR) != null) {
413: width = intFromAttr(atts, WIDTH_ATTR);
414: }
415: Color c = new Color(r, g, b);
416: BorderUIResource.LineBorderUIResource resource = new BorderUIResource.LineBorderUIResource(
417: c);
418: defaults.put(key, resource);
419: }
420:
421: private final int intFromAttr(final org.xml.sax.AttributeList atts,
422: final String key) throws SAXException {
423: try {
424: return Integer.valueOf(atts.getValue(key)).intValue();
425: } catch (NumberFormatException nfe) {
426: throw new SAXException(atts.getValue(key)
427: + " is not an integer");
428: }
429: }
430:
431: public void characters(char[] p1, int p2, int p3)
432: throws org.xml.sax.SAXException {
433: }
434:
435: Locator locator = null;
436:
437: public void setDocumentLocator(org.xml.sax.Locator locator) {
438: this .locator = locator;
439: }
440:
441: public void endElement(java.lang.String p1)
442: throws org.xml.sax.SAXException {
443: if (p1.equals(THEME_TAG)) {
444: inActiveTheme = false;
445: }
446: }
447:
448: public void ignorableWhitespace(char[] p1, int p2, int p3)
449: throws org.xml.sax.SAXException {
450: }
451:
452: public void processingInstruction(java.lang.String p1,
453: java.lang.String p2) throws org.xml.sax.SAXException {
454: }
455:
456: private final ColorUIResource getColor(String key) {
457: return (ColorUIResource) defaults.get(key);
458: }
459:
460: private final FontUIResource getFont(String key) {
461: return (FontUIResource) defaults.get(key);
462: }
463:
464: public FontUIResource getControlTextFont() {
465: return getFont(CONTROLFONT);
466: }
467:
468: public FontUIResource getSystemTextFont() {
469: return getFont(SYSTEMFONT);
470: }
471:
472: public FontUIResource getUserTextFont() {
473: return getFont(USERFONT);
474: }
475:
476: public FontUIResource getMenuTextFont() {
477: return getFont(MENUFONT);
478: }
479:
480: public FontUIResource getWindowTitleFont() {
481: return getFont(WINDOWTITLEFONT);
482: }
483:
484: public FontUIResource getSubTextFont() {
485: return getFont(SUBFONT);
486: }
487:
488: protected ColorUIResource getPrimary1() {
489: return getColor(PRIMARY1);
490: }
491:
492: protected ColorUIResource getPrimary2() {
493: return getColor(PRIMARY2);
494: }
495:
496: protected ColorUIResource getPrimary3() {
497: return getColor(PRIMARY3);
498: }
499:
500: protected ColorUIResource getSecondary1() {
501: return getColor(SECONDARY1);
502: }
503:
504: protected ColorUIResource getSecondary2() {
505: return getColor(SECONDARY2);
506: }
507:
508: protected ColorUIResource getSecondary3() {
509: return getColor(SECONDARY3);
510: }
511:
512: protected ColorUIResource getWhite() {
513: return getColor(WHITE);
514: }
515:
516: protected ColorUIResource getBlack() {
517: return getColor(BLACK);
518: }
519:
520: }
|