KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > synth > SynthLookAndFeel


1 /*
2  * @(#)SynthLookAndFeel.java 1.45 04/05/07
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.plaf.synth;
8
9 import sun.swing.DefaultLookup;
10 import java.awt.*;
11 import java.awt.event.*;
12 import java.beans.*;
13 import java.io.*;
14 import java.lang.ref.*;
15 import java.text.*;
16 import java.util.*;
17 import javax.swing.*;
18 import javax.swing.border.*;
19 import javax.swing.plaf.*;
20 import javax.swing.plaf.basic.*;
21 import sun.awt.AppContext;
22 import sun.swing.plaf.synth.*;
23
24 /**
25  * SynthLookAndFeel provides the basis for creating a customized look and
26  * feel. SynthLookAndFeel does not directly provide a look, all painting is
27  * delegated.
28  * You need to either provide a configuration file, by way of the
29  * {@link #load} method, or provide your own {@link SynthStyleFactory}
30  * to {@link #setStyleFactory}. Refer to the
31  * <a HREF="package-summary.html">package summary</a> for an example of
32  * loading a file, and {@link javax.swing.plaf.synth.SynthStyleFactory} for
33  * an example of providing your own <code>SynthStyleFactory</code> to
34  * <code>setStyleFactory</code>.
35  * <p>
36  * <strong>Warning:</strong>
37  * This class implements {@link Serializable} as a side effect of it
38  * extending {@link BasicLookAndFeel}. It is not intended to be serialized.
39  * An attempt to serialize it will
40  * result in {@link NotSerializableException}.
41  *
42  * @serial exclude
43  * @version 1.45, 05/07/04
44  * @since 1.5
45  * @author Scott Violet
46  */

47 public class SynthLookAndFeel extends BasicLookAndFeel {
48     /**
49      * Used in a handful of places where we need an empty Insets.
50      */

51     static final Insets EMPTY_UIRESOURCE_INSETS = new InsetsUIResource(
52                                                             0, 0, 0, 0);
53
54     /**
55      * AppContext key to get the current SynthStyleFactory.
56      */

57     private static final Object JavaDoc STYLE_FACTORY_KEY =
58                   new StringBuffer JavaDoc("com.sun.java.swing.plaf.gtk.StyleCache");
59
60     /**
61      * The last SynthStyleFactory that was asked for from AppContext
62      * <code>lastContext</code>.
63      */

64     private static SynthStyleFactory JavaDoc lastFactory;
65     /**
66      * If this is true it indicates there is more than one AppContext active
67      * and that we need to make sure in getStyleCache the requesting
68      * AppContext matches that of <code>lastContext</code> before returning
69      * it.
70      */

71     private static boolean multipleApps;
72     /**
73      * AppContext lastLAF came from.
74      */

75     private static AppContext lastContext;
76
77     // Refer to setSelectedUI
78
static ComponentUI selectedUI;
79     // Refer to setSelectedUI
80
static int selectedUIState;
81
82     /**
83      * SynthStyleFactory for the this SynthLookAndFeel.
84      */

85     private SynthStyleFactory JavaDoc factory;
86
87     /**
88      * Map of defaults table entries. This is populated via the load
89      * method.
90      */

91     private Map defaultsMap;
92
93
94     /**
95      * Used by the renderers. For the most part the renderers are implemented
96      * as Labels, which is problematic in so far as they are never selected.
97      * To accomodate this SynthLabelUI checks if the current
98      * UI matches that of <code>selectedUI</code> (which this methods sets), if
99      * it does, then a state as set by this method is returned. This provides
100      * a way for labels to have a state other than selected.
101      */

102     static void setSelectedUI(ComponentUI uix, boolean selected,
103                               boolean focused, boolean enabled) {
104         selectedUI = uix;
105         selectedUIState = 0;
106         if (selected) {
107             selectedUIState = SynthConstants.SELECTED;
108             if (focused) {
109                 selectedUIState |= SynthConstants.FOCUSED;
110             }
111         }
112         else {
113             selectedUIState = SynthConstants.FOCUSED;
114             if (enabled) {
115                 selectedUIState |= SynthConstants.ENABLED;
116             }
117             else {
118                 selectedUIState |= SynthConstants.DISABLED;
119             }
120         }
121     }
122
123     /**
124      * Clears out the selected UI that was last set in setSelectedUI.
125      */

126     static void resetSelectedUI() {
127         selectedUI = null;
128     }
129
130
131     /**
132      * Sets the SynthStyleFactory that the UI classes provided by
133      * synth will use to obtain a SynthStyle.
134      *
135      * @param cache SynthStyleFactory the UIs should use.
136      */

137     public static void setStyleFactory(SynthStyleFactory JavaDoc cache) {
138         // We assume the setter is called BEFORE the getter has been invoked
139
// for a particular AppContext.
140
synchronized(SynthLookAndFeel JavaDoc.class) {
141             AppContext context = AppContext.getAppContext();
142             if (!multipleApps && context != lastContext &&
143                                  lastContext != null) {
144                 multipleApps = true;
145             }
146             lastFactory = cache;
147             lastContext = context;
148             context.put(STYLE_FACTORY_KEY, cache);
149         }
150     }
151
152     /**
153      * Returns the current SynthStyleFactory.
154      *
155      * @return SynthStyleFactory
156      */

157     public static SynthStyleFactory JavaDoc getStyleFactory() {
158         synchronized(SynthLookAndFeel JavaDoc.class) {
159             if (!multipleApps) {
160                 return lastFactory;
161             }
162             AppContext context = AppContext.getAppContext();
163
164             if (lastContext == context) {
165                 return lastFactory;
166             }
167             lastContext = context;
168             lastFactory = (SynthStyleFactory JavaDoc)AppContext.getAppContext().get
169                                            (STYLE_FACTORY_KEY);
170             return lastFactory;
171         }
172     }
173
174     /**
175      * Returns the component state for the specified component. This should
176      * only be used for Components that don't have any special state beyond
177      * that of ENABLED, DISABLED or FOCUSED. For example, buttons shouldn't
178      * call into this method.
179      */

180     static int getComponentState(Component c) {
181         if (c.isEnabled()) {
182             if (c.isFocusOwner()) {
183                 return SynthUI.ENABLED | SynthUI.FOCUSED;
184             }
185             return SynthUI.ENABLED;
186         }
187         return SynthUI.DISABLED;
188     }
189
190     /**
191      * Gets a SynthStyle for the specified region of the specified component.
192      * This is not for general consumption, only custom UIs should call this
193      * method.
194      *
195      * @param c JComponent to get the SynthStyle for
196      * @param region Identifies the region of the specified component
197      * @return SynthStyle to use.
198      */

199     public static SynthStyle JavaDoc getStyle(JComponent c, Region JavaDoc region) {
200         return getStyleFactory().getStyle(c, region);
201     }
202
203     /**
204      * Returns true if the Style should be updated in response to the
205      * specified PropertyChangeEvent. This forwards to
206      * <code>shouldUpdateStyleOnAncestorChanged</code> as necessary.
207      */

208     static boolean shouldUpdateStyle(PropertyChangeEvent event) {
209         String JavaDoc eName = event.getPropertyName();
210         if ("name" == eName) {
211             // Always update on a name change
212
return true;
213         }
214         if ("ancestor" == eName && event.getNewValue() != null) {
215             // Only update on an ancestor change when getting a valid
216
// parent and the LookAndFeel wants this.
217
LookAndFeel laf = UIManager.getLookAndFeel();
218             return (laf instanceof SynthLookAndFeel JavaDoc &&
219                     ((SynthLookAndFeel JavaDoc)laf).
220                      shouldUpdateStyleOnAncestorChanged());
221         }
222         return false;
223     }
224
225     /**
226      * A convience method that will reset the Style of StyleContext if
227      * necessary.
228      *
229      * @return newStyle
230      */

231     static SynthStyle JavaDoc updateStyle(SynthContext JavaDoc context, SynthUI ui) {
232         SynthStyle JavaDoc newStyle = getStyle(context.getComponent(),
233                                        context.getRegion());
234         SynthStyle JavaDoc oldStyle = context.getStyle();
235
236         if (newStyle != oldStyle) {
237             if (oldStyle != null) {
238                 oldStyle.uninstallDefaults(context);
239             }
240             context.setStyle(newStyle);
241             newStyle.installDefaults(context, ui);
242         }
243         return newStyle;
244     }
245
246     /**
247      * Updates the style associated with <code>c</code>, and all its children.
248      * This is a lighter version of
249      * <code>SwingUtilities.updateComponentTreeUI</code>.
250      *
251      * @param c Component to update style for.
252      */

253     public static void updateStyles(Component c) {
254         _updateStyles(c);
255         c.repaint();
256     }
257
258     // Implementation for updateStyles
259
private static void _updateStyles(Component c) {
260         if (c instanceof JComponent) {
261             // Yes, this is hacky. A better solution is to get the UI
262
// and cast, but JComponent doesn't expose a getter for the UI
263
// (each of the UIs do), making that approach impractical.
264
String JavaDoc name = c.getName();
265             c.setName(null);
266             if (name != null) {
267                 c.setName(name);
268             }
269             ((JComponent)c).revalidate();
270         }
271         Component[] children = null;
272         if (c instanceof JMenu) {
273             children = ((JMenu)c).getMenuComponents();
274         }
275         else if (c instanceof Container) {
276             children = ((Container)c).getComponents();
277         }
278         if (children != null) {
279             for(int i = 0; i < children.length; i++) {
280                 updateStyles(children[i]);
281             }
282         }
283     }
284
285     /**
286      * Returns the Region for the JComponent <code>c</code>.
287      *
288      * @param c JComponent to fetch the Region for
289      * @return Region corresponding to <code>c</code>
290      */

291     public static Region JavaDoc getRegion(JComponent c) {
292         return Region.getRegion(c);
293     }
294
295     /**
296      * A convenience method to return where the foreground should be
297      * painted for the Component identified by the passed in
298      * AbstractSynthContext.
299      */

300     static Insets getPaintingInsets(SynthContext JavaDoc state, Insets insets) {
301         if (state.isSubregion()) {
302             insets = state.getStyle().getInsets(state, insets);
303         }
304         else {
305             insets = state.getComponent().getInsets(insets);
306         }
307         return insets;
308     }
309
310     /**
311      * A convenience method that handles painting of the background.
312      * All SynthUI implementations should override update and invoke
313      * this method.
314      */

315     static void update(SynthContext JavaDoc state, Graphics g) {
316         paintRegion(state, g, null);
317     }
318
319     /**
320      * A convenience method that handles painting of the background for
321      * subregions. All SynthUI's that have subregions should invoke
322      * this method, than paint the foreground.
323      */

324     static void updateSubregion(SynthContext JavaDoc state, Graphics g,
325                                 Rectangle bounds) {
326         paintRegion(state, g, bounds);
327     }
328
329     private static void paintRegion(SynthContext JavaDoc state, Graphics g,
330                                     Rectangle bounds) {
331         JComponent c = state.getComponent();
332         SynthStyle JavaDoc style = state.getStyle();
333         int x, y, width, height;
334
335         if (bounds == null) {
336             x = 0;
337             y = 0;
338             width = c.getWidth();
339             height = c.getHeight();
340         }
341         else {
342             x = bounds.x;
343             y = bounds.y;
344             width = bounds.width;
345             height = bounds.height;
346         }
347
348         // Fill in the background, if necessary.
349
boolean subregion = state.isSubregion();
350         if ((subregion && style.isOpaque(state)) ||
351                           (!subregion && c.isOpaque())) {
352             g.setColor(style.getColor(state, ColorType.BACKGROUND));
353             g.fillRect(x, y, width, height);
354         }
355     }
356
357     static boolean isLeftToRight(Component c) {
358         return c.getComponentOrientation().isLeftToRight();
359     }
360
361     /**
362      * Returns the ui that is of type <code>klass</code>, or null if
363      * one can not be found.
364      */

365     static Object JavaDoc getUIOfType(ComponentUI ui, Class JavaDoc klass) {
366         if (klass.isInstance(ui)) {
367             return ui;
368         }
369         return null;
370     }
371
372     /**
373      * Creates the Synth look and feel <code>ComponentUI</code> for
374      * the passed in <code>JComponent</code>.
375      *
376      * @param c JComponent to create the <code>ComponentUI</code> for
377      * @return ComponentUI to use for <code>c</code>
378      */

379     public static ComponentUI createUI(JComponent c) {
380         String JavaDoc key = c.getUIClassID().intern();
381
382         if (key == "ButtonUI") {
383             return SynthButtonUI.createUI(c);
384         }
385         else if (key == "CheckBoxUI") {
386             return SynthCheckBoxUI.createUI(c);
387         }
388         else if (key == "CheckBoxMenuItemUI") {
389             return SynthCheckBoxMenuItemUI.createUI(c);
390         }
391         else if (key == "ColorChooserUI") {
392             return SynthColorChooserUI.createUI(c);
393         }
394         else if (key == "ComboBoxUI") {
395             return SynthComboBoxUI.createUI(c);
396         }
397         else if (key == "DesktopPaneUI") {
398             return SynthDesktopPaneUI.createUI(c);
399         }
400         else if (key == "DesktopIconUI") {
401             return SynthDesktopIconUI.createUI(c);
402         }
403         else if (key == "EditorPaneUI") {
404             return SynthEditorPaneUI.createUI(c);
405         }
406         else if (key == "FileChooserUI") {
407             return SynthFileChooserUI.createUI(c);
408         }
409         else if (key == "FormattedTextFieldUI") {
410             return SynthFormattedTextFieldUI.createUI(c);
411         }
412         else if (key == "InternalFrameUI") {
413             return SynthInternalFrameUI.createUI(c);
414         }
415         else if (key == "LabelUI") {
416             return SynthLabelUI.createUI(c);
417         }
418         else if (key == "ListUI") {
419             return SynthListUI.createUI(c);
420         }
421         else if (key == "MenuBarUI") {
422             return SynthMenuBarUI.createUI(c);
423         }
424         else if (key == "MenuUI") {
425             return SynthMenuUI.createUI(c);
426         }
427         else if (key == "MenuItemUI") {
428             return SynthMenuItemUI.createUI(c);
429         }
430         else if (key == "OptionPaneUI") {
431             return SynthOptionPaneUI.createUI(c);
432         }
433         else if (key == "PanelUI") {
434             return SynthPanelUI.createUI(c);
435         }
436         else if (key == "PasswordFieldUI") {
437             return SynthPasswordFieldUI.createUI(c);
438         }
439         else if (key == "PopupMenuSeparatorUI") {
440             return SynthSeparatorUI.createUI(c);
441         }
442         else if (key == "PopupMenuUI") {
443             return SynthPopupMenuUI.createUI(c);
444         }
445         else if (key == "ProgressBarUI") {
446             return SynthProgressBarUI.createUI(c);
447         }
448         else if (key == "RadioButtonUI") {
449             return SynthRadioButtonUI.createUI(c);
450         }
451         else if (key == "RadioButtonMenuItemUI") {
452             return SynthRadioButtonMenuItemUI.createUI(c);
453         }
454         else if (key == "RootPaneUI") {
455             return SynthRootPaneUI.createUI(c);
456         }
457         else if (key == "ScrollBarUI") {
458             return SynthScrollBarUI.createUI(c);
459         }
460         else if (key == "ScrollPaneUI") {
461             return SynthScrollPaneUI.createUI(c);
462         }
463         else if (key == "SeparatorUI") {
464             return SynthSeparatorUI.createUI(c);
465         }
466         else if (key == "SliderUI") {
467             return SynthSliderUI.createUI(c);
468         }
469         else if (key == "SpinnerUI") {
470             return SynthSpinnerUI.createUI(c);
471         }
472         else if (key == "SplitPaneUI") {
473             return SynthSplitPaneUI.createUI(c);
474         }
475         else if (key == "TabbedPaneUI") {
476             return SynthTabbedPaneUI.createUI(c);
477         }
478         else if (key == "TableUI") {
479             return SynthTableUI.createUI(c);
480         }
481         else if (key == "TableHeaderUI") {
482             return SynthTableHeaderUI.createUI(c);
483         }
484         else if (key == "TextAreaUI") {
485             return SynthTextAreaUI.createUI(c);
486         }
487         else if (key == "TextFieldUI") {
488             return SynthTextFieldUI.createUI(c);
489         }
490         else if (key == "TextPaneUI") {
491             return SynthTextPaneUI.createUI(c);
492         }
493         else if (key == "ToggleButtonUI") {
494             return SynthToggleButtonUI.createUI(c);
495         }
496         else if (key == "ToolBarSeparatorUI") {
497             return SynthSeparatorUI.createUI(c);
498         }
499         else if (key == "ToolBarUI") {
500             return SynthToolBarUI.createUI(c);
501         }
502         else if (key == "ToolTipUI") {
503             return SynthToolTipUI.createUI(c);
504         }
505         else if (key == "TreeUI") {
506             return SynthTreeUI.createUI(c);
507         }
508         else if (key == "ViewportUI") {
509             return SynthViewportUI.createUI(c);
510         }
511         return null;
512     }
513
514
515     /**
516      * Creates a SynthLookAndFeel.
517      * <p>
518      * For the returned <code>SynthLookAndFeel</code> to be useful you need to
519      * invoke <code>load</code> to specify the set of
520      * <code>SynthStyle</code>s, or invoke <code>setStyleFactory</code>.
521      *
522      * @see #load
523      * @see #setStyleFactory
524      */

525     public SynthLookAndFeel() {
526         factory = new DefaultSynthStyleFactory JavaDoc();
527     }
528
529     /**
530      * Loads the set of <code>SynthStyle</code>s that will be used by
531      * this <code>SynthLookAndFeel</code>. <code>resourceBase</code> is
532      * used to resolve any path based resources, for example an
533      * <code>Image</code> would be resolved by
534      * <code>resourceBase.getResource(path)</code>. Refer to
535      * <a HREF="doc-files/synthFileFormat.html">Synth File Format</a>
536      * for more information.
537      *
538      * @param input InputStream to load from
539      * @param resourceBase Used to resolve any images or other resources
540      * @throws ParseException If there is an error in parsing
541      * @throws IllegalArgumentException if input or resourceBase is null
542      */

543     public void load(InputStream input, Class JavaDoc<?> resourceBase) throws
544                        ParseException, IllegalArgumentException JavaDoc {
545         if (defaultsMap == null) {
546             defaultsMap = new HashMap();
547         }
548         new SynthParser JavaDoc().parse(input, (DefaultSynthStyleFactory JavaDoc)factory,
549                                 resourceBase, defaultsMap);
550     }
551
552     /**
553      * Called by UIManager when this look and feel is installed.
554      */

555     public void initialize() {
556         super.initialize();
557         DefaultLookup.setDefaultLookup(new SynthDefaultLookup JavaDoc());
558         setStyleFactory(factory);
559     }
560
561     /**
562      * Called by UIManager when this look and feel is uninstalled.
563      */

564     public void uninitialize() {
565         // We should uninstall the StyleFactory here, but unfortunately
566
// there are a handful of things that retain references to the
567
// LookAndFeel and expect things to work
568
super.uninitialize();
569     }
570
571     /**
572      * Returns the defaults for this SynthLookAndFeel.
573      *
574      * @return Defaults able.
575      */

576     public UIDefaults getDefaults() {
577     UIDefaults table = new UIDefaults();
578         Region.registerUIs(table);
579         table.setDefaultLocale(Locale.getDefault());
580         table.addResourceBundle(
581               "com.sun.swing.internal.plaf.basic.resources.basic" );
582         table.addResourceBundle("com.sun.swing.internal.plaf.synth.resources.synth");
583
584         // These need to be defined for JColorChooser to work.
585
table.put("ColorChooser.swatchesRecentSwatchSize",
586                   new Dimension(10, 10));
587         table.put("ColorChooser.swatchesDefaultRecentColor", Color.RED);
588         table.put("ColorChooser.swatchesSwatchSize", new Dimension(10, 10));
589
590         // These are needed for PopupMenu.
591
table.put("PopupMenu.selectedWindowInputMapBindings", new Object JavaDoc[] {
592           "ESCAPE", "cancel",
593                     "DOWN", "selectNext",
594          "KP_DOWN", "selectNext",
595               "UP", "selectPrevious",
596            "KP_UP", "selectPrevious",
597             "LEFT", "selectParent",
598          "KP_LEFT", "selectParent",
599            "RIGHT", "selectChild",
600         "KP_RIGHT", "selectChild",
601            "ENTER", "return",
602            "SPACE", "return"
603         });
604         table.put("PopupMenu.selectedWindowInputMapBindings.RightToLeft",
605                   new Object JavaDoc[] {
606             "LEFT", "selectChild",
607          "KP_LEFT", "selectChild",
608            "RIGHT", "selectParent",
609         "KP_RIGHT", "selectParent",
610                   });
611
612         if (defaultsMap != null) {
613             table.putAll(defaultsMap);
614         }
615         return table;
616     }
617
618     /**
619      * Returns true, SynthLookAndFeel is always supported.
620      *
621      * @return true.
622      */

623     public boolean isSupportedLookAndFeel() {
624         return true;
625     }
626
627     /**
628      * Returns false, SynthLookAndFeel is not a native look and feel.
629      *
630      * @return true.
631      */

632     public boolean isNativeLookAndFeel() {
633         return false;
634     }
635
636     /**
637      * Returns a textual description of SynthLookAndFeel.
638      *
639      * @return textual description of synth.
640      */

641     public String JavaDoc getDescription() {
642         return "Synth look and feel";
643     }
644
645     /**
646      * Return a short string that identifies this look and feel.
647      *
648      * @return a short string identifying this look and feel.
649      */

650     public String JavaDoc getName() {
651         return "Synth look and feel";
652     }
653
654     /**
655      * Return a string that identifies this look and feel.
656      *
657      * @return a short string identifying this look and feel.
658      */

659     public String JavaDoc getID() {
660         return "Synth";
661     }
662
663     /**
664      * Returns whether or not the UIs should update their
665      * <code>SynthStyles</code> from the <code>SynthStyleFactory</code>
666      * when the ancestor of the <code>JComponent</code> changes. A subclass
667      * that provided a <code>SynthStyleFactory</code> that based the
668      * return value from <code>getStyle</code> off the containment hierarchy
669      * would override this method to return true.
670      *
671      * @return whether or not the UIs should update their
672      * <code>SynthStyles</code> from the <code>SynthStyleFactory</code>
673      * when the ancestor changed.
674      */

675     public boolean shouldUpdateStyleOnAncestorChanged() {
676         return false;
677     }
678     
679     private void writeObject(java.io.ObjectOutputStream JavaDoc out)
680             throws IOException {
681         throw new NotSerializableException(this.getClass().getName());
682     }
683 }
684
Popular Tags