928 lines
30 KiB
Java
928 lines
30 KiB
Java
/*
|
|
* Copyright (c) 2002, 2013, 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.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 Object PENDING = new Object();
|
|
|
|
/**
|
|
* Should the component be opaque?
|
|
*/
|
|
private boolean opaque;
|
|
/**
|
|
* Insets.
|
|
*/
|
|
private Insets insets;
|
|
/**
|
|
* Information specific to ComponentState.
|
|
*/
|
|
private StateInfo[] states;
|
|
/**
|
|
* User specific data.
|
|
*/
|
|
private Map 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 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 data) {
|
|
this.data = data;
|
|
}
|
|
|
|
/**
|
|
* Returns the style specific data.
|
|
*
|
|
* @return Style specific data.
|
|
*/
|
|
public Map 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 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() {
|
|
StringBuffer buf = new StringBuffer();
|
|
|
|
buf.append(super.toString()).append(',');
|
|
|
|
buf.append("data=").append(data).append(',');
|
|
|
|
buf.append("font=").append(font).append(',');
|
|
|
|
buf.append("insets=").append(insets).append(',');
|
|
|
|
buf.append("synthGraphics=").append(synthGraphics).append(',');
|
|
|
|
buf.append("painter=").append(painter).append(',');
|
|
|
|
StateInfo[] states = getStateInfo();
|
|
if (states != null) {
|
|
buf.append("states[");
|
|
for (StateInfo state : states) {
|
|
buf.append(state.toString()).append(',');
|
|
}
|
|
buf.append(']').append(',');
|
|
}
|
|
|
|
// remove last newline
|
|
buf.deleteCharAt(buf.length() - 1);
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
|
|
/**
|
|
* StateInfo represents Style information specific to the state of
|
|
* a component.
|
|
*/
|
|
public static class StateInfo {
|
|
private Map 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 getData() {
|
|
return data;
|
|
}
|
|
|
|
public void setData(Map 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() {
|
|
StringBuffer buf = new StringBuffer();
|
|
|
|
buf.append(super.toString()).append(',');
|
|
|
|
buf.append("state=").append(Integer.toString(state)).append(',');
|
|
|
|
buf.append("font=").append(font).append(',');
|
|
|
|
if (colors != null) {
|
|
buf.append("colors=").append(Arrays.asList(colors)).
|
|
append(',');
|
|
}
|
|
return buf.toString();
|
|
}
|
|
}
|
|
}
|