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