--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,927 @@
+/*
+ * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.swing.plaf.synth;
+
+import javax.swing.plaf.synth.*;
+import java.awt.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.plaf.*;
+
+/**
+ * Default implementation of SynthStyle. Has setters for the various
+ * SynthStyle methods. Many of the properties can be specified for all states,
+ * using SynthStyle directly, or a specific state using one of the StateInfo
+ * methods.
+ * <p>
+ * Beyond the constructor a subclass should override the <code>addTo</code>
+ * and <code>clone</code> methods, these are used when the Styles are being
+ * merged into a resulting style.
+ *
+ * @author Scott Violet
+ */
+public class DefaultSynthStyle extends SynthStyle implements Cloneable {
+ private static final String PENDING = "Pending";
+
+ /**
+ * Should the component be opaque?
+ */
+ private boolean opaque;
+ /**
+ * Insets.
+ */
+ private Insets insets;
+ /**
+ * Information specific to ComponentState.
+ */
+ private StateInfo[] states;
+ /**
+ * User specific data.
+ */
+ private Map<Object, Object> data;
+
+ /**
+ * Font to use if there is no matching StateInfo, or the StateInfo doesn't
+ * define one.
+ */
+ private Font font;
+
+ /**
+ * SynthGraphics, may be null.
+ */
+ private SynthGraphicsUtils synthGraphics;
+
+ /**
+ * Painter to use if the StateInfo doesn't have one.
+ */
+ private SynthPainter painter;
+
+
+ /**
+ * Nullary constructor, intended for subclassers.
+ */
+ public DefaultSynthStyle() {
+ }
+
+ /**
+ * Creates a new DefaultSynthStyle that is a copy of the passed in
+ * style. Any StateInfo's of the passed in style are clonsed as well.
+ *
+ * @param style Style to duplicate
+ */
+ public DefaultSynthStyle(DefaultSynthStyle style) {
+ opaque = style.opaque;
+ if (style.insets != null) {
+ insets = new Insets(style.insets.top, style.insets.left,
+ style.insets.bottom, style.insets.right);
+ }
+ if (style.states != null) {
+ states = new StateInfo[style.states.length];
+ for (int counter = style.states.length - 1; counter >= 0;
+ counter--) {
+ states[counter] = (StateInfo)style.states[counter].clone();
+ }
+ }
+ if (style.data != null) {
+ data = new HashMap<>();
+ data.putAll(style.data);
+ }
+ font = style.font;
+ synthGraphics = style.synthGraphics;
+ painter = style.painter;
+ }
+
+ /**
+ * Creates a new DefaultSynthStyle.
+ *
+ * @param insets Insets for the Style
+ * @param opaque Whether or not the background is completely painted in
+ * an opaque color
+ * @param states StateInfos describing properties per state
+ * @param data Style specific data.
+ */
+ public DefaultSynthStyle(Insets insets, boolean opaque,
+ StateInfo[] states, Map<Object, Object> data) {
+ this.insets = insets;
+ this.opaque = opaque;
+ this.states = states;
+ this.data = data;
+ }
+
+ public Color getColor(SynthContext context, ColorType type) {
+ return getColor(context.getComponent(), context.getRegion(),
+ context.getComponentState(), type);
+ }
+
+ public Color getColor(JComponent c, Region id, int state,
+ ColorType type) {
+ // For the enabled state, prefer the widget's colors
+ if (!id.isSubregion() && state == SynthConstants.ENABLED) {
+ if (type == ColorType.BACKGROUND) {
+ return c.getBackground();
+ }
+ else if (type == ColorType.FOREGROUND) {
+ return c.getForeground();
+ }
+ else if (type == ColorType.TEXT_FOREGROUND) {
+ // If getForeground returns a non-UIResource it means the
+ // developer has explicitly set the foreground, use it over
+ // that of TEXT_FOREGROUND as that is typically the expected
+ // behavior.
+ Color color = c.getForeground();
+ if (!(color instanceof UIResource)) {
+ return color;
+ }
+ }
+ }
+ // Then use what we've locally defined
+ Color color = getColorForState(c, id, state, type);
+ if (color == null) {
+ // No color, fallback to that of the widget.
+ if (type == ColorType.BACKGROUND ||
+ type == ColorType.TEXT_BACKGROUND) {
+ return c.getBackground();
+ }
+ else if (type == ColorType.FOREGROUND ||
+ type == ColorType.TEXT_FOREGROUND) {
+ return c.getForeground();
+ }
+ }
+ return color;
+ }
+
+ protected Color getColorForState(SynthContext context, ColorType type) {
+ return getColorForState(context.getComponent(), context.getRegion(),
+ context.getComponentState(), type);
+ }
+
+ /**
+ * Returns the color for the specified state.
+ *
+ * @param c JComponent the style is associated with
+ * @param id Region identifier
+ * @param state State of the region.
+ * @param type Type of color being requested.
+ * @return Color to render with
+ */
+ protected Color getColorForState(JComponent c, Region id, int state,
+ ColorType type) {
+ // Use the best state.
+ StateInfo si = getStateInfo(state);
+ Color color;
+ if (si != null && (color = si.getColor(type)) != null) {
+ return color;
+ }
+ if (si == null || si.getComponentState() != 0) {
+ si = getStateInfo(0);
+ if (si != null) {
+ return si.getColor(type);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the font that is used if there is no matching StateInfo, or
+ * it does not define a font.
+ *
+ * @param font Font to use for rendering
+ */
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ public Font getFont(SynthContext state) {
+ return getFont(state.getComponent(), state.getRegion(),
+ state.getComponentState());
+ }
+
+ public Font getFont(JComponent c, Region id, int state) {
+ if (!id.isSubregion() && state == SynthConstants.ENABLED) {
+ return c.getFont();
+ }
+ Font cFont = c.getFont();
+ if (cFont != null && !(cFont instanceof UIResource)) {
+ return cFont;
+ }
+ return getFontForState(c, id, state);
+ }
+
+ /**
+ * Returns the font for the specified state. This should NOT callback
+ * to the JComponent.
+ *
+ * @param c JComponent the style is associated with
+ * @param id Region identifier
+ * @param state State of the region.
+ * @return Font to render with
+ */
+ protected Font getFontForState(JComponent c, Region id, int state) {
+ if (c == null) {
+ return this.font;
+ }
+ // First pass, look for the best match
+ StateInfo si = getStateInfo(state);
+ Font font;
+ if (si != null && (font = si.getFont()) != null) {
+ return font;
+ }
+ if (si == null || si.getComponentState() != 0) {
+ si = getStateInfo(0);
+ if (si != null && (font = si.getFont()) != null) {
+ return font;
+ }
+ }
+ // Fallback font.
+ return this.font;
+ }
+
+ protected Font getFontForState(SynthContext context) {
+ return getFontForState(context.getComponent(), context.getRegion(),
+ context.getComponentState());
+ }
+
+ /**
+ * Sets the SynthGraphicsUtils that will be used for rendering.
+ *
+ * @param graphics SynthGraphics
+ */
+ public void setGraphicsUtils(SynthGraphicsUtils graphics) {
+ this.synthGraphics = graphics;
+ }
+
+ /**
+ * Returns a SynthGraphicsUtils.
+ *
+ * @param context SynthContext identifying requestor
+ * @return SynthGraphicsUtils
+ */
+ public SynthGraphicsUtils getGraphicsUtils(SynthContext context) {
+ if (synthGraphics == null) {
+ return super.getGraphicsUtils(context);
+ }
+ return synthGraphics;
+ }
+
+ /**
+ * Sets the insets.
+ *
+ * @param Insets.
+ */
+ public void setInsets(Insets insets) {
+ this.insets = insets;
+ }
+
+ /**
+ * Returns the Insets. If <code>to</code> is non-null the resulting
+ * insets will be placed in it, otherwise a new Insets object will be
+ * created and returned.
+ *
+ * @param context SynthContext identifying requestor
+ * @param to Where to place Insets
+ * @return Insets.
+ */
+ public Insets getInsets(SynthContext state, Insets to) {
+ if (to == null) {
+ to = new Insets(0, 0, 0, 0);
+ }
+ if (insets != null) {
+ to.left = insets.left;
+ to.right = insets.right;
+ to.top = insets.top;
+ to.bottom = insets.bottom;
+ }
+ else {
+ to.left = to.right = to.top = to.bottom = 0;
+ }
+ return to;
+ }
+
+ /**
+ * Sets the Painter to use for the border.
+ *
+ * @param painter Painter for the Border.
+ */
+ public void setPainter(SynthPainter painter) {
+ this.painter = painter;
+ }
+
+ /**
+ * Returns the Painter for the passed in Component. This may return null.
+ *
+ * @param ss SynthContext identifying requestor
+ * @return Painter for the border
+ */
+ public SynthPainter getPainter(SynthContext ss) {
+ return painter;
+ }
+
+ /**
+ * Sets whether or not the JComponent should be opaque.
+ *
+ * @param opaque Whether or not the JComponent should be opaque.
+ */
+ public void setOpaque(boolean opaque) {
+ this.opaque = opaque;
+ }
+
+ /**
+ * Returns the value to initialize the opacity property of the Component
+ * to. A Style should NOT assume the opacity will remain this value, the
+ * developer may reset it or override it.
+ *
+ * @param ss SynthContext identifying requestor
+ * @return opaque Whether or not the JComponent is opaque.
+ */
+ public boolean isOpaque(SynthContext ss) {
+ return opaque;
+ }
+
+ /**
+ * Sets style specific values. This does NOT copy the data, it
+ * assigns it directly to this Style.
+ *
+ * @param data Style specific values
+ */
+ public void setData(Map<Object, Object> data) {
+ this.data = data;
+ }
+
+ /**
+ * Returns the style specific data.
+ *
+ * @return Style specific data.
+ */
+ public Map<Object, Object> getData() {
+ return data;
+ }
+
+ /**
+ * Getter for a region specific style property.
+ *
+ * @param state SynthContext identifying requestor
+ * @param key Property being requested.
+ * @return Value of the named property
+ */
+ public Object get(SynthContext state, Object key) {
+ // Look for the best match
+ StateInfo si = getStateInfo(state.getComponentState());
+ if (si != null && si.getData() != null && getKeyFromData(si.getData(), key) != null) {
+ return getKeyFromData(si.getData(), key);
+ }
+ si = getStateInfo(0);
+ if (si != null && si.getData() != null && getKeyFromData(si.getData(), key) != null) {
+ return getKeyFromData(si.getData(), key);
+ }
+ if(getKeyFromData(data, key) != null)
+ return getKeyFromData(data, key);
+ return getDefaultValue(state, key);
+ }
+
+
+ private Object getKeyFromData(Map<Object, Object> stateData, Object key) {
+ Object value = null;
+ if (stateData != null) {
+
+ synchronized(stateData) {
+ value = stateData.get(key);
+ }
+ while (value == PENDING) {
+ synchronized(stateData) {
+ try {
+ stateData.wait();
+ } catch (InterruptedException ie) {}
+ value = stateData.get(key);
+ }
+ }
+ if (value instanceof UIDefaults.LazyValue) {
+ synchronized(stateData) {
+ stateData.put(key, PENDING);
+ }
+ value = ((UIDefaults.LazyValue)value).createValue(null);
+ synchronized(stateData) {
+ stateData.put(key, value);
+ stateData.notifyAll();
+ }
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Returns the default value for a particular property. This is only
+ * invoked if this style doesn't define a property for <code>key</code>.
+ *
+ * @param state SynthContext identifying requestor
+ * @param key Property being requested.
+ * @return Value of the named property
+ */
+ public Object getDefaultValue(SynthContext context, Object key) {
+ return super.get(context, key);
+ }
+
+ /**
+ * Creates a clone of this style.
+ *
+ * @return Clone of this style
+ */
+ public Object clone() {
+ DefaultSynthStyle style;
+ try {
+ style = (DefaultSynthStyle)super.clone();
+ } catch (CloneNotSupportedException cnse) {
+ return null;
+ }
+ if (states != null) {
+ style.states = new StateInfo[states.length];
+ for (int counter = states.length - 1; counter >= 0; counter--) {
+ style.states[counter] = (StateInfo)states[counter].clone();
+ }
+ }
+ if (data != null) {
+ style.data = new HashMap<>();
+ style.data.putAll(data);
+ }
+ return style;
+ }
+
+ /**
+ * Merges the contents of this Style with that of the passed in Style,
+ * returning the resulting merged syle. Properties of this
+ * <code>DefaultSynthStyle</code> will take precedence over those of the
+ * passed in <code>DefaultSynthStyle</code>. For example, if this
+ * style specifics a non-null font, the returned style will have its
+ * font so to that regardless of the <code>style</code>'s font.
+ *
+ * @param style Style to add our styles to
+ * @return Merged style.
+ */
+ public DefaultSynthStyle addTo(DefaultSynthStyle style) {
+ if (insets != null) {
+ style.insets = this.insets;
+ }
+ if (font != null) {
+ style.font = this.font;
+ }
+ if (painter != null) {
+ style.painter = this.painter;
+ }
+ if (synthGraphics != null) {
+ style.synthGraphics = this.synthGraphics;
+ }
+ style.opaque = opaque;
+ if (states != null) {
+ if (style.states == null) {
+ style.states = new StateInfo[states.length];
+ for (int counter = states.length - 1; counter >= 0; counter--){
+ if (states[counter] != null) {
+ style.states[counter] = (StateInfo)states[counter].
+ clone();
+ }
+ }
+ }
+ else {
+ // Find the number of new states in unique, merging any
+ // matching states as we go. Also, move any merge styles
+ // to the end to give them precedence.
+ int unique = 0;
+ // Number of StateInfos that match.
+ int matchCount = 0;
+ int maxOStyles = style.states.length;
+ for (int thisCounter = states.length - 1; thisCounter >= 0;
+ thisCounter--) {
+ int state = states[thisCounter].getComponentState();
+ boolean found = false;
+
+ for (int oCounter = maxOStyles - 1 - matchCount;
+ oCounter >= 0; oCounter--) {
+ if (state == style.states[oCounter].
+ getComponentState()) {
+ style.states[oCounter] = states[thisCounter].
+ addTo(style.states[oCounter]);
+ // Move StateInfo to end, giving it precedence.
+ StateInfo tmp = style.states[maxOStyles - 1 -
+ matchCount];
+ style.states[maxOStyles - 1 - matchCount] =
+ style.states[oCounter];
+ style.states[oCounter] = tmp;
+ matchCount++;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ unique++;
+ }
+ }
+ if (unique != 0) {
+ // There are states that exist in this Style that
+ // don't exist in the other style, recreate the array
+ // and add them.
+ StateInfo[] newStates = new StateInfo[
+ unique + maxOStyles];
+ int newIndex = maxOStyles;
+
+ System.arraycopy(style.states, 0, newStates, 0,maxOStyles);
+ for (int thisCounter = states.length - 1; thisCounter >= 0;
+ thisCounter--) {
+ int state = states[thisCounter].getComponentState();
+ boolean found = false;
+
+ for (int oCounter = maxOStyles - 1; oCounter >= 0;
+ oCounter--) {
+ if (state == style.states[oCounter].
+ getComponentState()) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ newStates[newIndex++] = (StateInfo)states[
+ thisCounter].clone();
+ }
+ }
+ style.states = newStates;
+ }
+ }
+ }
+ if (data != null) {
+ if (style.data == null) {
+ style.data = new HashMap<>();
+ }
+ style.data.putAll(data);
+ }
+ return style;
+ }
+
+ /**
+ * Sets the array of StateInfo's which are used to specify properties
+ * specific to a particular style.
+ *
+ * @param states StateInfos
+ */
+ public void setStateInfo(StateInfo[] states) {
+ this.states = states;
+ }
+
+ /**
+ * Returns the array of StateInfo's that that are used to specify
+ * properties specific to a particular style.
+ *
+ * @return Array of StateInfos.
+ */
+ public StateInfo[] getStateInfo() {
+ return states;
+ }
+
+ /**
+ * Returns the best matching StateInfo for a particular state.
+ *
+ * @param state Component state.
+ * @return Best matching StateInfo, or null
+ */
+ public StateInfo getStateInfo(int state) {
+ // Use the StateInfo with the most bits that matches that of state.
+ // If there is none, than fallback to
+ // the StateInfo with a state of 0, indicating it'll match anything.
+
+ // Consider if we have 3 StateInfos a, b and c with states:
+ // SELECTED, SELECTED | ENABLED, 0
+ //
+ // Input Return Value
+ // ----- ------------
+ // SELECTED a
+ // SELECTED | ENABLED b
+ // MOUSE_OVER c
+ // SELECTED | ENABLED | FOCUSED b
+ // ENABLED c
+
+ if (states != null) {
+ int bestCount = 0;
+ int bestIndex = -1;
+ int wildIndex = -1;
+
+ if (state == 0) {
+ for (int counter = states.length - 1; counter >= 0;counter--) {
+ if (states[counter].getComponentState() == 0) {
+ return states[counter];
+ }
+ }
+ return null;
+ }
+ for (int counter = states.length - 1; counter >= 0; counter--) {
+ int oState = states[counter].getComponentState();
+
+ if (oState == 0) {
+ if (wildIndex == -1) {
+ wildIndex = counter;
+ }
+ }
+ else if ((state & oState) == oState) {
+ // This is key, we need to make sure all bits of the
+ // StateInfo match, otherwise a StateInfo with
+ // SELECTED | ENABLED would match ENABLED, which we
+ // don't want.
+
+ // This comes from BigInteger.bitCnt
+ int bitCount = oState;
+ bitCount -= (0xaaaaaaaa & bitCount) >>> 1;
+ bitCount = (bitCount & 0x33333333) + ((bitCount >>> 2) &
+ 0x33333333);
+ bitCount = bitCount + (bitCount >>> 4) & 0x0f0f0f0f;
+ bitCount += bitCount >>> 8;
+ bitCount += bitCount >>> 16;
+ bitCount = bitCount & 0xff;
+ if (bitCount > bestCount) {
+ bestIndex = counter;
+ bestCount = bitCount;
+ }
+ }
+ }
+ if (bestIndex != -1) {
+ return states[bestIndex];
+ }
+ if (wildIndex != -1) {
+ return states[wildIndex];
+ }
+ }
+ return null;
+ }
+
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(super.toString()).append(',');
+
+ sb.append("data=").append(data).append(',');
+
+ sb.append("font=").append(font).append(',');
+
+ sb.append("insets=").append(insets).append(',');
+
+ sb.append("synthGraphics=").append(synthGraphics).append(',');
+
+ sb.append("painter=").append(painter).append(',');
+
+ StateInfo[] states = getStateInfo();
+ if (states != null) {
+ sb.append("states[");
+ for (StateInfo state : states) {
+ sb.append(state.toString()).append(',');
+ }
+ sb.append(']').append(',');
+ }
+
+ // remove last newline
+ sb.deleteCharAt(sb.length() - 1);
+
+ return sb.toString();
+ }
+
+
+ /**
+ * StateInfo represents Style information specific to the state of
+ * a component.
+ */
+ public static class StateInfo {
+ private Map<Object, Object> data;
+ private Font font;
+ private Color[] colors;
+ private int state;
+
+ /**
+ * Creates a new StateInfo.
+ */
+ public StateInfo() {
+ }
+
+ /**
+ * Creates a new StateInfo with the specified properties
+ *
+ * @param state Component state(s) that this StateInfo should be used
+ * for
+ * @param painter Painter responsible for rendering
+ * @param bgPainter Painter responsible for rendering the background
+ * @param font Font for this state
+ * @param colors Colors for this state
+ */
+ public StateInfo(int state, Font font, Color[] colors) {
+ this.state = state;
+ this.font = font;
+ this.colors = colors;
+ }
+
+ /**
+ * Creates a new StateInfo that is a copy of the passed in
+ * StateInfo.
+ *
+ * @param info StateInfo to copy.
+ */
+ public StateInfo(StateInfo info) {
+ this.state = info.state;
+ this.font = info.font;
+ if(info.data != null) {
+ if(data == null) {
+ data = new HashMap<>();
+ }
+ data.putAll(info.data);
+ }
+ if (info.colors != null) {
+ this.colors = new Color[info.colors.length];
+ System.arraycopy(info.colors, 0, colors, 0,info.colors.length);
+ }
+ }
+
+ public Map<Object, Object> getData() {
+ return data;
+ }
+
+ public void setData(Map<Object, Object> data) {
+ this.data = data;
+ }
+
+ /**
+ * Sets the font for this state.
+ *
+ * @param font Font to use for rendering
+ */
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ /**
+ * Returns the font for this state.
+ *
+ * @return Returns the font to use for rendering this state
+ */
+ public Font getFont() {
+ return font;
+ }
+
+ /**
+ * Sets the array of colors to use for rendering this state. This
+ * is indexed by <code>ColorType.getID()</code>.
+ *
+ * @param colors Array of colors
+ */
+ public void setColors(Color[] colors) {
+ this.colors = colors;
+ }
+
+ /**
+ * Returns the array of colors to use for rendering this state. This
+ * is indexed by <code>ColorType.getID()</code>.
+ *
+ * @return Array of colors
+ */
+ public Color[] getColors() {
+ return colors;
+ }
+
+ /**
+ * Returns the Color to used for the specified ColorType.
+ *
+ * @return Color.
+ */
+ public Color getColor(ColorType type) {
+ if (colors != null) {
+ int id = type.getID();
+
+ if (id < colors.length) {
+ return colors[id];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Merges the contents of this StateInfo with that of the passed in
+ * StateInfo, returning the resulting merged StateInfo. Properties of
+ * this <code>StateInfo</code> will take precedence over those of the
+ * passed in <code>StateInfo</code>. For example, if this
+ * StateInfo specifics a non-null font, the returned StateInfo will
+ * have its font so to that regardless of the <code>StateInfo</code>'s
+ * font.
+ *
+ * @param info StateInfo to add our styles to
+ * @return Merged StateInfo.
+ */
+ public StateInfo addTo(StateInfo info) {
+ if (font != null) {
+ info.font = font;
+ }
+ if(data != null) {
+ if(info.data == null) {
+ info.data = new HashMap<>();
+ }
+ info.data.putAll(data);
+ }
+ if (colors != null) {
+ if (info.colors == null) {
+ info.colors = new Color[colors.length];
+ System.arraycopy(colors, 0, info.colors, 0,
+ colors.length);
+ }
+ else {
+ if (info.colors.length < colors.length) {
+ Color[] old = info.colors;
+
+ info.colors = new Color[colors.length];
+ System.arraycopy(old, 0, info.colors, 0, old.length);
+ }
+ for (int counter = colors.length - 1; counter >= 0;
+ counter--) {
+ if (colors[counter] != null) {
+ info.colors[counter] = colors[counter];
+ }
+ }
+ }
+ }
+ return info;
+ }
+
+ /**
+ * Sets the state this StateInfo corresponds to.
+ *
+ * @see SynthConstants
+ * @param state info.
+ */
+ public void setComponentState(int state) {
+ this.state = state;
+ }
+
+ /**
+ * Returns the state this StateInfo corresponds to.
+ *
+ * @see SynthConstants
+ * @return state info.
+ */
+ public int getComponentState() {
+ return state;
+ }
+
+ /**
+ * Returns the number of states that are similar between the
+ * ComponentState this StateInfo represents and val.
+ */
+ private int getMatchCount(int val) {
+ // This comes from BigInteger.bitCnt
+ val &= state;
+ val -= (0xaaaaaaaa & val) >>> 1;
+ val = (val & 0x33333333) + ((val >>> 2) & 0x33333333);
+ val = val + (val >>> 4) & 0x0f0f0f0f;
+ val += val >>> 8;
+ val += val >>> 16;
+ return val & 0xff;
+ }
+
+ /**
+ * Creates and returns a copy of this StateInfo.
+ *
+ * @return Copy of this StateInfo.
+ */
+ public Object clone() {
+ return new StateInfo(this);
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(super.toString()).append(',');
+
+ sb.append("state=").append(Integer.toString(state)).append(',');
+
+ sb.append("font=").append(font).append(',');
+
+ if (colors != null) {
+ sb.append("colors=").append(Arrays.asList(colors)).
+ append(',');
+ }
+ return sb.toString();
+ }
+ }
+}