1. /*
  2. * @(#)XPStyle.java 1.19 03/12/19
  3. *
  4. * Copyright 2004 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.19 12/19/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. !(UIManager.getLookAndFeel() instanceof WindowsClassicLookAndFeel)) {
  71. xp = new XPStyle();
  72. }
  73. }
  74. return xp;
  75. }
  76. /** Get a named <code>String</code> value from the current style
  77. *
  78. * @param key a <code>String</code>
  79. * @return a <code>String</code> or null if key is not found
  80. * in the current style
  81. */
  82. private static String getString(String key) {
  83. // Example: getString("a.b(c).d") => return getString("a.b", "c", "d");
  84. String category = "";
  85. String state = "";
  86. String attributeKey = key;
  87. int i = key.lastIndexOf('.');
  88. if (i > 0 && key.length() > i+1) {
  89. category = key.substring(0, i);
  90. attributeKey = key.substring(i+1);
  91. i = category.lastIndexOf('(');
  92. if (i > 0) {
  93. int i2 = category.indexOf(')', i);
  94. if (i2 == category.length() - 1) {
  95. state = category.substring(i+1, i2);
  96. category = category.substring(0, i);
  97. }
  98. }
  99. }
  100. return getString(category, state, attributeKey);
  101. }
  102. /** Get a named <code>String</code> value from the current style
  103. *
  104. * @param category a <code>String</code>
  105. * @param state a <code>String</code>
  106. * @param attributeKey a <code>String</code>
  107. * @return a <code>String</code> or null if key is not found
  108. * in the current style
  109. */
  110. static String getString(String category, String state, String attributeKey) {
  111. Toolkit toolkit = Toolkit.getDefaultToolkit();
  112. Map resources = (Map)toolkit.getDesktopProperty("win.xpstyle.resources.strings");
  113. String value;
  114. if (category == null || category.length() == 0) {
  115. value = (String)resources.get(attributeKey);
  116. } else {
  117. if (state == null || state.length() == 0) {
  118. value = (String)resources.get(category + "." + attributeKey);
  119. } else {
  120. value = (String)resources.get(category + "(" + state + ")." + attributeKey);
  121. }
  122. }
  123. if (value == null) {
  124. // Look for inheritance. For example, the attributeKey "transparent" in
  125. // category "window.closebutton" can be inherited from category "window".
  126. int i = category.lastIndexOf('.');
  127. if (i > 0) {
  128. value = getString(category.substring(0, i), state, attributeKey);
  129. }
  130. }
  131. if (value == null && state != null && state.length() > 0) {
  132. // Try again without a state specified
  133. value = getString(category, "", attributeKey);
  134. }
  135. return value;
  136. }
  137. static BufferedImage getBitmapResource(String key) {
  138. Toolkit toolkit = Toolkit.getDefaultToolkit();
  139. Map resources = (Map)toolkit.getDesktopProperty("win.xpstyle.resources.images");
  140. return (BufferedImage)resources.get(key);
  141. }
  142. /** Get a named <code>int</code> value from the current style
  143. *
  144. * @param key a <code>String</code>
  145. * @return an <code>int</code> or null if key is not found
  146. * in the current style
  147. */
  148. int getInt(String key, int fallback) {
  149. return parseInt(getString(key), fallback);
  150. }
  151. private int parseInt(String str, int fallback) {
  152. if (str != null) {
  153. try {
  154. return Integer.parseInt(str);
  155. } catch (NumberFormatException nfe) {
  156. abandonXP();
  157. }
  158. }
  159. return fallback;
  160. }
  161. /** Get a named <code>Dimension</code> value from the current style
  162. *
  163. * @param key a <code>String</code>
  164. * @return a <code>Dimension</code> or null if key is not found
  165. * in the current style
  166. */
  167. synchronized Dimension getDimension(String key) {
  168. Dimension d = (Dimension)map.get("Dimension "+key);
  169. if (d == null) {
  170. String str = getString(key);
  171. if (str != null) {
  172. StringTokenizer tok = new StringTokenizer(str, " \t,");
  173. d = new Dimension(parseInt(tok.nextToken(), 0),
  174. parseInt(tok.nextToken(), 0));
  175. map.put("Dimension "+key, d);
  176. }
  177. }
  178. return d;
  179. }
  180. /** Get a named <code>Point</code> (e.g. a location or an offset) value
  181. * from the current style
  182. *
  183. * @param key a <code>String</code>
  184. * @return a <code>Point</code> or null if key is not found
  185. * in the current style
  186. */
  187. synchronized Point getPoint(String key) {
  188. Point p = (Point)map.get("Point "+key);
  189. if (p == null) {
  190. String str = getString(key);
  191. if (str != null) {
  192. StringTokenizer tok = new StringTokenizer(str, " \t,");
  193. p = new Point(parseInt(tok.nextToken(), 0),
  194. parseInt(tok.nextToken(), 0));
  195. map.put("Point "+key, p);
  196. }
  197. }
  198. return p;
  199. }
  200. /** Get a named <code>Insets</code> value from the current style
  201. *
  202. * @param key a <code>String</code>
  203. * @return an <code>Insets</code> object or null if key is not found
  204. * in the current style
  205. */
  206. synchronized Insets getMargin(String key) {
  207. Insets insets = (Insets)map.get("Margin "+key);
  208. if (insets == null) {
  209. String str = getString(key);
  210. if (str != null) {
  211. StringTokenizer tok = new StringTokenizer(str, " \t,");
  212. insets = new Insets(0, 0, 0, 0);
  213. insets.left = parseInt(tok.nextToken(), 0);
  214. insets.right = parseInt(tok.nextToken(), 0);
  215. insets.top = parseInt(tok.nextToken(), 0);
  216. insets.bottom = parseInt(tok.nextToken(), 0);
  217. map.put("Margin "+key, insets);
  218. }
  219. }
  220. return insets;
  221. }
  222. /** Get a named <code>Color</code> value from the current style
  223. *
  224. * @param key a <code>String</code>
  225. * @return a <code>Color</code> or null if key is not found
  226. * in the current style
  227. */
  228. synchronized Color getColor(String key, Color fallback) {
  229. Color color = (Color)map.get("Color "+key);
  230. if (color == null) {
  231. String str = getString(key);
  232. if (str != null) {
  233. StringTokenizer tok = new StringTokenizer(str, " \t,");
  234. int r = parseInt(tok.nextToken(), 0);
  235. int g = parseInt(tok.nextToken(), 0);
  236. int b = parseInt(tok.nextToken(), 0);
  237. if (r >= 0 && g >=0 && b >= 0) {
  238. color = new Color(r, g, b);
  239. map.put("Color "+key, color);
  240. }
  241. }
  242. }
  243. return (color != null) ? color : fallback;
  244. }
  245. /** Get a named <code>Border</code> value from the current style
  246. *
  247. * @param key a <code>String</code>
  248. * @return a <code>Border</code> or null if key is not found
  249. * in the current style or if the style for the particular
  250. * category is not defined as "borderfill".
  251. */
  252. synchronized Border getBorder(String category) {
  253. if (category == "menu") {
  254. // Special case because XP has no skin for menus
  255. if (getBoolean("sysmetrics.flatmenus") == Boolean.TRUE) {
  256. // TODO: The classic border uses this color, but we should create
  257. // a new UI property called "PopupMenu.borderColor" instead.
  258. return new XPFillBorder(UIManager.getColor("InternalFrame.borderShadow"), 1);
  259. } else {
  260. return null; // Will cause L&F to use classic border
  261. }
  262. }
  263. Border border = (Border)map.get("Border "+category);
  264. if (border == null) {
  265. String bgType = getString(category + ".bgtype");
  266. if ("borderfill".equalsIgnoreCase(bgType)) {
  267. int thickness = getInt(category + ".bordersize", 1);
  268. Color color = getColor(category + ".bordercolor", Color.black);
  269. border = new XPFillBorder(color, thickness);
  270. } else if ("imagefile".equalsIgnoreCase(bgType)) {
  271. Insets m = getMargin(category + ".sizingmargins");
  272. if (m != null) {
  273. if (getBoolean(category + ".borderonly") == Boolean.TRUE) {
  274. border = new XPImageBorder(category);
  275. } else {
  276. border = new XPEmptyBorder(m);
  277. }
  278. }
  279. }
  280. if (border != null) {
  281. map.put("Border "+category, border);
  282. }
  283. }
  284. return border;
  285. }
  286. private class XPFillBorder extends LineBorder implements UIResource {
  287. XPFillBorder(Color color, int thickness) {
  288. super(color, thickness);
  289. }
  290. public Insets getBorderInsets(Component c) {
  291. return getBorderInsets(c, new Insets(0,0,0,0));
  292. }
  293. public Insets getBorderInsets(Component c, Insets insets) {
  294. Insets margin = null;
  295. //
  296. // Ideally we'd have an interface defined for classes which
  297. // support margins (to avoid this hackery), but we've
  298. // decided against it for simplicity
  299. //
  300. if (c instanceof AbstractButton) {
  301. margin = ((AbstractButton)c).getMargin();
  302. } else if (c instanceof JToolBar) {
  303. margin = ((JToolBar)c).getMargin();
  304. } else if (c instanceof JTextComponent) {
  305. margin = ((JTextComponent)c).getMargin();
  306. }
  307. insets.top = (margin != null? margin.top : 0) + thickness;
  308. insets.left = (margin != null? margin.left : 0) + thickness;
  309. insets.bottom = (margin != null? margin.bottom : 0) + thickness;
  310. insets.right = (margin != null? margin.right : 0) + thickness;
  311. return insets;
  312. }
  313. }
  314. private class XPImageBorder extends AbstractBorder implements UIResource {
  315. Skin skin;
  316. XPImageBorder(String category) {
  317. this.skin = getSkin(category);
  318. }
  319. public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
  320. skin.paintSkin(g, x, y, width, height, 0);
  321. }
  322. public Insets getBorderInsets(Component c) {
  323. return getBorderInsets(c, new Insets(0,0,0,0));
  324. }
  325. public Insets getBorderInsets(Component c, Insets insets) {
  326. Insets margin = null;
  327. Insets borderInsets = skin.getContentMargin();
  328. //
  329. // Ideally we'd have an interface defined for classes which
  330. // support margins (to avoid this hackery), but we've
  331. // decided against it for simplicity
  332. //
  333. if (c instanceof AbstractButton) {
  334. margin = ((AbstractButton)c).getMargin();
  335. } else if (c instanceof JToolBar) {
  336. margin = ((JToolBar)c).getMargin();
  337. } else if (c instanceof JTextComponent) {
  338. margin = ((JTextComponent)c).getMargin();
  339. }
  340. insets.top = (margin != null? margin.top : 0) + borderInsets.top;
  341. insets.left = (margin != null? margin.left : 0) + borderInsets.left;
  342. insets.bottom = (margin != null? margin.bottom : 0) + borderInsets.bottom;
  343. insets.right = (margin != null? margin.right : 0) + borderInsets.right;
  344. return insets;
  345. }
  346. }
  347. private class XPEmptyBorder extends EmptyBorder implements UIResource {
  348. XPEmptyBorder(Insets m) {
  349. super(m.top+2, m.left+2, m.bottom+2, m.right+2);
  350. }
  351. public Insets getBorderInsets(Component c) {
  352. return getBorderInsets(c, getBorderInsets());
  353. }
  354. public Insets getBorderInsets(Component c, Insets insets) {
  355. Insets margin = null;
  356. if (c instanceof AbstractButton) {
  357. margin = ((AbstractButton)c).getMargin();
  358. } else if (c instanceof JToolBar) {
  359. margin = ((JToolBar)c).getMargin();
  360. } else if (c instanceof JTextComponent) {
  361. margin = ((JTextComponent)c).getMargin();
  362. }
  363. if (margin != null) {
  364. insets.top = margin.top + 2;
  365. insets.left = margin.left + 2;
  366. insets.bottom = margin.bottom + 2;
  367. insets.right = margin.right + 2;
  368. }
  369. return insets;
  370. }
  371. }
  372. /** Get an <code>XPStyle.Skin</code> object from the current style
  373. * for a named category (component type)
  374. *
  375. * @param category a <code>String</code>
  376. * @return an <code>XPStyle.Skin</code> object or null if the category is
  377. * not found in the current style
  378. */
  379. synchronized Skin getSkin(String category) {
  380. Skin skin = (Skin)map.get("Skin "+category);
  381. if (skin == null) {
  382. skin = new Skin(category);
  383. map.put("Skin "+category, skin);
  384. }
  385. return skin;
  386. }
  387. /** A class which encapsulates attributes for a given category
  388. * (component type) and which provides methods for painting backgrounds
  389. * and glyphs
  390. */
  391. class Skin {
  392. private String category;
  393. private Image image;
  394. private Insets contentMargin;
  395. private int w, h;
  396. private String imageSelectType;
  397. private Image scaledImage;
  398. private int nScaledImages;
  399. private int scaledToWidth = 0;
  400. private int scaledToHeight = 0;
  401. private Image glyphImage;
  402. private int frameCount;
  403. private Insets paintMargin;
  404. private boolean tile;
  405. private boolean sourceShrink;
  406. private boolean verticalFrames;
  407. /** The background image for the skin.
  408. * If this is null then width and height are not valid.
  409. */
  410. Image getImage() {
  411. if (image != null) {
  412. return image;
  413. } else if (scaledImage != null) {
  414. return scaledImage;
  415. } else {
  416. return null;
  417. }
  418. }
  419. /** The content margin for a component skin is useful in
  420. * determining the minimum and preferred sizes for a component.
  421. */
  422. Insets getContentMargin() {
  423. // This is only called by WindowsTableHeaderUI so far.
  424. return paintMargin;
  425. }
  426. /** The width of the source image for this skin.
  427. * Not valid if getImage() returns null
  428. */
  429. int getWidth() {
  430. return w;
  431. }
  432. /** The height of the source image for this skin.
  433. * Not valid if getImage() returns null
  434. */
  435. int getHeight() {
  436. return h;
  437. }
  438. private Skin(String category) {
  439. this.category = category;
  440. XPStyle xp = getXP();
  441. // Load main image
  442. image = xp.getImage(category+".imagefile",
  443. getBoolean(category+".transparent"),
  444. xp.getColor(category+".transparentcolor", null));
  445. // Look for additional (prescaled) images
  446. nScaledImages = 0;
  447. while (true) {
  448. if (xp.getString(category+".imagefile"+(nScaledImages+1)) == null) {
  449. break;
  450. }
  451. nScaledImages++;
  452. }
  453. if (nScaledImages > 0) {
  454. int index = (nScaledImages / 2) + 1;
  455. imageSelectType = getString(category+".imageselecttype").toLowerCase();
  456. if ("dpi".equals(imageSelectType)) {
  457. int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
  458. index = 1;
  459. for (int i = nScaledImages; i >= 1; i--) {
  460. int minDpi = xp.getInt(category+".mindpi"+i, -1);
  461. if (minDpi > 0 && dpi >= minDpi) {
  462. index = i;
  463. break;
  464. }
  465. }
  466. }
  467. scaledImage = getScaledImage(index);
  468. }
  469. frameCount = getInt(category+".imagecount", 1);
  470. paintMargin = getMargin(category+".sizingmargins");
  471. contentMargin = getMargin(category+".contentmargins");
  472. tile = "tile".equalsIgnoreCase(getString(category+".sizingtype"));
  473. sourceShrink = (getBoolean(category+".sourceshrink") == Boolean.TRUE);
  474. verticalFrames = "vertical".equalsIgnoreCase(getString(category+".imagelayout"));
  475. glyphImage = xp.getImage(category+".glyphimagefile",
  476. xp.getBoolean(category+".glyphtransparent"),
  477. null);
  478. Image im = image;
  479. if (im == null && scaledImage != null) {
  480. im = scaledImage;
  481. }
  482. if (im != null) {
  483. // Sanity check
  484. if (frameCount < 1) {
  485. abandonXP();
  486. }
  487. this.w = getImageWidth(im);
  488. this.h = getImageHeight(im);
  489. }
  490. }
  491. private int getImageWidth(Image im) {
  492. return im.getWidth(null) / (verticalFrames ? 1 : frameCount);
  493. }
  494. private int getImageHeight(Image im) {
  495. return im.getHeight(null) / (verticalFrames ? frameCount : 1);
  496. }
  497. private Image getScaledImage(int i) {
  498. Boolean useTransparency = xp.getBoolean(category+".transparent");
  499. if (useTransparency == null) {
  500. useTransparency = xp.getBoolean(category+".glyphtransparent");
  501. }
  502. return xp.getImage(category+".imagefile"+i,
  503. useTransparency,
  504. xp.getColor(category+".transparentcolor", null));
  505. }
  506. private Image getScaledImage(int dw, int dh) {
  507. if ("size".equals(imageSelectType) && nScaledImages > 1
  508. && (scaledToWidth != dw || scaledToHeight != dh)) {
  509. scaledImage = getScaledImage(1);
  510. for (int i = 2; i <= nScaledImages; i++) {
  511. Image im = getScaledImage(i);
  512. int w = dw;
  513. int h = dh;
  514. if (contentMargin != null) {
  515. w -= (contentMargin.left + contentMargin.right);
  516. h -= (contentMargin.top + contentMargin.bottom);
  517. }
  518. if (getImageWidth(im) <= w && getImageHeight(im) <= h) {
  519. scaledImage = im;
  520. } else {
  521. break;
  522. }
  523. }
  524. scaledToWidth = dw;
  525. scaledToHeight = dh;
  526. }
  527. return scaledImage;
  528. }
  529. /** Paint a skin at x, y.
  530. *
  531. * @param g the graphics context to use for painting
  532. * @param dx the destination <i>x</i> coordinate.
  533. * @param dy the destination <i>y</i> coordinate.
  534. * @param index which subimage to paint (usually depends on component state)
  535. */
  536. void paintSkin(Graphics g, int dx, int dy, int index) {
  537. paintSkin(g, dx, dy, w, h, index);
  538. }
  539. /** Paint a skin in an area defined by a rectangle.
  540. *
  541. * @param g the graphics context to use for painting
  542. * @param r a <code>Rectangle</code> defining the area to fill, may cause
  543. * the image to be stretched or tiled
  544. * @param index which subimage to paint (usually depends on component state)
  545. */
  546. void paintSkin(Graphics g, Rectangle r, int index) {
  547. paintSkin(g, r.x, r.y, r.width, r.height, index);
  548. }
  549. /** Paint a skin at a defined position and size
  550. *
  551. * @param g the graphics context to use for painting
  552. * @param dx the destination <i>x</i> coordinate.
  553. * @param dy the destination <i>y</i> coordinate.
  554. * @param dw the width of the area to fill, may cause
  555. * the image to be stretched or tiled
  556. * @param dh the height of the area to fill, may cause
  557. * the image to be stretched or tiled
  558. * @param index which subimage to paint (usually depends on component state)
  559. */
  560. void paintSkin(Graphics g, int dx, int dy, int dw, int dh, int index) {
  561. // Sanity check
  562. if ((image != null || scaledImage != null || glyphImage != null)
  563. && (index < 0 || index >= frameCount)) {
  564. abandonXP();
  565. }
  566. if (image != null) {
  567. int sy = 0;
  568. if (h - ((paintMargin != null) ? (paintMargin.top+paintMargin.bottom) : 0) > dh
  569. && tile && !sourceShrink) {
  570. // Special case for vertical progress bar where the source image is
  571. // higher than the chunk size and the bottom of the image needs to
  572. // be painted instead of the top.
  573. sy = h - dh;
  574. }
  575. paint9(g, image, dx, dy, dw, dh,
  576. verticalFrames ? 0 : (index*w),
  577. (verticalFrames ? (index*h) : 0) + sy,
  578. w, h, paintMargin, tile, sourceShrink);
  579. }
  580. // Paint glyph on top of background image
  581. Image im = getScaledImage(dw, dh);
  582. if (im == null) {
  583. im = glyphImage;
  584. }
  585. if (im != null) {
  586. // The glyph is pre-scaled, so don't stretch it here
  587. int sw = getImageWidth(im);
  588. int sh = getImageHeight(im);
  589. int sx = verticalFrames ? 0 : (index*sw);
  590. int sy = verticalFrames ? (index*sh) : 0;
  591. dx += (dw-sw)/2;
  592. dy += (dh-sh)/2;
  593. g.drawImage(im, dx, dy, dx+sw, dy+sh, sx, sy, sx+sw, sy+sh, null);
  594. }
  595. }
  596. private void paint9(Graphics g, Image im,
  597. int dx, int dy, int dw, int dh,
  598. int sx, int sy, int sw, int sh,
  599. Insets margin, boolean tile, boolean sourceShrink) {
  600. int th, bh, lw, rw;
  601. if (margin != null) {
  602. th = margin.top;
  603. bh = margin.bottom;
  604. lw = margin.left;
  605. rw = margin.right;
  606. } else {
  607. th = bh = lw = rw = 0;
  608. }
  609. if (tile) {
  610. // mid middle, left, right
  611. paintTile(g, im, dx+lw, dy+th, dw-lw-rw, dh-th-bh, sx+lw, sy+th, sw-lw-rw, sh-th-bh, sourceShrink);
  612. paintTile(g, im, dx, dy+th, lw, dh-th-bh, sx, sy+th, lw, sh-th-bh, sourceShrink);
  613. paintTile(g, im, dx+dw-rw, dy+th, rw, dh-th-bh, sx+sw-rw, sy+th, rw, sh-th-bh, sourceShrink);
  614. // top middle
  615. paintTile(g, im, dx+lw, dy, dw-lw-rw, th, sx+lw, sy, sw-lw-rw, th, sourceShrink);
  616. // bottom middle
  617. paintTile(g, im, dx+lw, dy+dh-bh, dw-lw-rw, bh, sx+lw, sy+sh-bh, sw-lw-rw, bh, sourceShrink);
  618. } else {
  619. // mid middle, left, right
  620. g.drawImage(im, dx+lw, dy+th, dx+dw-rw, dy+dh-bh, sx+lw, sy+th, sx+sw-rw, sy+sh-bh, null);
  621. g.drawImage(im, dx, dy+th, dx+lw, dy+dh-bh, sx, sy+th, sx+lw, sy+sh-bh, null);
  622. g.drawImage(im, dx+dw-rw, dy+th, dx+dw, dy+dh-bh, sx+sw-rw, sy+th, sx+sw, sy+sh-bh, null);
  623. // top middle
  624. g.drawImage(im, dx+lw, dy, dx+dw-rw, dy+th, sx+lw, sy, sx+sw-rw, sy+th, null);
  625. // bottom middle
  626. g.drawImage(im, dx+lw, dy+dh-bh, dx+dw-rw, dy+dh, sx+lw, sy+sh-bh, sx+sw-rw, sy+sh, null);
  627. }
  628. // top left, right
  629. g.drawImage(im, dx, dy, dx+lw, dy+th, sx, sy, sx+lw, sy+th, null);
  630. g.drawImage(im, dx+dw-rw, dy, dx+dw, dy+th, sx+sw-rw, sy, sx+sw, sy+th, null);
  631. // bottom left, right
  632. g.drawImage(im, dx, dy+dh-bh, dx+lw, dy+dh, sx, sy+sh-bh, sx+lw, sy+sh, null);
  633. g.drawImage(im, dx+dw-rw, dy+dh-bh, dx+dw, dy+dh, sx+sw-rw, sy+sh-bh, sx+sw, sy+sh, null);
  634. }
  635. private void paintTile(Graphics g, Image im,
  636. int dx, int dy, int dw, int dh,
  637. int sx, int sy, int sw, int sh,
  638. boolean sourceShrink) {
  639. if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) {
  640. return;
  641. }
  642. if (sourceShrink && (sw > dw || sh > dh)) {
  643. if (sw > dw && sh > dh) {
  644. // shrink width and height
  645. g.drawImage(im, dx, dy, dx+dw, dy+dh, sx, sy, sx+sw, sy+sh, null);
  646. } else if (sh > dh) {
  647. // tile width, shrink height
  648. int x = dx;
  649. BufferedImage bImage = new BufferedImage(sw, dh, BufferedImage.TYPE_INT_ARGB);
  650. Graphics bg = bImage.getGraphics();
  651. bg.drawImage(im, 0, 0, sw, dh, sx, sy, sx+sw, sy+sh, null);
  652. while (x < dx + dw) {
  653. int swm = Math.min(sw, dx + dw - x);
  654. g.drawImage(bImage, x, dy, x+swm, dy+dh, 0, 0, swm, dh, null);
  655. x += swm;
  656. }
  657. bg.dispose();
  658. } else {
  659. // shrink width, tile height
  660. int y = dy;
  661. BufferedImage bImage = new BufferedImage(dw, sh, BufferedImage.TYPE_INT_ARGB);
  662. Graphics bg = bImage.getGraphics();
  663. bg.drawImage(im, 0, 0, dw, sh, sx, sy, sx+sw, sy+sh, null);
  664. while (y < dy + dh) {
  665. sh = Math.min(sh, dy + dh - y);
  666. g.drawImage(bImage, dx, y, dx+dw, y+sh, 0, 0, dw, sh, null);
  667. y += sh;
  668. }
  669. bg.dispose();
  670. }
  671. } else {
  672. // tile width and height
  673. int y = dy;
  674. while (y < dy + dh) {
  675. sh = Math.min(sh, dy + dh - y);
  676. int x = dx;
  677. while (x < dx + dw) {
  678. int swm = Math.min(sw, dx + dw - x);
  679. g.drawImage(im, x, y, x+swm, y+sh, sx, sy, sx+swm, sy+sh, null);
  680. x += swm;
  681. }
  682. y += sh;
  683. }
  684. }
  685. }
  686. }
  687. static class GlyphButton extends JButton {
  688. private Skin skin;
  689. private Image glyphImage;
  690. private boolean vertical;
  691. public GlyphButton(String category) {
  692. //setRolloverEnabled(true);
  693. XPStyle xp = getXP();
  694. skin = xp.getSkin(category);
  695. glyphImage = xp.getImage(category+".glyphimagefile",
  696. xp.getBoolean(category+".glyphtransparent"),
  697. null);
  698. setBorder(null);
  699. setContentAreaFilled(false);
  700. }
  701. public boolean isFocusTraversable() {
  702. return false;
  703. }
  704. public void paintComponent(Graphics g) {
  705. int index = 0;
  706. if (!isEnabled()) {
  707. index = 3;
  708. } else if (getModel().isPressed()) {
  709. index = 2;
  710. } else if (getModel().isRollover()) {
  711. index = 1;
  712. }
  713. Dimension d = getSize();
  714. skin.paintSkin(g, 0, 0, d.width, d.height, index);
  715. }
  716. protected void paintBorder(Graphics g) {
  717. }
  718. public Dimension getPreferredSize() {
  719. return new Dimension(16, 16);
  720. }
  721. public Dimension getMinimumSize() {
  722. return new Dimension(5, 5);
  723. }
  724. public Dimension getMaximumSize() {
  725. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  726. }
  727. }
  728. // Private constructor
  729. private XPStyle() {
  730. map = new HashMap();
  731. Toolkit toolkit = Toolkit.getDefaultToolkit();
  732. styleFile = (String)toolkit.getDesktopProperty("win.xpstyle.dllName");
  733. if (styleFile != null) {
  734. themeFile = getString("themeFile");
  735. }
  736. // Note: All further access to the map must be synchronized
  737. }
  738. private synchronized Image getImage(String key,
  739. Boolean useTransparency,
  740. Color transparentColor) {
  741. String imageName = getString(key);
  742. if (imageName == null) {
  743. return null;
  744. }
  745. String imageKey = "Image " + imageName;
  746. BufferedImage image = (BufferedImage)map.get(imageKey);
  747. if (image != null) {
  748. return image;
  749. }
  750. image = getBitmapResource(imageName);
  751. if (image == null) {
  752. return null;
  753. }
  754. int oldTransparency = image.getColorModel().getTransparency();
  755. // Transparency can be forced to false for 32-bit images and forced
  756. // to true for 8/24-bit images. Do nothing if useTransparency is null.
  757. if (useTransparency == Boolean.FALSE && oldTransparency != Transparency.OPAQUE) {
  758. image = convertToOpaque(image);
  759. } else if (useTransparency == Boolean.TRUE && oldTransparency == Transparency.OPAQUE) {
  760. // Assume we only use images from 8/24-bit bitmaps here
  761. image = convertToTransparent(image, transparentColor);
  762. }
  763. // We cache images separately because multiple keys/skins
  764. // can point to the same image
  765. map.put(imageKey, image);
  766. return image;
  767. }
  768. private BufferedImage convertToOpaque(BufferedImage src) {
  769. GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().
  770. getDefaultScreenDevice().getDefaultConfiguration();
  771. BufferedImage dest = (BufferedImage)gc.
  772. createCompatibleImage(src.getWidth(), src.getHeight(), Transparency.OPAQUE);
  773. ColorConvertOp op = new ColorConvertOp(src.getColorModel().getColorSpace(),
  774. dest.getColorModel().getColorSpace(), null);
  775. op.filter(src, dest);
  776. return dest;
  777. }
  778. private BufferedImage convertToTransparent(BufferedImage src, Color transparentColor) {
  779. int w = src.getWidth();
  780. int h = src.getHeight();
  781. GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
  782. GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
  783. BufferedImage dest = gc.createCompatibleImage(w, h, Transparency.BITMASK);
  784. int trgb;
  785. if (transparentColor != null) {
  786. trgb = transparentColor.getRGB() | 0xff000000;
  787. } else {
  788. trgb = 0xffff00ff;
  789. }
  790. // Copy pixels a scan line at a time
  791. int buf[] = new int[w];
  792. for (int y = 0; y < h; y++) {
  793. src.getRGB(0, y, w, 1, buf, 0, w);
  794. for (int x = 0; x < w; x++) {
  795. if (buf[x] == trgb) {
  796. buf[x] = 0;
  797. }
  798. }
  799. dest.setRGB(0, y, w, 1, buf, 0, w);
  800. }
  801. return dest;
  802. }
  803. private Boolean getBoolean(String key) {
  804. String value = getString(key);
  805. return (value != null) ? Boolean.valueOf("true".equalsIgnoreCase(value)) : null;
  806. }
  807. private void abandonXP() {
  808. if (AccessController.doPrivileged(new GetPropertyAction("swing.debug")) != null) {
  809. System.err.println("An error occured in XPStyle while reading resource "+themeFile + " in " + styleFile);
  810. new Exception().printStackTrace();
  811. }
  812. xp = null;
  813. }
  814. }