001 /*
002 * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.plaf.synth;
026
027 import java.awt.*;
028 import java.beans.*;
029 import java.io.*;
030 import java.lang.ref.*;
031 import java.net.*;
032 import java.security.*;
033 import java.text.*;
034 import java.util.*;
035 import javax.swing.*;
036 import javax.swing.plaf.*;
037 import javax.swing.plaf.basic.*;
038
039 import sun.awt.*;
040 import sun.security.action.*;
041 import sun.swing.*;
042 import sun.swing.plaf.synth.*;
043
044 /**
045 * SynthLookAndFeel provides the basis for creating a customized look and
046 * feel. SynthLookAndFeel does not directly provide a look, all painting is
047 * delegated.
048 * You need to either provide a configuration file, by way of the
049 * {@link #load} method, or provide your own {@link SynthStyleFactory}
050 * to {@link #setStyleFactory}. Refer to the
051 * <a href="package-summary.html">package summary</a> for an example of
052 * loading a file, and {@link javax.swing.plaf.synth.SynthStyleFactory} for
053 * an example of providing your own <code>SynthStyleFactory</code> to
054 * <code>setStyleFactory</code>.
055 * <p>
056 * <strong>Warning:</strong>
057 * This class implements {@link Serializable} as a side effect of it
058 * extending {@link BasicLookAndFeel}. It is not intended to be serialized.
059 * An attempt to serialize it will
060 * result in {@link NotSerializableException}.
061 *
062 * @serial exclude
063 * @version 1.48, 05/24/05
064 * @since 1.5
065 * @author Scott Violet
066 */
067 public class SynthLookAndFeel extends BasicLookAndFeel {
068 /**
069 * Used in a handful of places where we need an empty Insets.
070 */
071 static final Insets EMPTY_UIRESOURCE_INSETS = new InsetsUIResource(
072 0, 0, 0, 0);
073
074 /**
075 * AppContext key to get the current SynthStyleFactory.
076 */
077 private static final Object STYLE_FACTORY_KEY = new StringBuffer(
078 "com.sun.java.swing.plaf.gtk.StyleCache");
079
080 /**
081 * The last SynthStyleFactory that was asked for from AppContext
082 * <code>lastContext</code>.
083 */
084 private static SynthStyleFactory lastFactory;
085 /**
086 * If this is true it indicates there is more than one AppContext active
087 * and that we need to make sure in getStyleCache the requesting
088 * AppContext matches that of <code>lastContext</code> before returning
089 * it.
090 */
091 private static boolean multipleApps;
092 /**
093 * AppContext lastLAF came from.
094 */
095 private static AppContext lastContext;
096
097 // Refer to setSelectedUI
098 static ComponentUI selectedUI;
099 // Refer to setSelectedUI
100 static int selectedUIState;
101
102 /**
103 * SynthStyleFactory for the this SynthLookAndFeel.
104 */
105 private SynthStyleFactory factory;
106
107 /**
108 * Map of defaults table entries. This is populated via the load
109 * method.
110 */
111 private Map defaultsMap;
112
113 private Handler _handler;
114
115 /**
116 * Used by the renderers. For the most part the renderers are implemented
117 * as Labels, which is problematic in so far as they are never selected.
118 * To accomodate this SynthLabelUI checks if the current
119 * UI matches that of <code>selectedUI</code> (which this methods sets), if
120 * it does, then a state as set by this method is returned. This provides
121 * a way for labels to have a state other than selected.
122 */
123 static void setSelectedUI(ComponentUI uix, boolean selected,
124 boolean focused, boolean enabled, boolean rollover) {
125 selectedUI = uix;
126 selectedUIState = 0;
127 if (selected) {
128 selectedUIState = SynthConstants.SELECTED;
129 if (focused) {
130 selectedUIState |= SynthConstants.FOCUSED;
131 }
132 } else if (rollover && enabled) {
133 selectedUIState |= SynthConstants.MOUSE_OVER
134 | SynthConstants.ENABLED;
135 if (focused) {
136 selectedUIState |= SynthConstants.FOCUSED;
137 }
138 } else {
139 selectedUIState = SynthConstants.FOCUSED;
140 if (enabled) {
141 selectedUIState |= SynthConstants.ENABLED;
142 } else {
143 selectedUIState |= SynthConstants.DISABLED;
144 }
145 }
146 }
147
148 /**
149 * Clears out the selected UI that was last set in setSelectedUI.
150 */
151 static void resetSelectedUI() {
152 selectedUI = null;
153 }
154
155 /**
156 * Sets the SynthStyleFactory that the UI classes provided by
157 * synth will use to obtain a SynthStyle.
158 *
159 * @param cache SynthStyleFactory the UIs should use.
160 */
161 public static void setStyleFactory(SynthStyleFactory cache) {
162 // We assume the setter is called BEFORE the getter has been invoked
163 // for a particular AppContext.
164 synchronized (SynthLookAndFeel.class) {
165 AppContext context = AppContext.getAppContext();
166 if (!multipleApps && context != lastContext
167 && lastContext != null) {
168 multipleApps = true;
169 }
170 lastFactory = cache;
171 lastContext = context;
172 context.put(STYLE_FACTORY_KEY, cache);
173 }
174 }
175
176 /**
177 * Returns the current SynthStyleFactory.
178 *
179 * @return SynthStyleFactory
180 */
181 public static SynthStyleFactory getStyleFactory() {
182 synchronized (SynthLookAndFeel.class) {
183 if (!multipleApps) {
184 return lastFactory;
185 }
186 AppContext context = AppContext.getAppContext();
187
188 if (lastContext == context) {
189 return lastFactory;
190 }
191 lastContext = context;
192 lastFactory = (SynthStyleFactory) AppContext
193 .getAppContext().get(STYLE_FACTORY_KEY);
194 return lastFactory;
195 }
196 }
197
198 /**
199 * Returns the component state for the specified component. This should
200 * only be used for Components that don't have any special state beyond
201 * that of ENABLED, DISABLED or FOCUSED. For example, buttons shouldn't
202 * call into this method.
203 */
204 static int getComponentState(Component c) {
205 if (c.isEnabled()) {
206 if (c.isFocusOwner()) {
207 return SynthUI.ENABLED | SynthUI.FOCUSED;
208 }
209 return SynthUI.ENABLED;
210 }
211 return SynthUI.DISABLED;
212 }
213
214 /**
215 * Gets a SynthStyle for the specified region of the specified component.
216 * This is not for general consumption, only custom UIs should call this
217 * method.
218 *
219 * @param c JComponent to get the SynthStyle for
220 * @param region Identifies the region of the specified component
221 * @return SynthStyle to use.
222 */
223 public static SynthStyle getStyle(JComponent c, Region region) {
224 return getStyleFactory().getStyle(c, region);
225 }
226
227 /**
228 * Returns true if the Style should be updated in response to the
229 * specified PropertyChangeEvent. This forwards to
230 * <code>shouldUpdateStyleOnAncestorChanged</code> as necessary.
231 */
232 static boolean shouldUpdateStyle(PropertyChangeEvent event) {
233 String eName = event.getPropertyName();
234 if ("name" == eName) {
235 // Always update on a name change
236 return true;
237 } else if ("componentOrientation" == eName) {
238 // Always update on a component orientation change
239 return true;
240 } else if ("ancestor" == eName && event.getNewValue() != null) {
241 // Only update on an ancestor change when getting a valid
242 // parent and the LookAndFeel wants this.
243 LookAndFeel laf = UIManager.getLookAndFeel();
244 return (laf instanceof SynthLookAndFeel && ((SynthLookAndFeel) laf)
245 .shouldUpdateStyleOnAncestorChanged());
246 }
247 return false;
248 }
249
250 /**
251 * A convience method that will reset the Style of StyleContext if
252 * necessary.
253 *
254 * @return newStyle
255 */
256 static SynthStyle updateStyle(SynthContext context, SynthUI ui) {
257 SynthStyle newStyle = getStyle(context.getComponent(), context
258 .getRegion());
259 SynthStyle oldStyle = context.getStyle();
260
261 if (newStyle != oldStyle) {
262 if (oldStyle != null) {
263 oldStyle.uninstallDefaults(context);
264 }
265 context.setStyle(newStyle);
266 newStyle.installDefaults(context, ui);
267 }
268 return newStyle;
269 }
270
271 /**
272 * Updates the style associated with <code>c</code>, and all its children.
273 * This is a lighter version of
274 * <code>SwingUtilities.updateComponentTreeUI</code>.
275 *
276 * @param c Component to update style for.
277 */
278 public static void updateStyles(Component c) {
279 _updateStyles(c);
280 c.repaint();
281 }
282
283 // Implementation for updateStyles
284 private static void _updateStyles(Component c) {
285 if (c instanceof JComponent) {
286 // Yes, this is hacky. A better solution is to get the UI
287 // and cast, but JComponent doesn't expose a getter for the UI
288 // (each of the UIs do), making that approach impractical.
289 String name = c.getName();
290 c.setName(null);
291 if (name != null) {
292 c.setName(name);
293 }
294 ((JComponent) c).revalidate();
295 }
296 Component[] children = null;
297 if (c instanceof JMenu) {
298 children = ((JMenu) c).getMenuComponents();
299 } else if (c instanceof Container) {
300 children = ((Container) c).getComponents();
301 }
302 if (children != null) {
303 for (int i = 0; i < children.length; i++) {
304 updateStyles(children[i]);
305 }
306 }
307 }
308
309 /**
310 * Returns the Region for the JComponent <code>c</code>.
311 *
312 * @param c JComponent to fetch the Region for
313 * @return Region corresponding to <code>c</code>
314 */
315 public static Region getRegion(JComponent c) {
316 return Region.getRegion(c);
317 }
318
319 /**
320 * A convenience method to return where the foreground should be
321 * painted for the Component identified by the passed in
322 * AbstractSynthContext.
323 */
324 static Insets getPaintingInsets(SynthContext state, Insets insets) {
325 if (state.isSubregion()) {
326 insets = state.getStyle().getInsets(state, insets);
327 } else {
328 insets = state.getComponent().getInsets(insets);
329 }
330 return insets;
331 }
332
333 /**
334 * A convenience method that handles painting of the background.
335 * All SynthUI implementations should override update and invoke
336 * this method.
337 */
338 static void update(SynthContext state, Graphics g) {
339 paintRegion(state, g, null);
340 }
341
342 /**
343 * A convenience method that handles painting of the background for
344 * subregions. All SynthUI's that have subregions should invoke
345 * this method, than paint the foreground.
346 */
347 static void updateSubregion(SynthContext state, Graphics g,
348 Rectangle bounds) {
349 paintRegion(state, g, bounds);
350 }
351
352 private static void paintRegion(SynthContext state, Graphics g,
353 Rectangle bounds) {
354 JComponent c = state.getComponent();
355 SynthStyle style = state.getStyle();
356 int x, y, width, height;
357
358 if (bounds == null) {
359 x = 0;
360 y = 0;
361 width = c.getWidth();
362 height = c.getHeight();
363 } else {
364 x = bounds.x;
365 y = bounds.y;
366 width = bounds.width;
367 height = bounds.height;
368 }
369
370 // Fill in the background, if necessary.
371 boolean subregion = state.isSubregion();
372 if ((subregion && style.isOpaque(state))
373 || (!subregion && c.isOpaque())) {
374 g.setColor(style.getColor(state, ColorType.BACKGROUND));
375 g.fillRect(x, y, width, height);
376 }
377 }
378
379 static boolean isLeftToRight(Component c) {
380 return c.getComponentOrientation().isLeftToRight();
381 }
382
383 /**
384 * Returns the ui that is of type <code>klass</code>, or null if
385 * one can not be found.
386 */
387 static Object getUIOfType(ComponentUI ui, Class klass) {
388 if (klass.isInstance(ui)) {
389 return ui;
390 }
391 return null;
392 }
393
394 /**
395 * Creates the Synth look and feel <code>ComponentUI</code> for
396 * the passed in <code>JComponent</code>.
397 *
398 * @param c JComponent to create the <code>ComponentUI</code> for
399 * @return ComponentUI to use for <code>c</code>
400 */
401 public static ComponentUI createUI(JComponent c) {
402 String key = c.getUIClassID().intern();
403
404 if (key == "ButtonUI") {
405 return SynthButtonUI.createUI(c);
406 } else if (key == "CheckBoxUI") {
407 return SynthCheckBoxUI.createUI(c);
408 } else if (key == "CheckBoxMenuItemUI") {
409 return SynthCheckBoxMenuItemUI.createUI(c);
410 } else if (key == "ColorChooserUI") {
411 return SynthColorChooserUI.createUI(c);
412 } else if (key == "ComboBoxUI") {
413 return SynthComboBoxUI.createUI(c);
414 } else if (key == "DesktopPaneUI") {
415 return SynthDesktopPaneUI.createUI(c);
416 } else if (key == "DesktopIconUI") {
417 return SynthDesktopIconUI.createUI(c);
418 } else if (key == "EditorPaneUI") {
419 return SynthEditorPaneUI.createUI(c);
420 } else if (key == "FileChooserUI") {
421 return SynthFileChooserUI.createUI(c);
422 } else if (key == "FormattedTextFieldUI") {
423 return SynthFormattedTextFieldUI.createUI(c);
424 } else if (key == "InternalFrameUI") {
425 return SynthInternalFrameUI.createUI(c);
426 } else if (key == "LabelUI") {
427 return SynthLabelUI.createUI(c);
428 } else if (key == "ListUI") {
429 return SynthListUI.createUI(c);
430 } else if (key == "MenuBarUI") {
431 return SynthMenuBarUI.createUI(c);
432 } else if (key == "MenuUI") {
433 return SynthMenuUI.createUI(c);
434 } else if (key == "MenuItemUI") {
435 return SynthMenuItemUI.createUI(c);
436 } else if (key == "OptionPaneUI") {
437 return SynthOptionPaneUI.createUI(c);
438 } else if (key == "PanelUI") {
439 return SynthPanelUI.createUI(c);
440 } else if (key == "PasswordFieldUI") {
441 return SynthPasswordFieldUI.createUI(c);
442 } else if (key == "PopupMenuSeparatorUI") {
443 return SynthSeparatorUI.createUI(c);
444 } else if (key == "PopupMenuUI") {
445 return SynthPopupMenuUI.createUI(c);
446 } else if (key == "ProgressBarUI") {
447 return SynthProgressBarUI.createUI(c);
448 } else if (key == "RadioButtonUI") {
449 return SynthRadioButtonUI.createUI(c);
450 } else if (key == "RadioButtonMenuItemUI") {
451 return SynthRadioButtonMenuItemUI.createUI(c);
452 } else if (key == "RootPaneUI") {
453 return SynthRootPaneUI.createUI(c);
454 } else if (key == "ScrollBarUI") {
455 return SynthScrollBarUI.createUI(c);
456 } else if (key == "ScrollPaneUI") {
457 return SynthScrollPaneUI.createUI(c);
458 } else if (key == "SeparatorUI") {
459 return SynthSeparatorUI.createUI(c);
460 } else if (key == "SliderUI") {
461 return SynthSliderUI.createUI(c);
462 } else if (key == "SpinnerUI") {
463 return SynthSpinnerUI.createUI(c);
464 } else if (key == "SplitPaneUI") {
465 return SynthSplitPaneUI.createUI(c);
466 } else if (key == "TabbedPaneUI") {
467 return SynthTabbedPaneUI.createUI(c);
468 } else if (key == "TableUI") {
469 return SynthTableUI.createUI(c);
470 } else if (key == "TableHeaderUI") {
471 return SynthTableHeaderUI.createUI(c);
472 } else if (key == "TextAreaUI") {
473 return SynthTextAreaUI.createUI(c);
474 } else if (key == "TextFieldUI") {
475 return SynthTextFieldUI.createUI(c);
476 } else if (key == "TextPaneUI") {
477 return SynthTextPaneUI.createUI(c);
478 } else if (key == "ToggleButtonUI") {
479 return SynthToggleButtonUI.createUI(c);
480 } else if (key == "ToolBarSeparatorUI") {
481 return SynthSeparatorUI.createUI(c);
482 } else if (key == "ToolBarUI") {
483 return SynthToolBarUI.createUI(c);
484 } else if (key == "ToolTipUI") {
485 return SynthToolTipUI.createUI(c);
486 } else if (key == "TreeUI") {
487 return SynthTreeUI.createUI(c);
488 } else if (key == "ViewportUI") {
489 return SynthViewportUI.createUI(c);
490 }
491 return null;
492 }
493
494 /**
495 * Creates a SynthLookAndFeel.
496 * <p>
497 * For the returned <code>SynthLookAndFeel</code> to be useful you need to
498 * invoke <code>load</code> to specify the set of
499 * <code>SynthStyle</code>s, or invoke <code>setStyleFactory</code>.
500 *
501 * @see #load
502 * @see #setStyleFactory
503 */
504 public SynthLookAndFeel() {
505 factory = new DefaultSynthStyleFactory();
506 _handler = new Handler();
507 }
508
509 /**
510 * Loads the set of <code>SynthStyle</code>s that will be used by
511 * this <code>SynthLookAndFeel</code>. <code>resourceBase</code> is
512 * used to resolve any path based resources, for example an
513 * <code>Image</code> would be resolved by
514 * <code>resourceBase.getResource(path)</code>. Refer to
515 * <a href="doc-files/synthFileFormat.html">Synth File Format</a>
516 * for more information.
517 *
518 * @param input InputStream to load from
519 * @param resourceBase used to resolve any images or other resources
520 * @throws ParseException if there is an error in parsing
521 * @throws IllegalArgumentException if input or resourceBase is <code>null</code>
522 */
523 public void load(InputStream input, Class<?> resourceBase)
524 throws ParseException {
525 if (resourceBase == null) {
526 throw new IllegalArgumentException(
527 "You must supply a valid resource base Class");
528 }
529
530 if (defaultsMap == null) {
531 defaultsMap = new HashMap();
532 }
533
534 new SynthParser().parse(input,
535 (DefaultSynthStyleFactory) factory, null, resourceBase,
536 defaultsMap);
537 }
538
539 /**
540 * Loads the set of <code>SynthStyle</code>s that will be used by
541 * this <code>SynthLookAndFeel</code>. Path based resources are resolved
542 * relatively to the specified <code>URL</code> of the style. For example
543 * an <code>Image</code> would be resolved by
544 * <code>new URL(synthFile, path)</code>. Refer to
545 * <a href="doc-files/synthFileFormat.html">Synth File Format</a> for more
546 * information.
547 *
548 * @param url the <code>URL</code> to load the set of
549 * <code>SynthStyle</code> from
550 * @throws ParseException if there is an error in parsing
551 * @throws IllegalArgumentException if synthSet is <code>null</code>
552 * @throws IOException if synthSet cannot be opened as an <code>InputStream</code>
553 * @since 1.6
554 */
555 public void load(URL url) throws ParseException, IOException {
556 if (url == null) {
557 throw new IllegalArgumentException(
558 "You must supply a valid Synth set URL");
559 }
560
561 if (defaultsMap == null) {
562 defaultsMap = new HashMap();
563 }
564
565 InputStream input = url.openStream();
566 new SynthParser().parse(input,
567 (DefaultSynthStyleFactory) factory, url, null,
568 defaultsMap);
569 }
570
571 /**
572 * Called by UIManager when this look and feel is installed.
573 */
574 public void initialize() {
575 super .initialize();
576 DefaultLookup.setDefaultLookup(new SynthDefaultLookup());
577 setStyleFactory(factory);
578 KeyboardFocusManager.getCurrentKeyboardFocusManager()
579 .addPropertyChangeListener(_handler);
580 }
581
582 /**
583 * Called by UIManager when this look and feel is uninstalled.
584 */
585 public void uninitialize() {
586 KeyboardFocusManager.getCurrentKeyboardFocusManager()
587 .removePropertyChangeListener(_handler);
588 // We should uninstall the StyleFactory here, but unfortunately
589 // there are a handful of things that retain references to the
590 // LookAndFeel and expect things to work
591 super .uninitialize();
592 }
593
594 /**
595 * Returns the defaults for this SynthLookAndFeel.
596 *
597 * @return Defaults table.
598 */
599 public UIDefaults getDefaults() {
600 UIDefaults table = new UIDefaults(60, 0.75f);
601
602 Region.registerUIs(table);
603 table.setDefaultLocale(Locale.getDefault());
604 table
605 .addResourceBundle("com.sun.swing.internal.plaf.basic.resources.basic");
606 table
607 .addResourceBundle("com.sun.swing.internal.plaf.synth.resources.synth");
608
609 // SynthTabbedPaneUI supports rollover on tabs, GTK does not
610 table.put("TabbedPane.isTabRollover", Boolean.TRUE);
611
612 // These need to be defined for JColorChooser to work.
613 table.put("ColorChooser.swatchesRecentSwatchSize",
614 new Dimension(10, 10));
615 table.put("ColorChooser.swatchesDefaultRecentColor", Color.RED);
616 table.put("ColorChooser.swatchesSwatchSize", new Dimension(10,
617 10));
618
619 // These are needed for PopupMenu.
620 table.put("PopupMenu.selectedWindowInputMapBindings",
621 new Object[] { "ESCAPE", "cancel", "DOWN",
622 "selectNext", "KP_DOWN", "selectNext", "UP",
623 "selectPrevious", "KP_UP", "selectPrevious",
624 "LEFT", "selectParent", "KP_LEFT",
625 "selectParent", "RIGHT", "selectChild",
626 "KP_RIGHT", "selectChild", "ENTER", "return",
627 "SPACE", "return" });
628 table.put(
629 "PopupMenu.selectedWindowInputMapBindings.RightToLeft",
630 new Object[] { "LEFT", "selectChild", "KP_LEFT",
631 "selectChild", "RIGHT", "selectParent",
632 "KP_RIGHT", "selectParent", });
633
634 // enabled antialiasing depending on desktop settings
635 flushUnreferenced();
636 Object aaTextInfo = getAATextInfo();
637 table.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY, aaTextInfo);
638 new AATextListener(this );
639
640 if (defaultsMap != null) {
641 table.putAll(defaultsMap);
642 }
643 return table;
644 }
645
646 /**
647 * Returns true, SynthLookAndFeel is always supported.
648 *
649 * @return true.
650 */
651 public boolean isSupportedLookAndFeel() {
652 return true;
653 }
654
655 /**
656 * Returns false, SynthLookAndFeel is not a native look and feel.
657 *
658 * @return false
659 */
660 public boolean isNativeLookAndFeel() {
661 return false;
662 }
663
664 /**
665 * Returns a textual description of SynthLookAndFeel.
666 *
667 * @return textual description of synth.
668 */
669 public String getDescription() {
670 return "Synth look and feel";
671 }
672
673 /**
674 * Return a short string that identifies this look and feel.
675 *
676 * @return a short string identifying this look and feel.
677 */
678 public String getName() {
679 return "Synth look and feel";
680 }
681
682 /**
683 * Return a string that identifies this look and feel.
684 *
685 * @return a short string identifying this look and feel.
686 */
687 public String getID() {
688 return "Synth";
689 }
690
691 /**
692 * Returns whether or not the UIs should update their
693 * <code>SynthStyles</code> from the <code>SynthStyleFactory</code>
694 * when the ancestor of the <code>JComponent</code> changes. A subclass
695 * that provided a <code>SynthStyleFactory</code> that based the
696 * return value from <code>getStyle</code> off the containment hierarchy
697 * would override this method to return true.
698 *
699 * @return whether or not the UIs should update their
700 * <code>SynthStyles</code> from the <code>SynthStyleFactory</code>
701 * when the ancestor changed.
702 */
703 public boolean shouldUpdateStyleOnAncestorChanged() {
704 return false;
705 }
706
707 /**
708 * Returns the antialiasing information as specified by the host desktop.
709 * Antialiasing might be forced off if the desktop is GNOME and the user
710 * has set his locale to Chinese, Japanese or Korean. This is consistent
711 * with what GTK does. See com.sun.java.swing.plaf.gtk.GtkLookAndFeel
712 * for more information about CJK and antialiased fonts.
713 *
714 * @return the text antialiasing information associated to the desktop
715 */
716 private static Object getAATextInfo() {
717 String language = Locale.getDefault().getLanguage();
718 String desktop = (String) AccessController
719 .doPrivileged(new GetPropertyAction("sun.desktop"));
720
721 boolean isCjkLocale = (Locale.CHINESE.getLanguage().equals(
722 language)
723 || Locale.JAPANESE.getLanguage().equals(language) || Locale.KOREAN
724 .getLanguage().equals(language));
725 boolean isGnome = "gnome".equals(desktop);
726 boolean isLocal = SwingUtilities2.isLocalDisplay();
727
728 boolean setAA = isLocal && (!isGnome || !isCjkLocale);
729
730 Object aaTextInfo = SwingUtilities2.AATextInfo
731 .getAATextInfo(setAA);
732 return aaTextInfo;
733 }
734
735 private static ReferenceQueue queue = new ReferenceQueue();
736
737 private static void flushUnreferenced() {
738 AATextListener aatl;
739 while ((aatl = (AATextListener) queue.poll()) != null) {
740 aatl.dispose();
741 }
742 }
743
744 private static class AATextListener extends WeakReference implements
745 PropertyChangeListener {
746 private String key = SunToolkit.DESKTOPFONTHINTS;
747
748 AATextListener(LookAndFeel laf) {
749 super (laf, queue);
750 Toolkit tk = Toolkit.getDefaultToolkit();
751 tk.addPropertyChangeListener(key, this );
752 }
753
754 public void propertyChange(PropertyChangeEvent pce) {
755 UIDefaults defaults = UIManager.getLookAndFeelDefaults();
756 if (defaults.getBoolean("Synth.doNotSetTextAA")) {
757 dispose();
758 return;
759 }
760
761 LookAndFeel laf = (LookAndFeel) get();
762 if (laf == null || laf != UIManager.getLookAndFeel()) {
763 dispose();
764 return;
765 }
766
767 Object aaTextInfo = getAATextInfo();
768 defaults.put(SwingUtilities2.AA_TEXT_PROPERTY_KEY,
769 aaTextInfo);
770
771 updateUI();
772 }
773
774 void dispose() {
775 Toolkit tk = Toolkit.getDefaultToolkit();
776 tk.removePropertyChangeListener(key, this );
777 }
778
779 /**
780 * Updates the UI of the passed in window and all its children.
781 */
782 private static void updateWindowUI(Window window) {
783 updateStyles(window);
784 Window ownedWins[] = window.getOwnedWindows();
785 for (int i = 0; i < ownedWins.length; i++) {
786 updateWindowUI(ownedWins[i]);
787 }
788 }
789
790 /**
791 * Updates the UIs of all the known Frames.
792 */
793 private static void updateAllUIs() {
794 Frame appFrames[] = Frame.getFrames();
795 for (int i = 0; i < appFrames.length; i++) {
796 updateWindowUI(appFrames[i]);
797 }
798 }
799
800 /**
801 * Indicates if an updateUI call is pending.
802 */
803 private static boolean updatePending;
804
805 /**
806 * Sets whether or not an updateUI call is pending.
807 */
808 private static synchronized void setUpdatePending(boolean update) {
809 updatePending = update;
810 }
811
812 /**
813 * Returns true if a UI update is pending.
814 */
815 private static synchronized boolean isUpdatePending() {
816 return updatePending;
817 }
818
819 protected void updateUI() {
820 if (!isUpdatePending()) {
821 setUpdatePending(true);
822 Runnable uiUpdater = new Runnable() {
823 public void run() {
824 updateAllUIs();
825 setUpdatePending(false);
826 }
827 };
828 SwingUtilities.invokeLater(uiUpdater);
829 }
830 }
831 }
832
833 private void writeObject(java.io.ObjectOutputStream out)
834 throws IOException {
835 throw new NotSerializableException(this .getClass().getName());
836 }
837
838 private class Handler implements PropertyChangeListener {
839 public void propertyChange(PropertyChangeEvent evt) {
840 String propertyName = evt.getPropertyName();
841 Object newValue = evt.getNewValue();
842 Object oldValue = evt.getOldValue();
843
844 if ("focusOwner" == propertyName) {
845 if (oldValue instanceof JComponent) {
846 repaintIfBackgroundsDiffer((JComponent) oldValue);
847
848 }
849
850 if (newValue instanceof JComponent) {
851 repaintIfBackgroundsDiffer((JComponent) newValue);
852 }
853 } else if ("managingFocus" == propertyName) {
854 // De-register listener on old keyboard focus manager and
855 // register it on the new one.
856 KeyboardFocusManager manager = (KeyboardFocusManager) evt
857 .getSource();
858 if (((Boolean) newValue).equals(Boolean.FALSE)) {
859 manager.removePropertyChangeListener(_handler);
860 } else {
861 manager.addPropertyChangeListener(_handler);
862 }
863 }
864 }
865
866 /**
867 * This is a support method that will check if the background colors of
868 * the specified component differ between focused and unfocused states.
869 * If the color differ the component will then repaint itself.
870 *
871 * @comp the component to check
872 */
873 private void repaintIfBackgroundsDiffer(JComponent comp) {
874 ComponentUI ui = (ComponentUI) comp
875 .getClientProperty(SwingUtilities2.COMPONENT_UI_PROPERTY_KEY);
876 if (ui instanceof SynthUI) {
877 SynthUI synthUI = (SynthUI) ui;
878 SynthContext context = synthUI.getContext(comp);
879 SynthStyle style = context.getStyle();
880 int state = context.getComponentState();
881
882 // Get the current background color.
883 Color currBG = style.getColor(context,
884 ColorType.BACKGROUND);
885
886 // Get the last background color.
887 state ^= SynthConstants.FOCUSED;
888 context.setComponentState(state);
889 Color lastBG = style.getColor(context,
890 ColorType.BACKGROUND);
891
892 // Reset the component state back to original.
893 state ^= SynthConstants.FOCUSED;
894 context.setComponentState(state);
895
896 // Repaint the component if the backgrounds differed.
897 if (currBG != null && !currBG.equals(lastBG)) {
898 comp.repaint();
899 }
900 context.dispose();
901 }
902 }
903 }
904 }
|