/*
* @(#)BlueprintStyle.java 1.12 03/12/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.sun.java.swing.plaf.gtk;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import java.security.*;
import javax.swing.plaf.synth.*;
import sun.swing.plaf.synth.DefaultSynthStyle;
/**
* BlueprintStyle extends GTKStyle adding support for a set of Info
s.
*
* @version 1.12 12/19/03
* @author Scott Violet
*/
class BlueprintStyle extends GTKStyle implements GTKConstants {
/**
* There should only ever be one blueprint engine.
*/
private static final GTKEngine BLUEPRINT_ENGINE = new BlueprintEngine();
private static final BlueprintGraphicsUtils BLUEPRINT_GRAPHICS =
new BlueprintGraphicsUtils();
/**
* Set of Infos used to determine what to paint.
*/
private Info[] info;
/**
* Comes from the top level icon_colorize setting in rc files.
*/
private boolean iconColorize;
/**
* Comes from the top level icon_colorize_ancestor_type setting
* in rec files. These Strings will all be interned by the parser.
*/
private String[] iconAncestorTypes;
/**
* Comes from the top level colorize_color setting in rc files.
*/
private Color colorizeColor;
/**
* Creates a duplicate of the passed in style.
*/
public BlueprintStyle(DefaultSynthStyle style) {
super(style);
if (style instanceof BlueprintStyle) {
BlueprintStyle bpStyle = (BlueprintStyle)style;
this.info = bpStyle.info;
this.iconColorize = bpStyle.iconColorize;
this.iconAncestorTypes = bpStyle.iconAncestorTypes;
this.colorizeColor = bpStyle.colorizeColor;
}
}
/**
* Creates a BlueprintStyle from the passed in arguments.
*/
public BlueprintStyle(StateInfo[] states,
CircularIdentityList classSpecificValues,
Font font,
int xThickness, int yThickness,
GTKStockIconInfo[] icons,
Info[] info,
boolean iconColorize,
String[] iconAncestorTypes,
Color colorizeColor) {
super(states, classSpecificValues, font, xThickness, yThickness, icons);
this.info = info;
this.iconColorize = iconColorize;
this.iconAncestorTypes = iconAncestorTypes;
this.colorizeColor = colorizeColor;
}
public SynthGraphicsUtils getGraphicsUtils(SynthContext context) {
return BLUEPRINT_GRAPHICS;
}
/**
* Adds the state of this BlueprintStyle to that of s
* returning a combined SynthStyle.
*/
public DefaultSynthStyle addTo(DefaultSynthStyle s) {
if (!(s instanceof BlueprintStyle)) {
s = new BlueprintStyle(s);
}
BlueprintStyle style = (BlueprintStyle)super.addTo(s);
if (info != null) {
if (style.info == null) {
style.info = info;
}
else {
// Place the more specific first.
Info[] merged = new Info[style.info.length + info.length];
System.arraycopy(info, 0, merged, 0, info.length);
System.arraycopy(style.info, 0, merged, info.length,
style.info.length);
style.info = merged;
}
}
// like the real blueprint, we only overwrite when iconColorize is true
if (iconColorize) {
style.iconColorize = true;
style.colorizeColor = colorizeColor;
}
// like the real blueprint, we always overwrite
style.iconAncestorTypes = iconAncestorTypes;
return style;
}
/**
* Creates a copy of the reciever and returns it.
*/
public Object clone() {
BlueprintStyle style = (BlueprintStyle)super.clone();
// These fields are immutable, no need to clone them
style.info = this.info;
style.iconAncestorTypes = this.iconAncestorTypes;
style.colorizeColor = this.colorizeColor;
return style;
}
/**
* Returns a GTKEngine to use for rendering.
*/
public GTKEngine getEngine(SynthContext context) {
return BLUEPRINT_ENGINE;
}
/**
* Returns the first instance of Info that matches the past in args, may
* return null if nothing matches.
*
* @param function String name for the painting method
* @param detail Description of what is being painted
* @param componentState State of the Component
* @param shadow Shadow type
* @param orientation Orientation of what is being painted
* @param gapSide Side of the gap being drawn
* @param arrowDirection direction of the arrow.
* @return Best matching Info, or null if none match
*/
public Info getInfo(String function, String detail, int componentState,
int shadow, int orientation, int gapSide,
int arrowDirection, String parentType) {
if (info != null) {
for (int counter = 0, max = info.length; counter < max;counter++) {
if (info[counter].getFunction() == function && info[counter].
getMatchCount(detail, componentState, shadow,
orientation, gapSide, arrowDirection,
parentType) != -1) {
return info[counter];
}
}
}
return null;
}
/**
* Returns the number of non-null arugments and arguments that are
* != UNDEFINED.
*/
private int getMaxMatchCount(int componentState, int shadow,
int orientation, int gapSide,
int arrowDirection, String detail) {
int count = 0;
if (detail != null) {
count++;
}
if (componentState != UNDEFINED) {
count++;
}
if (shadow != UNDEFINED) {
count++;
}
if (orientation != UNDEFINED) {
count++;
}
if (gapSide != UNDEFINED) {
count++;
}
if (arrowDirection != UNDEFINED) {
count++;
}
return count;
}
/**
* A description of how to paint a portion of a widget.
*/
public static class Info {
// match data
private String function = null;
private String detail = null;
int gapSide = UNDEFINED;
int orientation = UNDEFINED;
int componentState = UNDEFINED;
int shadow = UNDEFINED;
int arrowDirection = UNDEFINED;
// strings in this list will be interned
// this list could be null
ArrayList parentTypeList = null;
boolean useAsBkgMask = false;
// background
Object image = null;
Insets fileInsets = null;
boolean stretch = false;
boolean recolorable = false;
Color colorizeColor = null;
// overlay
Object overlayImage = null;
Insets overlayInsets = null;
boolean overlayStretch = false;
boolean overlayRecolorable = false;
Color overlayColorizeColor = null;
// gap start
Object gapStartImage = null;
Insets gapStartInsets = null;
// gap
Object gapImage = null;
Insets gapInsets = null;
// gap end
Object gapEndImage = null;
Insets gapEndInsets = null;
public void setFunction(String function) {
this.function = function.intern();
}
public void setDetail(String detail) {
this.detail = detail.intern();
}
public String getFunction() {
return function;
}
public Image getImage() {
image = getImage(image);
return (Image)image;
}
public boolean isRecolorable() {
return recolorable;
}
public Color getColorizeColor() {
return colorizeColor;
}
public boolean isBkgMask() {
return useAsBkgMask;
}
public Insets getImageInsets() {
return fileInsets;
}
public boolean getStretch() {
return stretch;
}
public String getDetail() {
return detail;
}
public int getComponentState() {
return componentState;
}
public int getShadow() {
return shadow;
}
public int getGapSide() {
return gapSide;
}
public Image getGapImage() {
gapImage = getImage(gapImage);
return (Image)gapImage;
}
public Insets getGapInsets() {
return gapInsets;
}
public Image getGapStartImage() {
gapStartImage = getImage(gapStartImage);
return (Image)gapStartImage;
}
public Insets getGapStartInsets() {
return gapStartInsets;
}
public Image getGapEndImage() {
gapEndImage = getImage(gapEndImage);
return (Image)gapEndImage;
}
public Insets getGapEndInsets() {
return gapEndInsets;
}
public Image getOverlayImage() {
overlayImage = getImage(overlayImage);
return (Image)overlayImage;
}
public Insets getOverlayInsets() {
return overlayInsets;
}
public boolean getOverlayStretch() {
return overlayStretch;
}
public boolean getOverlayRecolorable() {
return overlayRecolorable;
}
public Color getOverlayColorizeColor() {
return overlayColorizeColor;
}
public int getArrowDirection() {
return arrowDirection;
}
public int getOrientation() {
return orientation;
}
private Image getImage(final Object o) {
if (o == null || o instanceof Image) {
return (Image)o;
}
ImageIcon ii = (ImageIcon)AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new ImageIcon((String)o);
}
});
if (ii.getIconWidth() > 0 && ii.getIconHeight() > 0) {
return ii.getImage();
}
return null;
}
/**
* Will return < 0 if doesn't match, otherwise return the
* number of parameters that match.
*/
int getMatchCount(String detail, int componentState, int shadow,
int orientation, int gapSide, int arrowDirection,
String parentType) {
int matchCount = 0;
if (this.componentState != UNDEFINED) {
if (componentState != UNDEFINED &&
this.componentState != componentState) {
return -1;
}
matchCount++;
}
if (this.shadow != UNDEFINED) {
if (shadow != UNDEFINED && this.shadow != shadow) {
return -1;
}
matchCount++;
}
if (this.arrowDirection != UNDEFINED) {
if (arrowDirection != UNDEFINED &&
this.arrowDirection != arrowDirection) {
return -1;
}
matchCount++;
}
if (this.orientation != UNDEFINED) {
if (orientation != UNDEFINED &&
this.orientation != orientation) {
return -1;
}
matchCount++;
}
if (this.gapSide != UNDEFINED) {
if (gapSide != UNDEFINED && this.gapSide != gapSide) {
return -1;
}
matchCount++;
}
if (this.detail != null) {
if (this.detail != detail) {
return -1;
}
matchCount++;
}
if (this.parentTypeList != null) {
boolean found = false;
String type;
Iterator itr = parentTypeList.iterator();
while (itr.hasNext() && !found) {
type = (String)itr.next();
// NOTE: Maybe we should compare all lowercase.
if (type == parentType) {
found = true;
}
}
if (!found) {
return -1;
}
matchCount++;
}
return matchCount;
}
/**
* Returns true if this Info matches that of the passed in Info.
* This differs from equals in so far as this only compares the
* properties that are used in lookup vs the actual images or
* insets.
*
* @return true if the receiver and info can be considered equal
* for lookup.
*/
boolean matches(Info info) {
return (info.function == function && info.detail == detail &&
info.componentState == componentState &&
info.shadow == shadow && info.gapSide == gapSide &&
info.arrowDirection == arrowDirection &&
info.orientation == orientation);
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("IMAGE:\n");
if (function != null) {
buf.append(" function=").append(function).append('\n');
}
if (detail != null) {
buf.append(" detail=").append(detail).append('\n');
}
if (gapSide != UNDEFINED) {
buf.append(" gapSide=");
buf.append(getSideName(gapSide)).append('\n');
}
if (orientation != UNDEFINED) {
buf.append(" orientation=");
buf.append(getOrientName(orientation)).append('\n');
}
if (componentState != UNDEFINED) {
buf.append(" componentState=");
buf.append(getStateName(componentState, "UNDEFINED")).append('\n');
}
if (shadow != UNDEFINED) {
buf.append(" shadow=");
buf.append(getShadowName(shadow)).append('\n');
}
if (arrowDirection != UNDEFINED) {
buf.append(" arrowDirection=");
buf.append(getArrowDirectionName(arrowDirection)).append('\n');
}
if (parentTypeList != null) {
buf.append(" parent_type={");
for (Iterator iter = parentTypeList.iterator();
iter.hasNext(); ) {
buf.append(iter.next()).append(", ");
}
buf.deleteCharAt(buf.length() - 1).deleteCharAt(buf.length() - 1);
buf.append("}\n");
}
if (useAsBkgMask != false) {
buf.append(" use_as_bkg_mask=").append(useAsBkgMask).append('\n');
}
if (image != null) {
buf.append(" image=").append(image).append('\n');
}
if (fileInsets != null) {
buf.append(" fileInsets=").append(fileInsets).append('\n');
}
if (stretch != false) {
buf.append(" stretch=").append(stretch).append('\n');
}
if (recolorable != false) {
buf.append(" recolorable=").append(recolorable).append('\n');
}
if (colorizeColor != null) {
buf.append(" colorize_color=");
buf.append(getColorStringWithAlpha(colorizeColor)).append('\n');
}
if (overlayImage != null) {
buf.append(" overlayImage=").append(overlayImage).append('\n');
}
if (overlayInsets != null) {
buf.append(" overlayInsets=").append(overlayInsets).append('\n');
}
if (overlayStretch != false) {
buf.append(" overlayStretch=").append(overlayStretch).append('\n');
}
if (overlayRecolorable != false) {
buf.append(" overlay_recolorable=").append(overlayRecolorable).append('\n');
}
if (overlayColorizeColor != null) {
buf.append(" overlay_colorize_color=");
buf.append(getColorStringWithAlpha(overlayColorizeColor)).append('\n');
}
if (gapStartImage != null) {
buf.append(" gapStartImage=").append(gapStartImage).append('\n');
}
if (gapStartInsets != null) {
buf.append(" gapStartInsets=").append(gapStartInsets).append('\n');
}
if (gapImage != null) {
buf.append(" gapImage=").append(gapImage).append('\n');
}
if (gapInsets != null) {
buf.append(" gapInsets=").append(gapInsets).append('\n');
}
if (gapEndImage != null) {
buf.append(" gapEndImage=").append(gapEndImage).append('\n');
}
if (gapEndInsets != null) {
buf.append(" gapEndInsets=").append(gapEndInsets).append('\n');
}
buf.deleteCharAt(buf.length() - 1);
return buf.toString();
}
private static String getSideName(int side) {
switch(side) {
case TOP: return "TOP";
case BOTTOM: return "BOTTOM";
case LEFT: return "LEFT";
case RIGHT: return "RIGHT";
case UNDEFINED: return "UNDEFINED";
}
return "UNKNOWN";
}
private static String getOrientName(int orient) {
switch(orient) {
case HORIZONTAL: return "HORIZONTAL";
case VERTICAL: return "VERTICAL";
case UNDEFINED: return "UNDEFINED";
}
return "UNKNOWN";
}
private static String getShadowName(int shadow) {
switch(shadow) {
case SHADOW_IN: return "SHADOW_IN";
case SHADOW_OUT: return "SHADOW_OUT";
case SHADOW_ETCHED_IN: return "SHADOW_ETCHED_IN";
case SHADOW_ETCHED_OUT: return "SHADOW_ETCHED_OUT";
case SHADOW_NONE: return "SHADOW_NONE";
case UNDEFINED: return "UNDEFINED";
}
return "UNKNOWN";
}
private static String getArrowDirectionName(int dir) {
switch(dir) {
case ARROW_UP: return "ARROW_UP";
case ARROW_DOWN: return "ARROW_DOWN";
case ARROW_LEFT: return "ARROW_LEFT";
case ARROW_RIGHT: return "ARROW_RIGHT";
case UNDEFINED: return "UNDEFINED";
}
return "UNKNOWN";
}
}
private static String getColorStringWithAlpha(Color c) {
if (c == null) {
return "null";
}
StringBuffer buf = new StringBuffer();
buf.append('{');
buf.append(c.getRed()).append(", ");
buf.append(c.getGreen()).append(", ");
buf.append(c.getBlue()).append(", ");
buf.append(c.getAlpha()).append("}");
return buf.toString();
}
public String toString() {
StringBuffer buf = new StringBuffer(super.toString());
if (buf.length() > 0) {
buf.append('\n');
}
buf.append("*** Blueprint Engine Info ***\n");
buf.append("icon_colorize = " + iconColorize + '\n');
buf.append("icon_colorize_ancestor_type = ");
if (iconAncestorTypes == null) {
buf.append("null\n");
} else {
buf.append('{');
for (int i = 0; i < iconAncestorTypes.length; i++) {
buf.append(iconAncestorTypes[i] + ", ");
}
buf.deleteCharAt(buf.length() - 1);
buf.deleteCharAt(buf.length() - 1);
buf.append("}\n");
}
buf.append("colorize_color = ");
buf.append(getColorStringWithAlpha(colorizeColor));
buf.append('\n');
if (info != null) {
for (int i = 0; i < info.length; i++) {
buf.append(info[i].toString()).append('\n');
}
}
// remove last newline
buf.deleteCharAt(buf.length() - 1);
return buf.toString();
}
}