alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (Metacity.java)

This example Java source code file (Metacity.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt, border, color, dimension, geometry, graphics, graphics2d, gui, image, insets, jcomponent, jinternalframe, namednodemap, node, object, rectangle, string, swing, url, xml

The Metacity.java Java example source code

/*
 * Copyright (c) 2002, 2012, 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 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 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();
        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();
                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();

    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();
        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];
            }
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java Metacity.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.