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