You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2160 lines
81 KiB
2160 lines
81 KiB
/*
|
|
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
package com.sun.java.swing.plaf.gtk;
|
|
|
|
import sun.swing.SwingUtilities2;
|
|
import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType;
|
|
import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType;
|
|
|
|
import javax.swing.plaf.ColorUIResource;
|
|
import javax.swing.plaf.synth.*;
|
|
|
|
import java.awt.*;
|
|
import java.awt.geom.*;
|
|
import java.awt.image.*;
|
|
import java.io.*;
|
|
import java.net.*;
|
|
import java.security.*;
|
|
import java.util.*;
|
|
|
|
import javax.swing.*;
|
|
import javax.swing.border.*;
|
|
|
|
import javax.xml.parsers.*;
|
|
import org.xml.sax.SAXException;
|
|
import org.w3c.dom.*;
|
|
|
|
/**
|
|
*/
|
|
class Metacity implements SynthConstants {
|
|
// Tutorial:
|
|
// http://developer.gnome.org/doc/tutorials/metacity/metacity-themes.html
|
|
|
|
// Themes:
|
|
// http://art.gnome.org/theme_list.php?category=metacity
|
|
|
|
static Metacity INSTANCE;
|
|
|
|
private static final String[] themeNames = {
|
|
getUserTheme(),
|
|
"blueprint",
|
|
"Bluecurve",
|
|
"Crux",
|
|
"SwingFallbackTheme"
|
|
};
|
|
|
|
static {
|
|
for (String themeName : themeNames) {
|
|
if (themeName != null) {
|
|
try {
|
|
INSTANCE = new Metacity(themeName);
|
|
} catch (FileNotFoundException ex) {
|
|
} catch (IOException ex) {
|
|
logError(themeName, ex);
|
|
} catch (ParserConfigurationException ex) {
|
|
logError(themeName, ex);
|
|
} catch (SAXException ex) {
|
|
logError(themeName, ex);
|
|
}
|
|
}
|
|
if (INSTANCE != null) {
|
|
break;
|
|
}
|
|
}
|
|
if (INSTANCE == null) {
|
|
throw new Error("Could not find any installed metacity theme, and fallback failed");
|
|
}
|
|
}
|
|
|
|
private static boolean errorLogged = false;
|
|
private static DocumentBuilder documentBuilder;
|
|
private static Document xmlDoc;
|
|
private static String userHome;
|
|
|
|
private Node frame_style_set;
|
|
private Map<String, Object> frameGeometry;
|
|
private Map<String, Map<String, Object>> frameGeometries;
|
|
|
|
private LayoutManager titlePaneLayout = new TitlePaneLayout();
|
|
|
|
private ColorizeImageFilter imageFilter = new ColorizeImageFilter();
|
|
private URL themeDir = null;
|
|
private SynthContext context;
|
|
private String themeName;
|
|
|
|
private ArithmeticExpressionEvaluator aee = new ArithmeticExpressionEvaluator();
|
|
private Map<String, Integer> variables;
|
|
|
|
// Reusable clip shape object
|
|
private RoundRectClipShape roundedClipShape;
|
|
|
|
protected Metacity(String themeName) throws IOException, ParserConfigurationException, SAXException {
|
|
this.themeName = themeName;
|
|
themeDir = getThemeDir(themeName);
|
|
if (themeDir != null) {
|
|
URL themeURL = new URL(themeDir, "metacity-theme-1.xml");
|
|
xmlDoc = getXMLDoc(themeURL);
|
|
if (xmlDoc == null) {
|
|
throw new IOException(themeURL.toString());
|
|
}
|
|
} else {
|
|
throw new FileNotFoundException(themeName);
|
|
}
|
|
|
|
// Initialize constants
|
|
variables = new HashMap<String, Integer>();
|
|
NodeList nodes = xmlDoc.getElementsByTagName("constant");
|
|
int n = nodes.getLength();
|
|
for (int i = 0; i < n; i++) {
|
|
Node node = nodes.item(i);
|
|
String name = getStringAttr(node, "name");
|
|
if (name != null) {
|
|
String value = getStringAttr(node, "value");
|
|
if (value != null) {
|
|
try {
|
|
variables.put(name, Integer.parseInt(value));
|
|
} catch (NumberFormatException ex) {
|
|
logError(themeName, ex);
|
|
// Ignore bad value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache frame geometries
|
|
frameGeometries = new HashMap<String, Map<String, Object>>();
|
|
nodes = xmlDoc.getElementsByTagName("frame_geometry");
|
|
n = nodes.getLength();
|
|
for (int i = 0; i < n; i++) {
|
|
Node node = nodes.item(i);
|
|
String name = getStringAttr(node, "name");
|
|
if (name != null) {
|
|
HashMap<String, Object> gm = new HashMap<String, Object>();
|
|
frameGeometries.put(name, gm);
|
|
|
|
String parentGM = getStringAttr(node, "parent");
|
|
if (parentGM != null) {
|
|
gm.putAll(frameGeometries.get(parentGM));
|
|
}
|
|
|
|
gm.put("has_title",
|
|
Boolean.valueOf(getBooleanAttr(node, "has_title", true)));
|
|
gm.put("rounded_top_left",
|
|
Boolean.valueOf(getBooleanAttr(node, "rounded_top_left", false)));
|
|
gm.put("rounded_top_right",
|
|
Boolean.valueOf(getBooleanAttr(node, "rounded_top_right", false)));
|
|
gm.put("rounded_bottom_left",
|
|
Boolean.valueOf(getBooleanAttr(node, "rounded_bottom_left", false)));
|
|
gm.put("rounded_bottom_right",
|
|
Boolean.valueOf(getBooleanAttr(node, "rounded_bottom_right", false)));
|
|
|
|
NodeList childNodes = node.getChildNodes();
|
|
int nc = childNodes.getLength();
|
|
for (int j = 0; j < nc; j++) {
|
|
Node child = childNodes.item(j);
|
|
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
|
name = child.getNodeName();
|
|
Object value = null;
|
|
if ("distance".equals(name)) {
|
|
value = Integer.valueOf(getIntAttr(child, "value", 0));
|
|
} else if ("border".equals(name)) {
|
|
value = new Insets(getIntAttr(child, "top", 0),
|
|
getIntAttr(child, "left", 0),
|
|
getIntAttr(child, "bottom", 0),
|
|
getIntAttr(child, "right", 0));
|
|
} else if ("aspect_ratio".equals(name)) {
|
|
value = new Float(getFloatAttr(child, "value", 1.0F));
|
|
} else {
|
|
logError(themeName, "Unknown Metacity frame geometry value type: "+name);
|
|
}
|
|
String childName = getStringAttr(child, "name");
|
|
if (childName != null && value != null) {
|
|
gm.put(childName, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
frameGeometry = frameGeometries.get("normal");
|
|
}
|
|
|
|
|
|
public static LayoutManager getTitlePaneLayout() {
|
|
return INSTANCE.titlePaneLayout;
|
|
}
|
|
|
|
private Shape getRoundedClipShape(int x, int y, int w, int h,
|
|
int arcw, int arch, int corners) {
|
|
if (roundedClipShape == null) {
|
|
roundedClipShape = new RoundRectClipShape();
|
|
}
|
|
roundedClipShape.setRoundedRect(x, y, w, h, arcw, arch, corners);
|
|
|
|
return roundedClipShape;
|
|
}
|
|
|
|
void paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h) {
|
|
updateFrameGeometry(context);
|
|
|
|
this.context = context;
|
|
JButton button = (JButton)context.getComponent();
|
|
String buttonName = button.getName();
|
|
int buttonState = context.getComponentState();
|
|
|
|
JComponent titlePane = (JComponent)button.getParent();
|
|
Container titlePaneParent = titlePane.getParent();
|
|
|
|
JInternalFrame jif;
|
|
if (titlePaneParent instanceof JInternalFrame) {
|
|
jif = (JInternalFrame)titlePaneParent;
|
|
} else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) {
|
|
jif = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame();
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
boolean active = jif.isSelected();
|
|
button.setOpaque(false);
|
|
|
|
String state = "normal";
|
|
if ((buttonState & PRESSED) != 0) {
|
|
state = "pressed";
|
|
} else if ((buttonState & MOUSE_OVER) != 0) {
|
|
state = "prelight";
|
|
}
|
|
|
|
String function = null;
|
|
String location = null;
|
|
boolean left_corner = false;
|
|
boolean right_corner = false;
|
|
|
|
|
|
if (buttonName == "InternalFrameTitlePane.menuButton") {
|
|
function = "menu";
|
|
location = "left_left";
|
|
left_corner = true;
|
|
} else if (buttonName == "InternalFrameTitlePane.iconifyButton") {
|
|
function = "minimize";
|
|
int nButtons = ((jif.isIconifiable() ? 1 : 0) +
|
|
(jif.isMaximizable() ? 1 : 0) +
|
|
(jif.isClosable() ? 1 : 0));
|
|
right_corner = (nButtons == 1);
|
|
switch (nButtons) {
|
|
case 1: location = "right_right"; break;
|
|
case 2: location = "right_middle"; break;
|
|
case 3: location = "right_left"; break;
|
|
}
|
|
} else if (buttonName == "InternalFrameTitlePane.maximizeButton") {
|
|
function = "maximize";
|
|
right_corner = !jif.isClosable();
|
|
location = jif.isClosable() ? "right_middle" : "right_right";
|
|
} else if (buttonName == "InternalFrameTitlePane.closeButton") {
|
|
function = "close";
|
|
right_corner = true;
|
|
location = "right_right";
|
|
}
|
|
|
|
Node frame = getNode(frame_style_set, "frame", new String[] {
|
|
"focus", (active ? "yes" : "no"),
|
|
"state", (jif.isMaximum() ? "maximized" : "normal")
|
|
});
|
|
|
|
if (function != null && frame != null) {
|
|
Node frame_style = getNode("frame_style", new String[] {
|
|
"name", getStringAttr(frame, "style")
|
|
});
|
|
if (frame_style != null) {
|
|
Shape oldClip = g.getClip();
|
|
if ((right_corner && getBoolean("rounded_top_right", false)) ||
|
|
(left_corner && getBoolean("rounded_top_left", false))) {
|
|
|
|
Point buttonLoc = button.getLocation();
|
|
if (right_corner) {
|
|
g.setClip(getRoundedClipShape(0, 0, w, h,
|
|
12, 12, RoundRectClipShape.TOP_RIGHT));
|
|
} else {
|
|
g.setClip(getRoundedClipShape(0, 0, w, h,
|
|
11, 11, RoundRectClipShape.TOP_LEFT));
|
|
}
|
|
|
|
Rectangle clipBounds = oldClip.getBounds();
|
|
g.clipRect(clipBounds.x, clipBounds.y,
|
|
clipBounds.width, clipBounds.height);
|
|
}
|
|
drawButton(frame_style, location+"_background", state, g, w, h, jif);
|
|
drawButton(frame_style, function, state, g, w, h, jif);
|
|
g.setClip(oldClip);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void drawButton(Node frame_style, String function, String state,
|
|
Graphics g, int w, int h, JInternalFrame jif) {
|
|
Node buttonNode = getNode(frame_style, "button",
|
|
new String[] { "function", function, "state", state });
|
|
if (buttonNode == null && !state.equals("normal")) {
|
|
buttonNode = getNode(frame_style, "button",
|
|
new String[] { "function", function, "state", "normal" });
|
|
}
|
|
if (buttonNode != null) {
|
|
Node draw_ops;
|
|
String draw_ops_name = getStringAttr(buttonNode, "draw_ops");
|
|
if (draw_ops_name != null) {
|
|
draw_ops = getNode("draw_ops", new String[] { "name", draw_ops_name });
|
|
} else {
|
|
draw_ops = getNode(buttonNode, "draw_ops", null);
|
|
}
|
|
variables.put("width", w);
|
|
variables.put("height", h);
|
|
draw(draw_ops, g, jif);
|
|
}
|
|
}
|
|
|
|
void paintFrameBorder(SynthContext context, Graphics g, int x0, int y0, int width, int height) {
|
|
updateFrameGeometry(context);
|
|
|
|
this.context = context;
|
|
JComponent comp = context.getComponent();
|
|
JComponent titlePane = findChild(comp, "InternalFrame.northPane");
|
|
|
|
if (titlePane == null) {
|
|
return;
|
|
}
|
|
|
|
JInternalFrame jif = null;
|
|
if (comp instanceof JInternalFrame) {
|
|
jif = (JInternalFrame)comp;
|
|
} else if (comp instanceof JInternalFrame.JDesktopIcon) {
|
|
jif = ((JInternalFrame.JDesktopIcon)comp).getInternalFrame();
|
|
} else {
|
|
assert false : "component is not JInternalFrame or JInternalFrame.JDesktopIcon";
|
|
return;
|
|
}
|
|
|
|
boolean active = jif.isSelected();
|
|
Font oldFont = g.getFont();
|
|
g.setFont(titlePane.getFont());
|
|
g.translate(x0, y0);
|
|
|
|
Rectangle titleRect = calculateTitleArea(jif);
|
|
JComponent menuButton = findChild(titlePane, "InternalFrameTitlePane.menuButton");
|
|
|
|
Icon frameIcon = jif.getFrameIcon();
|
|
variables.put("mini_icon_width",
|
|
(frameIcon != null) ? frameIcon.getIconWidth() : 0);
|
|
variables.put("mini_icon_height",
|
|
(frameIcon != null) ? frameIcon.getIconHeight() : 0);
|
|
variables.put("title_width", calculateTitleTextWidth(g, jif));
|
|
FontMetrics fm = SwingUtilities2.getFontMetrics(jif, g);
|
|
variables.put("title_height", fm.getAscent() + fm.getDescent());
|
|
|
|
// These don't seem to apply here, but the Galaxy theme uses them. Not sure why.
|
|
variables.put("icon_width", 32);
|
|
variables.put("icon_height", 32);
|
|
|
|
if (frame_style_set != null) {
|
|
Node frame = getNode(frame_style_set, "frame", new String[] {
|
|
"focus", (active ? "yes" : "no"),
|
|
"state", (jif.isMaximum() ? "maximized" : "normal")
|
|
});
|
|
|
|
if (frame != null) {
|
|
Node frame_style = getNode("frame_style", new String[] {
|
|
"name", getStringAttr(frame, "style")
|
|
});
|
|
if (frame_style != null) {
|
|
Shape oldClip = g.getClip();
|
|
boolean roundTopLeft = getBoolean("rounded_top_left", false);
|
|
boolean roundTopRight = getBoolean("rounded_top_right", false);
|
|
boolean roundBottomLeft = getBoolean("rounded_bottom_left", false);
|
|
boolean roundBottomRight = getBoolean("rounded_bottom_right", false);
|
|
|
|
if (roundTopLeft || roundTopRight || roundBottomLeft || roundBottomRight) {
|
|
jif.setOpaque(false);
|
|
|
|
g.setClip(getRoundedClipShape(0, 0, width, height, 12, 12,
|
|
(roundTopLeft ? RoundRectClipShape.TOP_LEFT : 0) |
|
|
(roundTopRight ? RoundRectClipShape.TOP_RIGHT : 0) |
|
|
(roundBottomLeft ? RoundRectClipShape.BOTTOM_LEFT : 0) |
|
|
(roundBottomRight ? RoundRectClipShape.BOTTOM_RIGHT : 0)));
|
|
}
|
|
|
|
Rectangle clipBounds = oldClip.getBounds();
|
|
g.clipRect(clipBounds.x, clipBounds.y,
|
|
clipBounds.width, clipBounds.height);
|
|
|
|
int titleHeight = titlePane.getHeight();
|
|
|
|
boolean minimized = jif.isIcon();
|
|
Insets insets = getBorderInsets(context, null);
|
|
|
|
int leftTitlebarEdge = getInt("left_titlebar_edge");
|
|
int rightTitlebarEdge = getInt("right_titlebar_edge");
|
|
int topTitlebarEdge = getInt("top_titlebar_edge");
|
|
int bottomTitlebarEdge = getInt("bottom_titlebar_edge");
|
|
|
|
if (!minimized) {
|
|
drawPiece(frame_style, g, "entire_background",
|
|
0, 0, width, height, jif);
|
|
}
|
|
drawPiece(frame_style, g, "titlebar",
|
|
0, 0, width, titleHeight, jif);
|
|
drawPiece(frame_style, g, "titlebar_middle",
|
|
leftTitlebarEdge, topTitlebarEdge,
|
|
width - leftTitlebarEdge - rightTitlebarEdge,
|
|
titleHeight - topTitlebarEdge - bottomTitlebarEdge,
|
|
jif);
|
|
drawPiece(frame_style, g, "left_titlebar_edge",
|
|
0, 0, leftTitlebarEdge, titleHeight, jif);
|
|
drawPiece(frame_style, g, "right_titlebar_edge",
|
|
width - rightTitlebarEdge, 0,
|
|
rightTitlebarEdge, titleHeight, jif);
|
|
drawPiece(frame_style, g, "top_titlebar_edge",
|
|
0, 0, width, topTitlebarEdge, jif);
|
|
drawPiece(frame_style, g, "bottom_titlebar_edge",
|
|
0, titleHeight - bottomTitlebarEdge,
|
|
width, bottomTitlebarEdge, jif);
|
|
drawPiece(frame_style, g, "title",
|
|
titleRect.x, titleRect.y, titleRect.width, titleRect.height, jif);
|
|
if (!minimized) {
|
|
drawPiece(frame_style, g, "left_edge",
|
|
0, titleHeight, insets.left, height-titleHeight, jif);
|
|
drawPiece(frame_style, g, "right_edge",
|
|
width-insets.right, titleHeight, insets.right, height-titleHeight, jif);
|
|
drawPiece(frame_style, g, "bottom_edge",
|
|
0, height - insets.bottom, width, insets.bottom, jif);
|
|
drawPiece(frame_style, g, "overlay",
|
|
0, 0, width, height, jif);
|
|
}
|
|
g.setClip(oldClip);
|
|
}
|
|
}
|
|
}
|
|
g.translate(-x0, -y0);
|
|
g.setFont(oldFont);
|
|
}
|
|
|
|
|
|
|
|
private static class Privileged implements PrivilegedAction<Object> {
|
|
private static int GET_THEME_DIR = 0;
|
|
private static int GET_USER_THEME = 1;
|
|
private static int GET_IMAGE = 2;
|
|
private int type;
|
|
private Object arg;
|
|
|
|
public Object doPrivileged(int type, Object arg) {
|
|
this.type = type;
|
|
this.arg = arg;
|
|
return AccessController.doPrivileged(this);
|
|
}
|
|
|
|
public Object run() {
|
|
if (type == GET_THEME_DIR) {
|
|
String sep = File.separator;
|
|
String[] dirs = new String[] {
|
|
userHome + sep + ".themes",
|
|
System.getProperty("swing.metacitythemedir"),
|
|
"/usr/X11R6/share/themes",
|
|
"/usr/X11R6/share/gnome/themes",
|
|
"/usr/local/share/themes",
|
|
"/usr/local/share/gnome/themes",
|
|
"/usr/share/themes",
|
|
"/usr/gnome/share/themes", // Debian/Redhat/Solaris
|
|
"/opt/gnome2/share/themes" // SuSE
|
|
};
|
|
|
|
URL themeDir = null;
|
|
for (int i = 0; i < dirs.length; i++) {
|
|
// System property may not be set so skip null directories.
|
|
if (dirs[i] == null) {
|
|
continue;
|
|
}
|
|
File dir =
|
|
new File(dirs[i] + sep + arg + sep + "metacity-1");
|
|
if (new File(dir, "metacity-theme-1.xml").canRead()) {
|
|
try {
|
|
themeDir = dir.toURI().toURL();
|
|
} catch (MalformedURLException ex) {
|
|
themeDir = null;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (themeDir == null) {
|
|
String filename = "resources/metacity/" + arg +
|
|
"/metacity-1/metacity-theme-1.xml";
|
|
URL url = getClass().getResource(filename);
|
|
if (url != null) {
|
|
String str = url.toString();
|
|
try {
|
|
themeDir = new URL(str.substring(0, str.lastIndexOf('/'))+"/");
|
|
} catch (MalformedURLException ex) {
|
|
themeDir = null;
|
|
}
|
|
}
|
|
}
|
|
return themeDir;
|
|
} else if (type == GET_USER_THEME) {
|
|
try {
|
|
// Set userHome here because we need the privilege
|
|
userHome = System.getProperty("user.home");
|
|
|
|
String theme = System.getProperty("swing.metacitythemename");
|
|
if (theme != null) {
|
|
return theme;
|
|
}
|
|
// Note: this is a small file (< 1024 bytes) so it's not worth
|
|
// starting an XML parser or even to use a buffered reader.
|
|
URL url = new URL(new File(userHome).toURI().toURL(),
|
|
".gconf/apps/metacity/general/%25gconf.xml");
|
|
// Pending: verify character encoding spec for gconf
|
|
Reader reader = new InputStreamReader(url.openStream(), "ISO-8859-1");
|
|
char[] buf = new char[1024];
|
|
StringBuffer strBuf = new StringBuffer();
|
|
int n;
|
|
while ((n = reader.read(buf)) >= 0) {
|
|
strBuf.append(buf, 0, n);
|
|
}
|
|
reader.close();
|
|
String str = strBuf.toString();
|
|
if (str != null) {
|
|
String strLowerCase = str.toLowerCase();
|
|
int i = strLowerCase.indexOf("<entry name=\"theme\"");
|
|
if (i >= 0) {
|
|
i = strLowerCase.indexOf("<stringvalue>", i);
|
|
if (i > 0) {
|
|
i += "<stringvalue>".length();
|
|
int i2 = str.indexOf("<", i);
|
|
return str.substring(i, i2);
|
|
}
|
|
}
|
|
}
|
|
} catch (MalformedURLException ex) {
|
|
// OK to just ignore. We'll use a fallback theme.
|
|
} catch (IOException ex) {
|
|
// OK to just ignore. We'll use a fallback theme.
|
|
}
|
|
return null;
|
|
} else if (type == GET_IMAGE) {
|
|
return new ImageIcon((URL)arg).getImage();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static URL getThemeDir(String themeName) {
|
|
return (URL)new Privileged().doPrivileged(Privileged.GET_THEME_DIR, themeName);
|
|
}
|
|
|
|
private static String getUserTheme() {
|
|
return (String)new Privileged().doPrivileged(Privileged.GET_USER_THEME, null);
|
|
}
|
|
|
|
protected void tileImage(Graphics g, Image image, int x0, int y0, int w, int h, float[] alphas) {
|
|
Graphics2D g2 = (Graphics2D)g;
|
|
Composite oldComp = g2.getComposite();
|
|
|
|
int sw = image.getWidth(null);
|
|
int sh = image.getHeight(null);
|
|
int y = y0;
|
|
while (y < y0 + h) {
|
|
sh = Math.min(sh, y0 + h - y);
|
|
int x = x0;
|
|
while (x < x0 + w) {
|
|
float f = (alphas.length - 1.0F) * x / (x0 + w);
|
|
int i = (int)f;
|
|
f -= (int)f;
|
|
float alpha = (1-f) * alphas[i];
|
|
if (i+1 < alphas.length) {
|
|
alpha += f * alphas[i+1];
|
|
}
|
|
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
|
|
int swm = Math.min(sw, x0 + w - x);
|
|
g.drawImage(image, x, y, x+swm, y+sh, 0, 0, swm, sh, null);
|
|
x += swm;
|
|
}
|
|
y += sh;
|
|
}
|
|
g2.setComposite(oldComp);
|
|
}
|
|
|
|
private HashMap<String, Image> images = new HashMap<String, Image>();
|
|
|
|
protected Image getImage(String key, Color c) {
|
|
Image image = images.get(key+"-"+c.getRGB());
|
|
if (image == null) {
|
|
image = imageFilter.colorize(getImage(key), c);
|
|
if (image != null) {
|
|
images.put(key+"-"+c.getRGB(), image);
|
|
}
|
|
}
|
|
return image;
|
|
}
|
|
|
|
protected Image getImage(String key) {
|
|
Image image = images.get(key);
|
|
if (image == null) {
|
|
if (themeDir != null) {
|
|
try {
|
|
URL url = new URL(themeDir, key);
|
|
image = (Image)new Privileged().doPrivileged(Privileged.GET_IMAGE, url);
|
|
} catch (MalformedURLException ex) {
|
|
//log("Bad image url: "+ themeDir + "/" + key);
|
|
}
|
|
}
|
|
if (image != null) {
|
|
images.put(key, image);
|
|
}
|
|
}
|
|
return image;
|
|
}
|
|
|
|
private class ColorizeImageFilter extends RGBImageFilter {
|
|
double cr, cg, cb;
|
|
|
|
public ColorizeImageFilter() {
|
|
canFilterIndexColorModel = true;
|
|
}
|
|
|
|
public void setColor(Color color) {
|
|
cr = color.getRed() / 255.0;
|
|
cg = color.getGreen() / 255.0;
|
|
cb = color.getBlue() / 255.0;
|
|
}
|
|
|
|
public Image colorize(Image fromImage, Color c) {
|
|
setColor(c);
|
|
ImageProducer producer = new FilteredImageSource(fromImage.getSource(), this);
|
|
return new ImageIcon(context.getComponent().createImage(producer)).getImage();
|
|
}
|
|
|
|
public int filterRGB(int x, int y, int rgb) {
|
|
// Assume all rgb values are shades of gray
|
|
double grayLevel = 2 * (rgb & 0xff) / 255.0;
|
|
double r, g, b;
|
|
|
|
if (grayLevel <= 1.0) {
|
|
r = cr * grayLevel;
|
|
g = cg * grayLevel;
|
|
b = cb * grayLevel;
|
|
} else {
|
|
grayLevel -= 1.0;
|
|
r = cr + (1.0 - cr) * grayLevel;
|
|
g = cg + (1.0 - cg) * grayLevel;
|
|
b = cb + (1.0 - cb) * grayLevel;
|
|
}
|
|
|
|
return ((rgb & 0xff000000) +
|
|
(((int)(r * 255)) << 16) +
|
|
(((int)(g * 255)) << 8) +
|
|
(int)(b * 255));
|
|
}
|
|
}
|
|
|
|
protected static JComponent findChild(JComponent parent, String name) {
|
|
int n = parent.getComponentCount();
|
|
for (int i = 0; i < n; i++) {
|
|
JComponent c = (JComponent)parent.getComponent(i);
|
|
if (name.equals(c.getName())) {
|
|
return c;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
protected class TitlePaneLayout implements LayoutManager {
|
|
public void addLayoutComponent(String name, Component c) {}
|
|
public void removeLayoutComponent(Component c) {}
|
|
public Dimension preferredLayoutSize(Container c) {
|
|
return minimumLayoutSize(c);
|
|
}
|
|
|
|
public Dimension minimumLayoutSize(Container c) {
|
|
JComponent titlePane = (JComponent)c;
|
|
Container titlePaneParent = titlePane.getParent();
|
|
JInternalFrame frame;
|
|
if (titlePaneParent instanceof JInternalFrame) {
|
|
frame = (JInternalFrame)titlePaneParent;
|
|
} else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) {
|
|
frame = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame();
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
Dimension buttonDim = calculateButtonSize(titlePane);
|
|
Insets title_border = (Insets)getFrameGeometry().get("title_border");
|
|
Insets button_border = (Insets)getFrameGeometry().get("button_border");
|
|
|
|
// Calculate width.
|
|
int width = getInt("left_titlebar_edge") + buttonDim.width + getInt("right_titlebar_edge");
|
|
if (title_border != null) {
|
|
width += title_border.left + title_border.right;
|
|
}
|
|
if (frame.isClosable()) {
|
|
width += buttonDim.width;
|
|
}
|
|
if (frame.isMaximizable()) {
|
|
width += buttonDim.width;
|
|
}
|
|
if (frame.isIconifiable()) {
|
|
width += buttonDim.width;
|
|
}
|
|
FontMetrics fm = frame.getFontMetrics(titlePane.getFont());
|
|
String frameTitle = frame.getTitle();
|
|
int title_w = frameTitle != null ? SwingUtilities2.stringWidth(
|
|
frame, fm, frameTitle) : 0;
|
|
int title_length = frameTitle != null ? frameTitle.length() : 0;
|
|
|
|
// Leave room for three characters in the title.
|
|
if (title_length > 3) {
|
|
int subtitle_w = SwingUtilities2.stringWidth(
|
|
frame, fm, frameTitle.substring(0, 3) + "...");
|
|
width += (title_w < subtitle_w) ? title_w : subtitle_w;
|
|
} else {
|
|
width += title_w;
|
|
}
|
|
|
|
// Calculate height.
|
|
int titleHeight = fm.getHeight() + getInt("title_vertical_pad");
|
|
if (title_border != null) {
|
|
titleHeight += title_border.top + title_border.bottom;
|
|
}
|
|
int buttonHeight = buttonDim.height;
|
|
if (button_border != null) {
|
|
buttonHeight += button_border.top + button_border.bottom;
|
|
}
|
|
int height = Math.max(buttonHeight, titleHeight);
|
|
|
|
return new Dimension(width, height);
|
|
}
|
|
|
|
public void layoutContainer(Container c) {
|
|
JComponent titlePane = (JComponent)c;
|
|
Container titlePaneParent = titlePane.getParent();
|
|
JInternalFrame frame;
|
|
if (titlePaneParent instanceof JInternalFrame) {
|
|
frame = (JInternalFrame)titlePaneParent;
|
|
} else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) {
|
|
frame = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame();
|
|
} else {
|
|
return;
|
|
}
|
|
Map gm = getFrameGeometry();
|
|
|
|
int w = titlePane.getWidth();
|
|
int h = titlePane.getHeight();
|
|
|
|
JComponent menuButton = findChild(titlePane, "InternalFrameTitlePane.menuButton");
|
|
JComponent minimizeButton = findChild(titlePane, "InternalFrameTitlePane.iconifyButton");
|
|
JComponent maximizeButton = findChild(titlePane, "InternalFrameTitlePane.maximizeButton");
|
|
JComponent closeButton = findChild(titlePane, "InternalFrameTitlePane.closeButton");
|
|
|
|
Insets button_border = (Insets)gm.get("button_border");
|
|
Dimension buttonDim = calculateButtonSize(titlePane);
|
|
|
|
int y = (button_border != null) ? button_border.top : 0;
|
|
if (titlePaneParent.getComponentOrientation().isLeftToRight()) {
|
|
int x = getInt("left_titlebar_edge");
|
|
|
|
menuButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
|
|
x = w - buttonDim.width - getInt("right_titlebar_edge");
|
|
if (button_border != null) {
|
|
x -= button_border.right;
|
|
}
|
|
|
|
if (frame.isClosable()) {
|
|
closeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
x -= buttonDim.width;
|
|
}
|
|
|
|
if (frame.isMaximizable()) {
|
|
maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
x -= buttonDim.width;
|
|
}
|
|
|
|
if (frame.isIconifiable()) {
|
|
minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
}
|
|
} else {
|
|
int x = w - buttonDim.width - getInt("right_titlebar_edge");
|
|
|
|
menuButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
|
|
x = getInt("left_titlebar_edge");
|
|
if (button_border != null) {
|
|
x += button_border.left;
|
|
}
|
|
|
|
if (frame.isClosable()) {
|
|
closeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
x += buttonDim.width;
|
|
}
|
|
|
|
if (frame.isMaximizable()) {
|
|
maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
x += buttonDim.width;
|
|
}
|
|
|
|
if (frame.isIconifiable()) {
|
|
minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
|
|
}
|
|
}
|
|
}
|
|
} // end TitlePaneLayout
|
|
|
|
protected Map getFrameGeometry() {
|
|
return frameGeometry;
|
|
}
|
|
|
|
protected void setFrameGeometry(JComponent titlePane, Map gm) {
|
|
this.frameGeometry = gm;
|
|
if (getInt("top_height") == 0 && titlePane != null) {
|
|
gm.put("top_height", Integer.valueOf(titlePane.getHeight()));
|
|
}
|
|
}
|
|
|
|
protected int getInt(String key) {
|
|
Integer i = (Integer)frameGeometry.get(key);
|
|
if (i == null) {
|
|
i = variables.get(key);
|
|
}
|
|
return (i != null) ? i.intValue() : 0;
|
|
}
|
|
|
|
protected boolean getBoolean(String key, boolean fallback) {
|
|
Boolean b = (Boolean)frameGeometry.get(key);
|
|
return (b != null) ? b.booleanValue() : fallback;
|
|
}
|
|
|
|
|
|
protected void drawArc(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
Color color = parseColor(getStringAttr(attrs, "color"));
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
int start_angle = aee.evaluate(getStringAttr(attrs, "start_angle"));
|
|
int extent_angle = aee.evaluate(getStringAttr(attrs, "extent_angle"));
|
|
boolean filled = getBooleanAttr(node, "filled", false);
|
|
if (getInt("width") == -1) {
|
|
x -= w;
|
|
}
|
|
if (getInt("height") == -1) {
|
|
y -= h;
|
|
}
|
|
g.setColor(color);
|
|
if (filled) {
|
|
g.fillArc(x, y, w, h, start_angle, extent_angle);
|
|
} else {
|
|
g.drawArc(x, y, w, h, start_angle, extent_angle);
|
|
}
|
|
}
|
|
|
|
protected void drawLine(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
Color color = parseColor(getStringAttr(attrs, "color"));
|
|
int x1 = aee.evaluate(getStringAttr(attrs, "x1"));
|
|
int y1 = aee.evaluate(getStringAttr(attrs, "y1"));
|
|
int x2 = aee.evaluate(getStringAttr(attrs, "x2"));
|
|
int y2 = aee.evaluate(getStringAttr(attrs, "y2"));
|
|
int lineWidth = aee.evaluate(getStringAttr(attrs, "width"), 1);
|
|
g.setColor(color);
|
|
if (lineWidth != 1) {
|
|
Graphics2D g2d = (Graphics2D)g;
|
|
Stroke stroke = g2d.getStroke();
|
|
g2d.setStroke(new BasicStroke((float)lineWidth));
|
|
g2d.drawLine(x1, y1, x2, y2);
|
|
g2d.setStroke(stroke);
|
|
} else {
|
|
g.drawLine(x1, y1, x2, y2);
|
|
}
|
|
}
|
|
|
|
protected void drawRectangle(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
Color color = parseColor(getStringAttr(attrs, "color"));
|
|
boolean filled = getBooleanAttr(node, "filled", false);
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
g.setColor(color);
|
|
if (getInt("width") == -1) {
|
|
x -= w;
|
|
}
|
|
if (getInt("height") == -1) {
|
|
y -= h;
|
|
}
|
|
if (filled) {
|
|
g.fillRect(x, y, w, h);
|
|
} else {
|
|
g.drawRect(x, y, w, h);
|
|
}
|
|
}
|
|
|
|
protected void drawTile(Node node, Graphics g, JInternalFrame jif) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
int x0 = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y0 = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
int tw = aee.evaluate(getStringAttr(attrs, "tile_width"));
|
|
int th = aee.evaluate(getStringAttr(attrs, "tile_height"));
|
|
int width = getInt("width");
|
|
int height = getInt("height");
|
|
if (width == -1) {
|
|
x0 -= w;
|
|
}
|
|
if (height == -1) {
|
|
y0 -= h;
|
|
}
|
|
Shape oldClip = g.getClip();
|
|
if (g instanceof Graphics2D) {
|
|
((Graphics2D)g).clip(new Rectangle(x0, y0, w, h));
|
|
}
|
|
variables.put("width", tw);
|
|
variables.put("height", th);
|
|
|
|
Node draw_ops = getNode("draw_ops", new String[] { "name", getStringAttr(node, "name") });
|
|
|
|
int y = y0;
|
|
while (y < y0 + h) {
|
|
int x = x0;
|
|
while (x < x0 + w) {
|
|
g.translate(x, y);
|
|
draw(draw_ops, g, jif);
|
|
g.translate(-x, -y);
|
|
x += tw;
|
|
}
|
|
y += th;
|
|
}
|
|
|
|
variables.put("width", width);
|
|
variables.put("height", height);
|
|
g.setClip(oldClip);
|
|
}
|
|
|
|
protected void drawTint(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
Color color = parseColor(getStringAttr(attrs, "color"));
|
|
float alpha = Float.parseFloat(getStringAttr(attrs, "alpha"));
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
if (getInt("width") == -1) {
|
|
x -= w;
|
|
}
|
|
if (getInt("height") == -1) {
|
|
y -= h;
|
|
}
|
|
if (g instanceof Graphics2D) {
|
|
Graphics2D g2 = (Graphics2D)g;
|
|
Composite oldComp = g2.getComposite();
|
|
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
|
|
g2.setComposite(ac);
|
|
g2.setColor(color);
|
|
g2.fillRect(x, y, w, h);
|
|
g2.setComposite(oldComp);
|
|
}
|
|
}
|
|
|
|
protected void drawTitle(Node node, Graphics g, JInternalFrame jif) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
String colorStr = getStringAttr(attrs, "color");
|
|
int i = colorStr.indexOf("gtk:fg[");
|
|
if (i > 0) {
|
|
colorStr = colorStr.substring(0, i) + "gtk:text[" + colorStr.substring(i+7);
|
|
}
|
|
Color color = parseColor(colorStr);
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
|
|
String title = jif.getTitle();
|
|
if (title != null) {
|
|
FontMetrics fm = SwingUtilities2.getFontMetrics(jif, g);
|
|
title = SwingUtilities2.clipStringIfNecessary(jif, fm, title,
|
|
calculateTitleArea(jif).width);
|
|
g.setColor(color);
|
|
SwingUtilities2.drawString(jif, g, title, x, y + fm.getAscent());
|
|
}
|
|
}
|
|
|
|
protected Dimension calculateButtonSize(JComponent titlePane) {
|
|
int buttonHeight = getInt("button_height");
|
|
if (buttonHeight == 0) {
|
|
buttonHeight = titlePane.getHeight();
|
|
if (buttonHeight == 0) {
|
|
buttonHeight = 13;
|
|
} else {
|
|
Insets button_border = (Insets)frameGeometry.get("button_border");
|
|
if (button_border != null) {
|
|
buttonHeight -= (button_border.top + button_border.bottom);
|
|
}
|
|
}
|
|
}
|
|
int buttonWidth = getInt("button_width");
|
|
if (buttonWidth == 0) {
|
|
buttonWidth = buttonHeight;
|
|
Float aspect_ratio = (Float)frameGeometry.get("aspect_ratio");
|
|
if (aspect_ratio != null) {
|
|
buttonWidth = (int)(buttonHeight / aspect_ratio.floatValue());
|
|
}
|
|
}
|
|
return new Dimension(buttonWidth, buttonHeight);
|
|
}
|
|
|
|
protected Rectangle calculateTitleArea(JInternalFrame jif) {
|
|
JComponent titlePane = findChild(jif, "InternalFrame.northPane");
|
|
Dimension buttonDim = calculateButtonSize(titlePane);
|
|
Insets title_border = (Insets)frameGeometry.get("title_border");
|
|
Insets button_border = (Insets)getFrameGeometry().get("button_border");
|
|
|
|
Rectangle r = new Rectangle();
|
|
r.x = getInt("left_titlebar_edge");
|
|
r.y = 0;
|
|
r.height = titlePane.getHeight();
|
|
if (title_border != null) {
|
|
r.x += title_border.left;
|
|
r.y += title_border.top;
|
|
r.height -= (title_border.top + title_border.bottom);
|
|
}
|
|
|
|
if (titlePane.getParent().getComponentOrientation().isLeftToRight()) {
|
|
r.x += buttonDim.width;
|
|
if (button_border != null) {
|
|
r.x += button_border.left;
|
|
}
|
|
r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge");
|
|
if (jif.isClosable()) {
|
|
r.width -= buttonDim.width;
|
|
}
|
|
if (jif.isMaximizable()) {
|
|
r.width -= buttonDim.width;
|
|
}
|
|
if (jif.isIconifiable()) {
|
|
r.width -= buttonDim.width;
|
|
}
|
|
} else {
|
|
if (jif.isClosable()) {
|
|
r.x += buttonDim.width;
|
|
}
|
|
if (jif.isMaximizable()) {
|
|
r.x += buttonDim.width;
|
|
}
|
|
if (jif.isIconifiable()) {
|
|
r.x += buttonDim.width;
|
|
}
|
|
r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge")
|
|
- buttonDim.width;
|
|
if (button_border != null) {
|
|
r.x -= button_border.right;
|
|
}
|
|
}
|
|
if (title_border != null) {
|
|
r.width -= title_border.right;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
protected int calculateTitleTextWidth(Graphics g, JInternalFrame jif) {
|
|
String title = jif.getTitle();
|
|
if (title != null) {
|
|
Rectangle r = calculateTitleArea(jif);
|
|
return Math.min(SwingUtilities2.stringWidth(jif,
|
|
SwingUtilities2.getFontMetrics(jif, g), title), r.width);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
protected void setClip(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
if (getInt("width") == -1) {
|
|
x -= w;
|
|
}
|
|
if (getInt("height") == -1) {
|
|
y -= h;
|
|
}
|
|
if (g instanceof Graphics2D) {
|
|
((Graphics2D)g).clip(new Rectangle(x, y, w, h));
|
|
}
|
|
}
|
|
|
|
protected void drawGTKArrow(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
String arrow = getStringAttr(attrs, "arrow");
|
|
String shadow = getStringAttr(attrs, "shadow");
|
|
String stateStr = getStringAttr(attrs, "state").toUpperCase();
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
|
|
int state = -1;
|
|
if ("NORMAL".equals(stateStr)) {
|
|
state = ENABLED;
|
|
} else if ("SELECTED".equals(stateStr)) {
|
|
state = SELECTED;
|
|
} else if ("INSENSITIVE".equals(stateStr)) {
|
|
state = DISABLED;
|
|
} else if ("PRELIGHT".equals(stateStr)) {
|
|
state = MOUSE_OVER;
|
|
}
|
|
|
|
ShadowType shadowType = null;
|
|
if ("in".equals(shadow)) {
|
|
shadowType = ShadowType.IN;
|
|
} else if ("out".equals(shadow)) {
|
|
shadowType = ShadowType.OUT;
|
|
} else if ("etched_in".equals(shadow)) {
|
|
shadowType = ShadowType.ETCHED_IN;
|
|
} else if ("etched_out".equals(shadow)) {
|
|
shadowType = ShadowType.ETCHED_OUT;
|
|
} else if ("none".equals(shadow)) {
|
|
shadowType = ShadowType.NONE;
|
|
}
|
|
|
|
ArrowType direction = null;
|
|
if ("up".equals(arrow)) {
|
|
direction = ArrowType.UP;
|
|
} else if ("down".equals(arrow)) {
|
|
direction = ArrowType.DOWN;
|
|
} else if ("left".equals(arrow)) {
|
|
direction = ArrowType.LEFT;
|
|
} else if ("right".equals(arrow)) {
|
|
direction = ArrowType.RIGHT;
|
|
}
|
|
|
|
GTKPainter.INSTANCE.paintMetacityElement(context, g, state,
|
|
"metacity-arrow", x, y, w, h, shadowType, direction);
|
|
}
|
|
|
|
protected void drawGTKBox(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
String shadow = getStringAttr(attrs, "shadow");
|
|
String stateStr = getStringAttr(attrs, "state").toUpperCase();
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
|
|
int state = -1;
|
|
if ("NORMAL".equals(stateStr)) {
|
|
state = ENABLED;
|
|
} else if ("SELECTED".equals(stateStr)) {
|
|
state = SELECTED;
|
|
} else if ("INSENSITIVE".equals(stateStr)) {
|
|
state = DISABLED;
|
|
} else if ("PRELIGHT".equals(stateStr)) {
|
|
state = MOUSE_OVER;
|
|
}
|
|
|
|
ShadowType shadowType = null;
|
|
if ("in".equals(shadow)) {
|
|
shadowType = ShadowType.IN;
|
|
} else if ("out".equals(shadow)) {
|
|
shadowType = ShadowType.OUT;
|
|
} else if ("etched_in".equals(shadow)) {
|
|
shadowType = ShadowType.ETCHED_IN;
|
|
} else if ("etched_out".equals(shadow)) {
|
|
shadowType = ShadowType.ETCHED_OUT;
|
|
} else if ("none".equals(shadow)) {
|
|
shadowType = ShadowType.NONE;
|
|
}
|
|
GTKPainter.INSTANCE.paintMetacityElement(context, g, state,
|
|
"metacity-box", x, y, w, h, shadowType, null);
|
|
}
|
|
|
|
protected void drawGTKVLine(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
String stateStr = getStringAttr(attrs, "state").toUpperCase();
|
|
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y1 = aee.evaluate(getStringAttr(attrs, "y1"));
|
|
int y2 = aee.evaluate(getStringAttr(attrs, "y2"));
|
|
|
|
int state = -1;
|
|
if ("NORMAL".equals(stateStr)) {
|
|
state = ENABLED;
|
|
} else if ("SELECTED".equals(stateStr)) {
|
|
state = SELECTED;
|
|
} else if ("INSENSITIVE".equals(stateStr)) {
|
|
state = DISABLED;
|
|
} else if ("PRELIGHT".equals(stateStr)) {
|
|
state = MOUSE_OVER;
|
|
}
|
|
|
|
GTKPainter.INSTANCE.paintMetacityElement(context, g, state,
|
|
"metacity-vline", x, y1, 1, y2 - y1, null, null);
|
|
}
|
|
|
|
protected void drawGradient(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
String type = getStringAttr(attrs, "type");
|
|
float alpha = getFloatAttr(node, "alpha", -1F);
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
if (getInt("width") == -1) {
|
|
x -= w;
|
|
}
|
|
if (getInt("height") == -1) {
|
|
y -= h;
|
|
}
|
|
|
|
// Get colors from child nodes
|
|
Node[] colorNodes = getNodesByName(node, "color");
|
|
Color[] colors = new Color[colorNodes.length];
|
|
for (int i = 0; i < colorNodes.length; i++) {
|
|
colors[i] = parseColor(getStringAttr(colorNodes[i], "value"));
|
|
}
|
|
|
|
boolean horizontal = ("diagonal".equals(type) || "horizontal".equals(type));
|
|
boolean vertical = ("diagonal".equals(type) || "vertical".equals(type));
|
|
|
|
if (g instanceof Graphics2D) {
|
|
Graphics2D g2 = (Graphics2D)g;
|
|
Composite oldComp = g2.getComposite();
|
|
if (alpha >= 0F) {
|
|
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
|
|
}
|
|
int n = colors.length - 1;
|
|
for (int i = 0; i < n; i++) {
|
|
g2.setPaint(new GradientPaint(x + (horizontal ? (i*w/n) : 0),
|
|
y + (vertical ? (i*h/n) : 0),
|
|
colors[i],
|
|
x + (horizontal ? ((i+1)*w/n) : 0),
|
|
y + (vertical ? ((i+1)*h/n) : 0),
|
|
colors[i+1]));
|
|
g2.fillRect(x + (horizontal ? (i*w/n) : 0),
|
|
y + (vertical ? (i*h/n) : 0),
|
|
(horizontal ? (w/n) : w),
|
|
(vertical ? (h/n) : h));
|
|
}
|
|
g2.setComposite(oldComp);
|
|
}
|
|
}
|
|
|
|
protected void drawImage(Node node, Graphics g) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
String filename = getStringAttr(attrs, "filename");
|
|
String colorizeStr = getStringAttr(attrs, "colorize");
|
|
Color colorize = (colorizeStr != null) ? parseColor(colorizeStr) : null;
|
|
String alpha = getStringAttr(attrs, "alpha");
|
|
Image object = (colorize != null) ? getImage(filename, colorize) : getImage(filename);
|
|
variables.put("object_width", object.getWidth(null));
|
|
variables.put("object_height", object.getHeight(null));
|
|
String fill_type = getStringAttr(attrs, "fill_type");
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
if (getInt("width") == -1) {
|
|
x -= w;
|
|
}
|
|
if (getInt("height") == -1) {
|
|
y -= h;
|
|
}
|
|
|
|
if (alpha != null) {
|
|
if ("tile".equals(fill_type)) {
|
|
StringTokenizer tokenizer = new StringTokenizer(alpha, ":");
|
|
float[] alphas = new float[tokenizer.countTokens()];
|
|
for (int i = 0; i < alphas.length; i++) {
|
|
alphas[i] = Float.parseFloat(tokenizer.nextToken());
|
|
}
|
|
tileImage(g, object, x, y, w, h, alphas);
|
|
} else {
|
|
float a = Float.parseFloat(alpha);
|
|
if (g instanceof Graphics2D) {
|
|
Graphics2D g2 = (Graphics2D)g;
|
|
Composite oldComp = g2.getComposite();
|
|
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a));
|
|
g2.drawImage(object, x, y, w, h, null);
|
|
g2.setComposite(oldComp);
|
|
}
|
|
}
|
|
} else {
|
|
g.drawImage(object, x, y, w, h, null);
|
|
}
|
|
}
|
|
|
|
protected void drawIcon(Node node, Graphics g, JInternalFrame jif) {
|
|
Icon icon = jif.getFrameIcon();
|
|
if (icon == null) {
|
|
return;
|
|
}
|
|
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
String alpha = getStringAttr(attrs, "alpha");
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"));
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"));
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"));
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"));
|
|
if (getInt("width") == -1) {
|
|
x -= w;
|
|
}
|
|
if (getInt("height") == -1) {
|
|
y -= h;
|
|
}
|
|
|
|
if (alpha != null) {
|
|
float a = Float.parseFloat(alpha);
|
|
if (g instanceof Graphics2D) {
|
|
Graphics2D g2 = (Graphics2D)g;
|
|
Composite oldComp = g2.getComposite();
|
|
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a));
|
|
icon.paintIcon(jif, g, x, y);
|
|
g2.setComposite(oldComp);
|
|
}
|
|
} else {
|
|
icon.paintIcon(jif, g, x, y);
|
|
}
|
|
}
|
|
|
|
protected void drawInclude(Node node, Graphics g, JInternalFrame jif) {
|
|
int oldWidth = getInt("width");
|
|
int oldHeight = getInt("height");
|
|
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
int x = aee.evaluate(getStringAttr(attrs, "x"), 0);
|
|
int y = aee.evaluate(getStringAttr(attrs, "y"), 0);
|
|
int w = aee.evaluate(getStringAttr(attrs, "width"), -1);
|
|
int h = aee.evaluate(getStringAttr(attrs, "height"), -1);
|
|
|
|
if (w != -1) {
|
|
variables.put("width", w);
|
|
}
|
|
if (h != -1) {
|
|
variables.put("height", h);
|
|
}
|
|
|
|
Node draw_ops = getNode("draw_ops", new String[] {
|
|
"name", getStringAttr(node, "name")
|
|
});
|
|
g.translate(x, y);
|
|
draw(draw_ops, g, jif);
|
|
g.translate(-x, -y);
|
|
|
|
if (w != -1) {
|
|
variables.put("width", oldWidth);
|
|
}
|
|
if (h != -1) {
|
|
variables.put("height", oldHeight);
|
|
}
|
|
}
|
|
|
|
protected void draw(Node draw_ops, Graphics g, JInternalFrame jif) {
|
|
if (draw_ops != null) {
|
|
NodeList nodes = draw_ops.getChildNodes();
|
|
if (nodes != null) {
|
|
Shape oldClip = g.getClip();
|
|
for (int i = 0; i < nodes.getLength(); i++) {
|
|
Node child = nodes.item(i);
|
|
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
|
try {
|
|
String name = child.getNodeName();
|
|
if ("include".equals(name)) {
|
|
drawInclude(child, g, jif);
|
|
} else if ("arc".equals(name)) {
|
|
drawArc(child, g);
|
|
} else if ("clip".equals(name)) {
|
|
setClip(child, g);
|
|
} else if ("gradient".equals(name)) {
|
|
drawGradient(child, g);
|
|
} else if ("gtk_arrow".equals(name)) {
|
|
drawGTKArrow(child, g);
|
|
} else if ("gtk_box".equals(name)) {
|
|
drawGTKBox(child, g);
|
|
} else if ("gtk_vline".equals(name)) {
|
|
drawGTKVLine(child, g);
|
|
} else if ("image".equals(name)) {
|
|
drawImage(child, g);
|
|
} else if ("icon".equals(name)) {
|
|
drawIcon(child, g, jif);
|
|
} else if ("line".equals(name)) {
|
|
drawLine(child, g);
|
|
} else if ("rectangle".equals(name)) {
|
|
drawRectangle(child, g);
|
|
} else if ("tint".equals(name)) {
|
|
drawTint(child, g);
|
|
} else if ("tile".equals(name)) {
|
|
drawTile(child, g, jif);
|
|
} else if ("title".equals(name)) {
|
|
drawTitle(child, g, jif);
|
|
} else {
|
|
System.err.println("Unknown Metacity drawing op: "+child);
|
|
}
|
|
} catch (NumberFormatException ex) {
|
|
logError(themeName, ex);
|
|
}
|
|
}
|
|
}
|
|
g.setClip(oldClip);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void drawPiece(Node frame_style, Graphics g, String position, int x, int y,
|
|
int width, int height, JInternalFrame jif) {
|
|
Node piece = getNode(frame_style, "piece", new String[] { "position", position });
|
|
if (piece != null) {
|
|
Node draw_ops;
|
|
String draw_ops_name = getStringAttr(piece, "draw_ops");
|
|
if (draw_ops_name != null) {
|
|
draw_ops = getNode("draw_ops", new String[] { "name", draw_ops_name });
|
|
} else {
|
|
draw_ops = getNode(piece, "draw_ops", null);
|
|
}
|
|
variables.put("width", width);
|
|
variables.put("height", height);
|
|
g.translate(x, y);
|
|
draw(draw_ops, g, jif);
|
|
g.translate(-x, -y);
|
|
}
|
|
}
|
|
|
|
|
|
Insets getBorderInsets(SynthContext context, Insets insets) {
|
|
updateFrameGeometry(context);
|
|
|
|
if (insets == null) {
|
|
insets = new Insets(0, 0, 0, 0);
|
|
}
|
|
insets.top = ((Insets)frameGeometry.get("title_border")).top;
|
|
insets.bottom = getInt("bottom_height");
|
|
insets.left = getInt("left_width");
|
|
insets.right = getInt("right_width");
|
|
return insets;
|
|
}
|
|
|
|
|
|
private void updateFrameGeometry(SynthContext context) {
|
|
this.context = context;
|
|
JComponent comp = context.getComponent();
|
|
JComponent titlePane = findChild(comp, "InternalFrame.northPane");
|
|
|
|
JInternalFrame jif = null;
|
|
if (comp instanceof JInternalFrame) {
|
|
jif = (JInternalFrame)comp;
|
|
} else if (comp instanceof JInternalFrame.JDesktopIcon) {
|
|
jif = ((JInternalFrame.JDesktopIcon)comp).getInternalFrame();
|
|
} else {
|
|
assert false : "component is not JInternalFrame or JInternalFrame.JDesktopIcon";
|
|
return;
|
|
}
|
|
|
|
if (frame_style_set == null) {
|
|
Node window = getNode("window", new String[]{"type", "normal"});
|
|
|
|
if (window != null) {
|
|
frame_style_set = getNode("frame_style_set",
|
|
new String[] {"name", getStringAttr(window, "style_set")});
|
|
}
|
|
|
|
if (frame_style_set == null) {
|
|
frame_style_set = getNode("frame_style_set", new String[] {"name", "normal"});
|
|
}
|
|
}
|
|
|
|
if (frame_style_set != null) {
|
|
Node frame = getNode(frame_style_set, "frame", new String[] {
|
|
"focus", (jif.isSelected() ? "yes" : "no"),
|
|
"state", (jif.isMaximum() ? "maximized" : "normal")
|
|
});
|
|
|
|
if (frame != null) {
|
|
Node frame_style = getNode("frame_style", new String[] {
|
|
"name", getStringAttr(frame, "style")
|
|
});
|
|
if (frame_style != null) {
|
|
Map gm = frameGeometries.get(getStringAttr(frame_style, "geometry"));
|
|
|
|
setFrameGeometry(titlePane, gm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
protected static void logError(String themeName, Exception ex) {
|
|
logError(themeName, ex.toString());
|
|
}
|
|
|
|
protected static void logError(String themeName, String msg) {
|
|
if (!errorLogged) {
|
|
System.err.println("Exception in Metacity for theme \""+themeName+"\": "+msg);
|
|
errorLogged = true;
|
|
}
|
|
}
|
|
|
|
|
|
// XML Parsing
|
|
|
|
|
|
protected static Document getXMLDoc(final URL xmlFile)
|
|
throws IOException,
|
|
ParserConfigurationException,
|
|
SAXException {
|
|
if (documentBuilder == null) {
|
|
documentBuilder =
|
|
DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
|
}
|
|
InputStream inputStream =
|
|
AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
|
|
public InputStream run() {
|
|
try {
|
|
return new BufferedInputStream(xmlFile.openStream());
|
|
} catch (IOException ex) {
|
|
return null;
|
|
}
|
|
}
|
|
});
|
|
|
|
Document doc = null;
|
|
if (inputStream != null) {
|
|
doc = documentBuilder.parse(inputStream);
|
|
}
|
|
return doc;
|
|
}
|
|
|
|
|
|
protected Node[] getNodesByName(Node parent, String name) {
|
|
NodeList nodes = parent.getChildNodes(); // ElementNode
|
|
int n = nodes.getLength();
|
|
ArrayList<Node> list = new ArrayList<Node>();
|
|
for (int i=0; i < n; i++) {
|
|
Node node = nodes.item(i);
|
|
if (name.equals(node.getNodeName())) {
|
|
list.add(node);
|
|
}
|
|
}
|
|
return list.toArray(new Node[list.size()]);
|
|
}
|
|
|
|
|
|
|
|
protected Node getNode(String tagName, String[] attrs) {
|
|
NodeList nodes = xmlDoc.getElementsByTagName(tagName);
|
|
return (nodes != null) ? getNode(nodes, tagName, attrs) : null;
|
|
}
|
|
|
|
protected Node getNode(Node parent, String name, String[] attrs) {
|
|
Node node = null;
|
|
NodeList nodes = parent.getChildNodes();
|
|
if (nodes != null) {
|
|
node = getNode(nodes, name, attrs);
|
|
}
|
|
if (node == null) {
|
|
String inheritFrom = getStringAttr(parent, "parent");
|
|
if (inheritFrom != null) {
|
|
Node inheritFromNode = getNode(parent.getParentNode(),
|
|
parent.getNodeName(),
|
|
new String[] { "name", inheritFrom });
|
|
if (inheritFromNode != null) {
|
|
node = getNode(inheritFromNode, name, attrs);
|
|
}
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
protected Node getNode(NodeList nodes, String name, String[] attrs) {
|
|
int n = nodes.getLength();
|
|
for (int i=0; i < n; i++) {
|
|
Node node = nodes.item(i);
|
|
if (name.equals(node.getNodeName())) {
|
|
if (attrs != null) {
|
|
NamedNodeMap nodeAttrs = node.getAttributes();
|
|
if (nodeAttrs != null) {
|
|
boolean matches = true;
|
|
int nAttrs = attrs.length / 2;
|
|
for (int a = 0; a < nAttrs; a++) {
|
|
String aName = attrs[a * 2];
|
|
String aValue = attrs[a * 2 + 1];
|
|
Node attr = nodeAttrs.getNamedItem(aName);
|
|
if (attr == null ||
|
|
aValue != null && !aValue.equals(attr.getNodeValue())) {
|
|
matches = false;
|
|
break;
|
|
}
|
|
}
|
|
if (matches) {
|
|
return node;
|
|
}
|
|
}
|
|
} else {
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected String getStringAttr(Node node, String name) {
|
|
String value = null;
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
if (attrs != null) {
|
|
value = getStringAttr(attrs, name);
|
|
if (value == null) {
|
|
String inheritFrom = getStringAttr(attrs, "parent");
|
|
if (inheritFrom != null) {
|
|
Node inheritFromNode = getNode(node.getParentNode(),
|
|
node.getNodeName(),
|
|
new String[] { "name", inheritFrom });
|
|
if (inheritFromNode != null) {
|
|
value = getStringAttr(inheritFromNode, name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
protected String getStringAttr(NamedNodeMap attrs, String name) {
|
|
Node item = attrs.getNamedItem(name);
|
|
return (item != null) ? item.getNodeValue() : null;
|
|
}
|
|
|
|
protected boolean getBooleanAttr(Node node, String name, boolean fallback) {
|
|
String str = getStringAttr(node, name);
|
|
if (str != null) {
|
|
return Boolean.valueOf(str).booleanValue();
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
protected int getIntAttr(Node node, String name, int fallback) {
|
|
String str = getStringAttr(node, name);
|
|
int value = fallback;
|
|
if (str != null) {
|
|
try {
|
|
value = Integer.parseInt(str);
|
|
} catch (NumberFormatException ex) {
|
|
logError(themeName, ex);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
protected float getFloatAttr(Node node, String name, float fallback) {
|
|
String str = getStringAttr(node, name);
|
|
float value = fallback;
|
|
if (str != null) {
|
|
try {
|
|
value = Float.parseFloat(str);
|
|
} catch (NumberFormatException ex) {
|
|
logError(themeName, ex);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
|
|
|
|
protected Color parseColor(String str) {
|
|
StringTokenizer tokenizer = new StringTokenizer(str, "/");
|
|
int n = tokenizer.countTokens();
|
|
if (n > 1) {
|
|
String function = tokenizer.nextToken();
|
|
if ("shade".equals(function)) {
|
|
assert (n == 3);
|
|
Color c = parseColor2(tokenizer.nextToken());
|
|
float alpha = Float.parseFloat(tokenizer.nextToken());
|
|
return GTKColorType.adjustColor(c, 1.0F, alpha, alpha);
|
|
} else if ("blend".equals(function)) {
|
|
assert (n == 4);
|
|
Color bg = parseColor2(tokenizer.nextToken());
|
|
Color fg = parseColor2(tokenizer.nextToken());
|
|
float alpha = Float.parseFloat(tokenizer.nextToken());
|
|
if (alpha > 1.0f) {
|
|
alpha = 1.0f / alpha;
|
|
}
|
|
|
|
return new Color((int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)),
|
|
(int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)),
|
|
(int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)));
|
|
} else {
|
|
System.err.println("Unknown Metacity color function="+str);
|
|
return null;
|
|
}
|
|
} else {
|
|
return parseColor2(str);
|
|
}
|
|
}
|
|
|
|
protected Color parseColor2(String str) {
|
|
Color c = null;
|
|
if (str.startsWith("gtk:")) {
|
|
int i1 = str.indexOf('[');
|
|
if (i1 > 3) {
|
|
String typeStr = str.substring(4, i1).toLowerCase();
|
|
int i2 = str.indexOf(']');
|
|
if (i2 > i1+1) {
|
|
String stateStr = str.substring(i1+1, i2).toUpperCase();
|
|
int state = -1;
|
|
if ("ACTIVE".equals(stateStr)) {
|
|
state = PRESSED;
|
|
} else if ("INSENSITIVE".equals(stateStr)) {
|
|
state = DISABLED;
|
|
} else if ("NORMAL".equals(stateStr)) {
|
|
state = ENABLED;
|
|
} else if ("PRELIGHT".equals(stateStr)) {
|
|
state = MOUSE_OVER;
|
|
} else if ("SELECTED".equals(stateStr)) {
|
|
state = SELECTED;
|
|
}
|
|
ColorType type = null;
|
|
if ("fg".equals(typeStr)) {
|
|
type = GTKColorType.FOREGROUND;
|
|
} else if ("bg".equals(typeStr)) {
|
|
type = GTKColorType.BACKGROUND;
|
|
} else if ("base".equals(typeStr)) {
|
|
type = GTKColorType.TEXT_BACKGROUND;
|
|
} else if ("text".equals(typeStr)) {
|
|
type = GTKColorType.TEXT_FOREGROUND;
|
|
} else if ("dark".equals(typeStr)) {
|
|
type = GTKColorType.DARK;
|
|
} else if ("light".equals(typeStr)) {
|
|
type = GTKColorType.LIGHT;
|
|
}
|
|
if (state >= 0 && type != null) {
|
|
c = ((GTKStyle)context.getStyle()).getGTKColor(context, state, type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (c == null) {
|
|
c = parseColorString(str);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
private static Color parseColorString(String str) {
|
|
if (str.charAt(0) == '#') {
|
|
str = str.substring(1);
|
|
|
|
int i = str.length();
|
|
|
|
if (i < 3 || i > 12 || (i % 3) != 0) {
|
|
return null;
|
|
}
|
|
|
|
i /= 3;
|
|
|
|
int r;
|
|
int g;
|
|
int b;
|
|
|
|
try {
|
|
r = Integer.parseInt(str.substring(0, i), 16);
|
|
g = Integer.parseInt(str.substring(i, i * 2), 16);
|
|
b = Integer.parseInt(str.substring(i * 2, i * 3), 16);
|
|
} catch (NumberFormatException nfe) {
|
|
return null;
|
|
}
|
|
|
|
if (i == 4) {
|
|
return new ColorUIResource(r / 65535.0f, g / 65535.0f, b / 65535.0f);
|
|
} else if (i == 1) {
|
|
return new ColorUIResource(r / 15.0f, g / 15.0f, b / 15.0f);
|
|
} else if (i == 2) {
|
|
return new ColorUIResource(r, g, b);
|
|
} else {
|
|
return new ColorUIResource(r / 4095.0f, g / 4095.0f, b / 4095.0f);
|
|
}
|
|
} else {
|
|
return XColors.lookupColor(str);
|
|
}
|
|
}
|
|
|
|
class ArithmeticExpressionEvaluator {
|
|
private PeekableStringTokenizer tokenizer;
|
|
|
|
int evaluate(String expr) {
|
|
tokenizer = new PeekableStringTokenizer(expr, " \t+-*/%()", true);
|
|
return Math.round(expression());
|
|
}
|
|
|
|
int evaluate(String expr, int fallback) {
|
|
return (expr != null) ? evaluate(expr) : fallback;
|
|
}
|
|
|
|
public float expression() {
|
|
float value = getTermValue();
|
|
boolean done = false;
|
|
while (!done && tokenizer.hasMoreTokens()) {
|
|
String next = tokenizer.peek();
|
|
if ("+".equals(next) ||
|
|
"-".equals(next) ||
|
|
"`max`".equals(next) ||
|
|
"`min`".equals(next)) {
|
|
tokenizer.nextToken();
|
|
float value2 = getTermValue();
|
|
if ("+".equals(next)) {
|
|
value += value2;
|
|
} else if ("-".equals(next)) {
|
|
value -= value2;
|
|
} else if ("`max`".equals(next)) {
|
|
value = Math.max(value, value2);
|
|
} else if ("`min`".equals(next)) {
|
|
value = Math.min(value, value2);
|
|
}
|
|
} else {
|
|
done = true;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
public float getTermValue() {
|
|
float value = getFactorValue();
|
|
boolean done = false;
|
|
while (!done && tokenizer.hasMoreTokens()) {
|
|
String next = tokenizer.peek();
|
|
if ("*".equals(next) || "/".equals(next) || "%".equals(next)) {
|
|
tokenizer.nextToken();
|
|
float value2 = getFactorValue();
|
|
if ("*".equals(next)) {
|
|
value *= value2;
|
|
} else if ("/".equals(next)) {
|
|
value /= value2;
|
|
} else {
|
|
value %= value2;
|
|
}
|
|
} else {
|
|
done = true;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
public float getFactorValue() {
|
|
float value;
|
|
if ("(".equals(tokenizer.peek())) {
|
|
tokenizer.nextToken();
|
|
value = expression();
|
|
tokenizer.nextToken(); // skip right paren
|
|
} else {
|
|
String token = tokenizer.nextToken();
|
|
if (Character.isDigit(token.charAt(0))) {
|
|
value = Float.parseFloat(token);
|
|
} else {
|
|
Integer i = variables.get(token);
|
|
if (i == null) {
|
|
i = (Integer)getFrameGeometry().get(token);
|
|
}
|
|
if (i == null) {
|
|
logError(themeName, "Variable \"" + token + "\" not defined");
|
|
return 0;
|
|
}
|
|
value = (i != null) ? i.intValue() : 0F;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
static class PeekableStringTokenizer extends StringTokenizer {
|
|
String token = null;
|
|
|
|
public PeekableStringTokenizer(String str, String delim,
|
|
boolean returnDelims) {
|
|
super(str, delim, returnDelims);
|
|
peek();
|
|
}
|
|
|
|
public String peek() {
|
|
if (token == null) {
|
|
token = nextToken();
|
|
}
|
|
return token;
|
|
}
|
|
|
|
public boolean hasMoreTokens() {
|
|
return (token != null || super.hasMoreTokens());
|
|
}
|
|
|
|
public String nextToken() {
|
|
if (token != null) {
|
|
String t = token;
|
|
token = null;
|
|
if (hasMoreTokens()) {
|
|
peek();
|
|
}
|
|
return t;
|
|
} else {
|
|
String token = super.nextToken();
|
|
while ((token.equals(" ") || token.equals("\t"))
|
|
&& hasMoreTokens()) {
|
|
token = super.nextToken();
|
|
}
|
|
return token;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static class RoundRectClipShape extends RectangularShape {
|
|
static final int TOP_LEFT = 1;
|
|
static final int TOP_RIGHT = 2;
|
|
static final int BOTTOM_LEFT = 4;
|
|
static final int BOTTOM_RIGHT = 8;
|
|
|
|
int x;
|
|
int y;
|
|
int width;
|
|
int height;
|
|
int arcwidth;
|
|
int archeight;
|
|
int corners;
|
|
|
|
public RoundRectClipShape() {
|
|
}
|
|
|
|
public RoundRectClipShape(int x, int y, int w, int h,
|
|
int arcw, int arch, int corners) {
|
|
setRoundedRect(x, y, w, h, arcw, arch, corners);
|
|
}
|
|
|
|
public void setRoundedRect(int x, int y, int w, int h,
|
|
int arcw, int arch, int corners) {
|
|
this.corners = corners;
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = w;
|
|
this.height = h;
|
|
this.arcwidth = arcw;
|
|
this.archeight = arch;
|
|
}
|
|
|
|
public double getX() {
|
|
return (double)x;
|
|
}
|
|
|
|
public double getY() {
|
|
return (double)y;
|
|
}
|
|
|
|
public double getWidth() {
|
|
return (double)width;
|
|
}
|
|
|
|
public double getHeight() {
|
|
return (double)height;
|
|
}
|
|
|
|
public double getArcWidth() {
|
|
return (double)arcwidth;
|
|
}
|
|
|
|
public double getArcHeight() {
|
|
return (double)archeight;
|
|
}
|
|
|
|
public boolean isEmpty() {
|
|
return false; // Not called
|
|
}
|
|
|
|
public Rectangle2D getBounds2D() {
|
|
return null; // Not called
|
|
}
|
|
|
|
public int getCornerFlags() {
|
|
return corners;
|
|
}
|
|
|
|
public void setFrame(double x, double y, double w, double h) {
|
|
// Not called
|
|
}
|
|
|
|
public boolean contains(double x, double y) {
|
|
return false; // Not called
|
|
}
|
|
|
|
private int classify(double coord, double left, double right, double arcsize) {
|
|
return 0; // Not called
|
|
}
|
|
|
|
public boolean intersects(double x, double y, double w, double h) {
|
|
return false; // Not called
|
|
}
|
|
|
|
public boolean contains(double x, double y, double w, double h) {
|
|
return false; // Not called
|
|
}
|
|
|
|
public PathIterator getPathIterator(AffineTransform at) {
|
|
return new RoundishRectIterator(this, at);
|
|
}
|
|
|
|
|
|
static class RoundishRectIterator implements PathIterator {
|
|
double x, y, w, h, aw, ah;
|
|
AffineTransform affine;
|
|
int index;
|
|
|
|
double ctrlpts[][];
|
|
int types[];
|
|
|
|
private static final double angle = Math.PI / 4.0;
|
|
private static final double a = 1.0 - Math.cos(angle);
|
|
private static final double b = Math.tan(angle);
|
|
private static final double c = Math.sqrt(1.0 + b * b) - 1 + a;
|
|
private static final double cv = 4.0 / 3.0 * a * b / c;
|
|
private static final double acv = (1.0 - cv) / 2.0;
|
|
|
|
// For each array:
|
|
// 4 values for each point {v0, v1, v2, v3}:
|
|
// point = (x + v0 * w + v1 * arcWidth,
|
|
// y + v2 * h + v3 * arcHeight);
|
|
private static final double CtrlPtTemplate[][] = {
|
|
{ 0.0, 0.0, 1.0, 0.0 }, /* BOTTOM LEFT corner */
|
|
{ 0.0, 0.0, 1.0, -0.5 }, /* BOTTOM LEFT arc start */
|
|
{ 0.0, 0.0, 1.0, -acv, /* BOTTOM LEFT arc curve */
|
|
0.0, acv, 1.0, 0.0,
|
|
0.0, 0.5, 1.0, 0.0 },
|
|
{ 1.0, 0.0, 1.0, 0.0 }, /* BOTTOM RIGHT corner */
|
|
{ 1.0, -0.5, 1.0, 0.0 }, /* BOTTOM RIGHT arc start */
|
|
{ 1.0, -acv, 1.0, 0.0, /* BOTTOM RIGHT arc curve */
|
|
1.0, 0.0, 1.0, -acv,
|
|
1.0, 0.0, 1.0, -0.5 },
|
|
{ 1.0, 0.0, 0.0, 0.0 }, /* TOP RIGHT corner */
|
|
{ 1.0, 0.0, 0.0, 0.5 }, /* TOP RIGHT arc start */
|
|
{ 1.0, 0.0, 0.0, acv, /* TOP RIGHT arc curve */
|
|
1.0, -acv, 0.0, 0.0,
|
|
1.0, -0.5, 0.0, 0.0 },
|
|
{ 0.0, 0.0, 0.0, 0.0 }, /* TOP LEFT corner */
|
|
{ 0.0, 0.5, 0.0, 0.0 }, /* TOP LEFT arc start */
|
|
{ 0.0, acv, 0.0, 0.0, /* TOP LEFT arc curve */
|
|
0.0, 0.0, 0.0, acv,
|
|
0.0, 0.0, 0.0, 0.5 },
|
|
{}, /* Closing path element */
|
|
};
|
|
private static final int CornerFlags[] = {
|
|
RoundRectClipShape.BOTTOM_LEFT,
|
|
RoundRectClipShape.BOTTOM_RIGHT,
|
|
RoundRectClipShape.TOP_RIGHT,
|
|
RoundRectClipShape.TOP_LEFT,
|
|
};
|
|
|
|
RoundishRectIterator(RoundRectClipShape rr, AffineTransform at) {
|
|
this.x = rr.getX();
|
|
this.y = rr.getY();
|
|
this.w = rr.getWidth();
|
|
this.h = rr.getHeight();
|
|
this.aw = Math.min(w, Math.abs(rr.getArcWidth()));
|
|
this.ah = Math.min(h, Math.abs(rr.getArcHeight()));
|
|
this.affine = at;
|
|
if (w < 0 || h < 0) {
|
|
// Don't draw anything...
|
|
ctrlpts = new double[0][];
|
|
types = new int[0];
|
|
} else {
|
|
int corners = rr.getCornerFlags();
|
|
int numedges = 5; // 4xCORNER_POINT, CLOSE
|
|
for (int i = 1; i < 0x10; i <<= 1) {
|
|
// Add one for each corner that has a curve
|
|
if ((corners & i) != 0) numedges++;
|
|
}
|
|
ctrlpts = new double[numedges][];
|
|
types = new int[numedges];
|
|
int j = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
types[j] = SEG_LINETO;
|
|
if ((corners & CornerFlags[i]) == 0) {
|
|
ctrlpts[j++] = CtrlPtTemplate[i*3+0];
|
|
} else {
|
|
ctrlpts[j++] = CtrlPtTemplate[i*3+1];
|
|
types[j] = SEG_CUBICTO;
|
|
ctrlpts[j++] = CtrlPtTemplate[i*3+2];
|
|
}
|
|
}
|
|
types[j] = SEG_CLOSE;
|
|
ctrlpts[j++] = CtrlPtTemplate[12];
|
|
types[0] = SEG_MOVETO;
|
|
}
|
|
}
|
|
|
|
public int getWindingRule() {
|
|
return WIND_NON_ZERO;
|
|
}
|
|
|
|
public boolean isDone() {
|
|
return index >= ctrlpts.length;
|
|
}
|
|
|
|
public void next() {
|
|
index++;
|
|
}
|
|
|
|
public int currentSegment(float[] coords) {
|
|
if (isDone()) {
|
|
throw new NoSuchElementException("roundrect iterator out of bounds");
|
|
}
|
|
double ctrls[] = ctrlpts[index];
|
|
int nc = 0;
|
|
for (int i = 0; i < ctrls.length; i += 4) {
|
|
coords[nc++] = (float) (x + ctrls[i + 0] * w + ctrls[i + 1] * aw);
|
|
coords[nc++] = (float) (y + ctrls[i + 2] * h + ctrls[i + 3] * ah);
|
|
}
|
|
if (affine != null) {
|
|
affine.transform(coords, 0, coords, 0, nc / 2);
|
|
}
|
|
return types[index];
|
|
}
|
|
|
|
public int currentSegment(double[] coords) {
|
|
if (isDone()) {
|
|
throw new NoSuchElementException("roundrect iterator out of bounds");
|
|
}
|
|
double ctrls[] = ctrlpts[index];
|
|
int nc = 0;
|
|
for (int i = 0; i < ctrls.length; i += 4) {
|
|
coords[nc++] = x + ctrls[i + 0] * w + ctrls[i + 1] * aw;
|
|
coords[nc++] = y + ctrls[i + 2] * h + ctrls[i + 3] * ah;
|
|
}
|
|
if (affine != null) {
|
|
affine.transform(coords, 0, coords, 0, nc / 2);
|
|
}
|
|
return types[index];
|
|
}
|
|
}
|
|
}
|
|
}
|