1173N/A/*
2362N/A * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
1173N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1173N/A *
1173N/A * This code is free software; you can redistribute it and/or modify it
1173N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
1173N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
1173N/A *
1173N/A * This code is distributed in the hope that it will be useful, but WITHOUT
1173N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1173N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1173N/A * version 2 for more details (a copy is included in the LICENSE file that
1173N/A * accompanied this code).
1173N/A *
1173N/A * You should have received a copy of the GNU General Public License version
1173N/A * 2 along with this work; if not, write to the Free Software Foundation,
1173N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1173N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
1173N/A */
1173N/Apackage ${PACKAGE};
1173N/A
1173N/Aimport javax.swing.Painter;
1173N/Aimport java.awt.Graphics;
1686N/Aimport sun.font.FontUtilities;
1173N/Aimport sun.swing.plaf.synth.DefaultSynthStyle;
1173N/Aimport javax.swing.BorderFactory;
1173N/Aimport javax.swing.JComponent;
1173N/Aimport javax.swing.JInternalFrame;
1173N/Aimport javax.swing.UIDefaults;
1173N/Aimport javax.swing.UIManager;
1173N/Aimport javax.swing.plaf.BorderUIResource;
1173N/Aimport javax.swing.plaf.ColorUIResource;
1173N/Aimport javax.swing.plaf.DimensionUIResource;
1173N/Aimport javax.swing.plaf.FontUIResource;
1173N/Aimport javax.swing.plaf.InsetsUIResource;
1173N/Aimport javax.swing.plaf.synth.Region;
1173N/Aimport javax.swing.plaf.synth.SynthStyle;
1173N/Aimport java.awt.Color;
1173N/Aimport java.awt.Component;
1173N/Aimport java.awt.Dimension;
1173N/Aimport java.awt.Font;
1173N/Aimport java.awt.Graphics2D;
1173N/Aimport java.awt.Insets;
1173N/Aimport java.awt.image.BufferedImage;
1173N/Aimport static java.awt.image.BufferedImage.*;
1173N/Aimport java.beans.PropertyChangeEvent;
1173N/Aimport java.beans.PropertyChangeListener;
1173N/Aimport java.lang.ref.WeakReference;
1173N/Aimport java.lang.reflect.Constructor;
1173N/Aimport java.util.ArrayList;
1173N/Aimport java.util.HashMap;
1173N/Aimport java.util.LinkedList;
1173N/Aimport java.util.List;
1173N/Aimport java.util.Map;
1173N/Aimport java.util.Set;
1173N/Aimport java.util.WeakHashMap;
1173N/Aimport javax.swing.border.Border;
1173N/Aimport javax.swing.plaf.UIResource;
1173N/A
1173N/A/**
1173N/A * This class contains all the implementation details related to
1173N/A * ${LAF_NAME}. It contains all the code for initializing the UIDefaults table,
1173N/A * as well as for selecting
1173N/A * a SynthStyle based on a JComponent/Region pair.
1173N/A *
1173N/A * @author Richard Bair
1173N/A */
1173N/Afinal class ${LAF_NAME}Defaults {
1173N/A /**
1173N/A * The map of SynthStyles. This map is keyed by Region. Each Region maps
1173N/A * to a List of LazyStyles. Each LazyStyle has a reference to the prefix
1173N/A * that was registered with it. This reference can then be inspected to see
1173N/A * if it is the proper lazy style.
1173N/A * <p/>
1173N/A * There can be more than one LazyStyle for a single Region if there is more
1173N/A * than one prefix defined for a given region. For example, both Button and
1173N/A * "MyButton" might be prefixes assigned to the Region.Button region.
1173N/A */
1173N/A private Map<Region, List<LazyStyle>> m;
1173N/A /**
1173N/A * A map of regions which have been registered.
1173N/A * This mapping is maintained so that the Region can be found based on
1173N/A * prefix in a very fast manner. This is used in the "matches" method of
1173N/A * LazyStyle.
1173N/A */
1173N/A private Map<String, Region> registeredRegions =
1173N/A new HashMap<String, Region>();
2139N/A
2139N/A private Map<JComponent, Map<Region, SynthStyle>> overridesCache =
2139N/A new WeakHashMap<JComponent, Map<Region, SynthStyle>>();
2139N/A
1173N/A /**
1173N/A * Our fallback style to avoid NPEs if the proper style cannot be found in
1173N/A * this class. Not sure if relying on DefaultSynthStyle is the best choice.
1173N/A */
1173N/A private DefaultSynthStyle defaultStyle;
1173N/A /**
1173N/A * The default font that will be used. I store this value so that it can be
1173N/A * set in the UIDefaults when requested.
1173N/A */
1173N/A private FontUIResource defaultFont;
1173N/A
1629N/A private ColorTree colorTree = new ColorTree();
1173N/A
1173N/A /** Listener for changes to user defaults table */
1173N/A private DefaultsListener defaultsListener = new DefaultsListener();
1173N/A
1173N/A /** Called by UIManager when this look and feel is installed. */
1173N/A void initialize() {
1173N/A // add listener for derived colors
1173N/A UIManager.addPropertyChangeListener(defaultsListener);
1629N/A UIManager.getDefaults().addPropertyChangeListener(colorTree);
1173N/A }
1173N/A
1173N/A /** Called by UIManager when this look and feel is uninstalled. */
1173N/A void uninitialize() {
1173N/A // remove listener for derived colors
1173N/A UIManager.removePropertyChangeListener(defaultsListener);
1629N/A UIManager.getDefaults().removePropertyChangeListener(colorTree);
1173N/A }
1173N/A
1173N/A /**
1173N/A * Create a new ${LAF_NAME}Defaults. This constructor is only called from
1173N/A * within ${LAF_NAME}LookAndFeel.
1173N/A */
1173N/A ${LAF_NAME}Defaults() {
1173N/A m = new HashMap<Region, List<LazyStyle>>();
1173N/A
1173N/A //Create the default font and default style. Also register all of the
1173N/A //regions and their states that this class will use for later lookup.
1173N/A //Additional regions can be registered later by 3rd party components.
1173N/A //These are simply the default registrations.
1686N/A defaultFont = FontUtilities.getFontConfigFUIR("sans", Font.PLAIN, 12);
1173N/A defaultStyle = new DefaultSynthStyle();
1173N/A defaultStyle.setFont(defaultFont);
1173N/A
1173N/A //initialize the map of styles
1173N/A${STYLE_INIT}
1173N/A }
1173N/A
1173N/A //--------------- Methods called by ${LAF_NAME}LookAndFeel
1173N/A
1173N/A /**
1173N/A * Called from ${LAF_NAME}LookAndFeel to initialize the UIDefaults.
1173N/A *
1173N/A * @param d UIDefaults table to initialize. This will never be null.
1173N/A * If listeners are attached to <code>d</code>, then you will
1173N/A * only receive notification of LookAndFeel level defaults, not
1173N/A * all defaults on the UIManager.
1173N/A */
1173N/A void initializeDefaults(UIDefaults d) {
1173N/A${UI_DEFAULT_INIT}
1173N/A }
1173N/A
1173N/A /**
1173N/A * <p>Registers the given region and prefix. The prefix, if it contains
1173N/A * quoted sections, refers to certain named components. If there are not
1173N/A * quoted sections, then the prefix refers to a generic component type.</p>
1173N/A *
1173N/A * <p>If the given region/prefix combo has already been registered, then
1173N/A * it will not be registered twice. The second registration attempt will
1173N/A * fail silently.</p>
1173N/A *
1173N/A * @param region The Synth Region that is being registered. Such as Button,
1173N/A * or ScrollBarThumb.
1173N/A * @param prefix The UIDefault prefix. For example, could be ComboBox, or if
1173N/A * a named components, "MyComboBox", or even something like
1173N/A * ToolBar:"MyComboBox":"ComboBox.arrowButton"
1173N/A */
1173N/A void register(Region region, String prefix) {
1173N/A //validate the method arguments
1173N/A if (region == null || prefix == null) {
1173N/A throw new IllegalArgumentException(
1173N/A "Neither Region nor Prefix may be null");
1173N/A }
1173N/A
1173N/A //Add a LazyStyle for this region/prefix to m.
1173N/A List<LazyStyle> styles = m.get(region);
1173N/A if (styles == null) {
1173N/A styles = new LinkedList<LazyStyle>();
1173N/A styles.add(new LazyStyle(prefix));
1173N/A m.put(region, styles);
1173N/A } else {
1173N/A //iterate over all the current styles and see if this prefix has
1173N/A //already been registered. If not, then register it.
1173N/A for (LazyStyle s : styles) {
1173N/A if (prefix.equals(s.prefix)) {
1173N/A return;
1173N/A }
1173N/A }
1173N/A styles.add(new LazyStyle(prefix));
1173N/A }
1173N/A
1173N/A //add this region to the map of registered regions
1173N/A registeredRegions.put(region.getName(), region);
1173N/A }
1173N/A
1173N/A /**
1173N/A * <p>Locate the style associated with the given region, and component.
1173N/A * This is called from ${LAF_NAME}LookAndFeel in the SynthStyleFactory
1173N/A * implementation.</p>
1173N/A *
1173N/A * <p>Lookup occurs as follows:<br/>
1173N/A * Check the map of styles <code>m</code>. If the map contains no styles at
1173N/A * all, then simply return the defaultStyle. If the map contains styles,
1173N/A * then iterate over all of the styles for the Region <code>r</code> looking
1173N/A * for the best match, based on prefix. If a match was made, then return
1173N/A * that SynthStyle. Otherwise, return the defaultStyle.</p>
1173N/A *
1173N/A * @param comp The component associated with this region. For example, if
1173N/A * the Region is Region.Button then the component will be a JButton.
1173N/A * If the Region is a subregion, such as ScrollBarThumb, then the
1173N/A * associated component will be the component that subregion belongs
1173N/A * to, such as JScrollBar. The JComponent may be named. It may not be
1173N/A * null.
1173N/A * @param r The region we are looking for a style for. May not be null.
1173N/A */
1173N/A SynthStyle getStyle(JComponent comp, Region r) {
1173N/A //validate method arguments
1173N/A if (comp == null || r == null) {
1173N/A throw new IllegalArgumentException(
1173N/A "Neither comp nor r may be null");
1173N/A }
1173N/A
1173N/A //if there are no lazy styles registered for the region r, then return
1173N/A //the default style
1173N/A List<LazyStyle> styles = m.get(r);
1173N/A if (styles == null || styles.size() == 0) {
1173N/A return defaultStyle;
1173N/A }
1173N/A
1173N/A //Look for the best SynthStyle for this component/region pair.
1173N/A LazyStyle foundStyle = null;
1173N/A for (LazyStyle s : styles) {
1173N/A if (s.matches(comp)) {
1173N/A //replace the foundStyle if foundStyle is null, or
1173N/A //if the new style "s" is more specific (ie, its path was
1173N/A //longer), or if the foundStyle was "simple" and the new style
1173N/A //was not (ie: the foundStyle was for something like Button and
1173N/A //the new style was for something like "MyButton", hence, being
1173N/A //more specific.) In all cases, favor the most specific style
1173N/A //found.
1173N/A if (foundStyle == null ||
1173N/A (foundStyle.parts.length < s.parts.length) ||
1173N/A (foundStyle.parts.length == s.parts.length
1173N/A && foundStyle.simple && !s.simple)) {
1173N/A foundStyle = s;
1173N/A }
1173N/A }
1173N/A }
1173N/A
1173N/A //return the style, if found, or the default style if not found
2139N/A return foundStyle == null ? defaultStyle : foundStyle.getStyle(comp, r);
2139N/A }
2139N/A
2139N/A public void clearOverridesCache(JComponent c) {
2139N/A overridesCache.remove(c);
1173N/A }
1173N/A
1173N/A /*
1173N/A Various public helper classes.
1173N/A These may be used to register 3rd party values into UIDefaults
1173N/A */
1173N/A
1173N/A /**
1173N/A * <p>Derives its font value based on a parent font and a set of offsets and
1173N/A * attributes. This class is an ActiveValue, meaning that it will recompute
1173N/A * its value each time it is requested from UIDefaults. It is therefore
1173N/A * recommended to read this value once and cache it in the UI delegate class
1173N/A * until asked to reinitialize.</p>
1173N/A *
1173N/A * <p>To use this class, create an instance with the key of the font in the
1173N/A * UI defaults table from which to derive this font, along with a size
1173N/A * offset (if any), and whether it is to be bold, italic, or left in its
1173N/A * default form.</p>
1173N/A */
1347N/A static final class DerivedFont implements UIDefaults.ActiveValue {
1173N/A private float sizeOffset;
1173N/A private Boolean bold;
1173N/A private Boolean italic;
1173N/A private String parentKey;
1173N/A
1173N/A /**
1173N/A * Create a new DerivedFont.
1173N/A *
1173N/A * @param key The UIDefault key associated with this derived font's
1173N/A * parent or source. If this key leads to a null value, or a
1173N/A * value that is not a font, then null will be returned as
1173N/A * the derived font. The key must not be null.
1173N/A * @param sizeOffset The size offset, as a percentage, to use. For
1173N/A * example, if the source font was a 12pt font and the
1173N/A * sizeOffset were specified as .9, then the new font
1173N/A * will be 90% of what the source font was, or, 10.8
1173N/A * pts which is rounded to 11pts. This fractional
1173N/A * based offset allows for proper font scaling in high
1173N/A * DPI or large system font scenarios.
1173N/A * @param bold Whether the new font should be bold. If null, then this
1173N/A * new font will inherit the bold setting of the source
1173N/A * font.
1173N/A * @param italic Whether the new font should be italicized. If null,
1173N/A * then this new font will inherit the italic setting of
1173N/A * the source font.
1173N/A */
1173N/A public DerivedFont(String key, float sizeOffset, Boolean bold,
1173N/A Boolean italic) {
1173N/A //validate the constructor arguments
1173N/A if (key == null) {
1173N/A throw new IllegalArgumentException("You must specify a key");
1173N/A }
1173N/A
1173N/A //set the values
1173N/A this.parentKey = key;
1173N/A this.sizeOffset = sizeOffset;
1173N/A this.bold = bold;
1173N/A this.italic = italic;
1173N/A }
1173N/A
1173N/A /**
1173N/A * @inheritDoc
1173N/A */
1173N/A @Override
1173N/A public Object createValue(UIDefaults defaults) {
1173N/A Font f = defaults.getFont(parentKey);
1173N/A if (f != null) {
1173N/A // always round size for now so we have exact int font size
1173N/A // (or we may have lame looking fonts)
1173N/A float size = Math.round(f.getSize2D() * sizeOffset);
1173N/A int style = f.getStyle();
1173N/A if (bold != null) {
1173N/A if (bold.booleanValue()) {
1173N/A style = style | Font.BOLD;
1173N/A } else {
1173N/A style = style & ~Font.BOLD;
1173N/A }
1173N/A }
1173N/A if (italic != null) {
1173N/A if (italic.booleanValue()) {
1173N/A style = style | Font.ITALIC;
1173N/A } else {
1173N/A style = style & ~Font.ITALIC;
1173N/A }
1173N/A }
1173N/A return f.deriveFont(style, size);
1173N/A } else {
1173N/A return null;
1173N/A }
1173N/A }
1173N/A }
1173N/A
1173N/A
1173N/A /**
1173N/A * This class is private because it relies on the constructor of the
1173N/A * auto-generated AbstractRegionPainter subclasses. Hence, it is not
1173N/A * generally useful, and is private.
1173N/A * <p/>
1173N/A * LazyPainter is a LazyValue class. It will create the
1173N/A * AbstractRegionPainter lazily, when asked. It uses reflection to load the
1173N/A * proper class and invoke its constructor.
1173N/A */
1173N/A private static final class LazyPainter implements UIDefaults.LazyValue {
1173N/A private int which;
1173N/A private AbstractRegionPainter.PaintContext ctx;
1173N/A private String className;
1173N/A
1173N/A LazyPainter(String className, int which, Insets insets,
1173N/A Dimension canvasSize, boolean inverted) {
1173N/A if (className == null) {
1173N/A throw new IllegalArgumentException(
1173N/A "The className must be specified");
1173N/A }
1173N/A
1173N/A this.className = className;
1173N/A this.which = which;
1173N/A this.ctx = new AbstractRegionPainter.PaintContext(
1173N/A insets, canvasSize, inverted);
1173N/A }
1173N/A
1173N/A LazyPainter(String className, int which, Insets insets,
1173N/A Dimension canvasSize, boolean inverted,
1173N/A AbstractRegionPainter.PaintContext.CacheMode cacheMode,
1173N/A double maxH, double maxV) {
1173N/A if (className == null) {
1173N/A throw new IllegalArgumentException(
1173N/A "The className must be specified");
1173N/A }
1173N/A
1173N/A this.className = className;
1173N/A this.which = which;
1173N/A this.ctx = new AbstractRegionPainter.PaintContext(
1173N/A insets, canvasSize, inverted, cacheMode, maxH, maxV);
1173N/A }
1173N/A
1173N/A @Override
1173N/A public Object createValue(UIDefaults table) {
1173N/A try {
1173N/A Class c;
1173N/A Object cl;
1173N/A // See if we should use a separate ClassLoader
1173N/A if (table == null || !((cl = table.get("ClassLoader"))
1173N/A instanceof ClassLoader)) {
1173N/A cl = Thread.currentThread().
1173N/A getContextClassLoader();
1173N/A if (cl == null) {
1173N/A // Fallback to the system class loader.
1173N/A cl = ClassLoader.getSystemClassLoader();
1173N/A }
1173N/A }
1173N/A
1173N/A c = Class.forName(className, true, (ClassLoader)cl);
1173N/A Constructor constructor = c.getConstructor(
1173N/A AbstractRegionPainter.PaintContext.class, int.class);
1173N/A if (constructor == null) {
1173N/A throw new NullPointerException(
1173N/A "Failed to find the constructor for the class: " +
1173N/A className);
1173N/A }
1173N/A return constructor.newInstance(ctx, which);
1173N/A } catch (Exception e) {
1173N/A e.printStackTrace();
1173N/A return null;
1173N/A }
1173N/A }
1173N/A }
1173N/A
1173N/A /**
1173N/A * A class which creates the NimbusStyle associated with it lazily, but also
1173N/A * manages a lot more information about the style. It is less of a LazyValue
1173N/A * type of class, and more of an Entry or Item type of class, as it
1173N/A * represents an entry in the list of LazyStyles in the map m.
1173N/A *
1173N/A * The primary responsibilities of this class include:
1173N/A * <ul>
1173N/A * <li>Determining whether a given component/region pair matches this
1173N/A * style</li>
1173N/A * <li>Splitting the prefix specified in the constructor into its
1173N/A * constituent parts to facilitate quicker matching</li>
1173N/A * <li>Creating and vending a NimbusStyle lazily.</li>
1173N/A * </ul>
1173N/A */
1173N/A private final class LazyStyle {
1173N/A /**
1173N/A * The prefix this LazyStyle was registered with. Something like
1173N/A * Button or ComboBox:"ComboBox.arrowButton"
1173N/A */
1173N/A private String prefix;
1173N/A /**
1173N/A * Whether or not this LazyStyle represents an unnamed component
1173N/A */
1173N/A private boolean simple = true;
1173N/A /**
1173N/A * The various parts, or sections, of the prefix. For example,
1173N/A * the prefix:
1173N/A * ComboBox:"ComboBox.arrowButton"
1173N/A *
1173N/A * will be broken into two parts,
1173N/A * ComboBox and "ComboBox.arrowButton"
1173N/A */
1173N/A private Part[] parts;
1173N/A /**
1173N/A * Cached shared style.
1173N/A */
1173N/A private NimbusStyle style;
1173N/A
1173N/A /**
1173N/A * Create a new LazyStyle.
1173N/A *
1173N/A * @param prefix The prefix associated with this style. Cannot be null.
1173N/A */
1173N/A private LazyStyle(String prefix) {
1173N/A if (prefix == null) {
1173N/A throw new IllegalArgumentException(
1173N/A "The prefix must not be null");
1173N/A }
1173N/A
1173N/A this.prefix = prefix;
1173N/A
1173N/A //there is one odd case that needs to be supported here: cell
1173N/A //renderers. A cell renderer is defined as a named internal
1173N/A //component, so for example:
1173N/A // List."List.cellRenderer"
1173N/A //The problem is that the component named List.cellRenderer is not a
1173N/A //child of a JList. Rather, it is treated more as a direct component
1173N/A //Thus, if the prefix ends with "cellRenderer", then remove all the
1173N/A //previous dotted parts of the prefix name so that it becomes, for
1173N/A //example: "List.cellRenderer"
1173N/A //Likewise, we have a hacked work around for cellRenderer, renderer,
1173N/A //and listRenderer.
1173N/A String temp = prefix;
1173N/A if (temp.endsWith("cellRenderer\"")
1173N/A || temp.endsWith("renderer\"")
1173N/A || temp.endsWith("listRenderer\"")) {
1173N/A temp = temp.substring(temp.lastIndexOf(":\"") + 1);
1173N/A }
1173N/A
1173N/A //otherwise, normal code path
1173N/A List<String> sparts = split(temp);
1173N/A parts = new Part[sparts.size()];
1173N/A for (int i = 0; i < parts.length; i++) {
1173N/A parts[i] = new Part(sparts.get(i));
1173N/A if (parts[i].named) {
1173N/A simple = false;
1173N/A }
1173N/A }
1173N/A }
1173N/A
1173N/A /**
1173N/A * Gets the style. Creates it if necessary.
1173N/A * @return the style
1173N/A */
2139N/A SynthStyle getStyle(JComponent c, Region r) {
1173N/A // if the component has overrides, it gets its own unique style
1173N/A // instead of the shared style.
1173N/A if (c.getClientProperty("Nimbus.Overrides") != null) {
2139N/A Map<Region, SynthStyle> map = overridesCache.get(c);
2139N/A SynthStyle s = null;
2139N/A if (map == null) {
2139N/A map = new HashMap<Region, SynthStyle>();
2139N/A overridesCache.put(c, map);
2139N/A } else {
2139N/A s = map.get(r);
2139N/A }
1173N/A if (s == null) {
1173N/A s = new NimbusStyle(prefix, c);
2139N/A map.put(r, s);
1173N/A }
1173N/A return s;
1173N/A }
1173N/A
1173N/A // lazily create the style if necessary
1173N/A if (style == null)
1173N/A style = new NimbusStyle(prefix, null);
1173N/A
1173N/A // return the style
1173N/A return style;
1173N/A }
1173N/A
1173N/A /**
1173N/A * This LazyStyle is a match for the given component if, and only if,
1173N/A * for each part of the prefix the component hierarchy matches exactly.
1173N/A * That is, if given "a":something:"b", then:
1173N/A * c.getName() must equals "b"
1173N/A * c.getParent() can be anything
1173N/A * c.getParent().getParent().getName() must equal "a".
1173N/A */
1173N/A boolean matches(JComponent c) {
1173N/A return matches(c, parts.length - 1);
1173N/A }
1173N/A
1173N/A private boolean matches(Component c, int partIndex) {
1173N/A if (partIndex < 0) return true;
1173N/A if (c == null) return false;
1173N/A //only get here if partIndex > 0 and c == null
1173N/A
1173N/A String name = c.getName();
1173N/A if (parts[partIndex].named && parts[partIndex].s.equals(name)) {
1173N/A //so far so good, recurse
1173N/A return matches(c.getParent(), partIndex - 1);
1173N/A } else if (!parts[partIndex].named) {
1173N/A //if c is not named, and parts[partIndex] has an expected class
1173N/A //type registered, then check to make sure c is of the
1173N/A //right type;
1173N/A Class clazz = parts[partIndex].c;
1173N/A if (clazz != null && clazz.isAssignableFrom(c.getClass())) {
1173N/A //so far so good, recurse
1173N/A return matches(c.getParent(), partIndex - 1);
1173N/A } else if (clazz == null &&
1173N/A registeredRegions.containsKey(parts[partIndex].s)) {
1173N/A Region r = registeredRegions.get(parts[partIndex].s);
1173N/A Component parent = r.isSubregion() ? c : c.getParent();
1173N/A //special case the JInternalFrameTitlePane, because it
1173N/A //doesn't fit the mold. very, very funky.
1173N/A if (r == Region.INTERNAL_FRAME_TITLE_PANE && parent != null
1173N/A && parent instanceof JInternalFrame.JDesktopIcon) {
1173N/A JInternalFrame.JDesktopIcon icon =
1173N/A (JInternalFrame.JDesktopIcon) parent;
1173N/A parent = icon.getInternalFrame();
1173N/A }
1173N/A //it was the name of a region. So far, so good. Recurse.
1173N/A return matches(parent, partIndex - 1);
1173N/A }
1173N/A }
1173N/A
1173N/A return false;
1173N/A }
1173N/A
1173N/A /**
1173N/A * Given some dot separated prefix, split on the colons that are
1173N/A * not within quotes, and not within brackets.
1173N/A *
1173N/A * @param prefix
1173N/A * @return
1173N/A */
1173N/A private List<String> split(String prefix) {
1173N/A List<String> parts = new ArrayList<String>();
1173N/A int bracketCount = 0;
1173N/A boolean inquotes = false;
1173N/A int lastIndex = 0;
1173N/A for (int i = 0; i < prefix.length(); i++) {
1173N/A char c = prefix.charAt(i);
1173N/A
1173N/A if (c == '[') {
1173N/A bracketCount++;
1173N/A continue;
1173N/A } else if (c == '"') {
1173N/A inquotes = !inquotes;
1173N/A continue;
1173N/A } else if (c == ']') {
1173N/A bracketCount--;
1173N/A if (bracketCount < 0) {
1173N/A throw new RuntimeException(
1173N/A "Malformed prefix: " + prefix);
1173N/A }
1173N/A continue;
1173N/A }
1173N/A
1173N/A if (c == ':' && !inquotes && bracketCount == 0) {
1173N/A //found a character to split on.
1173N/A parts.add(prefix.substring(lastIndex, i));
1173N/A lastIndex = i + 1;
1173N/A }
1173N/A }
1173N/A if (lastIndex < prefix.length() - 1 && !inquotes
1173N/A && bracketCount == 0) {
1173N/A parts.add(prefix.substring(lastIndex));
1173N/A }
1173N/A return parts;
1173N/A
1173N/A }
1173N/A
1173N/A private final class Part {
1173N/A private String s;
1173N/A //true if this part represents a component name
1173N/A private boolean named;
1173N/A private Class c;
1173N/A
1173N/A Part(String s) {
1173N/A named = s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"';
1173N/A if (named) {
1173N/A this.s = s.substring(1, s.length() - 1);
1173N/A } else {
1173N/A this.s = s;
1173N/A //TODO use a map of known regions for Synth and Swing, and
1173N/A //then use [classname] instead of org_class_name style
1173N/A try {
1173N/A c = Class.forName("javax.swing.J" + s);
1173N/A } catch (Exception e) {
1173N/A }
1173N/A try {
1173N/A c = Class.forName(s.replace("_", "."));
1173N/A } catch (Exception e) {
1173N/A }
1173N/A }
1173N/A }
1173N/A }
1173N/A }
1173N/A
1629N/A private void addColor(UIDefaults d, String uin, int r, int g, int b, int a) {
1629N/A Color color = new ColorUIResource(new Color(r, g, b, a));
1629N/A colorTree.addColor(uin, color);
1629N/A d.put(uin, color);
1629N/A }
1629N/A
1629N/A private void addColor(UIDefaults d, String uin, String parentUin,
1629N/A float hOffset, float sOffset, float bOffset, int aOffset) {
1629N/A addColor(d, uin, parentUin, hOffset, sOffset, bOffset, aOffset, true);
1629N/A }
1629N/A
1629N/A private void addColor(UIDefaults d, String uin, String parentUin,
1629N/A float hOffset, float sOffset, float bOffset,
1629N/A int aOffset, boolean uiResource) {
1629N/A Color color = getDerivedColor(uin, parentUin,
1629N/A hOffset, sOffset, bOffset, aOffset, uiResource);
1629N/A d.put(uin, color);
1173N/A }
1173N/A
1173N/A /**
1173N/A * Get a derived color, derived colors are shared instances and will be
1173N/A * updated when its parent UIDefault color changes.
1173N/A *
1173N/A * @param uiDefaultParentName The parent UIDefault key
1173N/A * @param hOffset The hue offset
1173N/A * @param sOffset The saturation offset
1173N/A * @param bOffset The brightness offset
1173N/A * @param aOffset The alpha offset
1173N/A * @param uiResource True if the derived color should be a UIResource,
1173N/A * false if it should not be a UIResource
1173N/A * @return The stored derived color
1173N/A */
1629N/A public DerivedColor getDerivedColor(String parentUin,
1173N/A float hOffset, float sOffset,
1173N/A float bOffset, int aOffset,
1173N/A boolean uiResource){
1629N/A return getDerivedColor(null, parentUin,
1629N/A hOffset, sOffset, bOffset, aOffset, uiResource);
1629N/A }
1629N/A
1629N/A private DerivedColor getDerivedColor(String uin, String parentUin,
1629N/A float hOffset, float sOffset,
1629N/A float bOffset, int aOffset,
1629N/A boolean uiResource) {
1629N/A DerivedColor color;
1629N/A if (uiResource) {
1629N/A color = new DerivedColor.UIResource(parentUin,
1629N/A hOffset, sOffset, bOffset, aOffset);
1629N/A } else {
1629N/A color = new DerivedColor(parentUin, hOffset, sOffset,
1629N/A bOffset, aOffset);
1173N/A }
1629N/A
1629N/A if (derivedColors.containsKey(color)) {
1629N/A return derivedColors.get(color);
1629N/A } else {
1629N/A derivedColors.put(color, color);
1629N/A color.rederiveColor(); /// move to ARP.decodeColor() ?
1629N/A colorTree.addColor(uin, color);
1629N/A return color;
1629N/A }
1173N/A }
1173N/A
1629N/A private Map<DerivedColor, DerivedColor> derivedColors =
1629N/A new HashMap<DerivedColor, DerivedColor>();
1173N/A
1629N/A private class ColorTree implements PropertyChangeListener {
1629N/A private Node root = new Node(null, null);
1629N/A private Map<String, Node> nodes = new HashMap<String, Node>();
1173N/A
1629N/A public Color getColor(String uin) {
1629N/A return nodes.get(uin).color;
1173N/A }
1173N/A
1629N/A public void addColor(String uin, Color color) {
1629N/A Node parent = getParentNode(color);
1629N/A Node node = new Node(color, parent);
1629N/A parent.children.add(node);
1629N/A if (uin != null) {
1629N/A nodes.put(uin, node);
1629N/A }
1629N/A }
1629N/A
1629N/A private Node getParentNode(Color color) {
1629N/A Node parent = root;
1629N/A if (color instanceof DerivedColor) {
1629N/A String parentUin = ((DerivedColor)color).getUiDefaultParentName();
1629N/A Node p = nodes.get(parentUin);
1629N/A if (p != null) {
1629N/A parent = p;
1629N/A }
1629N/A }
1629N/A return parent;
1629N/A }
1629N/A
1629N/A public void update() {
1629N/A root.update();
1173N/A }
1173N/A
1173N/A @Override
1629N/A public void propertyChange(PropertyChangeEvent ev) {
1629N/A String name = ev.getPropertyName();
1629N/A Node node = nodes.get(name);
1629N/A if (node != null) {
1629N/A // this is a registered color
1629N/A node.parent.children.remove(node);
1629N/A Color color = (Color) ev.getNewValue();
1629N/A Node parent = getParentNode(color);
1629N/A node.set(color, parent);
1629N/A parent.children.add(node);
1629N/A node.update();
1629N/A }
1173N/A }
1173N/A
1629N/A class Node {
1629N/A Color color;
1629N/A Node parent;
1629N/A List<Node> children = new LinkedList<Node>();
1629N/A
1629N/A Node(Color color, Node parent) {
1629N/A set(color, parent);
1629N/A }
1629N/A
1629N/A public void set(Color color, Node parent) {
1629N/A this.color = color;
1629N/A this.parent = parent;
1629N/A }
1629N/A
1629N/A public void update() {
1629N/A if (color instanceof DerivedColor) {
1629N/A ((DerivedColor)color).rederiveColor();
1629N/A }
1629N/A for (Node child: children) {
1629N/A child.update();
1629N/A }
1629N/A }
1173N/A }
1173N/A }
1173N/A
1173N/A /**
1173N/A * Listener to update derived colors on UIManager Defaults changes
1173N/A */
1173N/A private class DefaultsListener implements PropertyChangeListener {
1173N/A @Override
1173N/A public void propertyChange(PropertyChangeEvent evt) {
1629N/A if ("lookAndFeel".equals(evt.getPropertyName())) {
1173N/A // LAF has been installed, this is the first point at which we
1173N/A // can access our defaults table via UIManager so before now
1173N/A // all derived colors will be incorrect.
1173N/A // First we need to update
1629N/A colorTree.update();
1173N/A }
1173N/A }
1173N/A }
1173N/A
1173N/A private static final class PainterBorder implements Border, UIResource {
1173N/A private Insets insets;
1173N/A private Painter painter;
1173N/A private String painterKey;
1173N/A
1173N/A PainterBorder(String painterKey, Insets insets) {
1173N/A this.insets = insets;
1173N/A this.painterKey = painterKey;
1173N/A }
1173N/A
1173N/A @Override
1173N/A public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
1173N/A if (painter == null) {
1173N/A painter = (Painter)UIManager.get(painterKey);
1173N/A if (painter == null) return;
1173N/A }
1173N/A
1173N/A g.translate(x, y);
1173N/A if (g instanceof Graphics2D)
1173N/A painter.paint((Graphics2D)g, c, w, h);
1173N/A else {
1173N/A BufferedImage img = new BufferedImage(w, h, TYPE_INT_ARGB);
1173N/A Graphics2D gfx = img.createGraphics();
1173N/A painter.paint(gfx, c, w, h);
1173N/A gfx.dispose();
1173N/A g.drawImage(img, x, y, null);
1173N/A img = null;
1173N/A }
1173N/A g.translate(-x, -y);
1173N/A }
1173N/A
1173N/A @Override
1173N/A public Insets getBorderInsets(Component c) {
1173N/A return (Insets)insets.clone();
1173N/A }
1173N/A
1173N/A @Override
1173N/A public boolean isBorderOpaque() {
1173N/A return false;
1173N/A }
1173N/A }
1173N/A}