1 /* 2 * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.java.swing.plaf.gtk; 26 27 import sun.swing.SwingUtilities2; 28 import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType; 29 import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType; 30 31 import javax.swing.plaf.ColorUIResource; 32 import javax.swing.plaf.synth.*; 33 34 import java.awt.*; 35 import java.awt.geom.*; 36 import java.awt.image.*; 37 import java.io.*; 38 import java.net.*; 39 import java.security.*; 40 import java.util.*; 41 42 import javax.swing.*; 43 import javax.swing.border.*; 44 45 import javax.xml.parsers.*; 46 import org.xml.sax.SAXException; 47 import org.w3c.dom.*; 48 49 /** 50 */ 51 class Metacity implements SynthConstants { 52 // Tutorial: 53 // http://developer.gnome.org/doc/tutorials/metacity/metacity-themes.html 54 55 // Themes: 56 // http://art.gnome.org/theme_list.php?category=metacity 57 58 static Metacity INSTANCE; 59 60 private static final String[] themeNames = { 61 getUserTheme(), 62 "blueprint", 63 "Bluecurve", 64 "Crux", 65 "SwingFallbackTheme" 66 }; 67 68 static { 69 for (String themeName : themeNames) { 70 if (themeName != null) { 71 try { 72 INSTANCE = new Metacity(themeName); 73 } catch (FileNotFoundException ex) { 74 } catch (IOException ex) { 75 logError(themeName, ex); 76 } catch (ParserConfigurationException ex) { 77 logError(themeName, ex); 78 } catch (SAXException ex) { 79 logError(themeName, ex); 80 } 81 } 82 if (INSTANCE != null) { 83 break; 84 } 85 } 86 if (INSTANCE == null) { 87 throw new Error("Could not find any installed metacity theme, and fallback failed"); 88 } 89 } 90 91 private static boolean errorLogged = false; 92 private static DocumentBuilder documentBuilder; 93 private static Document xmlDoc; 94 private static String userHome; 95 96 private Node frame_style_set; 97 private Map<String, Object> frameGeometry; 98 private Map<String, Map<String, Object>> frameGeometries; 99 100 private LayoutManager titlePaneLayout = new TitlePaneLayout(); 101 102 private ColorizeImageFilter imageFilter = new ColorizeImageFilter(); 103 private URL themeDir = null; 104 private SynthContext context; 105 private String themeName; 106 107 private ArithmeticExpressionEvaluator aee = new ArithmeticExpressionEvaluator(); 108 private Map<String, Integer> variables; 109 110 // Reusable clip shape object 111 private RoundRectClipShape roundedClipShape; 112 Metacity(String themeName)113 protected Metacity(String themeName) throws IOException, ParserConfigurationException, SAXException { 114 this.themeName = themeName; 115 themeDir = getThemeDir(themeName); 116 if (themeDir != null) { 117 URL themeURL = new URL(themeDir, "metacity-theme-1.xml"); 118 xmlDoc = getXMLDoc(themeURL); 119 if (xmlDoc == null) { 120 throw new IOException(themeURL.toString()); 121 } 122 } else { 123 throw new FileNotFoundException(themeName); 124 } 125 126 // Initialize constants 127 variables = new HashMap<String, Integer>(); 128 NodeList nodes = xmlDoc.getElementsByTagName("constant"); 129 int n = nodes.getLength(); 130 for (int i = 0; i < n; i++) { 131 Node node = nodes.item(i); 132 String name = getStringAttr(node, "name"); 133 if (name != null) { 134 String value = getStringAttr(node, "value"); 135 if (value != null) { 136 try { 137 variables.put(name, Integer.parseInt(value)); 138 } catch (NumberFormatException ex) { 139 logError(themeName, ex); 140 // Ignore bad value 141 } 142 } 143 } 144 } 145 146 // Cache frame geometries 147 frameGeometries = new HashMap<String, Map<String, Object>>(); 148 nodes = xmlDoc.getElementsByTagName("frame_geometry"); 149 n = nodes.getLength(); 150 for (int i = 0; i < n; i++) { 151 Node node = nodes.item(i); 152 String name = getStringAttr(node, "name"); 153 if (name != null) { 154 HashMap<String, Object> gm = new HashMap<String, Object>(); 155 frameGeometries.put(name, gm); 156 157 String parentGM = getStringAttr(node, "parent"); 158 if (parentGM != null) { 159 gm.putAll(frameGeometries.get(parentGM)); 160 } 161 162 gm.put("has_title", 163 Boolean.valueOf(getBooleanAttr(node, "has_title", true))); 164 gm.put("rounded_top_left", 165 Boolean.valueOf(getBooleanAttr(node, "rounded_top_left", false))); 166 gm.put("rounded_top_right", 167 Boolean.valueOf(getBooleanAttr(node, "rounded_top_right", false))); 168 gm.put("rounded_bottom_left", 169 Boolean.valueOf(getBooleanAttr(node, "rounded_bottom_left", false))); 170 gm.put("rounded_bottom_right", 171 Boolean.valueOf(getBooleanAttr(node, "rounded_bottom_right", false))); 172 173 NodeList childNodes = node.getChildNodes(); 174 int nc = childNodes.getLength(); 175 for (int j = 0; j < nc; j++) { 176 Node child = childNodes.item(j); 177 if (child.getNodeType() == Node.ELEMENT_NODE) { 178 name = child.getNodeName(); 179 Object value = null; 180 if ("distance".equals(name)) { 181 value = Integer.valueOf(getIntAttr(child, "value", 0)); 182 } else if ("border".equals(name)) { 183 value = new Insets(getIntAttr(child, "top", 0), 184 getIntAttr(child, "left", 0), 185 getIntAttr(child, "bottom", 0), 186 getIntAttr(child, "right", 0)); 187 } else if ("aspect_ratio".equals(name)) { 188 value = new Float(getFloatAttr(child, "value", 1.0F)); 189 } else { 190 logError(themeName, "Unknown Metacity frame geometry value type: "+name); 191 } 192 String childName = getStringAttr(child, "name"); 193 if (childName != null && value != null) { 194 gm.put(childName, value); 195 } 196 } 197 } 198 } 199 } 200 frameGeometry = frameGeometries.get("normal"); 201 } 202 203 getTitlePaneLayout()204 public static LayoutManager getTitlePaneLayout() { 205 return INSTANCE.titlePaneLayout; 206 } 207 getRoundedClipShape(int x, int y, int w, int h, int arcw, int arch, int corners)208 private Shape getRoundedClipShape(int x, int y, int w, int h, 209 int arcw, int arch, int corners) { 210 if (roundedClipShape == null) { 211 roundedClipShape = new RoundRectClipShape(); 212 } 213 roundedClipShape.setRoundedRect(x, y, w, h, arcw, arch, corners); 214 215 return roundedClipShape; 216 } 217 paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)218 void paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h) { 219 updateFrameGeometry(context); 220 221 this.context = context; 222 JButton button = (JButton)context.getComponent(); 223 String buttonName = button.getName(); 224 int buttonState = context.getComponentState(); 225 226 JComponent titlePane = (JComponent)button.getParent(); 227 Container titlePaneParent = titlePane.getParent(); 228 229 JInternalFrame jif; 230 if (titlePaneParent instanceof JInternalFrame) { 231 jif = (JInternalFrame)titlePaneParent; 232 } else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) { 233 jif = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame(); 234 } else { 235 return; 236 } 237 238 boolean active = jif.isSelected(); 239 button.setOpaque(false); 240 241 String state = "normal"; 242 if ((buttonState & PRESSED) != 0) { 243 state = "pressed"; 244 } else if ((buttonState & MOUSE_OVER) != 0) { 245 state = "prelight"; 246 } 247 248 String function = null; 249 String location = null; 250 boolean left_corner = false; 251 boolean right_corner = false; 252 253 254 if (buttonName == "InternalFrameTitlePane.menuButton") { 255 function = "menu"; 256 location = "left_left"; 257 left_corner = true; 258 } else if (buttonName == "InternalFrameTitlePane.iconifyButton") { 259 function = "minimize"; 260 int nButtons = ((jif.isIconifiable() ? 1 : 0) + 261 (jif.isMaximizable() ? 1 : 0) + 262 (jif.isClosable() ? 1 : 0)); 263 right_corner = (nButtons == 1); 264 switch (nButtons) { 265 case 1: location = "right_right"; break; 266 case 2: location = "right_middle"; break; 267 case 3: location = "right_left"; break; 268 } 269 } else if (buttonName == "InternalFrameTitlePane.maximizeButton") { 270 function = "maximize"; 271 right_corner = !jif.isClosable(); 272 location = jif.isClosable() ? "right_middle" : "right_right"; 273 } else if (buttonName == "InternalFrameTitlePane.closeButton") { 274 function = "close"; 275 right_corner = true; 276 location = "right_right"; 277 } 278 279 Node frame = getNode(frame_style_set, "frame", new String[] { 280 "focus", (active ? "yes" : "no"), 281 "state", (jif.isMaximum() ? "maximized" : "normal") 282 }); 283 284 if (function != null && frame != null) { 285 Node frame_style = getNode("frame_style", new String[] { 286 "name", getStringAttr(frame, "style") 287 }); 288 if (frame_style != null) { 289 Shape oldClip = g.getClip(); 290 if ((right_corner && getBoolean("rounded_top_right", false)) || 291 (left_corner && getBoolean("rounded_top_left", false))) { 292 293 Point buttonLoc = button.getLocation(); 294 if (right_corner) { 295 g.setClip(getRoundedClipShape(0, 0, w, h, 296 12, 12, RoundRectClipShape.TOP_RIGHT)); 297 } else { 298 g.setClip(getRoundedClipShape(0, 0, w, h, 299 11, 11, RoundRectClipShape.TOP_LEFT)); 300 } 301 302 Rectangle clipBounds = oldClip.getBounds(); 303 g.clipRect(clipBounds.x, clipBounds.y, 304 clipBounds.width, clipBounds.height); 305 } 306 drawButton(frame_style, location+"_background", state, g, w, h, jif); 307 drawButton(frame_style, function, state, g, w, h, jif); 308 g.setClip(oldClip); 309 } 310 } 311 } 312 drawButton(Node frame_style, String function, String state, Graphics g, int w, int h, JInternalFrame jif)313 protected void drawButton(Node frame_style, String function, String state, 314 Graphics g, int w, int h, JInternalFrame jif) { 315 Node buttonNode = getNode(frame_style, "button", 316 new String[] { "function", function, "state", state }); 317 if (buttonNode == null && !state.equals("normal")) { 318 buttonNode = getNode(frame_style, "button", 319 new String[] { "function", function, "state", "normal" }); 320 } 321 if (buttonNode != null) { 322 Node draw_ops; 323 String draw_ops_name = getStringAttr(buttonNode, "draw_ops"); 324 if (draw_ops_name != null) { 325 draw_ops = getNode("draw_ops", new String[] { "name", draw_ops_name }); 326 } else { 327 draw_ops = getNode(buttonNode, "draw_ops", null); 328 } 329 variables.put("width", w); 330 variables.put("height", h); 331 draw(draw_ops, g, jif); 332 } 333 } 334 paintFrameBorder(SynthContext context, Graphics g, int x0, int y0, int width, int height)335 void paintFrameBorder(SynthContext context, Graphics g, int x0, int y0, int width, int height) { 336 updateFrameGeometry(context); 337 338 this.context = context; 339 JComponent comp = context.getComponent(); 340 JComponent titlePane = findChild(comp, "InternalFrame.northPane"); 341 342 if (titlePane == null) { 343 return; 344 } 345 346 JInternalFrame jif = null; 347 if (comp instanceof JInternalFrame) { 348 jif = (JInternalFrame)comp; 349 } else if (comp instanceof JInternalFrame.JDesktopIcon) { 350 jif = ((JInternalFrame.JDesktopIcon)comp).getInternalFrame(); 351 } else { 352 assert false : "component is not JInternalFrame or JInternalFrame.JDesktopIcon"; 353 return; 354 } 355 356 boolean active = jif.isSelected(); 357 Font oldFont = g.getFont(); 358 g.setFont(titlePane.getFont()); 359 g.translate(x0, y0); 360 361 Rectangle titleRect = calculateTitleArea(jif); 362 JComponent menuButton = findChild(titlePane, "InternalFrameTitlePane.menuButton"); 363 364 Icon frameIcon = jif.getFrameIcon(); 365 variables.put("mini_icon_width", 366 (frameIcon != null) ? frameIcon.getIconWidth() : 0); 367 variables.put("mini_icon_height", 368 (frameIcon != null) ? frameIcon.getIconHeight() : 0); 369 variables.put("title_width", calculateTitleTextWidth(g, jif)); 370 FontMetrics fm = SwingUtilities2.getFontMetrics(jif, g); 371 variables.put("title_height", fm.getAscent() + fm.getDescent()); 372 373 // These don't seem to apply here, but the Galaxy theme uses them. Not sure why. 374 variables.put("icon_width", 32); 375 variables.put("icon_height", 32); 376 377 if (frame_style_set != null) { 378 Node frame = getNode(frame_style_set, "frame", new String[] { 379 "focus", (active ? "yes" : "no"), 380 "state", (jif.isMaximum() ? "maximized" : "normal") 381 }); 382 383 if (frame != null) { 384 Node frame_style = getNode("frame_style", new String[] { 385 "name", getStringAttr(frame, "style") 386 }); 387 if (frame_style != null) { 388 Shape oldClip = g.getClip(); 389 boolean roundTopLeft = getBoolean("rounded_top_left", false); 390 boolean roundTopRight = getBoolean("rounded_top_right", false); 391 boolean roundBottomLeft = getBoolean("rounded_bottom_left", false); 392 boolean roundBottomRight = getBoolean("rounded_bottom_right", false); 393 394 if (roundTopLeft || roundTopRight || roundBottomLeft || roundBottomRight) { 395 jif.setOpaque(false); 396 397 g.setClip(getRoundedClipShape(0, 0, width, height, 12, 12, 398 (roundTopLeft ? RoundRectClipShape.TOP_LEFT : 0) | 399 (roundTopRight ? RoundRectClipShape.TOP_RIGHT : 0) | 400 (roundBottomLeft ? RoundRectClipShape.BOTTOM_LEFT : 0) | 401 (roundBottomRight ? RoundRectClipShape.BOTTOM_RIGHT : 0))); 402 } 403 404 Rectangle clipBounds = oldClip.getBounds(); 405 g.clipRect(clipBounds.x, clipBounds.y, 406 clipBounds.width, clipBounds.height); 407 408 int titleHeight = titlePane.getHeight(); 409 410 boolean minimized = jif.isIcon(); 411 Insets insets = getBorderInsets(context, null); 412 413 int leftTitlebarEdge = getInt("left_titlebar_edge"); 414 int rightTitlebarEdge = getInt("right_titlebar_edge"); 415 int topTitlebarEdge = getInt("top_titlebar_edge"); 416 int bottomTitlebarEdge = getInt("bottom_titlebar_edge"); 417 418 if (!minimized) { 419 drawPiece(frame_style, g, "entire_background", 420 0, 0, width, height, jif); 421 } 422 drawPiece(frame_style, g, "titlebar", 423 0, 0, width, titleHeight, jif); 424 drawPiece(frame_style, g, "titlebar_middle", 425 leftTitlebarEdge, topTitlebarEdge, 426 width - leftTitlebarEdge - rightTitlebarEdge, 427 titleHeight - topTitlebarEdge - bottomTitlebarEdge, 428 jif); 429 drawPiece(frame_style, g, "left_titlebar_edge", 430 0, 0, leftTitlebarEdge, titleHeight, jif); 431 drawPiece(frame_style, g, "right_titlebar_edge", 432 width - rightTitlebarEdge, 0, 433 rightTitlebarEdge, titleHeight, jif); 434 drawPiece(frame_style, g, "top_titlebar_edge", 435 0, 0, width, topTitlebarEdge, jif); 436 drawPiece(frame_style, g, "bottom_titlebar_edge", 437 0, titleHeight - bottomTitlebarEdge, 438 width, bottomTitlebarEdge, jif); 439 drawPiece(frame_style, g, "title", 440 titleRect.x, titleRect.y, titleRect.width, titleRect.height, jif); 441 if (!minimized) { 442 drawPiece(frame_style, g, "left_edge", 443 0, titleHeight, insets.left, height-titleHeight, jif); 444 drawPiece(frame_style, g, "right_edge", 445 width-insets.right, titleHeight, insets.right, height-titleHeight, jif); 446 drawPiece(frame_style, g, "bottom_edge", 447 0, height - insets.bottom, width, insets.bottom, jif); 448 drawPiece(frame_style, g, "overlay", 449 0, 0, width, height, jif); 450 } 451 g.setClip(oldClip); 452 } 453 } 454 } 455 g.translate(-x0, -y0); 456 g.setFont(oldFont); 457 } 458 459 460 461 private static class Privileged implements PrivilegedAction<Object> { 462 private static int GET_THEME_DIR = 0; 463 private static int GET_USER_THEME = 1; 464 private static int GET_IMAGE = 2; 465 private int type; 466 private Object arg; 467 doPrivileged(int type, Object arg)468 public Object doPrivileged(int type, Object arg) { 469 this.type = type; 470 this.arg = arg; 471 return AccessController.doPrivileged(this); 472 } 473 run()474 public Object run() { 475 if (type == GET_THEME_DIR) { 476 String sep = File.separator; 477 String[] dirs = new String[] { 478 userHome + sep + ".themes", 479 System.getProperty("swing.metacitythemedir"), 480 "/usr/local/share/themes", 481 "/usr/local/share/gnome/themes", 482 "/usr/share/themes", 483 "/usr/gnome/share/themes", // Debian/Redhat/Solaris 484 "/opt/gnome2/share/themes" // SuSE 485 }; 486 487 URL themeDir = null; 488 for (int i = 0; i < dirs.length; i++) { 489 // System property may not be set so skip null directories. 490 if (dirs[i] == null) { 491 continue; 492 } 493 File dir = 494 new File(dirs[i] + sep + arg + sep + "metacity-1"); 495 if (new File(dir, "metacity-theme-1.xml").canRead()) { 496 try { 497 themeDir = dir.toURI().toURL(); 498 } catch (MalformedURLException ex) { 499 themeDir = null; 500 } 501 break; 502 } 503 } 504 if (themeDir == null) { 505 String filename = "resources/metacity/" + arg + 506 "/metacity-1/metacity-theme-1.xml"; 507 URL url = getClass().getResource(filename); 508 if (url != null) { 509 String str = url.toString(); 510 try { 511 themeDir = new URL(str.substring(0, str.lastIndexOf('/'))+"/"); 512 } catch (MalformedURLException ex) { 513 themeDir = null; 514 } 515 } 516 } 517 return themeDir; 518 } else if (type == GET_USER_THEME) { 519 try { 520 // Set userHome here because we need the privilege 521 userHome = System.getProperty("user.home"); 522 523 String theme = System.getProperty("swing.metacitythemename"); 524 if (theme != null) { 525 return theme; 526 } 527 // Note: this is a small file (< 1024 bytes) so it's not worth 528 // starting an XML parser or even to use a buffered reader. 529 URL url = new URL(new File(userHome).toURI().toURL(), 530 ".gconf/apps/metacity/general/%25gconf.xml"); 531 // Pending: verify character encoding spec for gconf 532 Reader reader = new InputStreamReader(url.openStream(), "ISO-8859-1"); 533 char[] buf = new char[1024]; 534 StringBuffer strBuf = new StringBuffer(); 535 int n; 536 while ((n = reader.read(buf)) >= 0) { 537 strBuf.append(buf, 0, n); 538 } 539 reader.close(); 540 String str = strBuf.toString(); 541 if (str != null) { 542 String strLowerCase = str.toLowerCase(); 543 int i = strLowerCase.indexOf("<entry name=\"theme\""); 544 if (i >= 0) { 545 i = strLowerCase.indexOf("<stringvalue>", i); 546 if (i > 0) { 547 i += "<stringvalue>".length(); 548 int i2 = str.indexOf("<", i); 549 return str.substring(i, i2); 550 } 551 } 552 } 553 } catch (MalformedURLException ex) { 554 // OK to just ignore. We'll use a fallback theme. 555 } catch (IOException ex) { 556 // OK to just ignore. We'll use a fallback theme. 557 } 558 return null; 559 } else if (type == GET_IMAGE) { 560 return new ImageIcon((URL)arg).getImage(); 561 } else { 562 return null; 563 } 564 } 565 } 566 getThemeDir(String themeName)567 private static URL getThemeDir(String themeName) { 568 return (URL)new Privileged().doPrivileged(Privileged.GET_THEME_DIR, themeName); 569 } 570 getUserTheme()571 private static String getUserTheme() { 572 return (String)new Privileged().doPrivileged(Privileged.GET_USER_THEME, null); 573 } 574 tileImage(Graphics g, Image image, int x0, int y0, int w, int h, float[] alphas)575 protected void tileImage(Graphics g, Image image, int x0, int y0, int w, int h, float[] alphas) { 576 Graphics2D g2 = (Graphics2D)g; 577 Composite oldComp = g2.getComposite(); 578 579 int sw = image.getWidth(null); 580 int sh = image.getHeight(null); 581 int y = y0; 582 while (y < y0 + h) { 583 sh = Math.min(sh, y0 + h - y); 584 int x = x0; 585 while (x < x0 + w) { 586 float f = (alphas.length - 1.0F) * x / (x0 + w); 587 int i = (int)f; 588 f -= (int)f; 589 float alpha = (1-f) * alphas[i]; 590 if (i+1 < alphas.length) { 591 alpha += f * alphas[i+1]; 592 } 593 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 594 int swm = Math.min(sw, x0 + w - x); 595 g.drawImage(image, x, y, x+swm, y+sh, 0, 0, swm, sh, null); 596 x += swm; 597 } 598 y += sh; 599 } 600 g2.setComposite(oldComp); 601 } 602 603 private HashMap<String, Image> images = new HashMap<String, Image>(); 604 getImage(String key, Color c)605 protected Image getImage(String key, Color c) { 606 Image image = images.get(key+"-"+c.getRGB()); 607 if (image == null) { 608 image = imageFilter.colorize(getImage(key), c); 609 if (image != null) { 610 images.put(key+"-"+c.getRGB(), image); 611 } 612 } 613 return image; 614 } 615 getImage(String key)616 protected Image getImage(String key) { 617 Image image = images.get(key); 618 if (image == null) { 619 if (themeDir != null) { 620 try { 621 URL url = new URL(themeDir, key); 622 image = (Image)new Privileged().doPrivileged(Privileged.GET_IMAGE, url); 623 } catch (MalformedURLException ex) { 624 //log("Bad image url: "+ themeDir + "/" + key); 625 } 626 } 627 if (image != null) { 628 images.put(key, image); 629 } 630 } 631 return image; 632 } 633 634 private class ColorizeImageFilter extends RGBImageFilter { 635 double cr, cg, cb; 636 ColorizeImageFilter()637 public ColorizeImageFilter() { 638 canFilterIndexColorModel = true; 639 } 640 setColor(Color color)641 public void setColor(Color color) { 642 cr = color.getRed() / 255.0; 643 cg = color.getGreen() / 255.0; 644 cb = color.getBlue() / 255.0; 645 } 646 colorize(Image fromImage, Color c)647 public Image colorize(Image fromImage, Color c) { 648 setColor(c); 649 ImageProducer producer = new FilteredImageSource(fromImage.getSource(), this); 650 return new ImageIcon(context.getComponent().createImage(producer)).getImage(); 651 } 652 filterRGB(int x, int y, int rgb)653 public int filterRGB(int x, int y, int rgb) { 654 // Assume all rgb values are shades of gray 655 double grayLevel = 2 * (rgb & 0xff) / 255.0; 656 double r, g, b; 657 658 if (grayLevel <= 1.0) { 659 r = cr * grayLevel; 660 g = cg * grayLevel; 661 b = cb * grayLevel; 662 } else { 663 grayLevel -= 1.0; 664 r = cr + (1.0 - cr) * grayLevel; 665 g = cg + (1.0 - cg) * grayLevel; 666 b = cb + (1.0 - cb) * grayLevel; 667 } 668 669 return ((rgb & 0xff000000) + 670 (((int)(r * 255)) << 16) + 671 (((int)(g * 255)) << 8) + 672 (int)(b * 255)); 673 } 674 } 675 findChild(JComponent parent, String name)676 protected static JComponent findChild(JComponent parent, String name) { 677 int n = parent.getComponentCount(); 678 for (int i = 0; i < n; i++) { 679 JComponent c = (JComponent)parent.getComponent(i); 680 if (name.equals(c.getName())) { 681 return c; 682 } 683 } 684 return null; 685 } 686 687 688 protected class TitlePaneLayout implements LayoutManager { addLayoutComponent(String name, Component c)689 public void addLayoutComponent(String name, Component c) {} removeLayoutComponent(Component c)690 public void removeLayoutComponent(Component c) {} preferredLayoutSize(Container c)691 public Dimension preferredLayoutSize(Container c) { 692 return minimumLayoutSize(c); 693 } 694 minimumLayoutSize(Container c)695 public Dimension minimumLayoutSize(Container c) { 696 JComponent titlePane = (JComponent)c; 697 Container titlePaneParent = titlePane.getParent(); 698 JInternalFrame frame; 699 if (titlePaneParent instanceof JInternalFrame) { 700 frame = (JInternalFrame)titlePaneParent; 701 } else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) { 702 frame = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame(); 703 } else { 704 return null; 705 } 706 707 Dimension buttonDim = calculateButtonSize(titlePane); 708 Insets title_border = (Insets)getFrameGeometry().get("title_border"); 709 Insets button_border = (Insets)getFrameGeometry().get("button_border"); 710 711 // Calculate width. 712 int width = getInt("left_titlebar_edge") + buttonDim.width + getInt("right_titlebar_edge"); 713 if (title_border != null) { 714 width += title_border.left + title_border.right; 715 } 716 if (frame.isClosable()) { 717 width += buttonDim.width; 718 } 719 if (frame.isMaximizable()) { 720 width += buttonDim.width; 721 } 722 if (frame.isIconifiable()) { 723 width += buttonDim.width; 724 } 725 FontMetrics fm = frame.getFontMetrics(titlePane.getFont()); 726 String frameTitle = frame.getTitle(); 727 int title_w = frameTitle != null ? SwingUtilities2.stringWidth( 728 frame, fm, frameTitle) : 0; 729 int title_length = frameTitle != null ? frameTitle.length() : 0; 730 731 // Leave room for three characters in the title. 732 if (title_length > 3) { 733 int subtitle_w = SwingUtilities2.stringWidth( 734 frame, fm, frameTitle.substring(0, 3) + "..."); 735 width += (title_w < subtitle_w) ? title_w : subtitle_w; 736 } else { 737 width += title_w; 738 } 739 740 // Calculate height. 741 int titleHeight = fm.getHeight() + getInt("title_vertical_pad"); 742 if (title_border != null) { 743 titleHeight += title_border.top + title_border.bottom; 744 } 745 int buttonHeight = buttonDim.height; 746 if (button_border != null) { 747 buttonHeight += button_border.top + button_border.bottom; 748 } 749 int height = Math.max(buttonHeight, titleHeight); 750 751 return new Dimension(width, height); 752 } 753 layoutContainer(Container c)754 public void layoutContainer(Container c) { 755 JComponent titlePane = (JComponent)c; 756 Container titlePaneParent = titlePane.getParent(); 757 JInternalFrame frame; 758 if (titlePaneParent instanceof JInternalFrame) { 759 frame = (JInternalFrame)titlePaneParent; 760 } else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) { 761 frame = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame(); 762 } else { 763 return; 764 } 765 Map gm = getFrameGeometry(); 766 767 int w = titlePane.getWidth(); 768 int h = titlePane.getHeight(); 769 770 JComponent menuButton = findChild(titlePane, "InternalFrameTitlePane.menuButton"); 771 JComponent minimizeButton = findChild(titlePane, "InternalFrameTitlePane.iconifyButton"); 772 JComponent maximizeButton = findChild(titlePane, "InternalFrameTitlePane.maximizeButton"); 773 JComponent closeButton = findChild(titlePane, "InternalFrameTitlePane.closeButton"); 774 775 Insets button_border = (Insets)gm.get("button_border"); 776 Dimension buttonDim = calculateButtonSize(titlePane); 777 778 int y = (button_border != null) ? button_border.top : 0; 779 if (titlePaneParent.getComponentOrientation().isLeftToRight()) { 780 int x = getInt("left_titlebar_edge"); 781 782 menuButton.setBounds(x, y, buttonDim.width, buttonDim.height); 783 784 x = w - buttonDim.width - getInt("right_titlebar_edge"); 785 if (button_border != null) { 786 x -= button_border.right; 787 } 788 789 if (frame.isClosable()) { 790 closeButton.setBounds(x, y, buttonDim.width, buttonDim.height); 791 x -= buttonDim.width; 792 } 793 794 if (frame.isMaximizable()) { 795 maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); 796 x -= buttonDim.width; 797 } 798 799 if (frame.isIconifiable()) { 800 minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); 801 } 802 } else { 803 int x = w - buttonDim.width - getInt("right_titlebar_edge"); 804 805 menuButton.setBounds(x, y, buttonDim.width, buttonDim.height); 806 807 x = getInt("left_titlebar_edge"); 808 if (button_border != null) { 809 x += button_border.left; 810 } 811 812 if (frame.isClosable()) { 813 closeButton.setBounds(x, y, buttonDim.width, buttonDim.height); 814 x += buttonDim.width; 815 } 816 817 if (frame.isMaximizable()) { 818 maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); 819 x += buttonDim.width; 820 } 821 822 if (frame.isIconifiable()) { 823 minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); 824 } 825 } 826 } 827 } // end TitlePaneLayout 828 getFrameGeometry()829 protected Map getFrameGeometry() { 830 return frameGeometry; 831 } 832 setFrameGeometry(JComponent titlePane, Map gm)833 protected void setFrameGeometry(JComponent titlePane, Map gm) { 834 this.frameGeometry = gm; 835 if (getInt("top_height") == 0 && titlePane != null) { 836 gm.put("top_height", Integer.valueOf(titlePane.getHeight())); 837 } 838 } 839 getInt(String key)840 protected int getInt(String key) { 841 Integer i = (Integer)frameGeometry.get(key); 842 if (i == null) { 843 i = variables.get(key); 844 } 845 return (i != null) ? i.intValue() : 0; 846 } 847 getBoolean(String key, boolean fallback)848 protected boolean getBoolean(String key, boolean fallback) { 849 Boolean b = (Boolean)frameGeometry.get(key); 850 return (b != null) ? b.booleanValue() : fallback; 851 } 852 853 drawArc(Node node, Graphics g)854 protected void drawArc(Node node, Graphics g) { 855 NamedNodeMap attrs = node.getAttributes(); 856 Color color = parseColor(getStringAttr(attrs, "color")); 857 int x = aee.evaluate(getStringAttr(attrs, "x")); 858 int y = aee.evaluate(getStringAttr(attrs, "y")); 859 int w = aee.evaluate(getStringAttr(attrs, "width")); 860 int h = aee.evaluate(getStringAttr(attrs, "height")); 861 int start_angle = aee.evaluate(getStringAttr(attrs, "start_angle")); 862 int extent_angle = aee.evaluate(getStringAttr(attrs, "extent_angle")); 863 boolean filled = getBooleanAttr(node, "filled", false); 864 if (getInt("width") == -1) { 865 x -= w; 866 } 867 if (getInt("height") == -1) { 868 y -= h; 869 } 870 g.setColor(color); 871 if (filled) { 872 g.fillArc(x, y, w, h, start_angle, extent_angle); 873 } else { 874 g.drawArc(x, y, w, h, start_angle, extent_angle); 875 } 876 } 877 drawLine(Node node, Graphics g)878 protected void drawLine(Node node, Graphics g) { 879 NamedNodeMap attrs = node.getAttributes(); 880 Color color = parseColor(getStringAttr(attrs, "color")); 881 int x1 = aee.evaluate(getStringAttr(attrs, "x1")); 882 int y1 = aee.evaluate(getStringAttr(attrs, "y1")); 883 int x2 = aee.evaluate(getStringAttr(attrs, "x2")); 884 int y2 = aee.evaluate(getStringAttr(attrs, "y2")); 885 int lineWidth = aee.evaluate(getStringAttr(attrs, "width"), 1); 886 g.setColor(color); 887 if (lineWidth != 1) { 888 Graphics2D g2d = (Graphics2D)g; 889 Stroke stroke = g2d.getStroke(); 890 g2d.setStroke(new BasicStroke((float)lineWidth)); 891 g2d.drawLine(x1, y1, x2, y2); 892 g2d.setStroke(stroke); 893 } else { 894 g.drawLine(x1, y1, x2, y2); 895 } 896 } 897 drawRectangle(Node node, Graphics g)898 protected void drawRectangle(Node node, Graphics g) { 899 NamedNodeMap attrs = node.getAttributes(); 900 Color color = parseColor(getStringAttr(attrs, "color")); 901 boolean filled = getBooleanAttr(node, "filled", false); 902 int x = aee.evaluate(getStringAttr(attrs, "x")); 903 int y = aee.evaluate(getStringAttr(attrs, "y")); 904 int w = aee.evaluate(getStringAttr(attrs, "width")); 905 int h = aee.evaluate(getStringAttr(attrs, "height")); 906 g.setColor(color); 907 if (getInt("width") == -1) { 908 x -= w; 909 } 910 if (getInt("height") == -1) { 911 y -= h; 912 } 913 if (filled) { 914 g.fillRect(x, y, w, h); 915 } else { 916 g.drawRect(x, y, w, h); 917 } 918 } 919 drawTile(Node node, Graphics g, JInternalFrame jif)920 protected void drawTile(Node node, Graphics g, JInternalFrame jif) { 921 NamedNodeMap attrs = node.getAttributes(); 922 int x0 = aee.evaluate(getStringAttr(attrs, "x")); 923 int y0 = aee.evaluate(getStringAttr(attrs, "y")); 924 int w = aee.evaluate(getStringAttr(attrs, "width")); 925 int h = aee.evaluate(getStringAttr(attrs, "height")); 926 int tw = aee.evaluate(getStringAttr(attrs, "tile_width")); 927 int th = aee.evaluate(getStringAttr(attrs, "tile_height")); 928 int width = getInt("width"); 929 int height = getInt("height"); 930 if (width == -1) { 931 x0 -= w; 932 } 933 if (height == -1) { 934 y0 -= h; 935 } 936 Shape oldClip = g.getClip(); 937 if (g instanceof Graphics2D) { 938 ((Graphics2D)g).clip(new Rectangle(x0, y0, w, h)); 939 } 940 variables.put("width", tw); 941 variables.put("height", th); 942 943 Node draw_ops = getNode("draw_ops", new String[] { "name", getStringAttr(node, "name") }); 944 945 int y = y0; 946 while (y < y0 + h) { 947 int x = x0; 948 while (x < x0 + w) { 949 g.translate(x, y); 950 draw(draw_ops, g, jif); 951 g.translate(-x, -y); 952 x += tw; 953 } 954 y += th; 955 } 956 957 variables.put("width", width); 958 variables.put("height", height); 959 g.setClip(oldClip); 960 } 961 drawTint(Node node, Graphics g)962 protected void drawTint(Node node, Graphics g) { 963 NamedNodeMap attrs = node.getAttributes(); 964 Color color = parseColor(getStringAttr(attrs, "color")); 965 float alpha = Float.parseFloat(getStringAttr(attrs, "alpha")); 966 int x = aee.evaluate(getStringAttr(attrs, "x")); 967 int y = aee.evaluate(getStringAttr(attrs, "y")); 968 int w = aee.evaluate(getStringAttr(attrs, "width")); 969 int h = aee.evaluate(getStringAttr(attrs, "height")); 970 if (getInt("width") == -1) { 971 x -= w; 972 } 973 if (getInt("height") == -1) { 974 y -= h; 975 } 976 if (g instanceof Graphics2D) { 977 Graphics2D g2 = (Graphics2D)g; 978 Composite oldComp = g2.getComposite(); 979 AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); 980 g2.setComposite(ac); 981 g2.setColor(color); 982 g2.fillRect(x, y, w, h); 983 g2.setComposite(oldComp); 984 } 985 } 986 drawTitle(Node node, Graphics g, JInternalFrame jif)987 protected void drawTitle(Node node, Graphics g, JInternalFrame jif) { 988 NamedNodeMap attrs = node.getAttributes(); 989 String colorStr = getStringAttr(attrs, "color"); 990 int i = colorStr.indexOf("gtk:fg["); 991 if (i > 0) { 992 colorStr = colorStr.substring(0, i) + "gtk:text[" + colorStr.substring(i+7); 993 } 994 Color color = parseColor(colorStr); 995 int x = aee.evaluate(getStringAttr(attrs, "x")); 996 int y = aee.evaluate(getStringAttr(attrs, "y")); 997 998 String title = jif.getTitle(); 999 if (title != null) { 1000 FontMetrics fm = SwingUtilities2.getFontMetrics(jif, g); 1001 title = SwingUtilities2.clipStringIfNecessary(jif, fm, title, 1002 calculateTitleArea(jif).width); 1003 g.setColor(color); 1004 SwingUtilities2.drawString(jif, g, title, x, y + fm.getAscent()); 1005 } 1006 } 1007 calculateButtonSize(JComponent titlePane)1008 protected Dimension calculateButtonSize(JComponent titlePane) { 1009 int buttonHeight = getInt("button_height"); 1010 if (buttonHeight == 0) { 1011 buttonHeight = titlePane.getHeight(); 1012 if (buttonHeight == 0) { 1013 buttonHeight = 13; 1014 } else { 1015 Insets button_border = (Insets)frameGeometry.get("button_border"); 1016 if (button_border != null) { 1017 buttonHeight -= (button_border.top + button_border.bottom); 1018 } 1019 } 1020 } 1021 int buttonWidth = getInt("button_width"); 1022 if (buttonWidth == 0) { 1023 buttonWidth = buttonHeight; 1024 Float aspect_ratio = (Float)frameGeometry.get("aspect_ratio"); 1025 if (aspect_ratio != null) { 1026 buttonWidth = (int)(buttonHeight / aspect_ratio.floatValue()); 1027 } 1028 } 1029 return new Dimension(buttonWidth, buttonHeight); 1030 } 1031 calculateTitleArea(JInternalFrame jif)1032 protected Rectangle calculateTitleArea(JInternalFrame jif) { 1033 JComponent titlePane = findChild(jif, "InternalFrame.northPane"); 1034 Dimension buttonDim = calculateButtonSize(titlePane); 1035 Insets title_border = (Insets)frameGeometry.get("title_border"); 1036 Insets button_border = (Insets)getFrameGeometry().get("button_border"); 1037 1038 Rectangle r = new Rectangle(); 1039 r.x = getInt("left_titlebar_edge"); 1040 r.y = 0; 1041 r.height = titlePane.getHeight(); 1042 if (title_border != null) { 1043 r.x += title_border.left; 1044 r.y += title_border.top; 1045 r.height -= (title_border.top + title_border.bottom); 1046 } 1047 1048 if (titlePane.getParent().getComponentOrientation().isLeftToRight()) { 1049 r.x += buttonDim.width; 1050 if (button_border != null) { 1051 r.x += button_border.left; 1052 } 1053 r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge"); 1054 if (jif.isClosable()) { 1055 r.width -= buttonDim.width; 1056 } 1057 if (jif.isMaximizable()) { 1058 r.width -= buttonDim.width; 1059 } 1060 if (jif.isIconifiable()) { 1061 r.width -= buttonDim.width; 1062 } 1063 } else { 1064 if (jif.isClosable()) { 1065 r.x += buttonDim.width; 1066 } 1067 if (jif.isMaximizable()) { 1068 r.x += buttonDim.width; 1069 } 1070 if (jif.isIconifiable()) { 1071 r.x += buttonDim.width; 1072 } 1073 r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge") 1074 - buttonDim.width; 1075 if (button_border != null) { 1076 r.x -= button_border.right; 1077 } 1078 } 1079 if (title_border != null) { 1080 r.width -= title_border.right; 1081 } 1082 return r; 1083 } 1084 1085 calculateTitleTextWidth(Graphics g, JInternalFrame jif)1086 protected int calculateTitleTextWidth(Graphics g, JInternalFrame jif) { 1087 String title = jif.getTitle(); 1088 if (title != null) { 1089 Rectangle r = calculateTitleArea(jif); 1090 return Math.min(SwingUtilities2.stringWidth(jif, 1091 SwingUtilities2.getFontMetrics(jif, g), title), r.width); 1092 } 1093 return 0; 1094 } 1095 setClip(Node node, Graphics g)1096 protected void setClip(Node node, Graphics g) { 1097 NamedNodeMap attrs = node.getAttributes(); 1098 int x = aee.evaluate(getStringAttr(attrs, "x")); 1099 int y = aee.evaluate(getStringAttr(attrs, "y")); 1100 int w = aee.evaluate(getStringAttr(attrs, "width")); 1101 int h = aee.evaluate(getStringAttr(attrs, "height")); 1102 if (getInt("width") == -1) { 1103 x -= w; 1104 } 1105 if (getInt("height") == -1) { 1106 y -= h; 1107 } 1108 if (g instanceof Graphics2D) { 1109 ((Graphics2D)g).clip(new Rectangle(x, y, w, h)); 1110 } 1111 } 1112 drawGTKArrow(Node node, Graphics g)1113 protected void drawGTKArrow(Node node, Graphics g) { 1114 NamedNodeMap attrs = node.getAttributes(); 1115 String arrow = getStringAttr(attrs, "arrow"); 1116 String shadow = getStringAttr(attrs, "shadow"); 1117 String stateStr = getStringAttr(attrs, "state").toUpperCase(); 1118 int x = aee.evaluate(getStringAttr(attrs, "x")); 1119 int y = aee.evaluate(getStringAttr(attrs, "y")); 1120 int w = aee.evaluate(getStringAttr(attrs, "width")); 1121 int h = aee.evaluate(getStringAttr(attrs, "height")); 1122 1123 int state = -1; 1124 if ("NORMAL".equals(stateStr)) { 1125 state = ENABLED; 1126 } else if ("SELECTED".equals(stateStr)) { 1127 state = SELECTED; 1128 } else if ("INSENSITIVE".equals(stateStr)) { 1129 state = DISABLED; 1130 } else if ("PRELIGHT".equals(stateStr)) { 1131 state = MOUSE_OVER; 1132 } 1133 1134 ShadowType shadowType = null; 1135 if ("in".equals(shadow)) { 1136 shadowType = ShadowType.IN; 1137 } else if ("out".equals(shadow)) { 1138 shadowType = ShadowType.OUT; 1139 } else if ("etched_in".equals(shadow)) { 1140 shadowType = ShadowType.ETCHED_IN; 1141 } else if ("etched_out".equals(shadow)) { 1142 shadowType = ShadowType.ETCHED_OUT; 1143 } else if ("none".equals(shadow)) { 1144 shadowType = ShadowType.NONE; 1145 } 1146 1147 ArrowType direction = null; 1148 if ("up".equals(arrow)) { 1149 direction = ArrowType.UP; 1150 } else if ("down".equals(arrow)) { 1151 direction = ArrowType.DOWN; 1152 } else if ("left".equals(arrow)) { 1153 direction = ArrowType.LEFT; 1154 } else if ("right".equals(arrow)) { 1155 direction = ArrowType.RIGHT; 1156 } 1157 1158 GTKPainter.INSTANCE.paintMetacityElement(context, g, state, 1159 "metacity-arrow", x, y, w, h, shadowType, direction); 1160 } 1161 drawGTKBox(Node node, Graphics g)1162 protected void drawGTKBox(Node node, Graphics g) { 1163 NamedNodeMap attrs = node.getAttributes(); 1164 String shadow = getStringAttr(attrs, "shadow"); 1165 String stateStr = getStringAttr(attrs, "state").toUpperCase(); 1166 int x = aee.evaluate(getStringAttr(attrs, "x")); 1167 int y = aee.evaluate(getStringAttr(attrs, "y")); 1168 int w = aee.evaluate(getStringAttr(attrs, "width")); 1169 int h = aee.evaluate(getStringAttr(attrs, "height")); 1170 1171 int state = -1; 1172 if ("NORMAL".equals(stateStr)) { 1173 state = ENABLED; 1174 } else if ("SELECTED".equals(stateStr)) { 1175 state = SELECTED; 1176 } else if ("INSENSITIVE".equals(stateStr)) { 1177 state = DISABLED; 1178 } else if ("PRELIGHT".equals(stateStr)) { 1179 state = MOUSE_OVER; 1180 } 1181 1182 ShadowType shadowType = null; 1183 if ("in".equals(shadow)) { 1184 shadowType = ShadowType.IN; 1185 } else if ("out".equals(shadow)) { 1186 shadowType = ShadowType.OUT; 1187 } else if ("etched_in".equals(shadow)) { 1188 shadowType = ShadowType.ETCHED_IN; 1189 } else if ("etched_out".equals(shadow)) { 1190 shadowType = ShadowType.ETCHED_OUT; 1191 } else if ("none".equals(shadow)) { 1192 shadowType = ShadowType.NONE; 1193 } 1194 GTKPainter.INSTANCE.paintMetacityElement(context, g, state, 1195 "metacity-box", x, y, w, h, shadowType, null); 1196 } 1197 drawGTKVLine(Node node, Graphics g)1198 protected void drawGTKVLine(Node node, Graphics g) { 1199 NamedNodeMap attrs = node.getAttributes(); 1200 String stateStr = getStringAttr(attrs, "state").toUpperCase(); 1201 1202 int x = aee.evaluate(getStringAttr(attrs, "x")); 1203 int y1 = aee.evaluate(getStringAttr(attrs, "y1")); 1204 int y2 = aee.evaluate(getStringAttr(attrs, "y2")); 1205 1206 int state = -1; 1207 if ("NORMAL".equals(stateStr)) { 1208 state = ENABLED; 1209 } else if ("SELECTED".equals(stateStr)) { 1210 state = SELECTED; 1211 } else if ("INSENSITIVE".equals(stateStr)) { 1212 state = DISABLED; 1213 } else if ("PRELIGHT".equals(stateStr)) { 1214 state = MOUSE_OVER; 1215 } 1216 1217 GTKPainter.INSTANCE.paintMetacityElement(context, g, state, 1218 "metacity-vline", x, y1, 1, y2 - y1, null, null); 1219 } 1220 drawGradient(Node node, Graphics g)1221 protected void drawGradient(Node node, Graphics g) { 1222 NamedNodeMap attrs = node.getAttributes(); 1223 String type = getStringAttr(attrs, "type"); 1224 float alpha = getFloatAttr(node, "alpha", -1F); 1225 int x = aee.evaluate(getStringAttr(attrs, "x")); 1226 int y = aee.evaluate(getStringAttr(attrs, "y")); 1227 int w = aee.evaluate(getStringAttr(attrs, "width")); 1228 int h = aee.evaluate(getStringAttr(attrs, "height")); 1229 if (getInt("width") == -1) { 1230 x -= w; 1231 } 1232 if (getInt("height") == -1) { 1233 y -= h; 1234 } 1235 1236 // Get colors from child nodes 1237 Node[] colorNodes = getNodesByName(node, "color"); 1238 Color[] colors = new Color[colorNodes.length]; 1239 for (int i = 0; i < colorNodes.length; i++) { 1240 colors[i] = parseColor(getStringAttr(colorNodes[i], "value")); 1241 } 1242 1243 boolean horizontal = ("diagonal".equals(type) || "horizontal".equals(type)); 1244 boolean vertical = ("diagonal".equals(type) || "vertical".equals(type)); 1245 1246 if (g instanceof Graphics2D) { 1247 Graphics2D g2 = (Graphics2D)g; 1248 Composite oldComp = g2.getComposite(); 1249 if (alpha >= 0F) { 1250 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 1251 } 1252 int n = colors.length - 1; 1253 for (int i = 0; i < n; i++) { 1254 g2.setPaint(new GradientPaint(x + (horizontal ? (i*w/n) : 0), 1255 y + (vertical ? (i*h/n) : 0), 1256 colors[i], 1257 x + (horizontal ? ((i+1)*w/n) : 0), 1258 y + (vertical ? ((i+1)*h/n) : 0), 1259 colors[i+1])); 1260 g2.fillRect(x + (horizontal ? (i*w/n) : 0), 1261 y + (vertical ? (i*h/n) : 0), 1262 (horizontal ? (w/n) : w), 1263 (vertical ? (h/n) : h)); 1264 } 1265 g2.setComposite(oldComp); 1266 } 1267 } 1268 drawImage(Node node, Graphics g)1269 protected void drawImage(Node node, Graphics g) { 1270 NamedNodeMap attrs = node.getAttributes(); 1271 String filename = getStringAttr(attrs, "filename"); 1272 String colorizeStr = getStringAttr(attrs, "colorize"); 1273 Color colorize = (colorizeStr != null) ? parseColor(colorizeStr) : null; 1274 String alpha = getStringAttr(attrs, "alpha"); 1275 Image object = (colorize != null) ? getImage(filename, colorize) : getImage(filename); 1276 variables.put("object_width", object.getWidth(null)); 1277 variables.put("object_height", object.getHeight(null)); 1278 String fill_type = getStringAttr(attrs, "fill_type"); 1279 int x = aee.evaluate(getStringAttr(attrs, "x")); 1280 int y = aee.evaluate(getStringAttr(attrs, "y")); 1281 int w = aee.evaluate(getStringAttr(attrs, "width")); 1282 int h = aee.evaluate(getStringAttr(attrs, "height")); 1283 if (getInt("width") == -1) { 1284 x -= w; 1285 } 1286 if (getInt("height") == -1) { 1287 y -= h; 1288 } 1289 1290 if (alpha != null) { 1291 if ("tile".equals(fill_type)) { 1292 StringTokenizer tokenizer = new StringTokenizer(alpha, ":"); 1293 float[] alphas = new float[tokenizer.countTokens()]; 1294 for (int i = 0; i < alphas.length; i++) { 1295 alphas[i] = Float.parseFloat(tokenizer.nextToken()); 1296 } 1297 tileImage(g, object, x, y, w, h, alphas); 1298 } else { 1299 float a = Float.parseFloat(alpha); 1300 if (g instanceof Graphics2D) { 1301 Graphics2D g2 = (Graphics2D)g; 1302 Composite oldComp = g2.getComposite(); 1303 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a)); 1304 g2.drawImage(object, x, y, w, h, null); 1305 g2.setComposite(oldComp); 1306 } 1307 } 1308 } else { 1309 g.drawImage(object, x, y, w, h, null); 1310 } 1311 } 1312 drawIcon(Node node, Graphics g, JInternalFrame jif)1313 protected void drawIcon(Node node, Graphics g, JInternalFrame jif) { 1314 Icon icon = jif.getFrameIcon(); 1315 if (icon == null) { 1316 return; 1317 } 1318 1319 NamedNodeMap attrs = node.getAttributes(); 1320 String alpha = getStringAttr(attrs, "alpha"); 1321 int x = aee.evaluate(getStringAttr(attrs, "x")); 1322 int y = aee.evaluate(getStringAttr(attrs, "y")); 1323 int w = aee.evaluate(getStringAttr(attrs, "width")); 1324 int h = aee.evaluate(getStringAttr(attrs, "height")); 1325 if (getInt("width") == -1) { 1326 x -= w; 1327 } 1328 if (getInt("height") == -1) { 1329 y -= h; 1330 } 1331 1332 if (alpha != null) { 1333 float a = Float.parseFloat(alpha); 1334 if (g instanceof Graphics2D) { 1335 Graphics2D g2 = (Graphics2D)g; 1336 Composite oldComp = g2.getComposite(); 1337 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a)); 1338 icon.paintIcon(jif, g, x, y); 1339 g2.setComposite(oldComp); 1340 } 1341 } else { 1342 icon.paintIcon(jif, g, x, y); 1343 } 1344 } 1345 drawInclude(Node node, Graphics g, JInternalFrame jif)1346 protected void drawInclude(Node node, Graphics g, JInternalFrame jif) { 1347 int oldWidth = getInt("width"); 1348 int oldHeight = getInt("height"); 1349 1350 NamedNodeMap attrs = node.getAttributes(); 1351 int x = aee.evaluate(getStringAttr(attrs, "x"), 0); 1352 int y = aee.evaluate(getStringAttr(attrs, "y"), 0); 1353 int w = aee.evaluate(getStringAttr(attrs, "width"), -1); 1354 int h = aee.evaluate(getStringAttr(attrs, "height"), -1); 1355 1356 if (w != -1) { 1357 variables.put("width", w); 1358 } 1359 if (h != -1) { 1360 variables.put("height", h); 1361 } 1362 1363 Node draw_ops = getNode("draw_ops", new String[] { 1364 "name", getStringAttr(node, "name") 1365 }); 1366 g.translate(x, y); 1367 draw(draw_ops, g, jif); 1368 g.translate(-x, -y); 1369 1370 if (w != -1) { 1371 variables.put("width", oldWidth); 1372 } 1373 if (h != -1) { 1374 variables.put("height", oldHeight); 1375 } 1376 } 1377 draw(Node draw_ops, Graphics g, JInternalFrame jif)1378 protected void draw(Node draw_ops, Graphics g, JInternalFrame jif) { 1379 if (draw_ops != null) { 1380 NodeList nodes = draw_ops.getChildNodes(); 1381 if (nodes != null) { 1382 Shape oldClip = g.getClip(); 1383 for (int i = 0; i < nodes.getLength(); i++) { 1384 Node child = nodes.item(i); 1385 if (child.getNodeType() == Node.ELEMENT_NODE) { 1386 try { 1387 String name = child.getNodeName(); 1388 if ("include".equals(name)) { 1389 drawInclude(child, g, jif); 1390 } else if ("arc".equals(name)) { 1391 drawArc(child, g); 1392 } else if ("clip".equals(name)) { 1393 setClip(child, g); 1394 } else if ("gradient".equals(name)) { 1395 drawGradient(child, g); 1396 } else if ("gtk_arrow".equals(name)) { 1397 drawGTKArrow(child, g); 1398 } else if ("gtk_box".equals(name)) { 1399 drawGTKBox(child, g); 1400 } else if ("gtk_vline".equals(name)) { 1401 drawGTKVLine(child, g); 1402 } else if ("image".equals(name)) { 1403 drawImage(child, g); 1404 } else if ("icon".equals(name)) { 1405 drawIcon(child, g, jif); 1406 } else if ("line".equals(name)) { 1407 drawLine(child, g); 1408 } else if ("rectangle".equals(name)) { 1409 drawRectangle(child, g); 1410 } else if ("tint".equals(name)) { 1411 drawTint(child, g); 1412 } else if ("tile".equals(name)) { 1413 drawTile(child, g, jif); 1414 } else if ("title".equals(name)) { 1415 drawTitle(child, g, jif); 1416 } else { 1417 System.err.println("Unknown Metacity drawing op: "+child); 1418 } 1419 } catch (NumberFormatException ex) { 1420 logError(themeName, ex); 1421 } 1422 } 1423 } 1424 g.setClip(oldClip); 1425 } 1426 } 1427 } 1428 drawPiece(Node frame_style, Graphics g, String position, int x, int y, int width, int height, JInternalFrame jif)1429 protected void drawPiece(Node frame_style, Graphics g, String position, int x, int y, 1430 int width, int height, JInternalFrame jif) { 1431 Node piece = getNode(frame_style, "piece", new String[] { "position", position }); 1432 if (piece != null) { 1433 Node draw_ops; 1434 String draw_ops_name = getStringAttr(piece, "draw_ops"); 1435 if (draw_ops_name != null) { 1436 draw_ops = getNode("draw_ops", new String[] { "name", draw_ops_name }); 1437 } else { 1438 draw_ops = getNode(piece, "draw_ops", null); 1439 } 1440 variables.put("width", width); 1441 variables.put("height", height); 1442 g.translate(x, y); 1443 draw(draw_ops, g, jif); 1444 g.translate(-x, -y); 1445 } 1446 } 1447 1448 getBorderInsets(SynthContext context, Insets insets)1449 Insets getBorderInsets(SynthContext context, Insets insets) { 1450 updateFrameGeometry(context); 1451 1452 if (insets == null) { 1453 insets = new Insets(0, 0, 0, 0); 1454 } 1455 insets.top = ((Insets)frameGeometry.get("title_border")).top; 1456 insets.bottom = getInt("bottom_height"); 1457 insets.left = getInt("left_width"); 1458 insets.right = getInt("right_width"); 1459 return insets; 1460 } 1461 1462 updateFrameGeometry(SynthContext context)1463 private void updateFrameGeometry(SynthContext context) { 1464 this.context = context; 1465 JComponent comp = context.getComponent(); 1466 JComponent titlePane = findChild(comp, "InternalFrame.northPane"); 1467 1468 JInternalFrame jif = null; 1469 if (comp instanceof JInternalFrame) { 1470 jif = (JInternalFrame)comp; 1471 } else if (comp instanceof JInternalFrame.JDesktopIcon) { 1472 jif = ((JInternalFrame.JDesktopIcon)comp).getInternalFrame(); 1473 } else { 1474 assert false : "component is not JInternalFrame or JInternalFrame.JDesktopIcon"; 1475 return; 1476 } 1477 1478 if (frame_style_set == null) { 1479 Node window = getNode("window", new String[]{"type", "normal"}); 1480 1481 if (window != null) { 1482 frame_style_set = getNode("frame_style_set", 1483 new String[] {"name", getStringAttr(window, "style_set")}); 1484 } 1485 1486 if (frame_style_set == null) { 1487 frame_style_set = getNode("frame_style_set", new String[] {"name", "normal"}); 1488 } 1489 } 1490 1491 if (frame_style_set != null) { 1492 Node frame = getNode(frame_style_set, "frame", new String[] { 1493 "focus", (jif.isSelected() ? "yes" : "no"), 1494 "state", (jif.isMaximum() ? "maximized" : "normal") 1495 }); 1496 1497 if (frame != null) { 1498 Node frame_style = getNode("frame_style", new String[] { 1499 "name", getStringAttr(frame, "style") 1500 }); 1501 if (frame_style != null) { 1502 Map gm = frameGeometries.get(getStringAttr(frame_style, "geometry")); 1503 1504 setFrameGeometry(titlePane, gm); 1505 } 1506 } 1507 } 1508 } 1509 1510 logError(String themeName, Exception ex)1511 protected static void logError(String themeName, Exception ex) { 1512 logError(themeName, ex.toString()); 1513 } 1514 logError(String themeName, String msg)1515 protected static void logError(String themeName, String msg) { 1516 if (!errorLogged) { 1517 System.err.println("Exception in Metacity for theme \""+themeName+"\": "+msg); 1518 errorLogged = true; 1519 } 1520 } 1521 1522 1523 // XML Parsing 1524 1525 getXMLDoc(final URL xmlFile)1526 protected static Document getXMLDoc(final URL xmlFile) 1527 throws IOException, 1528 ParserConfigurationException, 1529 SAXException { 1530 if (documentBuilder == null) { 1531 documentBuilder = 1532 DocumentBuilderFactory.newInstance().newDocumentBuilder(); 1533 } 1534 InputStream inputStream = 1535 AccessController.doPrivileged(new PrivilegedAction<InputStream>() { 1536 public InputStream run() { 1537 try { 1538 return new BufferedInputStream(xmlFile.openStream()); 1539 } catch (IOException ex) { 1540 return null; 1541 } 1542 } 1543 }); 1544 1545 Document doc = null; 1546 if (inputStream != null) { 1547 doc = documentBuilder.parse(inputStream); 1548 } 1549 return doc; 1550 } 1551 1552 getNodesByName(Node parent, String name)1553 protected Node[] getNodesByName(Node parent, String name) { 1554 NodeList nodes = parent.getChildNodes(); // ElementNode 1555 int n = nodes.getLength(); 1556 ArrayList<Node> list = new ArrayList<Node>(); 1557 for (int i=0; i < n; i++) { 1558 Node node = nodes.item(i); 1559 if (name.equals(node.getNodeName())) { 1560 list.add(node); 1561 } 1562 } 1563 return list.toArray(new Node[list.size()]); 1564 } 1565 1566 1567 getNode(String tagName, String[] attrs)1568 protected Node getNode(String tagName, String[] attrs) { 1569 NodeList nodes = xmlDoc.getElementsByTagName(tagName); 1570 return (nodes != null) ? getNode(nodes, tagName, attrs) : null; 1571 } 1572 getNode(Node parent, String name, String[] attrs)1573 protected Node getNode(Node parent, String name, String[] attrs) { 1574 Node node = null; 1575 NodeList nodes = parent.getChildNodes(); 1576 if (nodes != null) { 1577 node = getNode(nodes, name, attrs); 1578 } 1579 if (node == null) { 1580 String inheritFrom = getStringAttr(parent, "parent"); 1581 if (inheritFrom != null) { 1582 Node inheritFromNode = getNode(parent.getParentNode(), 1583 parent.getNodeName(), 1584 new String[] { "name", inheritFrom }); 1585 if (inheritFromNode != null) { 1586 node = getNode(inheritFromNode, name, attrs); 1587 } 1588 } 1589 } 1590 return node; 1591 } 1592 getNode(NodeList nodes, String name, String[] attrs)1593 protected Node getNode(NodeList nodes, String name, String[] attrs) { 1594 int n = nodes.getLength(); 1595 for (int i=0; i < n; i++) { 1596 Node node = nodes.item(i); 1597 if (name.equals(node.getNodeName())) { 1598 if (attrs != null) { 1599 NamedNodeMap nodeAttrs = node.getAttributes(); 1600 if (nodeAttrs != null) { 1601 boolean matches = true; 1602 int nAttrs = attrs.length / 2; 1603 for (int a = 0; a < nAttrs; a++) { 1604 String aName = attrs[a * 2]; 1605 String aValue = attrs[a * 2 + 1]; 1606 Node attr = nodeAttrs.getNamedItem(aName); 1607 if (attr == null || 1608 aValue != null && !aValue.equals(attr.getNodeValue())) { 1609 matches = false; 1610 break; 1611 } 1612 } 1613 if (matches) { 1614 return node; 1615 } 1616 } 1617 } else { 1618 return node; 1619 } 1620 } 1621 } 1622 return null; 1623 } 1624 getStringAttr(Node node, String name)1625 protected String getStringAttr(Node node, String name) { 1626 String value = null; 1627 NamedNodeMap attrs = node.getAttributes(); 1628 if (attrs != null) { 1629 value = getStringAttr(attrs, name); 1630 if (value == null) { 1631 String inheritFrom = getStringAttr(attrs, "parent"); 1632 if (inheritFrom != null) { 1633 Node inheritFromNode = getNode(node.getParentNode(), 1634 node.getNodeName(), 1635 new String[] { "name", inheritFrom }); 1636 if (inheritFromNode != null) { 1637 value = getStringAttr(inheritFromNode, name); 1638 } 1639 } 1640 } 1641 } 1642 return value; 1643 } 1644 getStringAttr(NamedNodeMap attrs, String name)1645 protected String getStringAttr(NamedNodeMap attrs, String name) { 1646 Node item = attrs.getNamedItem(name); 1647 return (item != null) ? item.getNodeValue() : null; 1648 } 1649 getBooleanAttr(Node node, String name, boolean fallback)1650 protected boolean getBooleanAttr(Node node, String name, boolean fallback) { 1651 String str = getStringAttr(node, name); 1652 if (str != null) { 1653 return Boolean.valueOf(str).booleanValue(); 1654 } 1655 return fallback; 1656 } 1657 getIntAttr(Node node, String name, int fallback)1658 protected int getIntAttr(Node node, String name, int fallback) { 1659 String str = getStringAttr(node, name); 1660 int value = fallback; 1661 if (str != null) { 1662 try { 1663 value = Integer.parseInt(str); 1664 } catch (NumberFormatException ex) { 1665 logError(themeName, ex); 1666 } 1667 } 1668 return value; 1669 } 1670 getFloatAttr(Node node, String name, float fallback)1671 protected float getFloatAttr(Node node, String name, float fallback) { 1672 String str = getStringAttr(node, name); 1673 float value = fallback; 1674 if (str != null) { 1675 try { 1676 value = Float.parseFloat(str); 1677 } catch (NumberFormatException ex) { 1678 logError(themeName, ex); 1679 } 1680 } 1681 return value; 1682 } 1683 1684 1685 parseColor(String str)1686 protected Color parseColor(String str) { 1687 StringTokenizer tokenizer = new StringTokenizer(str, "/"); 1688 int n = tokenizer.countTokens(); 1689 if (n > 1) { 1690 String function = tokenizer.nextToken(); 1691 if ("shade".equals(function)) { 1692 assert (n == 3); 1693 Color c = parseColor2(tokenizer.nextToken()); 1694 float alpha = Float.parseFloat(tokenizer.nextToken()); 1695 return GTKColorType.adjustColor(c, 1.0F, alpha, alpha); 1696 } else if ("blend".equals(function)) { 1697 assert (n == 4); 1698 Color bg = parseColor2(tokenizer.nextToken()); 1699 Color fg = parseColor2(tokenizer.nextToken()); 1700 float alpha = Float.parseFloat(tokenizer.nextToken()); 1701 if (alpha > 1.0f) { 1702 alpha = 1.0f / alpha; 1703 } 1704 1705 return new Color((int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)), 1706 (int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)), 1707 (int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha))); 1708 } else { 1709 System.err.println("Unknown Metacity color function="+str); 1710 return null; 1711 } 1712 } else { 1713 return parseColor2(str); 1714 } 1715 } 1716 parseColor2(String str)1717 protected Color parseColor2(String str) { 1718 Color c = null; 1719 if (str.startsWith("gtk:")) { 1720 int i1 = str.indexOf('['); 1721 if (i1 > 3) { 1722 String typeStr = str.substring(4, i1).toLowerCase(); 1723 int i2 = str.indexOf(']'); 1724 if (i2 > i1+1) { 1725 String stateStr = str.substring(i1+1, i2).toUpperCase(); 1726 int state = -1; 1727 if ("ACTIVE".equals(stateStr)) { 1728 state = PRESSED; 1729 } else if ("INSENSITIVE".equals(stateStr)) { 1730 state = DISABLED; 1731 } else if ("NORMAL".equals(stateStr)) { 1732 state = ENABLED; 1733 } else if ("PRELIGHT".equals(stateStr)) { 1734 state = MOUSE_OVER; 1735 } else if ("SELECTED".equals(stateStr)) { 1736 state = SELECTED; 1737 } 1738 ColorType type = null; 1739 if ("fg".equals(typeStr)) { 1740 type = GTKColorType.FOREGROUND; 1741 } else if ("bg".equals(typeStr)) { 1742 type = GTKColorType.BACKGROUND; 1743 } else if ("base".equals(typeStr)) { 1744 type = GTKColorType.TEXT_BACKGROUND; 1745 } else if ("text".equals(typeStr)) { 1746 type = GTKColorType.TEXT_FOREGROUND; 1747 } else if ("dark".equals(typeStr)) { 1748 type = GTKColorType.DARK; 1749 } else if ("light".equals(typeStr)) { 1750 type = GTKColorType.LIGHT; 1751 } 1752 if (state >= 0 && type != null) { 1753 c = ((GTKStyle)context.getStyle()).getGTKColor(context, state, type); 1754 } 1755 } 1756 } 1757 } 1758 if (c == null) { 1759 c = parseColorString(str); 1760 } 1761 return c; 1762 } 1763 parseColorString(String str)1764 private static Color parseColorString(String str) { 1765 if (str.charAt(0) == '#') { 1766 str = str.substring(1); 1767 1768 int i = str.length(); 1769 1770 if (i < 3 || i > 12 || (i % 3) != 0) { 1771 return null; 1772 } 1773 1774 i /= 3; 1775 1776 int r; 1777 int g; 1778 int b; 1779 1780 try { 1781 r = Integer.parseInt(str.substring(0, i), 16); 1782 g = Integer.parseInt(str.substring(i, i * 2), 16); 1783 b = Integer.parseInt(str.substring(i * 2, i * 3), 16); 1784 } catch (NumberFormatException nfe) { 1785 return null; 1786 } 1787 1788 if (i == 4) { 1789 return new ColorUIResource(r / 65535.0f, g / 65535.0f, b / 65535.0f); 1790 } else if (i == 1) { 1791 return new ColorUIResource(r / 15.0f, g / 15.0f, b / 15.0f); 1792 } else if (i == 2) { 1793 return new ColorUIResource(r, g, b); 1794 } else { 1795 return new ColorUIResource(r / 4095.0f, g / 4095.0f, b / 4095.0f); 1796 } 1797 } else { 1798 return XColors.lookupColor(str); 1799 } 1800 } 1801 1802 class ArithmeticExpressionEvaluator { 1803 private PeekableStringTokenizer tokenizer; 1804 evaluate(String expr)1805 int evaluate(String expr) { 1806 tokenizer = new PeekableStringTokenizer(expr, " \t+-*/%()", true); 1807 return Math.round(expression()); 1808 } 1809 evaluate(String expr, int fallback)1810 int evaluate(String expr, int fallback) { 1811 return (expr != null) ? evaluate(expr) : fallback; 1812 } 1813 expression()1814 public float expression() { 1815 float value = getTermValue(); 1816 boolean done = false; 1817 while (!done && tokenizer.hasMoreTokens()) { 1818 String next = tokenizer.peek(); 1819 if ("+".equals(next) || 1820 "-".equals(next) || 1821 "`max`".equals(next) || 1822 "`min`".equals(next)) { 1823 tokenizer.nextToken(); 1824 float value2 = getTermValue(); 1825 if ("+".equals(next)) { 1826 value += value2; 1827 } else if ("-".equals(next)) { 1828 value -= value2; 1829 } else if ("`max`".equals(next)) { 1830 value = Math.max(value, value2); 1831 } else if ("`min`".equals(next)) { 1832 value = Math.min(value, value2); 1833 } 1834 } else { 1835 done = true; 1836 } 1837 } 1838 return value; 1839 } 1840 getTermValue()1841 public float getTermValue() { 1842 float value = getFactorValue(); 1843 boolean done = false; 1844 while (!done && tokenizer.hasMoreTokens()) { 1845 String next = tokenizer.peek(); 1846 if ("*".equals(next) || "/".equals(next) || "%".equals(next)) { 1847 tokenizer.nextToken(); 1848 float value2 = getFactorValue(); 1849 if ("*".equals(next)) { 1850 value *= value2; 1851 } else if ("/".equals(next)) { 1852 value /= value2; 1853 } else { 1854 value %= value2; 1855 } 1856 } else { 1857 done = true; 1858 } 1859 } 1860 return value; 1861 } 1862 getFactorValue()1863 public float getFactorValue() { 1864 float value; 1865 if ("(".equals(tokenizer.peek())) { 1866 tokenizer.nextToken(); 1867 value = expression(); 1868 tokenizer.nextToken(); // skip right paren 1869 } else { 1870 String token = tokenizer.nextToken(); 1871 if (Character.isDigit(token.charAt(0))) { 1872 value = Float.parseFloat(token); 1873 } else { 1874 Integer i = variables.get(token); 1875 if (i == null) { 1876 i = (Integer)getFrameGeometry().get(token); 1877 } 1878 if (i == null) { 1879 logError(themeName, "Variable \"" + token + "\" not defined"); 1880 return 0; 1881 } 1882 value = (i != null) ? i.intValue() : 0F; 1883 } 1884 } 1885 return value; 1886 } 1887 1888 1889 } 1890 1891 static class PeekableStringTokenizer extends StringTokenizer { 1892 String token = null; 1893 PeekableStringTokenizer(String str, String delim, boolean returnDelims)1894 public PeekableStringTokenizer(String str, String delim, 1895 boolean returnDelims) { 1896 super(str, delim, returnDelims); 1897 peek(); 1898 } 1899 peek()1900 public String peek() { 1901 if (token == null) { 1902 token = nextToken(); 1903 } 1904 return token; 1905 } 1906 hasMoreTokens()1907 public boolean hasMoreTokens() { 1908 return (token != null || super.hasMoreTokens()); 1909 } 1910 nextToken()1911 public String nextToken() { 1912 if (token != null) { 1913 String t = token; 1914 token = null; 1915 if (hasMoreTokens()) { 1916 peek(); 1917 } 1918 return t; 1919 } else { 1920 String token = super.nextToken(); 1921 while ((token.equals(" ") || token.equals("\t")) 1922 && hasMoreTokens()) { 1923 token = super.nextToken(); 1924 } 1925 return token; 1926 } 1927 } 1928 } 1929 1930 1931 static class RoundRectClipShape extends RectangularShape { 1932 static final int TOP_LEFT = 1; 1933 static final int TOP_RIGHT = 2; 1934 static final int BOTTOM_LEFT = 4; 1935 static final int BOTTOM_RIGHT = 8; 1936 1937 int x; 1938 int y; 1939 int width; 1940 int height; 1941 int arcwidth; 1942 int archeight; 1943 int corners; 1944 RoundRectClipShape()1945 public RoundRectClipShape() { 1946 } 1947 RoundRectClipShape(int x, int y, int w, int h, int arcw, int arch, int corners)1948 public RoundRectClipShape(int x, int y, int w, int h, 1949 int arcw, int arch, int corners) { 1950 setRoundedRect(x, y, w, h, arcw, arch, corners); 1951 } 1952 setRoundedRect(int x, int y, int w, int h, int arcw, int arch, int corners)1953 public void setRoundedRect(int x, int y, int w, int h, 1954 int arcw, int arch, int corners) { 1955 this.corners = corners; 1956 this.x = x; 1957 this.y = y; 1958 this.width = w; 1959 this.height = h; 1960 this.arcwidth = arcw; 1961 this.archeight = arch; 1962 } 1963 getX()1964 public double getX() { 1965 return (double)x; 1966 } 1967 getY()1968 public double getY() { 1969 return (double)y; 1970 } 1971 getWidth()1972 public double getWidth() { 1973 return (double)width; 1974 } 1975 getHeight()1976 public double getHeight() { 1977 return (double)height; 1978 } 1979 getArcWidth()1980 public double getArcWidth() { 1981 return (double)arcwidth; 1982 } 1983 getArcHeight()1984 public double getArcHeight() { 1985 return (double)archeight; 1986 } 1987 isEmpty()1988 public boolean isEmpty() { 1989 return false; // Not called 1990 } 1991 getBounds2D()1992 public Rectangle2D getBounds2D() { 1993 return null; // Not called 1994 } 1995 getCornerFlags()1996 public int getCornerFlags() { 1997 return corners; 1998 } 1999 setFrame(double x, double y, double w, double h)2000 public void setFrame(double x, double y, double w, double h) { 2001 // Not called 2002 } 2003 contains(double x, double y)2004 public boolean contains(double x, double y) { 2005 return false; // Not called 2006 } 2007 classify(double coord, double left, double right, double arcsize)2008 private int classify(double coord, double left, double right, double arcsize) { 2009 return 0; // Not called 2010 } 2011 intersects(double x, double y, double w, double h)2012 public boolean intersects(double x, double y, double w, double h) { 2013 return false; // Not called 2014 } 2015 contains(double x, double y, double w, double h)2016 public boolean contains(double x, double y, double w, double h) { 2017 return false; // Not called 2018 } 2019 getPathIterator(AffineTransform at)2020 public PathIterator getPathIterator(AffineTransform at) { 2021 return new RoundishRectIterator(this, at); 2022 } 2023 2024 2025 static class RoundishRectIterator implements PathIterator { 2026 double x, y, w, h, aw, ah; 2027 AffineTransform affine; 2028 int index; 2029 2030 double ctrlpts[][]; 2031 int types[]; 2032 2033 private static final double angle = Math.PI / 4.0; 2034 private static final double a = 1.0 - Math.cos(angle); 2035 private static final double b = Math.tan(angle); 2036 private static final double c = Math.sqrt(1.0 + b * b) - 1 + a; 2037 private static final double cv = 4.0 / 3.0 * a * b / c; 2038 private static final double acv = (1.0 - cv) / 2.0; 2039 2040 // For each array: 2041 // 4 values for each point {v0, v1, v2, v3}: 2042 // point = (x + v0 * w + v1 * arcWidth, 2043 // y + v2 * h + v3 * arcHeight); 2044 private static final double CtrlPtTemplate[][] = { 2045 { 0.0, 0.0, 1.0, 0.0 }, /* BOTTOM LEFT corner */ 2046 { 0.0, 0.0, 1.0, -0.5 }, /* BOTTOM LEFT arc start */ 2047 { 0.0, 0.0, 1.0, -acv, /* BOTTOM LEFT arc curve */ 2048 0.0, acv, 1.0, 0.0, 2049 0.0, 0.5, 1.0, 0.0 }, 2050 { 1.0, 0.0, 1.0, 0.0 }, /* BOTTOM RIGHT corner */ 2051 { 1.0, -0.5, 1.0, 0.0 }, /* BOTTOM RIGHT arc start */ 2052 { 1.0, -acv, 1.0, 0.0, /* BOTTOM RIGHT arc curve */ 2053 1.0, 0.0, 1.0, -acv, 2054 1.0, 0.0, 1.0, -0.5 }, 2055 { 1.0, 0.0, 0.0, 0.0 }, /* TOP RIGHT corner */ 2056 { 1.0, 0.0, 0.0, 0.5 }, /* TOP RIGHT arc start */ 2057 { 1.0, 0.0, 0.0, acv, /* TOP RIGHT arc curve */ 2058 1.0, -acv, 0.0, 0.0, 2059 1.0, -0.5, 0.0, 0.0 }, 2060 { 0.0, 0.0, 0.0, 0.0 }, /* TOP LEFT corner */ 2061 { 0.0, 0.5, 0.0, 0.0 }, /* TOP LEFT arc start */ 2062 { 0.0, acv, 0.0, 0.0, /* TOP LEFT arc curve */ 2063 0.0, 0.0, 0.0, acv, 2064 0.0, 0.0, 0.0, 0.5 }, 2065 {}, /* Closing path element */ 2066 }; 2067 private static final int CornerFlags[] = { 2068 RoundRectClipShape.BOTTOM_LEFT, 2069 RoundRectClipShape.BOTTOM_RIGHT, 2070 RoundRectClipShape.TOP_RIGHT, 2071 RoundRectClipShape.TOP_LEFT, 2072 }; 2073 RoundishRectIterator(RoundRectClipShape rr, AffineTransform at)2074 RoundishRectIterator(RoundRectClipShape rr, AffineTransform at) { 2075 this.x = rr.getX(); 2076 this.y = rr.getY(); 2077 this.w = rr.getWidth(); 2078 this.h = rr.getHeight(); 2079 this.aw = Math.min(w, Math.abs(rr.getArcWidth())); 2080 this.ah = Math.min(h, Math.abs(rr.getArcHeight())); 2081 this.affine = at; 2082 if (w < 0 || h < 0) { 2083 // Don't draw anything... 2084 ctrlpts = new double[0][]; 2085 types = new int[0]; 2086 } else { 2087 int corners = rr.getCornerFlags(); 2088 int numedges = 5; // 4xCORNER_POINT, CLOSE 2089 for (int i = 1; i < 0x10; i <<= 1) { 2090 // Add one for each corner that has a curve 2091 if ((corners & i) != 0) numedges++; 2092 } 2093 ctrlpts = new double[numedges][]; 2094 types = new int[numedges]; 2095 int j = 0; 2096 for (int i = 0; i < 4; i++) { 2097 types[j] = SEG_LINETO; 2098 if ((corners & CornerFlags[i]) == 0) { 2099 ctrlpts[j++] = CtrlPtTemplate[i*3+0]; 2100 } else { 2101 ctrlpts[j++] = CtrlPtTemplate[i*3+1]; 2102 types[j] = SEG_CUBICTO; 2103 ctrlpts[j++] = CtrlPtTemplate[i*3+2]; 2104 } 2105 } 2106 types[j] = SEG_CLOSE; 2107 ctrlpts[j++] = CtrlPtTemplate[12]; 2108 types[0] = SEG_MOVETO; 2109 } 2110 } 2111 getWindingRule()2112 public int getWindingRule() { 2113 return WIND_NON_ZERO; 2114 } 2115 isDone()2116 public boolean isDone() { 2117 return index >= ctrlpts.length; 2118 } 2119 next()2120 public void next() { 2121 index++; 2122 } 2123 currentSegment(float[] coords)2124 public int currentSegment(float[] coords) { 2125 if (isDone()) { 2126 throw new NoSuchElementException("roundrect iterator out of bounds"); 2127 } 2128 double ctrls[] = ctrlpts[index]; 2129 int nc = 0; 2130 for (int i = 0; i < ctrls.length; i += 4) { 2131 coords[nc++] = (float) (x + ctrls[i + 0] * w + ctrls[i + 1] * aw); 2132 coords[nc++] = (float) (y + ctrls[i + 2] * h + ctrls[i + 3] * ah); 2133 } 2134 if (affine != null) { 2135 affine.transform(coords, 0, coords, 0, nc / 2); 2136 } 2137 return types[index]; 2138 } 2139 currentSegment(double[] coords)2140 public int currentSegment(double[] coords) { 2141 if (isDone()) { 2142 throw new NoSuchElementException("roundrect iterator out of bounds"); 2143 } 2144 double ctrls[] = ctrlpts[index]; 2145 int nc = 0; 2146 for (int i = 0; i < ctrls.length; i += 4) { 2147 coords[nc++] = x + ctrls[i + 0] * w + ctrls[i + 1] * aw; 2148 coords[nc++] = y + ctrls[i + 2] * h + ctrls[i + 3] * ah; 2149 } 2150 if (affine != null) { 2151 affine.transform(coords, 0, coords, 0, nc / 2); 2152 } 2153 return types[index]; 2154 } 2155 } 2156 } 2157 } 2158