1. /*
  2. * @(#)XPStyle.java 1.10 03/03/20
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * <p>These classes are designed to be used while the
  9. * corresponding <code>LookAndFeel</code> class has been installed
  10. * (<code>UIManager.setLookAndFeel(new <i>XXX</i>LookAndFeel())</code>).
  11. * Using them while a different <code>LookAndFeel</code> is installed
  12. * may produce unexpected results, including exceptions.
  13. * Additionally, changing the <code>LookAndFeel</code>
  14. * maintained by the <code>UIManager</code> without updating the
  15. * corresponding <code>ComponentUI</code> of any
  16. * <code>JComponent</code>s may also produce unexpected results,
  17. * such as the wrong colors showing up, and is generally not
  18. * encouraged.
  19. *
  20. */
  21. package com.sun.java.swing.plaf.windows;
  22. import java.awt.*;
  23. import java.awt.image.*;
  24. import java.io.*;
  25. import java.security.AccessController;
  26. import java.util.*;
  27. import java.util.prefs.*;
  28. import javax.swing.*;
  29. import javax.swing.border.*;
  30. import javax.swing.plaf.*;
  31. import javax.swing.text.JTextComponent;
  32. import sun.security.action.GetPropertyAction;
  33. /**
  34. * Implements Windows XP Styles for the Windows Look and Feel.
  35. *
  36. * @version 1.10 03/20/03
  37. * @author Leif Samuelsson
  38. */
  39. class XPStyle {
  40. // Singleton instance of this class
  41. private static XPStyle xp;
  42. private static Boolean themeActive = null;
  43. private HashMap map;
  44. private String styleFile;
  45. private String themeFile;
  46. static {
  47. invalidateStyle();
  48. }
  49. /** Static method for clearing the hashmap and loading the
  50. * current XP style and theme
  51. */
  52. static synchronized void invalidateStyle() {
  53. xp = null;
  54. themeActive = null;
  55. }
  56. /** Get the singleton instance of this class
  57. *
  58. * @return the signleton instance of this class or null if XP styles
  59. * are not active or if this is not Windows XP
  60. */
  61. static synchronized XPStyle getXP() {
  62. if (themeActive == null) {
  63. Toolkit toolkit = Toolkit.getDefaultToolkit();
  64. themeActive = (Boolean)toolkit.getDesktopProperty("win.xpstyle.themeActive");
  65. if (themeActive == null) {
  66. themeActive = Boolean.FALSE;
  67. }
  68. if (themeActive.booleanValue() &&
  69. AccessController.doPrivileged(new GetPropertyAction("swing.noxp")) == null) {
  70. xp = new XPStyle();
  71. }
  72. }
  73. return xp;
  74. }
  75. /** Get a named <code>String</code> value from the current style
  76. *
  77. * @param key a <code>String</code>
  78. * @return a <code>String</code> or null if key is not found
  79. * in the current style
  80. */
  81. synchronized String getString(String key) {
  82. return (String)map.get(key);
  83. }
  84. /** Get a named <code>int</code> value from the current style
  85. *
  86. * @param key a <code>String</code>
  87. * @return an <code>int</code> or null if key is not found
  88. * in the current style
  89. */
  90. int getInt(String key, int fallback) {
  91. return parseInt(getString(key), fallback);
  92. }
  93. private int parseInt(String str, int fallback) {
  94. if (str != null) {
  95. try {
  96. return Integer.parseInt(str);
  97. } catch (NumberFormatException nfe) {
  98. abandonXP();
  99. }
  100. }
  101. return fallback;
  102. }
  103. /** Get a named <code>Dimension</code> value from the current style
  104. *
  105. * @param key a <code>String</code>
  106. * @return a <code>Dimension</code> or null if key is not found
  107. * in the current style
  108. */
  109. synchronized Dimension getDimension(String key) {
  110. Dimension d = (Dimension)map.get("Dimension "+key);
  111. if (d == null) {
  112. String str = getString(key);
  113. if (str != null) {
  114. StringTokenizer tok = new StringTokenizer(str, " \t,");
  115. d = new Dimension(parseInt(tok.nextToken(), 0),
  116. parseInt(tok.nextToken(), 0));
  117. map.put("Dimension "+key, d);
  118. }
  119. }
  120. return d;
  121. }
  122. /** Get a named <code>Point</code> (e.g. a location or an offset) value
  123. * from the current style
  124. *
  125. * @param key a <code>String</code>
  126. * @return a <code>Point</code> or null if key is not found
  127. * in the current style
  128. */
  129. synchronized Point getPoint(String key) {
  130. Point p = (Point)map.get("Point "+key);
  131. if (p == null) {
  132. String str = getString(key);
  133. if (str != null) {
  134. StringTokenizer tok = new StringTokenizer(str, " \t,");
  135. p = new Point(parseInt(tok.nextToken(), 0),
  136. parseInt(tok.nextToken(), 0));
  137. map.put("Point "+key, p);
  138. }
  139. }
  140. return p;
  141. }
  142. /** Get a named <code>Insets</code> value from the current style
  143. *
  144. * @param key a <code>String</code>
  145. * @return an <code>Insets</code> object or null if key is not found
  146. * in the current style
  147. */
  148. synchronized Insets getMargin(String key) {
  149. Insets insets = (Insets)map.get("Margin "+key);
  150. if (insets == null) {
  151. String str = getString(key);
  152. if (str != null) {
  153. StringTokenizer tok = new StringTokenizer(str, " \t,");
  154. insets = new Insets(0, 0, 0, 0);
  155. insets.left = parseInt(tok.nextToken(), 0);
  156. insets.right = parseInt(tok.nextToken(), 0);
  157. insets.top = parseInt(tok.nextToken(), 0);
  158. insets.bottom = parseInt(tok.nextToken(), 0);
  159. map.put("Margin "+key, insets);
  160. }
  161. }
  162. return insets;
  163. }
  164. /** Get a named <code>Color</code> value from the current style
  165. *
  166. * @param key a <code>String</code>
  167. * @return a <code>Color</code> or null if key is not found
  168. * in the current style
  169. */
  170. synchronized Color getColor(String key, Color fallback) {
  171. Color color = (Color)map.get("Color "+key);
  172. if (color == null) {
  173. String str = getString(key);
  174. if (str != null) {
  175. StringTokenizer tok = new StringTokenizer(str, " \t,");
  176. int r = parseInt(tok.nextToken(), 0);
  177. int g = parseInt(tok.nextToken(), 0);
  178. int b = parseInt(tok.nextToken(), 0);
  179. if (r >= 0 && g >=0 && b >= 0) {
  180. color = new Color(r, g, b);
  181. map.put("Color "+key, color);
  182. }
  183. }
  184. }
  185. return (color != null) ? color : fallback;
  186. }
  187. /** Get a named <code>Border</code> value from the current style
  188. *
  189. * @param key a <code>String</code>
  190. * @return a <code>Border</code> or null if key is not found
  191. * in the current style or if the style for the particular
  192. * category is not defined as "borderfill".
  193. */
  194. synchronized Border getBorder(String category) {
  195. Border border = (Border)map.get("Border "+category);
  196. if (border == null) {
  197. String bgType = getString(category + ".bgtype");
  198. if ("borderfill".equalsIgnoreCase(bgType)) {
  199. int thickness = getInt(category + ".bordersize", 1);
  200. Color color = getColor(category + ".bordercolor", Color.black);
  201. border = new XPFillBorder(color, thickness);
  202. } else if ("imagefile".equalsIgnoreCase(bgType)) {
  203. Insets m = getMargin(category + ".sizingmargins");
  204. if (m != null) {
  205. border = new XPEmptyBorder(m);
  206. }
  207. }
  208. if (border != null) {
  209. map.put("Border "+category, border);
  210. }
  211. }
  212. return border;
  213. }
  214. private class XPFillBorder extends LineBorder implements UIResource {
  215. XPFillBorder(Color color, int thickness) {
  216. super(color, thickness);
  217. }
  218. public Insets getBorderInsets(Component c) {
  219. return getBorderInsets(c, new Insets(0,0,0,0));
  220. }
  221. public Insets getBorderInsets(Component c, Insets insets) {
  222. Insets margin = null;
  223. //
  224. // Ideally we'd have an interface defined for classes which
  225. // support margins (to avoid this hackery), but we've
  226. // decided against it for simplicity
  227. //
  228. if (c instanceof AbstractButton) {
  229. margin = ((AbstractButton)c).getMargin();
  230. } else if (c instanceof JToolBar) {
  231. margin = ((JToolBar)c).getMargin();
  232. } else if (c instanceof JTextComponent) {
  233. margin = ((JTextComponent)c).getMargin();
  234. }
  235. insets.top = (margin != null? margin.top : 0) + thickness;
  236. insets.left = (margin != null? margin.left : 0) + thickness;
  237. insets.bottom = (margin != null? margin.bottom : 0) + thickness;
  238. insets.right = (margin != null? margin.right : 0) + thickness;
  239. return insets;
  240. }
  241. }
  242. private class XPEmptyBorder extends EmptyBorder implements UIResource {
  243. XPEmptyBorder(Insets m) {
  244. super(m.top+2, m.left+2, m.bottom+2, m.right+2);
  245. }
  246. public Insets getBorderInsets(Component c) {
  247. return getBorderInsets(c, getBorderInsets());
  248. }
  249. public Insets getBorderInsets(Component c, Insets insets) {
  250. Insets margin = null;
  251. if (c instanceof AbstractButton) {
  252. margin = ((AbstractButton)c).getMargin();
  253. } else if (c instanceof JToolBar) {
  254. margin = ((JToolBar)c).getMargin();
  255. } else if (c instanceof JTextComponent) {
  256. margin = ((JTextComponent)c).getMargin();
  257. }
  258. if (margin != null) {
  259. insets.top = margin.top + 2;
  260. insets.left = margin.left + 2;
  261. insets.bottom = margin.bottom + 2;
  262. insets.right = margin.right + 2;
  263. }
  264. return insets;
  265. }
  266. }
  267. /** Get an <code>XPStyle.Skin</code> object from the current style
  268. * for a named category (component type)
  269. *
  270. * @param category a <code>String</code>
  271. * @return an <code>XPStyle.Skin</code> object or null if the category is
  272. * not found in the current style
  273. */
  274. synchronized Skin getSkin(String category) {
  275. Skin skin = (Skin)map.get("Skin "+category);
  276. if (skin == null) {
  277. skin = new Skin(category);
  278. map.put("Skin "+category, skin);
  279. }
  280. return skin;
  281. }
  282. /** A class which encapsulates attributes for a given category
  283. * (component type) and which provides methods for painting backgrounds
  284. * and glyphs
  285. */
  286. class Skin {
  287. private Image image;
  288. private Insets contentMargin;
  289. private int w, h;
  290. private Image scaledImage;
  291. private Image glyphImage;
  292. private int frameCount;
  293. private Insets paintMargin;
  294. private boolean tile;
  295. private boolean sourceShrink;
  296. private boolean verticalFrames;
  297. /** The background image for the skin.
  298. * If this is null then width and height are not valid.
  299. */
  300. Image getImage() {
  301. if (image != null) {
  302. return image;
  303. } else if (scaledImage != null) {
  304. return scaledImage;
  305. } else {
  306. return null;
  307. }
  308. }
  309. /** The content margin for a component skin is useful in
  310. * determining the minimum and preferred sizes for a component.
  311. */
  312. Insets getContentMargin() {
  313. return contentMargin;
  314. }
  315. /** The width of the source image for this skin.
  316. * Not valid if getImage() returns null
  317. */
  318. int getWidth() {
  319. return w;
  320. }
  321. /** The height of the source image for this skin.
  322. * Not valid if getImage() returns null
  323. */
  324. int getHeight() {
  325. return h;
  326. }
  327. private Skin(String category) {
  328. XPStyle xp = getXP();
  329. // Load main image
  330. image = xp.getImage(category+".imagefile",
  331. xp.getBoolean(category+".transparent", true));
  332. // Look for additional (prescaled) images
  333. int n = 0;
  334. while (true) {
  335. if (xp.getString(category+".imagefile"+(n+1)) == null) {
  336. break;
  337. }
  338. n++;
  339. }
  340. if (n > 0) {
  341. int index = (n / 2) + 1;
  342. if ("dpi".equalsIgnoreCase(getString(category+".imageselecttype"))) {
  343. int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
  344. index = 1;
  345. for (int i = n; i >= 1; i--) {
  346. int minDpi = xp.getInt(category+".mindpi"+i, -1);
  347. if (minDpi > 0 && dpi >= minDpi) {
  348. index = i;
  349. break;
  350. }
  351. }
  352. }
  353. scaledImage = xp.getImage(category+".imagefile"+index,
  354. xp.getBoolean(category+".transparent", false) ||
  355. xp.getBoolean(category+".glyphtransparent", false));
  356. }
  357. frameCount = getInt(category+".imagecount", 1);
  358. paintMargin = getMargin(category+".sizingmargins");
  359. contentMargin = getMargin(category+".contentmargins");
  360. tile = "tile".equalsIgnoreCase(getString(category+".sizingtype"));
  361. sourceShrink = getBoolean(category+".sourceshrink", false);
  362. verticalFrames = "vertical".equalsIgnoreCase(getString(category+".imagelayout"));
  363. glyphImage = xp.getImage(category+".glyphimagefile",
  364. xp.getBoolean(category+".glyphtransparent", false));
  365. Image im = image;
  366. if (im == null && scaledImage != null) {
  367. im = scaledImage;
  368. }
  369. if (im != null) {
  370. // Sanity check
  371. if (frameCount < 1) {
  372. abandonXP();
  373. }
  374. this.w = im.getWidth(null) / (verticalFrames ? 1 : frameCount);
  375. this.h = im.getHeight(null) / (verticalFrames ? frameCount : 1);
  376. }
  377. }
  378. /** Paint a skin at x, y.
  379. *
  380. * @param g the graphics context to use for painting
  381. * @param dx the destination <i>x</i> coordinate.
  382. * @param dy the destination <i>y</i> coordinate.
  383. * @param index which subimage to paint (usually depends on component state)
  384. */
  385. void paintSkin(Graphics g, int dx, int dy, int index) {
  386. paintSkin(g, dx, dy, w, h, index);
  387. }
  388. /** Paint a skin in an area defined by a rectangle.
  389. *
  390. * @param g the graphics context to use for painting
  391. * @param r a <code>Rectangle</code> defining the area to fill, may cause
  392. * the image to be stretched or tiled
  393. * @param index which subimage to paint (usually depends on component state)
  394. */
  395. void paintSkin(Graphics g, Rectangle r, int index) {
  396. paintSkin(g, r.x, r.y, r.width, r.height, index);
  397. }
  398. /** Paint a skin at a defined position and size
  399. *
  400. * @param g the graphics context to use for painting
  401. * @param dx the destination <i>x</i> coordinate.
  402. * @param dy the destination <i>y</i> coordinate.
  403. * @param dw the width of the area to fill, may cause
  404. * the image to be stretched or tiled
  405. * @param dh the height of the area to fill, may cause
  406. * the image to be stretched or tiled
  407. * @param index which subimage to paint (usually depends on component state)
  408. */
  409. void paintSkin(Graphics g, int dx, int dy, int dw, int dh, int index) {
  410. // Sanity check
  411. if ((image != null || scaledImage != null || glyphImage != null)
  412. && (index < 0 || index >= frameCount)) {
  413. abandonXP();
  414. }
  415. if (image != null) {
  416. int sy = 0;
  417. if (h - ((paintMargin != null) ? (paintMargin.top+paintMargin.bottom) : 0) > dh
  418. && tile && !sourceShrink) {
  419. // Special case for vertical progress bar where the source image is
  420. // higher than the chunk size and the bottom of the image needs to
  421. // be painted instead of the top.
  422. sy = h - dh;
  423. }
  424. paint9(g, image, dx, dy, dw, dh,
  425. verticalFrames ? 0 : (index*w),
  426. (verticalFrames ? (index*h) : 0) + sy,
  427. w, h, paintMargin, tile, sourceShrink);
  428. }
  429. if (scaledImage != null) {
  430. Image im = scaledImage;
  431. int sw = im.getWidth(null) / (verticalFrames ? 1 : frameCount);
  432. int sh = im.getHeight(null) / (verticalFrames ? frameCount : 1);
  433. int sx = verticalFrames ? 0 : (index*sw);
  434. int sy = verticalFrames ? (index*sh) : 0;
  435. dx += (w-sw)/2;
  436. dy += (h-sh)/2;
  437. g.drawImage(im, dx, dy, dx+sw, dy+sh, sx, sy, sx+sw, sy+sh, null);
  438. } else if (glyphImage != null) {
  439. int gsw = glyphImage.getWidth(null) / (verticalFrames ? 1 : frameCount);
  440. int gsh = glyphImage.getHeight(null) / (verticalFrames ? frameCount : 1);
  441. dx += (dw - gsw) / 2;
  442. dy += (dh - gsh) / 2;
  443. if (dx >= 0 && dy >= 0) {
  444. int gsx = 0, gsy = 0;
  445. if (verticalFrames) {
  446. gsy = index * gsh;
  447. } else {
  448. gsx = index * gsw;
  449. }
  450. g.drawImage(glyphImage, dx, dy, dx+gsw, dy+gsh, gsx, gsy, gsx+gsw, gsy+gsh, null);
  451. }
  452. }
  453. }
  454. private void paint9(Graphics g, Image im,
  455. int dx, int dy, int dw, int dh,
  456. int sx, int sy, int sw, int sh,
  457. Insets margin, boolean tile, boolean sourceShrink) {
  458. int th, bh, lw, rw;
  459. if (margin != null) {
  460. th = margin.top;
  461. bh = margin.bottom;
  462. lw = margin.left;
  463. rw = margin.right;
  464. } else {
  465. th = bh = lw = rw = 0;
  466. }
  467. if (tile) {
  468. // mid middle, left, right
  469. paintTile(g, im, dx+lw, dy+th, dw-lw-rw, dh-th-bh, sx+lw, sy+th, sw-lw-rw, sh-th-bh, sourceShrink);
  470. paintTile(g, im, dx, dy+th, lw, dh-th-bh, sx, sy+th, lw, sh-th-bh, sourceShrink);
  471. paintTile(g, im, dx+dw-rw, dy+th, rw, dh-th-bh, sx+sw-rw, sy+th, rw, sh-th-bh, sourceShrink);
  472. // top middle
  473. paintTile(g, im, dx+lw, dy, dw-lw-rw, th, sx+lw, sy, sw-lw-rw, th, sourceShrink);
  474. // bottom middle
  475. paintTile(g, im, dx+lw, dy+dh-bh, dw-lw-rw, bh, sx+lw, sy+sh-bh, sw-lw-rw, bh, sourceShrink);
  476. } else {
  477. // mid middle, left, right
  478. g.drawImage(im, dx+lw, dy+th, dx+dw-rw, dy+dh-bh, sx+lw, sy+th, sx+sw-rw, sy+sh-bh, null);
  479. g.drawImage(im, dx, dy+th, dx+lw, dy+dh-bh, sx, sy+th, sx+lw, sy+sh-bh, null);
  480. g.drawImage(im, dx+dw-rw, dy+th, dx+dw, dy+dh-bh, sx+sw-rw, sy+th, sx+sw, sy+sh-bh, null);
  481. // top middle
  482. g.drawImage(im, dx+lw, dy, dx+dw-rw, dy+th, sx+lw, sy, sx+sw-rw, sy+th, null);
  483. // bottom middle
  484. g.drawImage(im, dx+lw, dy+dh-bh, dx+dw-rw, dy+dh, sx+lw, sy+sh-bh, sx+sw-rw, sy+sh, null);
  485. }
  486. // top left, right
  487. g.drawImage(im, dx, dy, dx+lw, dy+th, sx, sy, sx+lw, sy+th, null);
  488. g.drawImage(im, dx+dw-rw, dy, dx+dw, dy+th, sx+sw-rw, sy, sx+sw, sy+th, null);
  489. // bottom left, right
  490. g.drawImage(im, dx, dy+dh-bh, dx+lw, dy+dh, sx, sy+sh-bh, sx+lw, sy+sh, null);
  491. g.drawImage(im, dx+dw-rw, dy+dh-bh, dx+dw, dy+dh, sx+sw-rw, sy+sh-bh, sx+sw, sy+sh, null);
  492. }
  493. private void paintTile(Graphics g, Image im,
  494. int dx, int dy, int dw, int dh,
  495. int sx, int sy, int sw, int sh,
  496. boolean sourceShrink) {
  497. if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) {
  498. return;
  499. }
  500. if (sourceShrink && (sw > dw || sh > dh)) {
  501. if (sw > dw && sh > dh) {
  502. // shrink width and height
  503. g.drawImage(im, dx, dy, dx+dw, dy+dh, sx, sy, sx+sw, sy+sh, null);
  504. } else if (sh > dh) {
  505. // tile width, shrink height
  506. int x = dx;
  507. BufferedImage bImage = new BufferedImage(sw, dh, BufferedImage.TYPE_INT_ARGB);
  508. Graphics bg = bImage.getGraphics();
  509. bg.drawImage(im, 0, 0, sw, dh, sx, sy, sx+sw, sy+sh, null);
  510. while (x < dx + dw) {
  511. int swm = Math.min(sw, dx + dw - x);
  512. g.drawImage(bImage, x, dy, x+swm, dy+dh, 0, 0, swm, dh, null);
  513. x += swm;
  514. }
  515. bg.dispose();
  516. } else {
  517. // shrink width, tile height
  518. int y = dy;
  519. BufferedImage bImage = new BufferedImage(dw, sh, BufferedImage.TYPE_INT_ARGB);
  520. Graphics bg = bImage.getGraphics();
  521. bg.drawImage(im, 0, 0, dw, sh, sx, sy, sx+sw, sy+sh, null);
  522. while (y < dy + dh) {
  523. sh = Math.min(sh, dy + dh - y);
  524. g.drawImage(bImage, dx, y, dx+dw, y+sh, 0, 0, dw, sh, null);
  525. y += sh;
  526. }
  527. bg.dispose();
  528. }
  529. } else {
  530. // tile width and height
  531. int y = dy;
  532. while (y < dy + dh) {
  533. sh = Math.min(sh, dy + dh - y);
  534. int x = dx;
  535. while (x < dx + dw) {
  536. int swm = Math.min(sw, dx + dw - x);
  537. g.drawImage(im, x, y, x+swm, y+sh, sx, sy, sx+swm, sy+sh, null);
  538. x += swm;
  539. }
  540. y += sh;
  541. }
  542. }
  543. }
  544. }
  545. static class GlyphButton extends JButton {
  546. private Skin skin;
  547. private Image glyphImage;
  548. private boolean vertical;
  549. public GlyphButton(String category) {
  550. //setRolloverEnabled(true);
  551. XPStyle xp = getXP();
  552. skin = xp.getSkin(category);
  553. glyphImage = xp.getImage(category+".glyphimagefile",
  554. xp.getBoolean(category+".glyphtransparent", true));
  555. setBorder(null);
  556. setContentAreaFilled(false);
  557. }
  558. public boolean isFocusTraversable() {
  559. return false;
  560. }
  561. public void paintComponent(Graphics g) {
  562. int index = 0;
  563. if (!isEnabled()) {
  564. index = 3;
  565. } else if (getModel().isPressed()) {
  566. index = 2;
  567. } else if (getModel().isRollover()) {
  568. index = 1;
  569. }
  570. Dimension d = getSize();
  571. skin.paintSkin(g, 0, 0, d.width, d.height, index);
  572. }
  573. protected void paintBorder(Graphics g) {
  574. }
  575. public Dimension getPreferredSize() {
  576. return new Dimension(16, 16);
  577. }
  578. public Dimension getMinimumSize() {
  579. return new Dimension(5, 5);
  580. }
  581. public Dimension getMaximumSize() {
  582. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  583. }
  584. }
  585. // Private constructor
  586. private XPStyle() {
  587. map = new HashMap();
  588. Toolkit toolkit = Toolkit.getDefaultToolkit();
  589. styleFile = (String)toolkit.getDesktopProperty("win.xpstyle.dllName");
  590. if (styleFile != null) {
  591. String sizeName = (String)toolkit.getDesktopProperty("win.xpstyle.sizeName");
  592. String colorName = (String)toolkit.getDesktopProperty("win.xpstyle.colorName");
  593. if (sizeName != null && colorName != null) {
  594. String[] sizeNames =
  595. splitTextResource(getTextResourceByInt(styleFile, 1, "SIZENAMES"));
  596. String[] colorNames =
  597. splitTextResource(getTextResourceByInt(styleFile, 1, "COLORNAMES"));
  598. String[] themeFileNames =
  599. splitTextResource(getTextResourceByInt(styleFile, 1, "FILERESNAMES"));
  600. if (sizeNames != null && colorNames != null && themeFileNames != null) {
  601. themeFile = null;
  602. for (int color = 0; color < colorNames.length; color++) {
  603. for (int size = 0; size < sizeNames.length; size++) {
  604. if (sizeName.equals(sizeNames[size]) &&
  605. colorName.equals(colorNames[color]) &&
  606. (color * sizeNames.length + size) < themeFileNames.length) {
  607. themeFile = themeFileNames[color * sizeNames.length + size];
  608. break;
  609. }
  610. }
  611. }
  612. if (themeFile != null) {
  613. String themeData = getTextResourceByName(styleFile, themeFile, "TEXTFILE");
  614. if (themeData != null) {
  615. merge(themeData);
  616. }
  617. }
  618. }
  619. }
  620. }
  621. // Note: All further access to the map must be synchronized
  622. }
  623. private static native int[] getBitmapResource(String path, String resource);
  624. private static native String getTextResourceByName(String path, String resource, String resType);
  625. private static native String getTextResourceByInt(String path, int resource, String resType);
  626. private void merge(String bytes) {
  627. StringTokenizer tok = new StringTokenizer(bytes, "\r\n");
  628. String category = "";
  629. while (tok.hasMoreElements()) {
  630. String line = tok.nextToken().trim();
  631. char[] chars = line.toCharArray();
  632. int len = chars.length;
  633. if (len > 1) {
  634. // Modify "[Category]" to "category."
  635. if (chars[0] == '[') {
  636. chars[len-1] = '.';
  637. toLowerCase(chars, 1, len-1);
  638. category = new String(chars, 1, len-1);
  639. } else {
  640. int i = line.indexOf('=');
  641. if (i >= 0) {
  642. while (i > 0 && (chars[i-1] == ' ' || chars[i-1] == '\t')) {
  643. i--;
  644. }
  645. toLowerCase(chars, 0, i);
  646. String key = category + new String(chars, 0, i);
  647. while (i < len &&
  648. (chars[i] == ' ' || chars[i] == '\t' || chars[i] == '=')) {
  649. i++;
  650. }
  651. String value = new String(chars, i, len-i);
  652. i = value.indexOf(';');
  653. if (i >= 0) {
  654. value = value.substring(0, i);
  655. }
  656. map.put(key, value.trim());
  657. }
  658. }
  659. }
  660. }
  661. }
  662. private void toLowerCase(char[] a, int start, int end) {
  663. for (int i = start; i < end; i++) {
  664. a[i] = Character.toLowerCase(a[i]);
  665. }
  666. }
  667. private synchronized Image getImage(String key, boolean useTransparency) {
  668. Image image = null;
  669. String imageName = getString(key);
  670. if (imageName != null) {
  671. // We cache images separately because multiple keys/skins
  672. // can point to the same image
  673. image = (Image)map.get("Image "+imageName);
  674. if (image == null) {
  675. // Replace \ and . with _ and convert to uppercase
  676. int i;
  677. String resourceName = imageName;
  678. while ((i = resourceName.indexOf("\\")) >= 0
  679. || (i = resourceName.indexOf(".")) >= 0) {
  680. resourceName = resourceName.substring(0, i) + "_" + resourceName.substring(i+1);
  681. }
  682. resourceName = resourceName.toUpperCase();
  683. int[] bits = getBitmapResource(styleFile, resourceName);
  684. if (bits != null) {
  685. // The last two ints in the array are the width and the transparency value
  686. int width = bits[bits.length-2];
  687. int transparency = useTransparency ? bits[bits.length-1] : Transparency.OPAQUE;
  688. int height = (bits.length - 2) / width;
  689. GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().
  690. getDefaultScreenDevice().getDefaultConfiguration();
  691. BufferedImage bImage =
  692. (BufferedImage)gc.createCompatibleImage(width, height, transparency);
  693. bImage.setRGB(0, 0, width, height, bits, 0, width);
  694. image = bImage;
  695. map.put("Image "+imageName, image);
  696. }
  697. }
  698. }
  699. return image;
  700. }
  701. private boolean getBoolean(String key, boolean fallback) {
  702. String value = getString(key);
  703. return ((value == null) ? fallback : "true".equalsIgnoreCase(value));
  704. }
  705. private void abandonXP() {
  706. if (AccessController.doPrivileged(new GetPropertyAction("swing.debug")) != null) {
  707. System.err.println("An error occured in XPStyle while reading resource "+themeFile + " in " + styleFile);
  708. new Exception().printStackTrace();
  709. }
  710. xp = null;
  711. }
  712. private String[] splitTextResource(String str) {
  713. if (str == null) {
  714. return null;
  715. }
  716. StringTokenizer tok = new StringTokenizer(str, "\0");
  717. String[] array = new String[tok.countTokens()];
  718. for (int i = 0; tok.hasMoreTokens(); i++) {
  719. array[i] = tok.nextToken();
  720. }
  721. return array;
  722. }
  723. }