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