1. /*
  2. * @(#)BlueprintStyle.java 1.12 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. package com.sun.java.swing.plaf.gtk;
  8. import java.awt.*;
  9. import java.util.*;
  10. import javax.swing.*;
  11. import java.security.*;
  12. import javax.swing.plaf.synth.*;
  13. import sun.swing.plaf.synth.DefaultSynthStyle;
  14. /**
  15. * BlueprintStyle extends GTKStyle adding support for a set of <code>Info</code>s.
  16. *
  17. * @version 1.12 12/19/03
  18. * @author Scott Violet
  19. */
  20. class BlueprintStyle extends GTKStyle implements GTKConstants {
  21. /**
  22. * There should only ever be one blueprint engine.
  23. */
  24. private static final GTKEngine BLUEPRINT_ENGINE = new BlueprintEngine();
  25. private static final BlueprintGraphicsUtils BLUEPRINT_GRAPHICS =
  26. new BlueprintGraphicsUtils();
  27. /**
  28. * Set of Infos used to determine what to paint.
  29. */
  30. private Info[] info;
  31. /**
  32. * Comes from the top level icon_colorize setting in rc files.
  33. */
  34. private boolean iconColorize;
  35. /**
  36. * Comes from the top level icon_colorize_ancestor_type setting
  37. * in rec files. These Strings will all be interned by the parser.
  38. */
  39. private String[] iconAncestorTypes;
  40. /**
  41. * Comes from the top level colorize_color setting in rc files.
  42. */
  43. private Color colorizeColor;
  44. /**
  45. * Creates a duplicate of the passed in style.
  46. */
  47. public BlueprintStyle(DefaultSynthStyle style) {
  48. super(style);
  49. if (style instanceof BlueprintStyle) {
  50. BlueprintStyle bpStyle = (BlueprintStyle)style;
  51. this.info = bpStyle.info;
  52. this.iconColorize = bpStyle.iconColorize;
  53. this.iconAncestorTypes = bpStyle.iconAncestorTypes;
  54. this.colorizeColor = bpStyle.colorizeColor;
  55. }
  56. }
  57. /**
  58. * Creates a BlueprintStyle from the passed in arguments.
  59. */
  60. public BlueprintStyle(StateInfo[] states,
  61. CircularIdentityList classSpecificValues,
  62. Font font,
  63. int xThickness, int yThickness,
  64. GTKStockIconInfo[] icons,
  65. Info[] info,
  66. boolean iconColorize,
  67. String[] iconAncestorTypes,
  68. Color colorizeColor) {
  69. super(states, classSpecificValues, font, xThickness, yThickness, icons);
  70. this.info = info;
  71. this.iconColorize = iconColorize;
  72. this.iconAncestorTypes = iconAncestorTypes;
  73. this.colorizeColor = colorizeColor;
  74. }
  75. public SynthGraphicsUtils getGraphicsUtils(SynthContext context) {
  76. return BLUEPRINT_GRAPHICS;
  77. }
  78. /**
  79. * Adds the state of this BlueprintStyle to that of <code>s</code>
  80. * returning a combined SynthStyle.
  81. */
  82. public DefaultSynthStyle addTo(DefaultSynthStyle s) {
  83. if (!(s instanceof BlueprintStyle)) {
  84. s = new BlueprintStyle(s);
  85. }
  86. BlueprintStyle style = (BlueprintStyle)super.addTo(s);
  87. if (info != null) {
  88. if (style.info == null) {
  89. style.info = info;
  90. }
  91. else {
  92. // Place the more specific first.
  93. Info[] merged = new Info[style.info.length + info.length];
  94. System.arraycopy(info, 0, merged, 0, info.length);
  95. System.arraycopy(style.info, 0, merged, info.length,
  96. style.info.length);
  97. style.info = merged;
  98. }
  99. }
  100. // like the real blueprint, we only overwrite when iconColorize is true
  101. if (iconColorize) {
  102. style.iconColorize = true;
  103. style.colorizeColor = colorizeColor;
  104. }
  105. // like the real blueprint, we always overwrite
  106. style.iconAncestorTypes = iconAncestorTypes;
  107. return style;
  108. }
  109. /**
  110. * Creates a copy of the reciever and returns it.
  111. */
  112. public Object clone() {
  113. BlueprintStyle style = (BlueprintStyle)super.clone();
  114. // These fields are immutable, no need to clone them
  115. style.info = this.info;
  116. style.iconAncestorTypes = this.iconAncestorTypes;
  117. style.colorizeColor = this.colorizeColor;
  118. return style;
  119. }
  120. /**
  121. * Returns a GTKEngine to use for rendering.
  122. */
  123. public GTKEngine getEngine(SynthContext context) {
  124. return BLUEPRINT_ENGINE;
  125. }
  126. /**
  127. * Returns the first instance of Info that matches the past in args, may
  128. * return null if nothing matches.
  129. *
  130. * @param function String name for the painting method
  131. * @param detail Description of what is being painted
  132. * @param componentState State of the Component
  133. * @param shadow Shadow type
  134. * @param orientation Orientation of what is being painted
  135. * @param gapSide Side of the gap being drawn
  136. * @param arrowDirection direction of the arrow.
  137. * @return Best matching Info, or null if none match
  138. */
  139. public Info getInfo(String function, String detail, int componentState,
  140. int shadow, int orientation, int gapSide,
  141. int arrowDirection, String parentType) {
  142. if (info != null) {
  143. for (int counter = 0, max = info.length; counter < max;counter++) {
  144. if (info[counter].getFunction() == function && info[counter].
  145. getMatchCount(detail, componentState, shadow,
  146. orientation, gapSide, arrowDirection,
  147. parentType) != -1) {
  148. return info[counter];
  149. }
  150. }
  151. }
  152. return null;
  153. }
  154. /**
  155. * Returns the number of non-null arugments and arguments that are
  156. * != UNDEFINED.
  157. */
  158. private int getMaxMatchCount(int componentState, int shadow,
  159. int orientation, int gapSide,
  160. int arrowDirection, String detail) {
  161. int count = 0;
  162. if (detail != null) {
  163. count++;
  164. }
  165. if (componentState != UNDEFINED) {
  166. count++;
  167. }
  168. if (shadow != UNDEFINED) {
  169. count++;
  170. }
  171. if (orientation != UNDEFINED) {
  172. count++;
  173. }
  174. if (gapSide != UNDEFINED) {
  175. count++;
  176. }
  177. if (arrowDirection != UNDEFINED) {
  178. count++;
  179. }
  180. return count;
  181. }
  182. /**
  183. * A description of how to paint a portion of a widget.
  184. */
  185. public static class Info {
  186. // match data
  187. private String function = null;
  188. private String detail = null;
  189. int gapSide = UNDEFINED;
  190. int orientation = UNDEFINED;
  191. int componentState = UNDEFINED;
  192. int shadow = UNDEFINED;
  193. int arrowDirection = UNDEFINED;
  194. // strings in this list will be interned
  195. // this list could be null
  196. ArrayList parentTypeList = null;
  197. boolean useAsBkgMask = false;
  198. // background
  199. Object image = null;
  200. Insets fileInsets = null;
  201. boolean stretch = false;
  202. boolean recolorable = false;
  203. Color colorizeColor = null;
  204. // overlay
  205. Object overlayImage = null;
  206. Insets overlayInsets = null;
  207. boolean overlayStretch = false;
  208. boolean overlayRecolorable = false;
  209. Color overlayColorizeColor = null;
  210. // gap start
  211. Object gapStartImage = null;
  212. Insets gapStartInsets = null;
  213. // gap
  214. Object gapImage = null;
  215. Insets gapInsets = null;
  216. // gap end
  217. Object gapEndImage = null;
  218. Insets gapEndInsets = null;
  219. public void setFunction(String function) {
  220. this.function = function.intern();
  221. }
  222. public void setDetail(String detail) {
  223. this.detail = detail.intern();
  224. }
  225. public String getFunction() {
  226. return function;
  227. }
  228. public Image getImage() {
  229. image = getImage(image);
  230. return (Image)image;
  231. }
  232. public boolean isRecolorable() {
  233. return recolorable;
  234. }
  235. public Color getColorizeColor() {
  236. return colorizeColor;
  237. }
  238. public boolean isBkgMask() {
  239. return useAsBkgMask;
  240. }
  241. public Insets getImageInsets() {
  242. return fileInsets;
  243. }
  244. public boolean getStretch() {
  245. return stretch;
  246. }
  247. public String getDetail() {
  248. return detail;
  249. }
  250. public int getComponentState() {
  251. return componentState;
  252. }
  253. public int getShadow() {
  254. return shadow;
  255. }
  256. public int getGapSide() {
  257. return gapSide;
  258. }
  259. public Image getGapImage() {
  260. gapImage = getImage(gapImage);
  261. return (Image)gapImage;
  262. }
  263. public Insets getGapInsets() {
  264. return gapInsets;
  265. }
  266. public Image getGapStartImage() {
  267. gapStartImage = getImage(gapStartImage);
  268. return (Image)gapStartImage;
  269. }
  270. public Insets getGapStartInsets() {
  271. return gapStartInsets;
  272. }
  273. public Image getGapEndImage() {
  274. gapEndImage = getImage(gapEndImage);
  275. return (Image)gapEndImage;
  276. }
  277. public Insets getGapEndInsets() {
  278. return gapEndInsets;
  279. }
  280. public Image getOverlayImage() {
  281. overlayImage = getImage(overlayImage);
  282. return (Image)overlayImage;
  283. }
  284. public Insets getOverlayInsets() {
  285. return overlayInsets;
  286. }
  287. public boolean getOverlayStretch() {
  288. return overlayStretch;
  289. }
  290. public boolean getOverlayRecolorable() {
  291. return overlayRecolorable;
  292. }
  293. public Color getOverlayColorizeColor() {
  294. return overlayColorizeColor;
  295. }
  296. public int getArrowDirection() {
  297. return arrowDirection;
  298. }
  299. public int getOrientation() {
  300. return orientation;
  301. }
  302. private Image getImage(final Object o) {
  303. if (o == null || o instanceof Image) {
  304. return (Image)o;
  305. }
  306. ImageIcon ii = (ImageIcon)AccessController.doPrivileged(new PrivilegedAction() {
  307. public Object run() {
  308. return new ImageIcon((String)o);
  309. }
  310. });
  311. if (ii.getIconWidth() > 0 && ii.getIconHeight() > 0) {
  312. return ii.getImage();
  313. }
  314. return null;
  315. }
  316. /**
  317. * Will return < 0 if doesn't match, otherwise return the
  318. * number of parameters that match.
  319. */
  320. int getMatchCount(String detail, int componentState, int shadow,
  321. int orientation, int gapSide, int arrowDirection,
  322. String parentType) {
  323. int matchCount = 0;
  324. if (this.componentState != UNDEFINED) {
  325. if (componentState != UNDEFINED &&
  326. this.componentState != componentState) {
  327. return -1;
  328. }
  329. matchCount++;
  330. }
  331. if (this.shadow != UNDEFINED) {
  332. if (shadow != UNDEFINED && this.shadow != shadow) {
  333. return -1;
  334. }
  335. matchCount++;
  336. }
  337. if (this.arrowDirection != UNDEFINED) {
  338. if (arrowDirection != UNDEFINED &&
  339. this.arrowDirection != arrowDirection) {
  340. return -1;
  341. }
  342. matchCount++;
  343. }
  344. if (this.orientation != UNDEFINED) {
  345. if (orientation != UNDEFINED &&
  346. this.orientation != orientation) {
  347. return -1;
  348. }
  349. matchCount++;
  350. }
  351. if (this.gapSide != UNDEFINED) {
  352. if (gapSide != UNDEFINED && this.gapSide != gapSide) {
  353. return -1;
  354. }
  355. matchCount++;
  356. }
  357. if (this.detail != null) {
  358. if (this.detail != detail) {
  359. return -1;
  360. }
  361. matchCount++;
  362. }
  363. if (this.parentTypeList != null) {
  364. boolean found = false;
  365. String type;
  366. Iterator itr = parentTypeList.iterator();
  367. while (itr.hasNext() && !found) {
  368. type = (String)itr.next();
  369. // NOTE: Maybe we should compare all lowercase.
  370. if (type == parentType) {
  371. found = true;
  372. }
  373. }
  374. if (!found) {
  375. return -1;
  376. }
  377. matchCount++;
  378. }
  379. return matchCount;
  380. }
  381. /**
  382. * Returns true if this Info matches that of the passed in Info.
  383. * This differs from equals in so far as this only compares the
  384. * properties that are used in lookup vs the actual images or
  385. * insets.
  386. *
  387. * @return true if the receiver and info can be considered equal
  388. * for lookup.
  389. */
  390. boolean matches(Info info) {
  391. return (info.function == function && info.detail == detail &&
  392. info.componentState == componentState &&
  393. info.shadow == shadow && info.gapSide == gapSide &&
  394. info.arrowDirection == arrowDirection &&
  395. info.orientation == orientation);
  396. }
  397. public String toString() {
  398. StringBuffer buf = new StringBuffer();
  399. buf.append("IMAGE:\n");
  400. if (function != null) {
  401. buf.append(" function=").append(function).append('\n');
  402. }
  403. if (detail != null) {
  404. buf.append(" detail=").append(detail).append('\n');
  405. }
  406. if (gapSide != UNDEFINED) {
  407. buf.append(" gapSide=");
  408. buf.append(getSideName(gapSide)).append('\n');
  409. }
  410. if (orientation != UNDEFINED) {
  411. buf.append(" orientation=");
  412. buf.append(getOrientName(orientation)).append('\n');
  413. }
  414. if (componentState != UNDEFINED) {
  415. buf.append(" componentState=");
  416. buf.append(getStateName(componentState, "UNDEFINED")).append('\n');
  417. }
  418. if (shadow != UNDEFINED) {
  419. buf.append(" shadow=");
  420. buf.append(getShadowName(shadow)).append('\n');
  421. }
  422. if (arrowDirection != UNDEFINED) {
  423. buf.append(" arrowDirection=");
  424. buf.append(getArrowDirectionName(arrowDirection)).append('\n');
  425. }
  426. if (parentTypeList != null) {
  427. buf.append(" parent_type={");
  428. for (Iterator iter = parentTypeList.iterator();
  429. iter.hasNext(); ) {
  430. buf.append(iter.next()).append(", ");
  431. }
  432. buf.deleteCharAt(buf.length() - 1).deleteCharAt(buf.length() - 1);
  433. buf.append("}\n");
  434. }
  435. if (useAsBkgMask != false) {
  436. buf.append(" use_as_bkg_mask=").append(useAsBkgMask).append('\n');
  437. }
  438. if (image != null) {
  439. buf.append(" image=").append(image).append('\n');
  440. }
  441. if (fileInsets != null) {
  442. buf.append(" fileInsets=").append(fileInsets).append('\n');
  443. }
  444. if (stretch != false) {
  445. buf.append(" stretch=").append(stretch).append('\n');
  446. }
  447. if (recolorable != false) {
  448. buf.append(" recolorable=").append(recolorable).append('\n');
  449. }
  450. if (colorizeColor != null) {
  451. buf.append(" colorize_color=");
  452. buf.append(getColorStringWithAlpha(colorizeColor)).append('\n');
  453. }
  454. if (overlayImage != null) {
  455. buf.append(" overlayImage=").append(overlayImage).append('\n');
  456. }
  457. if (overlayInsets != null) {
  458. buf.append(" overlayInsets=").append(overlayInsets).append('\n');
  459. }
  460. if (overlayStretch != false) {
  461. buf.append(" overlayStretch=").append(overlayStretch).append('\n');
  462. }
  463. if (overlayRecolorable != false) {
  464. buf.append(" overlay_recolorable=").append(overlayRecolorable).append('\n');
  465. }
  466. if (overlayColorizeColor != null) {
  467. buf.append(" overlay_colorize_color=");
  468. buf.append(getColorStringWithAlpha(overlayColorizeColor)).append('\n');
  469. }
  470. if (gapStartImage != null) {
  471. buf.append(" gapStartImage=").append(gapStartImage).append('\n');
  472. }
  473. if (gapStartInsets != null) {
  474. buf.append(" gapStartInsets=").append(gapStartInsets).append('\n');
  475. }
  476. if (gapImage != null) {
  477. buf.append(" gapImage=").append(gapImage).append('\n');
  478. }
  479. if (gapInsets != null) {
  480. buf.append(" gapInsets=").append(gapInsets).append('\n');
  481. }
  482. if (gapEndImage != null) {
  483. buf.append(" gapEndImage=").append(gapEndImage).append('\n');
  484. }
  485. if (gapEndInsets != null) {
  486. buf.append(" gapEndInsets=").append(gapEndInsets).append('\n');
  487. }
  488. buf.deleteCharAt(buf.length() - 1);
  489. return buf.toString();
  490. }
  491. private static String getSideName(int side) {
  492. switch(side) {
  493. case TOP: return "TOP";
  494. case BOTTOM: return "BOTTOM";
  495. case LEFT: return "LEFT";
  496. case RIGHT: return "RIGHT";
  497. case UNDEFINED: return "UNDEFINED";
  498. }
  499. return "UNKNOWN";
  500. }
  501. private static String getOrientName(int orient) {
  502. switch(orient) {
  503. case HORIZONTAL: return "HORIZONTAL";
  504. case VERTICAL: return "VERTICAL";
  505. case UNDEFINED: return "UNDEFINED";
  506. }
  507. return "UNKNOWN";
  508. }
  509. private static String getShadowName(int shadow) {
  510. switch(shadow) {
  511. case SHADOW_IN: return "SHADOW_IN";
  512. case SHADOW_OUT: return "SHADOW_OUT";
  513. case SHADOW_ETCHED_IN: return "SHADOW_ETCHED_IN";
  514. case SHADOW_ETCHED_OUT: return "SHADOW_ETCHED_OUT";
  515. case SHADOW_NONE: return "SHADOW_NONE";
  516. case UNDEFINED: return "UNDEFINED";
  517. }
  518. return "UNKNOWN";
  519. }
  520. private static String getArrowDirectionName(int dir) {
  521. switch(dir) {
  522. case ARROW_UP: return "ARROW_UP";
  523. case ARROW_DOWN: return "ARROW_DOWN";
  524. case ARROW_LEFT: return "ARROW_LEFT";
  525. case ARROW_RIGHT: return "ARROW_RIGHT";
  526. case UNDEFINED: return "UNDEFINED";
  527. }
  528. return "UNKNOWN";
  529. }
  530. }
  531. private static String getColorStringWithAlpha(Color c) {
  532. if (c == null) {
  533. return "null";
  534. }
  535. StringBuffer buf = new StringBuffer();
  536. buf.append('{');
  537. buf.append(c.getRed()).append(", ");
  538. buf.append(c.getGreen()).append(", ");
  539. buf.append(c.getBlue()).append(", ");
  540. buf.append(c.getAlpha()).append("}");
  541. return buf.toString();
  542. }
  543. public String toString() {
  544. StringBuffer buf = new StringBuffer(super.toString());
  545. if (buf.length() > 0) {
  546. buf.append('\n');
  547. }
  548. buf.append("*** Blueprint Engine Info ***\n");
  549. buf.append("icon_colorize = " + iconColorize + '\n');
  550. buf.append("icon_colorize_ancestor_type = ");
  551. if (iconAncestorTypes == null) {
  552. buf.append("null\n");
  553. } else {
  554. buf.append('{');
  555. for (int i = 0; i < iconAncestorTypes.length; i++) {
  556. buf.append(iconAncestorTypes[i] + ", ");
  557. }
  558. buf.deleteCharAt(buf.length() - 1);
  559. buf.deleteCharAt(buf.length() - 1);
  560. buf.append("}\n");
  561. }
  562. buf.append("colorize_color = ");
  563. buf.append(getColorStringWithAlpha(colorizeColor));
  564. buf.append('\n');
  565. if (info != null) {
  566. for (int i = 0; i < info.length; i++) {
  567. buf.append(info[i].toString()).append('\n');
  568. }
  569. }
  570. // remove last newline
  571. buf.deleteCharAt(buf.length() - 1);
  572. return buf.toString();
  573. }
  574. }