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