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;
043:
044: import java.awt.Toolkit;
045: import java.util.logging.Logger;
046: import org.netbeans.swing.plaf.aqua.AquaLFCustoms;
047: import org.netbeans.swing.plaf.gtk.GtkLFCustoms;
048: import org.netbeans.swing.plaf.metal.MetalLFCustoms;
049: import org.netbeans.swing.plaf.util.NbTheme;
050: import org.netbeans.swing.plaf.util.RelativeColor;
051: import org.netbeans.swing.plaf.util.UIBootstrapValue;
052: import org.netbeans.swing.plaf.util.UIUtils;
053:
054: import javax.swing.*;
055: import javax.swing.plaf.metal.MetalLookAndFeel;
056: import java.beans.PropertyChangeEvent;
057: import java.beans.PropertyChangeListener;
058: import java.net.URL;
059: import java.util.Arrays;
060: import java.util.HashSet;
061: import java.util.Set;
062: import org.netbeans.swing.plaf.winclassic.WindowsLFCustoms;
063: import org.netbeans.swing.plaf.winvista.VistaLFCustoms;
064: import org.netbeans.swing.plaf.winxp.XPLFCustoms;
065:
066: /** Singleton, manages customizers for various LFs. Installs, uninstalls them on LF change.
067: * LF customizers works with Swing UIManager, putting info about various UI elements
068: * in it. Other modules then can query UIManager to get UI elements to get needed
069: * visual design and behaviour.
070: *
071: * @author Dafe Simonek, Tim Boudreau
072: */
073: public final class Startup {
074: //originally LFCustomsManager
075:
076: /** For debugging purposes, enable forcing the customizations for, i.e.,
077: * Windows look and feel on a platform that doesn't support it */
078: private static final String FORCED_CUSTOMS = System
079: .getProperty("nb.forceui"); //NOI18N
080:
081: /** Provides the ability to disable customizations for applications which, for example, provide their own
082: * subclass of MetalLookAndFeel. See issue XXX
083: */
084: private static final boolean NO_CUSTOMIZATIONS = Boolean
085: .getBoolean("netbeans.plaf.disable.ui.customizations"); //NOI18N
086:
087: /** Constant for Nimbus L&F name */
088: private static final String NIMBUS = "Nimbus";
089:
090: /** Singleton instance */
091: private static Startup instance = null;
092:
093: /** Currently used LF customizer */
094: private LFCustoms curCustoms = null;
095: private LFCustoms globalCustoms = null;
096:
097: private static URL themeURL = null;
098: private static Class uiClass = null;
099:
100: private boolean installed = false;
101:
102: /** Starts handling of LF customizers. Called only from getInstance. */
103: private Startup() {
104: initialize();
105: }
106:
107: /** Initializes defaulf customs for all LFs and fills UIManager with
108: * references to LF customizers for supported LFs.
109: */
110: private void initialize() {
111: LookAndFeel lf = getLookAndFeel();
112: if (lf instanceof MetalLookAndFeel) {
113: //Metal theme must be assigned before using the look and feel
114: installTheme(lf);
115: }
116: // overall defaults for all LFs
117: // defaults for supported LFs
118:
119: try {
120: if (lf != UIManager.getLookAndFeel()) {
121: UIManager.setLookAndFeel(lf);
122: }
123: } catch (Exception e) {
124: System.err.println("Could not install look and feel " + lf);
125: }
126: }
127:
128: private LookAndFeel getLookAndFeel() {
129: // related to #118534 - log info about Nimbus L&F
130: if (uiClass != null && uiClass.getName().contains(NIMBUS)) {
131: Logger
132: .getLogger(getClass().getName())
133: .warning(
134: "L&F Warning: Nimbus L&F is not supported L&F yet and system "
135: + "may exhibit various drawing problems. Please use for experimental purposes only.");
136: }
137:
138: if (uiClass == null) {
139: String uiClassName;
140: if (isWindows()) {
141: uiClassName = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; //NOI18N
142: } else if (isMac()) {
143: uiClassName = "apple.laf.AquaLookAndFeel";
144: } else if (shouldUseMetal()) {
145: uiClassName = "javax.swing.plaf.metal.MetalLookAndFeel"; //NOI18N
146: } else {
147: //Should get us metal where it doesn't get us GTK
148: uiClassName = UIManager.getSystemLookAndFeelClassName();
149: // Enable GTK L&F only for JDK version 1.6.0 update 1 and later.
150: // GTK L&F quality unacceptable for earlier versions.
151: String javaVersion = System.getProperty("java.version");
152: if ("1.6.0_01".compareTo(javaVersion) > 0) {
153: // IDE runs on 1.5 or 1.6 - useGtk can enabled Gtk
154: if (uiClassName.indexOf("gtk") >= 0
155: && !Boolean.getBoolean("useGtk")) {
156: uiClassName = "javax.swing.plaf.metal.MetalLookAndFeel";
157: }
158: } else {
159: // IDE runs on 1.6_01 or higher - useGtk can disabled Gtk
160: if (uiClassName.indexOf("gtk") >= 0
161: && System.getProperty("useGtk") != null
162: && !Boolean.getBoolean("useGtk")) {
163: uiClassName = "javax.swing.plaf.metal.MetalLookAndFeel";
164: }
165: }
166:
167: // #118534 - don't allow Nimbus L&F as default system L&F,
168: // as we're not ready to support it yet
169: if (uiClassName.contains("Nimbus")) {
170: uiClassName = "javax.swing.plaf.metal.MetalLookAndFeel";
171: }
172: }
173: try {
174: uiClass = Class.forName(uiClassName);
175: } catch (ClassNotFoundException e) {
176: System.err.println("Custom UI class " + uiClassName
177: + " not on classpath."); // NOI18N
178: } catch (Exception e) {
179: System.err.println("While loading: " + uiClassName); // NOI18N
180: e.printStackTrace();
181: }
182: }
183: LookAndFeel result = null;
184: if (uiClass != null) {
185: try {
186:
187: LookAndFeel lf = UIManager.getLookAndFeel();
188: if (uiClass != lf.getClass()) {
189: result = (LookAndFeel) uiClass.newInstance();
190: } else {
191: result = UIManager.getLookAndFeel();
192: }
193: } catch (Exception e) {
194: System.err.println("Cannot load custom UI class "
195: + uiClass); //NOI18N
196: e.printStackTrace();
197: result = UIManager.getLookAndFeel();
198: }
199: }
200: return result;
201: }
202:
203: private void installTheme(LookAndFeel lf) {
204: //Load the theme
205: if (themeURL != null) {
206: NbTheme nbTheme = new NbTheme(themeURL, lf);
207: MetalLookAndFeel.setCurrentTheme(nbTheme);
208: }
209: }
210:
211: /** Enables, installs LF customization. */
212: private void install() {
213: if (installed) {
214: return;
215: }
216: if (globalCustoms == null) {
217: globalCustoms = new AllLFCustoms();
218: installLFCustoms(globalCustoms);
219: }
220: installPerLFDefaults();
221: installTheme(UIManager.getLookAndFeel());
222: attachListener();
223: }
224:
225: private void installPerLFDefaults() {
226: boolean isLFChange = curCustoms != null;
227:
228: curCustoms = findCustoms();
229: if (curCustoms != null) {
230: Integer in = (Integer) UIManager
231: .get(LFCustoms.CUSTOM_FONT_SIZE); //NOI18N
232: if (in == null
233: && UIManager.getLookAndFeel().getClass() == MetalLookAndFeel.class) {
234: in = new Integer(11);
235: }
236:
237: if (in != null) {
238: AllLFCustoms.initCustomFontSize(in.intValue());
239: }
240: installLFCustoms(curCustoms);
241: if (isLFChange) {
242: //make sure UIBootstrapValue.Lazy instances really get a chance
243: //to replace their values
244: loadAllLazyValues(curCustoms);
245: }
246: curCustoms.disposeValues();
247: }
248: }
249:
250: private void loadAllLazyValues(LFCustoms customs) {
251: if (globalCustoms != null) {
252: loadLazy(globalCustoms
253: .getApplicationSpecificKeysAndValues());
254: loadLazy(globalCustoms.getGuaranteedKeysAndValues());
255: loadLazy(globalCustoms
256: .getLookAndFeelCustomizationKeysAndValues());
257: }
258: loadLazy(customs.getApplicationSpecificKeysAndValues());
259: loadLazy(customs.getGuaranteedKeysAndValues());
260: loadLazy(customs.getLookAndFeelCustomizationKeysAndValues());
261: }
262:
263: private void loadLazy(Object[] o) {
264: if (o.length > 0) {
265: UIDefaults uidefaults = UIManager.getDefaults();
266: for (int i = 1; i < o.length; i += 2) {
267: if (o[i] instanceof UIBootstrapValue.Lazy) {
268: ((UIBootstrapValue.Lazy) o[i])
269: .createValue(uidefaults);
270: }
271: if (o[i] instanceof RelativeColor) {
272: ((RelativeColor) o[i]).clear();
273: }
274: }
275: }
276: }
277:
278: private void uninstallPerLFDefaults() {
279: assert globalCustoms != null;
280:
281: if (curCustoms != null) {
282: Set<Object> keep = new HashSet<Object>(Arrays
283: .asList(globalCustoms.allKeys()));
284: Object[] arr = curCustoms.allKeys();
285:
286: for (int i = 0; i < arr.length; i++) {
287: Object key = arr[i];
288: if (!keep.contains(key)) {
289: UIManager.put(key, null);
290: }
291: }
292: }
293: }
294:
295: private void attachListener() {
296: assert listener == null;
297: listener = new LFListener();
298: UIManager.addPropertyChangeListener(listener);
299: Toolkit.getDefaultToolkit().addPropertyChangeListener(
300: "win.xpstyle.themeActive", listener); //NOI18N
301: }
302:
303: private void installLFCustoms(LFCustoms customs) {
304: UIDefaults defaults = UIManager.getDefaults();
305:
306: // to make sure we always use system classloader
307: defaults.put("ClassLoader", new CLValue()); // NOI18N
308:
309: //Install values that some look and feels may leave out, which should
310: //be included
311: defaults.putDefaults(customs.getGuaranteedKeysAndValues());
312: //Install entries for custom NetBeans components, such as borders and
313: //colors
314: defaults.putDefaults(customs
315: .getApplicationSpecificKeysAndValues());
316:
317: if (!NO_CUSTOMIZATIONS) {
318: //See issue nnn - Nokia uses a custom metal-based look and feel,
319: //and do not want fonts or other things customized
320: defaults.putDefaults(customs
321: .getLookAndFeelCustomizationKeysAndValues());
322: }
323:
324: }
325:
326: /** Gets the value of system class loader and returns it.
327: */
328: private static final class CLValue implements
329: UIDefaults.ActiveValue {
330: public Object createValue(UIDefaults defs) {
331: return Thread.currentThread().getContextClassLoader();
332: }
333: }
334:
335: /** Finds and returns instance of LF customizer which is suitable for
336: * current look and feel.
337: */
338: private LFCustoms findCustoms() {
339: if (FORCED_CUSTOMS != null) {
340: System.err
341: .println("Using explicitly set UI customizations: "
342: + //NOI18N
343: FORCED_CUSTOMS);
344: if ("Vista".equals(FORCED_CUSTOMS)) { //NOI18N
345: return new VistaLFCustoms();
346: } else if ("XP".equals(FORCED_CUSTOMS)) { //NOI18N
347: return new XPLFCustoms();
348: } else if ("Aqua".equals(FORCED_CUSTOMS)) { //NOI18N
349: return new AquaLFCustoms();
350: } else if ("Metal".equals(FORCED_CUSTOMS)) { //NOI18N
351: return new MetalLFCustoms();
352: } else if ("Windows".equals(FORCED_CUSTOMS)) { //NOI18N
353: return new WindowsLFCustoms();
354: } else if ("GTK".equals(FORCED_CUSTOMS)) { //NOI18N
355: return new GtkLFCustoms();
356: } else {
357: try {
358: return (LFCustoms) Class.forName(FORCED_CUSTOMS)
359: .newInstance();
360: } catch (Exception e) {
361: System.err
362: .println("UI customizations class not found: " //NOI18N
363: + FORCED_CUSTOMS); //NOI18N
364: }
365: }
366: }
367:
368: StringBuffer buf = new StringBuffer(40);
369: buf.append("Nb."); //NOI18N
370: buf.append(UIManager.getLookAndFeel().getID());
371: if (UIUtils.isXPLF()) {
372: if (isWindowsVista()) {
373: buf.append("VistaLFCustoms"); //NOI18N
374: } else {
375: buf.append("XPLFCustoms"); //NOI18N
376: }
377: } else {
378: buf.append("LFCustoms"); //NOI18N
379: }
380:
381: LFCustoms result = null;
382: try {
383: result = (LFCustoms) UIManager.get(buf.toString());
384: } catch (ClassCastException cce) {
385: //do nothing - the look and feel happens to have something matching
386: //our generated key there
387: }
388: if (result == null) {
389: String[] knownLFs = new String[] { "Metal", "Windows",
390: "Aqua", "GTK" //NOI18N
391: };
392: switch (Arrays.asList(knownLFs).indexOf(
393: UIManager.getLookAndFeel().getID())) {
394: case 1:
395: if (UIUtils.isXPLF()) {
396: if (isWindowsVista()) {
397: result = new VistaLFCustoms();
398: } else {
399: result = new XPLFCustoms();
400: }
401: } else {
402: result = new WindowsLFCustoms();
403: }
404: break;
405: case 0:
406: result = new MetalLFCustoms();
407: break;
408: case 2:
409: result = new AquaLFCustoms();
410: break;
411: case 3:
412: result = new GtkLFCustoms();
413: break;
414: default:
415: // #79401 check if it's XP style LnF, for example jGoodies
416: if (UIUtils.isXPLF()) {
417: if (isWindowsVista()) {
418: result = new VistaLFCustoms();
419: } else {
420: result = new XPLFCustoms();
421: }
422: } else {
423: result = new WindowsLFCustoms();
424: }
425: }
426: }
427: return result;
428: }
429:
430: /**
431: * Initialize values in UIDefaults which need to be there for NetBeans' components; apply customizations such
432: * as setting up a custom font size and loading a theme.
433: *
434: * @param uiClass The UI class which should be used for the look and feel
435: * @param uiFontSize A custom fontsize, or 0. This will be retrievable via UIManager.get("customFontSize") after this method has returned
436: * if non 0. If non zero, all of the standard Swing font keys in UIDefaults will be customized to
437: * provide a font with the requested size. Results are undefined for values less than 0 or greater
438: * than any hard limit the platform imposes on font size.
439: * @param themeURL An optional URL for a theme file, or null. Theme file format documentation can be found
440: * <a href="ui.netbeans.org/project/ui/docs/ui/themes/themes.html">here</a>.
441: */
442: public static void run(Class uiClass, int uiFontSize, URL themeURL) {
443: if (instance == null) {
444: // Modify default font size to the font size passed as a command-line parameter
445: if (uiFontSize > 0) {
446: Integer customFontSize = new Integer(uiFontSize);
447: UIManager.put("customFontSize", customFontSize);
448: }
449: Startup.uiClass = uiClass;
450: Startup.themeURL = themeURL;
451: instance = new Startup();
452: instance.install();
453: }
454: }
455:
456: private boolean isWindows() {
457: String osName = System.getProperty("os.name");
458: return osName.startsWith("Windows");
459: }
460:
461: private boolean isWindowsVista() {
462: String osName = System.getProperty("os.name");
463: return osName.indexOf("Vista") >= 0
464: || (osName.equals("Windows NT (unknown)") && "6.0"
465: .equals(System.getProperty("os.version")));
466: }
467:
468: private boolean isMac() {
469: String osName = System.getProperty("os.name");
470: boolean result = osName.startsWith("Darwin")
471: || "Mac OS X".equals(osName);
472: return result;
473: }
474:
475: private boolean isSolaris10() {
476: String osName = System.getProperty("os.name");
477: String osVersion = System.getProperty("os.version");
478: boolean result = osName.startsWith("SunOS")
479: && "5.10".equals(osVersion);
480: return result;
481: }
482:
483: /** If it is solaris or linux, we can use GTK where supported by getting
484: * the platform specific look and feel.
485: *
486: * Also check to make sure under no
487: * circumstances do we use Motif look and feel.
488: *
489: * #97882: Use Metal on Solaris 10 as well, there is bug which crashes JDK with GTK L&F
490: *
491: * @return If metal L&F should be used
492: */
493: private boolean shouldUseMetal() {
494: String osName = System.getProperty("os.name");
495: boolean result = !"Solaris".equals(osName)
496: && !osName.startsWith("SunOS")
497: && !osName.endsWith("Linux")
498: || UIManager.getSystemLookAndFeelClassName().indexOf(
499: "Motif") > -1 || isSolaris10();
500: return result;
501: }
502:
503: private LFListener listener = null;
504:
505: private class LFListener implements PropertyChangeListener {
506: public void propertyChange(PropertyChangeEvent pcl) {
507: if ("lookAndFeel".equals(pcl.getPropertyName())
508: || "win.xpstyle.themeActive".equals(pcl
509: .getPropertyName())) { //NOI18N
510: uninstallPerLFDefaults();
511: installPerLFDefaults();
512: }
513: }
514: }
515:
516: }
|