1. /*
  2. * @(#)GTKStyle.java 1.116 04/06/24
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import com.sun.java.swing.SwingUtilities2;
  9. import javax.swing.plaf.synth.*;
  10. import java.awt.*;
  11. import java.awt.image.BufferedImage;
  12. import java.awt.image.RescaleOp;
  13. import java.lang.reflect.*;
  14. import javax.swing.*;
  15. import javax.swing.plaf.*;
  16. import java.util.*;
  17. import java.security.*;
  18. import sun.swing.plaf.synth.*;
  19. import sun.awt.AppContext;
  20. /**
  21. * SynthStyle implementation used in GTK. All painting is mapped to
  22. * a <code>GTKEngine</code>.
  23. *
  24. * @version 1.116, 06/24/04
  25. * @author Scott Violet
  26. */
  27. public class GTKStyle extends DefaultSynthStyle implements GTKConstants {
  28. private static final String ICON_PROPERTY_PREFIX = "gtk.icon.";
  29. static final Color BLACK_COLOR = new ColorUIResource(Color.BLACK);
  30. static final Color WHITE_COLOR = new ColorUIResource(Color.WHITE);
  31. private static final Color[][] DEFAULT_COLORS;
  32. /**
  33. * State the color array at an particular index in DEFAULT_COLORS
  34. * represents.
  35. */
  36. private static final int[] DEFAULT_COLOR_MAP;
  37. // NOTE: This will only be used if you weren't using GTKLookAndFeel
  38. // to create a GTKStyle, otherwise the default comes from GTKLookAndFeel.
  39. private static final Font DEFAULT_FONT = new FontUIResource(
  40. "sansserif", Font.PLAIN, 10);
  41. /**
  42. * Backing style properties that are used if the style does not
  43. * defined the property.
  44. */
  45. // PENDING: This needs to be massaged so that it does not need to
  46. // be AppContext specific. In particular some of the Map values are
  47. // exposed and mutable and could effect other applets.
  48. private static final HashMap DATA = new HashMap();
  49. /**
  50. * Maps from a key that is passed to Style.get to the equivalent class
  51. * specific key.
  52. */
  53. private static final HashMap CLASS_SPECIFIC_MAP;
  54. private static final GTKGraphicsUtils GTK_GRAPHICS =new GTKGraphicsUtils();
  55. /**
  56. * Indicates the thickness has not been set.
  57. */
  58. static final int UNDEFINED_THICKNESS = -1;
  59. // NOTE: If you add a new field to this class you will need to update
  60. // addto, and possibly the clone and GTKStyle(GTKStyle) constructors.
  61. private int xThickness = 2;
  62. private int yThickness = 2;
  63. /**
  64. * Represents the values that are specific to a particular class.
  65. * This is a CircularIdentityList of CircularIdentityLists, where the
  66. * first level entries are gtk class names, and the second
  67. * CircularIdentityLists contains the actual key/value pairs.
  68. */
  69. private CircularIdentityList classSpecificValues;
  70. /**
  71. * Icons.
  72. */
  73. private GTKStockIconInfo[] icons;
  74. /**
  75. * Calculates the LIGHT color from the background color.
  76. */
  77. static Color calculateLightColor(Color bg) {
  78. return GTKColorType.adjustColor(bg, 1.0f, 1.3f, 1.3f);
  79. }
  80. /**
  81. * Calculates the DARK color from the background color.
  82. */
  83. static Color calculateDarkColor(Color bg) {
  84. return GTKColorType.adjustColor(bg, 1.0f, .7f, .7f);
  85. }
  86. /**
  87. * Calculates the MID color from the light and dark colors.
  88. */
  89. static Color calculateMidColor(Color lightColor, Color darkColor) {
  90. int light = lightColor.getRGB();
  91. int dark = darkColor.getRGB();
  92. int rLight = (light & 0xFF0000) >> 16;
  93. int rDark = (dark & 0xFF0000) >> 16;
  94. int gLight = (light & 0x00FF00) >> 8;
  95. int gDark = (dark & 0x00FF00) >> 8;
  96. int bLight = (light & 0xFF);
  97. int bDark = (dark & 0xFF);
  98. return new ColorUIResource((((rLight + rDark) / 2) << 16) |
  99. (((gLight + gDark) / 2) << 8) |
  100. ((bLight + bDark) / 2));
  101. }
  102. /**
  103. * Calculates the MID color from the background color.
  104. */
  105. static Color calculateMidColor(Color bg) {
  106. return calculateMidColor(calculateLightColor(bg),
  107. calculateDarkColor(bg));
  108. }
  109. /**
  110. * Creates an array of colors populated based on the passed in
  111. * the background color. Specifically this sets the
  112. * BACKGROUND, LIGHT, DARK, MID, BLACK, WHITE and FOCUS colors
  113. * from that of color, which is assumed to be the background.
  114. */
  115. static Color[] getColorsFrom(Color bg, Color fg) {
  116. Color lightColor = calculateLightColor(bg);
  117. Color darkColor = calculateDarkColor(bg);
  118. Color midColor = calculateMidColor(lightColor, darkColor);
  119. Color[] colors = new Color[GTKColorType.MAX_COUNT];
  120. colors[GTKColorType.BACKGROUND.getID()] = bg;
  121. colors[GTKColorType.LIGHT.getID()] = lightColor;
  122. colors[GTKColorType.DARK.getID()] = darkColor;
  123. colors[GTKColorType.MID.getID()] = midColor;
  124. colors[GTKColorType.BLACK.getID()] = BLACK_COLOR;
  125. colors[GTKColorType.WHITE.getID()] = WHITE_COLOR;
  126. colors[GTKColorType.FOCUS.getID()] = BLACK_COLOR;
  127. colors[GTKColorType.FOREGROUND.getID()] = fg;
  128. colors[GTKColorType.TEXT_FOREGROUND.getID()] = fg;
  129. colors[GTKColorType.TEXT_BACKGROUND.getID()] = WHITE_COLOR;
  130. return colors;
  131. }
  132. /**
  133. * Creates a new GTKStyle that is a copy of the passed in style.
  134. */
  135. public GTKStyle(DefaultSynthStyle style) {
  136. super(style);
  137. if (style instanceof GTKStyle) {
  138. GTKStyle gStyle = (GTKStyle)style;
  139. xThickness = gStyle.xThickness;
  140. yThickness = gStyle.yThickness;
  141. icons = gStyle.icons;
  142. classSpecificValues = cloneClassSpecificValues(
  143. gStyle.classSpecificValues);
  144. }
  145. }
  146. /**
  147. * Creates an empty GTKStyle.
  148. */
  149. public GTKStyle() {
  150. super(new Insets(-1, -1, -1, -1), true, null, null);
  151. }
  152. /**
  153. * Creates a GTKStyle with the specified font.
  154. *
  155. * @param font Font to use in GTK.
  156. */
  157. public GTKStyle(Font font) {
  158. this();
  159. setFont(font);
  160. }
  161. /**
  162. * Creates a GTKStyle with the specified parameters.
  163. *
  164. * @param states StateInfo specifying the colors and fonts to use for
  165. * a particular state.
  166. * @param classSpecificValues Values that are specific to a particular
  167. * class
  168. * @param font to use.
  169. * @param xThickness X thickness
  170. * @param yThickness Y thickness
  171. * @param GTKStockIconInfo stock icons for this style.
  172. */
  173. GTKStyle(StateInfo[] states,
  174. CircularIdentityList classSpecificValues,
  175. Font font,
  176. int xThickness, int yThickness,
  177. GTKStockIconInfo[] icons) {
  178. super(new Insets(-1, -1, -1, -1), true, states, null);
  179. setFont(font);
  180. this.xThickness = xThickness;
  181. this.yThickness = yThickness;
  182. this.icons = icons;
  183. this.classSpecificValues = classSpecificValues;
  184. }
  185. /**
  186. * {@inheritDoc}
  187. */
  188. public void installDefaults(SynthContext context) {
  189. super.installDefaults(context);
  190. if (!context.getRegion().isSubregion()) {
  191. context.getComponent().putClientProperty(
  192. SwingUtilities2.AA_TEXT_PROPERTY_KEY,
  193. GTKLookAndFeel.aaText);
  194. }
  195. }
  196. public SynthGraphicsUtils getGraphicsUtils(SynthContext context) {
  197. return GTK_GRAPHICS;
  198. }
  199. /**
  200. * Returns the object used to renderer the look.
  201. *
  202. * @param context SynthContext indentifying requestor
  203. * @return GTKEngine used to provide the look
  204. */
  205. public GTKEngine getEngine(SynthContext context) {
  206. GTKEngine engine = (GTKEngine)get(context, "engine");
  207. if (engine == null) {
  208. return GTKEngine.INSTANCE;
  209. }
  210. return engine;
  211. }
  212. /**
  213. * Returns a <code>SynthPainter</code> that will route the appropriate
  214. * calls to a <code>GTKEngine</code>.
  215. *
  216. * @param state SynthContext indentifying requestor
  217. * @return SynthPainter
  218. */
  219. public SynthPainter getPainter(SynthContext state) {
  220. return GTKPainter.INSTANCE;
  221. }
  222. /**
  223. * Returns the Insets. If <code>to</code> is non-null the resulting
  224. * insets will be placed in it, otherwise a new Insets object will be
  225. * created and returned.
  226. *
  227. * @param context SynthContext indentifying requestor
  228. * @param to Where to place Insets
  229. * @return Insets.
  230. */
  231. public Insets getInsets(SynthContext state, Insets insets) {
  232. insets = super.getInsets(state, insets);
  233. if (insets.top == -1) {
  234. insets.left = insets.right = insets.top = insets.bottom = 0;
  235. insets = GTKPainter.INSTANCE.getInsets(state, insets);
  236. }
  237. return insets;
  238. }
  239. /**
  240. * Returns the value for a class specific property. A class specific value
  241. * is a value that will be picked up based on class hierarchy.
  242. * For example, a value specified for JComponent would be inherited on
  243. * JButtons and JTrees, but not Button.
  244. * <p>
  245. * Note, the key used here should only contain the letters A-Z, a-z, the
  246. * digits 0-9, and the '-' character. If you need to request a value for
  247. * a key having characters outside this list, replace any other characters
  248. * with '-'. (ie. "default_border" should be "default-border").
  249. *
  250. * @param region Region requesting class specific value
  251. * @param key Key identifying class specific value
  252. * @return Value, or null if one has not been defined.
  253. */
  254. public Object getClassSpecificValue(Region region, String key) {
  255. if (classSpecificValues != null) {
  256. String gtkClass = GTKStyleFactory.gtkClassFor(region);
  257. while (gtkClass != null) {
  258. CircularIdentityList classValues = (CircularIdentityList)
  259. classSpecificValues.get(gtkClass);
  260. if (classValues != null) {
  261. Object value = classValues.get(key);
  262. if (value != null) {
  263. return value;
  264. }
  265. }
  266. gtkClass = GTKStyleFactory.gtkSuperclass(gtkClass);
  267. }
  268. }
  269. return null;
  270. }
  271. /**
  272. * Returns the value for a class specific property. A class specific value
  273. * is a value that will be picked up based on class hierarchy.
  274. * For example, a value specified for JComponent would be inherited on
  275. * JButtons and JTrees, but not Button.
  276. * <p>
  277. * Note, the key used here should only contain the letters A-Z, a-z, the
  278. * digits 0-9, and the '-' character. If you need to request a value for
  279. * a key having characters outside this list, replace any other characters
  280. * with '-'. (ie. "default_border" should be "default-border").
  281. *
  282. * @param context SynthContext indentifying requestor
  283. * @param key Key identifying class specific value
  284. * @return Value, or null if one has not been defined.
  285. */
  286. public Object getClassSpecificValue(SynthContext context, String key) {
  287. return getClassSpecificValue(context.getRegion(), key);
  288. }
  289. /**
  290. * Convenience method to get a class specific integer value.
  291. *
  292. * @param context SynthContext indentifying requestor
  293. * @param key Key identifying class specific value
  294. * @param defaultValue Returned if there is no value for the specified
  295. * type
  296. * @return Value, or defaultValue if <code>key</code> is not defined
  297. */
  298. public int getClassSpecificIntValue(SynthContext context, String key,
  299. int defaultValue) {
  300. Object value = getClassSpecificValue(context, key);
  301. if (value instanceof Number) {
  302. return ((Number)value).intValue();
  303. }
  304. return defaultValue;
  305. }
  306. /**
  307. * Convenience method to get a class specific Insets value.
  308. *
  309. * @param context SynthContext indentifying requestor
  310. * @param key Key identifying class specific value
  311. * @param defaultValue Returned if there is no value for the specified
  312. * type
  313. * @return Value, or defaultValue if <code>key</code> is not defined
  314. */
  315. public Insets getClassSpecificInsetsValue(SynthContext context, String key,
  316. Insets defaultValue) {
  317. Object value = getClassSpecificValue(context, key);
  318. if (value instanceof Insets) {
  319. return (Insets)value;
  320. }
  321. return defaultValue;
  322. }
  323. /**
  324. * Convenience method to get a class specific Boolean value.
  325. *
  326. * @param context SynthContext indentifying requestor
  327. * @param key Key identifying class specific value
  328. * @param defaultValue Returned if there is no value for the specified
  329. * type
  330. * @return Value, or defaultValue if <code>key</code> is not defined
  331. */
  332. public boolean getClassSpecificBoolValue(SynthContext context, String key,
  333. boolean defaultValue) {
  334. Object value = getClassSpecificValue(context, key);
  335. if (value instanceof Boolean) {
  336. return ((Boolean)value).booleanValue();
  337. }
  338. return defaultValue;
  339. }
  340. public Object getDefaultValue(SynthContext context, Object key) {
  341. // See if this is a class specific value.
  342. Object classKey = CLASS_SPECIFIC_MAP.get(key);
  343. Object value = null;
  344. if (classKey != null) {
  345. value = getClassSpecificValue(context, (String)classKey);
  346. if (value != null) {
  347. return value;
  348. }
  349. }
  350. if (key == "ScrollPane.viewportBorderInsets") {
  351. return GTKPainter.INSTANCE.getScrollPaneInsets(context,
  352. new Insets(0,0,0,0));
  353. } else if (key == "Slider.tickColor") {
  354. return getColor(context.getComponent(), context.getRegion(),
  355. context.getComponentState(), ColorType.FOREGROUND);
  356. }
  357. synchronized (DATA) {
  358. value = DATA.get(key);
  359. }
  360. if (value instanceof StyleSpecificValue) {
  361. put(key, ((StyleSpecificValue)value).getValue(context));
  362. }
  363. if (value == null && key != "engine") {
  364. // For backward compatability we'll fallback to the UIManager.
  365. // We don't go to the UIManager for engine as the engine is GTK
  366. // specific.
  367. value = UIManager.get(key);
  368. if (key == "Table.rowHeight") {
  369. int focusLineWidth = getClassSpecificIntValue(
  370. context, "focus-line-width", 0);
  371. if (value == null && focusLineWidth > 0) {
  372. value = new Integer(16 + 2 * focusLineWidth);
  373. }
  374. }
  375. }
  376. // Don't call super, we don't want to pick up defaults from
  377. // SynthStyle.
  378. return value;
  379. }
  380. /**
  381. * Returns the font for the specified state. This should NOT callback
  382. * to the JComponent.
  383. *
  384. * @param c JComponent the style is associated with
  385. * @param id Region identifier
  386. * @param state State of the region.
  387. * @return Font to render with
  388. */
  389. protected Font getFontForState(JComponent c, Region id, int state) {
  390. state = GTKLookAndFeel.synthStateToGTKState(id, state);
  391. Font f = super.getFontForState(c, id, state);
  392. if (f == null) {
  393. return DEFAULT_FONT;
  394. }
  395. return f;
  396. }
  397. Color getGTKColor(int state, ColorType type) {
  398. return getGTKColor(null, null, state, type);
  399. }
  400. /**
  401. * This method is to be used from within GTK when do NOT want
  402. * the component state to be mapped. It will NOT remap the state as
  403. * the other various getters do.
  404. *
  405. * @param c JComponent the style is associated with
  406. * @param id Region identifier
  407. * @param state State of the region.
  408. * @param type Type of color being requested.
  409. * @return Color to render with
  410. */
  411. public Color getGTKColor(JComponent c, Region id,
  412. int state, ColorType type) {
  413. // NOTE: c and id are only ever null when this is called from
  414. // GTKLookAndFeel.loadSystemColorDefaults.
  415. if (c != null && id != null) {
  416. if (!id.isSubregion() &&
  417. (state & SynthConstants.ENABLED) == SynthConstants.ENABLED) {
  418. if (type == ColorType.BACKGROUND) {
  419. Color bg = c.getBackground();
  420. if (!(bg instanceof UIResource)) {
  421. return bg;
  422. }
  423. }
  424. else if (type == ColorType.FOREGROUND) {
  425. Color fg = c.getForeground();
  426. if (!(fg instanceof UIResource)) {
  427. return fg;
  428. }
  429. }
  430. else if (type == ColorType.TEXT_FOREGROUND) {
  431. Color fg = c.getForeground();
  432. if (!(fg instanceof UIResource)) {
  433. return fg;
  434. }
  435. }
  436. else if (type == ColorType.TEXT_BACKGROUND) {
  437. Color bg = c.getBackground();
  438. if (!(bg instanceof UIResource)) {
  439. return bg;
  440. }
  441. }
  442. }
  443. }
  444. Color color = super.getColorForState(c, id, state, type);
  445. if (color != null) {
  446. return color;
  447. }
  448. return getDefaultColor(c, id, state, type);
  449. }
  450. /**
  451. * getColor is overriden to map the state from a Synth state to the
  452. * GTK state, this should be not used when you have already mapped the
  453. * state. Additionally this will map TEXT_FOREGROUND to the
  454. * <code>c.getForeground()</code> if it is non-null and not a UIResource.
  455. *
  456. * @param c JComponent the style is associated with
  457. * @param id Region identifier
  458. * @param state State of the region.
  459. * @param type Type of color being requested.
  460. * @return Color to render with
  461. */
  462. public Color getColor(JComponent c, Region id, int state,
  463. ColorType type) {
  464. if (id == Region.LABEL && type == ColorType.TEXT_FOREGROUND) {
  465. type = ColorType.FOREGROUND;
  466. }
  467. state = GTKLookAndFeel.synthStateToGTKState(id, state);
  468. if (!id.isSubregion() &&
  469. (state & SynthConstants.ENABLED) == SynthConstants.ENABLED) {
  470. if (type == ColorType.BACKGROUND) {
  471. return c.getBackground();
  472. }
  473. else if (type == ColorType.FOREGROUND) {
  474. return c.getForeground();
  475. }
  476. else if (type == ColorType.TEXT_FOREGROUND) {
  477. Color fg = c.getForeground();
  478. if (fg != null && !(fg instanceof UIResource)) {
  479. // Only use the fg for text if specified.
  480. return fg;
  481. }
  482. }
  483. }
  484. return getColorForState(c, id, state, type);
  485. }
  486. /**
  487. * Returns the color for the specified state. This redirects to the
  488. * JComponent <code>c</code> as necessary. If this does not redirect
  489. * to the JComponent <code>getColorForState</code> is invoked.
  490. *
  491. * @param c JComponent the style is associated with
  492. * @param id Region identifier
  493. * @param state State of the region.
  494. * @param type Type of color being requested.
  495. * @return Color to render with
  496. */
  497. protected Color getColorForState(JComponent c, Region id, int state,
  498. ColorType type) {
  499. Color color = super.getColorForState(c, id, state, type);
  500. if (color != null) {
  501. return color;
  502. }
  503. if (type == ColorType.FOCUS) {
  504. return BLACK_COLOR;
  505. }
  506. else if (type == GTKColorType.BLACK) {
  507. return BLACK_COLOR;
  508. }
  509. else if (type == GTKColorType.WHITE) {
  510. return WHITE_COLOR;
  511. }
  512. if (type == ColorType.TEXT_FOREGROUND && (GTKStyleFactory.
  513. isLabelBearing(id) || id == Region.MENU_ITEM_ACCELERATOR ||
  514. id == Region.TABBED_PANE_TAB)) {
  515. type = ColorType.FOREGROUND;
  516. }
  517. else if (id == Region.TABLE || id == Region.LIST ||
  518. id == Region.TREE || id == Region.TREE_CELL){
  519. if (type == ColorType.FOREGROUND) {
  520. type = ColorType.TEXT_FOREGROUND;
  521. if (state == SynthConstants.PRESSED) {
  522. state = SynthConstants.SELECTED;
  523. }
  524. }
  525. else if (type == ColorType.BACKGROUND) {
  526. type = ColorType.TEXT_BACKGROUND;
  527. if (state == SynthConstants.PRESSED) {
  528. state = SynthConstants.SELECTED;
  529. }
  530. }
  531. }
  532. return getDefaultColor(c, id, state, type);
  533. }
  534. /**
  535. * Returns the Color to use if the GTKStyle does not specify a color.
  536. *
  537. * @param c JComponent the style is associated with
  538. * @param id Region identifier
  539. * @param state State of the region.
  540. * @param type Type of color being requested.
  541. * @return Color to render with
  542. */
  543. Color getDefaultColor(JComponent c, Region id, int state,
  544. ColorType type) {
  545. if (type == ColorType.FOCUS) {
  546. return BLACK_COLOR;
  547. }
  548. else if (type == GTKColorType.BLACK) {
  549. return BLACK_COLOR;
  550. }
  551. else if (type == GTKColorType.WHITE) {
  552. return WHITE_COLOR;
  553. }
  554. for (int counter = DEFAULT_COLOR_MAP.length - 1;
  555. counter >= 0; counter--) {
  556. if ((DEFAULT_COLOR_MAP[counter] & state) != 0) {
  557. if (type.getID() < DEFAULT_COLORS[counter].length) {
  558. return DEFAULT_COLORS[counter][type.getID()];
  559. }
  560. }
  561. }
  562. if (type.getID() < DEFAULT_COLORS[2].length) {
  563. return DEFAULT_COLORS[2][type.getID()];
  564. }
  565. return null;
  566. }
  567. /**
  568. * Returns the value to initialize the opacity property of the Component
  569. * to. A Style should NOT assume the opacity will remain this value, the
  570. * developer may reset it or override it.
  571. *
  572. * @param context SynthContext indentifying requestor
  573. * @return opaque Whether or not the JComponent is opaque.
  574. */
  575. public boolean isOpaque(SynthContext context) {
  576. Region region = context.getRegion();
  577. if (region == Region.COMBO_BOX ||
  578. region == Region.DESKTOP_PANE ||
  579. region == Region.DESKTOP_ICON ||
  580. region == Region.EDITOR_PANE ||
  581. region == Region.FORMATTED_TEXT_FIELD ||
  582. region == Region.INTERNAL_FRAME ||
  583. region == Region.LIST ||
  584. region == Region.MENU_BAR ||
  585. region == Region.PASSWORD_FIELD ||
  586. region == Region.POPUP_MENU ||
  587. region == Region.PROGRESS_BAR ||
  588. region == Region.ROOT_PANE ||
  589. region == Region.SCROLL_PANE ||
  590. region == Region.SPINNER ||
  591. region == Region.TABLE ||
  592. region == Region.TEXT_AREA ||
  593. region == Region.TEXT_FIELD ||
  594. region == Region.TEXT_PANE ||
  595. region == Region.TOOL_BAR_DRAG_WINDOW ||
  596. region == Region.TOOL_TIP ||
  597. region == Region.TREE ||
  598. region == Region.VIEWPORT) {
  599. return true;
  600. }
  601. Component c = context.getComponent();
  602. String name = c.getName();
  603. if (name == "ComboBox.renderer" || name == "ComboBox.listRenderer") {
  604. return true;
  605. }
  606. return false;
  607. }
  608. /**
  609. * Returns the X thickness to use for this GTKStyle.
  610. *
  611. * @return x thickness.
  612. */
  613. public int getXThickness() {
  614. return xThickness;
  615. }
  616. /**
  617. * Returns the Y thickness to use for this GTKStyle.
  618. *
  619. * @return x thickness.
  620. */
  621. public int getYThickness() {
  622. return yThickness;
  623. }
  624. private Icon getStockIcon(SynthContext context, String key, int type) {
  625. Icon icon = null;
  626. GTKStockIconInfo iconInfo = null;
  627. GTKIconSource bestSource = null;
  628. int direction = LTR;
  629. if (context != null) {
  630. ComponentOrientation co = context.getComponent().
  631. getComponentOrientation();
  632. if (co == null || co.isLeftToRight()) {
  633. direction = LTR;
  634. }
  635. else {
  636. direction = RTL;
  637. }
  638. }
  639. // See if the style defines an icon
  640. if (icons != null) {
  641. for (int i = 0; i < icons.length; i++) {
  642. // find the first one that matches our key
  643. if (icons[i].getKey() == key) {
  644. iconInfo = icons[i];
  645. break;
  646. }
  647. }
  648. if (iconInfo != null) {
  649. // PENDING(shannonh) - pass in actual state
  650. bestSource = iconInfo.getBestIconSource(direction,
  651. SynthConstants.ENABLED,
  652. type);
  653. }
  654. if (bestSource != null) {
  655. icon = bestSource.toIcon();
  656. }
  657. }
  658. if (icon == null) {
  659. // Use a default icon
  660. String propName = ICON_PROPERTY_PREFIX + key + '.' + type + '.' +
  661. (direction == RTL ? "rtl" : "ltr");
  662. Image img = (Image)Toolkit.getDefaultToolkit().
  663. getDesktopProperty(propName);
  664. if (img != null) {
  665. icon = new ImageIcon(img);
  666. return icon;
  667. } else {
  668. icon = (Icon)((UIDefaults.LazyValue)LookAndFeel.makeIcon(
  669. GTKStyle.class, "resources/" + key + "-" + type +
  670. ".png")).createValue(null);
  671. }
  672. }
  673. if (icon == null) {
  674. return null;
  675. }
  676. BufferedImage image = null;
  677. // If the stock icon we found had a wildcard size,
  678. // we force the size to match that requested
  679. if (bestSource == null || bestSource.getSize() == UNDEFINED) {
  680. Dimension iconSize = GTKStockIconInfo.getIconSize(type);
  681. if (iconSize != null && (icon.getIconWidth() != iconSize.width ||
  682. icon.getIconHeight() != iconSize.height)) {
  683. image = new BufferedImage(iconSize.width, iconSize.height,
  684. BufferedImage.TYPE_INT_ARGB);
  685. Graphics2D g2d = (Graphics2D)image.getGraphics();
  686. // for nicer scaling
  687. g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
  688. RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  689. Image oldImage = getImageFromIcon(icon, false);
  690. g2d.drawImage(oldImage, 0, 0, iconSize.width, iconSize.height, null);
  691. g2d.dispose();
  692. }
  693. }
  694. /* This is not done for now. We cache icons and use cached copies regardless
  695. of the component state, so we don't want to cache desaturated icons
  696. if (bestSource == null || bestSource.getState() == UNDEFINED) {
  697. // We may need to change saturation for some states
  698. int state = context.getComponentState();
  699. if (state == SynthConstants.DISABLED ||
  700. state == SynthConstants.MOUSE_OVER) {
  701. if (image == null) {
  702. image = (BufferedImage)getImageFromIcon(icon, true);
  703. }
  704. float rescaleFactor =
  705. (state == SynthConstants.DISABLED ? 0.8f : 1.2f);
  706. RescaleOp op = new RescaleOp(rescaleFactor, 0, null);
  707. // RescaleOp allows for in-place filtering
  708. op.filter(image, image);
  709. }
  710. }
  711. */
  712. if (image != null) {
  713. icon = new ImageIcon(image);
  714. }
  715. return icon;
  716. }
  717. private Image getImageFromIcon(Icon icon, boolean requireBufferedImage) {
  718. Image img = null;
  719. if (icon instanceof ImageIcon) {
  720. img = ((ImageIcon)icon).getImage();
  721. if (requireBufferedImage && !(img instanceof BufferedImage)) {
  722. img = null;
  723. }
  724. }
  725. if (img == null) {
  726. img = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(),
  727. BufferedImage.TYPE_INT_ARGB);
  728. Graphics g = img.getGraphics();
  729. icon.paintIcon(null, g, 0, 0);
  730. g.dispose();
  731. }
  732. return img;
  733. }
  734. /**
  735. * Adds the specific label based properties from <code>style</code> to
  736. * this style.
  737. */
  738. void addLabelProperties(GTKStyle style) {
  739. StateInfo[] states = getStateInfo();
  740. StateInfo[] oStates = style.getStateInfo();
  741. // Take the font
  742. setFont(style.getFontForState(null, null, 0));
  743. // And TEXT_FOREGROUND
  744. if (states == null) {
  745. if (oStates == null) {
  746. return;
  747. }
  748. states = new StateInfo[oStates.length];
  749. for (int counter = 0; counter < oStates.length; counter++) {
  750. Color color = oStates[counter].getColor(
  751. GTKColorType.FOREGROUND);
  752. states[counter] = createStateInfo(oStates[counter].
  753. getComponentState(), GTKColorType.TEXT_FOREGROUND, color);
  754. }
  755. }
  756. else {
  757. // Reset the text foreground of all our states, this will ensure
  758. // the correct color is picked up if style doesn't specify a
  759. // text color.
  760. for (int counter = states.length - 1; counter >= 0; counter--) {
  761. ((GTKStateInfo)states[counter]).setColor(
  762. GTKColorType.TEXT_FOREGROUND, null);
  763. }
  764. if (oStates != null) {
  765. for (int oCounter = oStates.length - 1; oCounter >= 0;
  766. oCounter--) {
  767. boolean matched = false;
  768. StateInfo oState = oStates[oCounter];
  769. int componentState = oState.getComponentState();
  770. Color color = oState.getColor(GTKColorType.FOREGROUND);
  771. for (int tCounter = states.length - 1; tCounter >= 0;
  772. tCounter--) {
  773. if (componentState == states[tCounter].
  774. getComponentState()) {
  775. ((GTKStateInfo)states[tCounter]).setColor(
  776. GTKColorType.TEXT_FOREGROUND, color);
  777. matched = true;
  778. break;
  779. }
  780. }
  781. if (!matched) {
  782. StateInfo[] newStates = new StateInfo[states.length+1];
  783. System.arraycopy(states, 0, newStates, 0,
  784. states.length);
  785. newStates[states.length] = createStateInfo(
  786. componentState, GTKColorType.TEXT_FOREGROUND,
  787. color);
  788. states = newStates;
  789. }
  790. }
  791. }
  792. }
  793. }
  794. /**
  795. * Creates a StateInfo with the specified component state, ColorType
  796. * and color. Subclasses that create a custom GTKStateInfo will need
  797. * to subclass and override this.
  798. */
  799. GTKStateInfo createStateInfo(int state, ColorType type, Color color) {
  800. Color[] colors = new Color[GTKColorType.MAX_COUNT];
  801. colors[type.getID()] = color;
  802. return new GTKStateInfo(state, null, colors, null);
  803. }
  804. /**
  805. * Adds a value specific to the style.
  806. */
  807. void put(Object key, Object value) {
  808. Map data = getData();
  809. if (data== null) {
  810. data = new HashMap();
  811. setData(data);
  812. }
  813. data.put(key, value);
  814. }
  815. /**
  816. * Returns true if the style should fill in the background of the
  817. * specified context for the specified state.
  818. */
  819. boolean fillBackground(SynthContext context, int state) {
  820. GTKStateInfo info = (GTKStateInfo)getStateInfo(state);
  821. if (info != null) {
  822. Object backgroundImage = info.getBackgroundImage();
  823. if (backgroundImage == "<none>" || backgroundImage == null) {
  824. return true;
  825. }
  826. return false;
  827. }
  828. return true;
  829. }
  830. /**
  831. * Returns the Icon to fill the background in with for the specified
  832. * context and state.
  833. */
  834. Image getBackgroundImage(SynthContext context, int state) {
  835. GTKStateInfo info = (GTKStateInfo)getStateInfo(state);
  836. if (info != null) {
  837. Object backgroundImage = info.getBackgroundImage();
  838. if (backgroundImage instanceof Image) {
  839. return (Image)backgroundImage;
  840. }
  841. }
  842. return null;
  843. }
  844. /**
  845. * Creates a clone of this GTKStyle.
  846. *
  847. * @return Clone of this GTKStyle.
  848. */
  849. public Object clone() {
  850. GTKStyle style = (GTKStyle)super.clone();
  851. style.classSpecificValues = cloneClassSpecificValues(
  852. style.classSpecificValues);
  853. return style;
  854. }
  855. /**
  856. * Merges the contents of this Style with that of the passed in Style,
  857. * returning the resulting merged syle. Properties of this
  858. * <code>GTKStyle</code> will take precedence over those of the
  859. * passed in <code>DefaultSynthStyle</code>. For example, if this
  860. * style specifics a non-null font, the returned style will have its
  861. * font so to that regardless of the <code>style</code>'s font.
  862. *
  863. * @param style Style to add our styles to
  864. * @return Merged style.
  865. */
  866. public DefaultSynthStyle addTo(DefaultSynthStyle style) {
  867. if (!(style instanceof GTKStyle)) {
  868. style = new GTKStyle(style);
  869. }
  870. GTKStyle gtkStyle = (GTKStyle)super.addTo(style);
  871. if (xThickness != UNDEFINED_THICKNESS) {
  872. gtkStyle.xThickness = xThickness;
  873. }
  874. if (yThickness != UNDEFINED_THICKNESS) {
  875. gtkStyle.yThickness = yThickness;
  876. }
  877. if (gtkStyle.icons == null) {
  878. gtkStyle.icons = icons;
  879. }
  880. else if (icons != null) {
  881. GTKStockIconInfo[] mergedIcons =
  882. new GTKStockIconInfo[gtkStyle.icons.length + icons.length];
  883. System.arraycopy(icons, 0, mergedIcons, 0, icons.length);
  884. System.arraycopy(gtkStyle.icons, 0, mergedIcons, icons.length, gtkStyle.icons.length);
  885. gtkStyle.icons = mergedIcons;
  886. }
  887. if (gtkStyle.classSpecificValues == null) {
  888. gtkStyle.classSpecificValues =
  889. cloneClassSpecificValues(classSpecificValues);
  890. } else {
  891. addClassSpecificValues(classSpecificValues, gtkStyle.classSpecificValues);
  892. }
  893. return gtkStyle;
  894. }
  895. /**
  896. * Adds the data from one set of class specific values into another.
  897. *
  898. * @param from the list to grab data from (may be null)
  899. * @param to the list to add data to (may not be null)
  900. */
  901. static void addClassSpecificValues(CircularIdentityList from,
  902. CircularIdentityList to) {
  903. if (to == null) {
  904. throw new IllegalArgumentException("to may not be null");
  905. }
  906. if (from == null) {
  907. return;
  908. }
  909. synchronized(from) {
  910. Object firstKey = from.next();
  911. if (firstKey != null) {
  912. Object key = firstKey;
  913. do {
  914. CircularIdentityList cList = ((CircularIdentityList)
  915. from.get());
  916. CircularIdentityList oSublist = (CircularIdentityList)
  917. to.get(key);
  918. if (oSublist == null) {
  919. to.set(key, cList.clone());
  920. }
  921. else {
  922. Object cFirstKey = cList.next();
  923. if (cFirstKey != null) {
  924. Object cKey = cFirstKey;
  925. do {
  926. oSublist.set(cKey, cList.get());
  927. cKey = cList.next();
  928. } while (cKey != cFirstKey);
  929. }
  930. }
  931. key = from.next();
  932. } while (key != firstKey);
  933. }
  934. }
  935. }
  936. /**
  937. * Clones the class specific values.
  938. */
  939. static CircularIdentityList cloneClassSpecificValues(
  940. CircularIdentityList list) {
  941. if (list == null) {
  942. return null;
  943. }
  944. CircularIdentityList clone;
  945. synchronized(list) {
  946. Object firstKey = list.next();
  947. if (firstKey == null) {
  948. // Empty list
  949. return null;
  950. }
  951. clone = new CircularIdentityList();
  952. Object key = firstKey;
  953. do {
  954. clone.set(key, ((CircularIdentityList)list.get()).clone());
  955. key = list.next();
  956. } while (key != firstKey);
  957. }
  958. return clone;
  959. }
  960. /**
  961. * GTKStockIconInfo mirrors the information from a stock declaration
  962. * in the rc file: stock icon id, and a set of icon source
  963. * specifications.
  964. */
  965. static class GTKStockIconInfo {
  966. private String key;
  967. private GTKIconSource[] sources;
  968. private static Map<String,Integer> ICON_TYPE_MAP;
  969. private static final Object ICON_SIZE_KEY = new StringBuffer("IconSize");
  970. GTKStockIconInfo(String key, GTKIconSource[] sources) {
  971. this.key = key.intern();
  972. this.sources = sources;
  973. Arrays.sort(this.sources);
  974. }
  975. public String getKey() {
  976. return key;
  977. }
  978. public GTKIconSource getBestIconSource(int direction, int state, int size) {
  979. for (int i = 0; i < sources.length; i++) {
  980. GTKIconSource src = sources[i];
  981. if ((src.direction == UNDEFINED || src.direction == direction)
  982. && (src.state == UNDEFINED || src.state == state)
  983. && (src.size == UNDEFINED || src.size == size)) {
  984. return src;
  985. }
  986. }
  987. return null;
  988. }
  989. public String toString() {
  990. StringBuffer buf = new StringBuffer("STOCK ICON " + key + ":\n");
  991. for (int i = 0; i < sources.length; i++) {
  992. buf.append(" ").append(sources[i].toString()).append('\n');
  993. }
  994. // remove last newline
  995. buf.deleteCharAt(buf.length() - 1);
  996. return buf.toString();
  997. }
  998. /**
  999. * Return icon type (GtkIconSize value) given a symbolic name which can
  1000. * occur in a theme file.
  1001. *
  1002. * @param size symbolic name, e.g. gtk-button
  1003. * @return icon type. Valid types are 1 to 6
  1004. */
  1005. public static int getIconType(String size) {
  1006. if (size == null) {
  1007. return UNDEFINED;
  1008. }
  1009. if (ICON_TYPE_MAP == null) {
  1010. initIconTypeMap();
  1011. }
  1012. Integer n = ICON_TYPE_MAP.get(size);
  1013. return n != null ? n.intValue() : UNDEFINED;
  1014. }
  1015. private static void initIconTypeMap() {
  1016. ICON_TYPE_MAP = new HashMap<String,Integer>();
  1017. ICON_TYPE_MAP.put("gtk-menu", new Integer(1));
  1018. ICON_TYPE_MAP.put("gtk-small-toolbar", new Integer(2));
  1019. ICON_TYPE_MAP.put("gtk-large-toolbar", new Integer(3));
  1020. ICON_TYPE_MAP.put("gtk-button", new Integer(4));
  1021. ICON_TYPE_MAP.put("gtk-dnd", new Integer(5));
  1022. ICON_TYPE_MAP.put("gtk-dialog", new Integer(6));
  1023. }
  1024. /**
  1025. * Return the size of a particular icon type (logical size)
  1026. *
  1027. * @param type icon type (GtkIconSize value)
  1028. * @return a Dimension object, or null if lsize is invalid
  1029. */
  1030. public static Dimension getIconSize(int type) {
  1031. Dimension[] iconSizes = getIconSizesMap();
  1032. return type >= 0 && type < iconSizes.length ?
  1033. iconSizes[type] : null;
  1034. }
  1035. /**
  1036. * Change icon size in a type to size mapping. This is called by code
  1037. * that parses the gtk-icon-sizes setting
  1038. *
  1039. * @param type icon type (GtkIconSize value)
  1040. * @param w the new icon width
  1041. * @param h the new icon height
  1042. */
  1043. public static void setIconSize(int type, int w, int h) {
  1044. Dimension[] iconSizes = getIconSizesMap();
  1045. if (type >= 0 && type < iconSizes.length) {
  1046. iconSizes[type] = new Dimension(w, h);
  1047. }
  1048. }
  1049. private static Dimension[] getIconSizesMap() {
  1050. AppContext appContext = AppContext.getAppContext();
  1051. Dimension[] iconSizes = (Dimension[])appContext.get(ICON_SIZE_KEY);
  1052. if (iconSizes == null) {
  1053. iconSizes = new Dimension[7];
  1054. iconSizes[0] = null; // GTK_ICON_SIZE_INVALID
  1055. iconSizes[1] = new Dimension(16, 16); // GTK_ICON_SIZE_MENU
  1056. iconSizes[2] = new Dimension(18, 18); // GTK_ICON_SIZE_SMALL_TOOLBAR
  1057. iconSizes[3] = new Dimension(24, 24); // GTK_ICON_SIZE_LARGE_TOOLBAR
  1058. iconSizes[4] = new Dimension(20, 20); // GTK_ICON_SIZE_BUTTON
  1059. iconSizes[5] = new Dimension(32, 32); // GTK_ICON_SIZE_DND
  1060. iconSizes[6] = new Dimension(48, 48); // GTK_ICON_SIZE_DIALOG
  1061. appContext.put(ICON_SIZE_KEY, iconSizes);
  1062. }
  1063. return iconSizes;
  1064. }
  1065. }
  1066. /**
  1067. * GTKIconSource represents a single icon source specification,
  1068. * as declared inside a stock definition in an rc file:
  1069. * path to image, size, direction, and state.
  1070. */
  1071. static class GTKIconSource implements Comparable {
  1072. private Object image;
  1073. private int direction;
  1074. private int state;
  1075. private int size;
  1076. GTKIconSource(String image, int direction, int state, String size) {
  1077. this.image = image;
  1078. this.direction = direction;
  1079. this.state = state;
  1080. this.size = GTKStockIconInfo.getIconType(size);
  1081. }
  1082. public int getDirection() {
  1083. return direction;
  1084. }
  1085. public int getState() {
  1086. return state;
  1087. }
  1088. public int getSize() {
  1089. return size;
  1090. }
  1091. public int compareTo(Object o) {
  1092. GTKIconSource other = (GTKIconSource)o;
  1093. if (direction != UNDEFINED && other.direction == UNDEFINED) {
  1094. return -1;
  1095. } else if (direction == UNDEFINED && other.direction != UNDEFINED) {
  1096. return 1;
  1097. } else if (state != UNDEFINED && other.state == UNDEFINED) {
  1098. return -1;
  1099. } else if (state == UNDEFINED && other.state != UNDEFINED) {
  1100. return 1;
  1101. } else if (size != UNDEFINED && other.size == UNDEFINED) {
  1102. return -1;
  1103. } else if (size == UNDEFINED && other.size != UNDEFINED) {
  1104. return 1;
  1105. } else {
  1106. return 0;
  1107. }
  1108. }
  1109. public String toString() {
  1110. return "image=" + image + ", dir=" + getDirectionName(direction)
  1111. + ", state=" + getStateName(state, "*")
  1112. + ", size=" + (size == UNDEFINED ? "*" : ""+size);
  1113. }
  1114. // used above by toString()
  1115. private static String getDirectionName(int dir) {
  1116. switch(dir) {
  1117. case LTR: return "LTR";
  1118. case RTL: return "RTL";
  1119. case UNDEFINED: return "*";
  1120. }
  1121. return "UNKNOWN";
  1122. }
  1123. public Icon toIcon() {
  1124. if (image == null || image instanceof Icon) {
  1125. return (Icon)image;
  1126. }
  1127. ImageIcon ii = (ImageIcon)AccessController.doPrivileged(new PrivilegedAction() {
  1128. public Object run() {
  1129. return new ImageIcon((String)image);
  1130. }
  1131. });
  1132. if (ii.getIconWidth() > 0 && ii.getIconHeight() > 0) {
  1133. image = ii;
  1134. } else {
  1135. // if we decide to mimic GTK and show a broken image,
  1136. // it would be assigned to 'image' here
  1137. image = null;
  1138. }
  1139. return (Icon)image;
  1140. }
  1141. }
  1142. public String toString() {
  1143. StringBuffer buf = new StringBuffer(super.toString());
  1144. if (xThickness != UNDEFINED_THICKNESS) {
  1145. buf.append("xt=").append(String.valueOf(xThickness)).append('\n');
  1146. }
  1147. if (yThickness != UNDEFINED_THICKNESS) {
  1148. buf.append("yt=").append(String.valueOf(yThickness)).append('\n');
  1149. }
  1150. if (classSpecificValues != null) {
  1151. buf.append("*** Properties ***\n");
  1152. buf.append(classSpecValsToString(classSpecificValues)).append('\n');
  1153. }
  1154. if (icons != null) {
  1155. buf.append("*** Stock Icons ***\n");
  1156. for (int i = 0; i < icons.length; i++) {
  1157. buf.append(icons[i].toString()).append('\n');
  1158. }
  1159. }
  1160. // remove last newline
  1161. buf.deleteCharAt(buf.length() - 1);
  1162. return buf.toString();
  1163. }
  1164. // used by toString()
  1165. private static String classSpecValsToString(CircularIdentityList parent) {
  1166. StringBuffer buf = new StringBuffer();
  1167. Object parentFirst = parent.next();
  1168. if (parentFirst == null) {
  1169. return "";
  1170. }
  1171. Object parentKey = parentFirst;
  1172. do {
  1173. buf.append(parentKey).append('\n');
  1174. CircularIdentityList child = (CircularIdentityList)parent.get();
  1175. Object childFirst = child.next();
  1176. if (childFirst == null) {
  1177. break;
  1178. }
  1179. Object childKey = childFirst;
  1180. do {
  1181. buf.append(" ").append(childKey).append('=').append(child.get()).append('\n');
  1182. childKey = child.next();
  1183. } while (childKey != childFirst);
  1184. parentKey = parent.next();
  1185. } while (parentKey != parentFirst);
  1186. // remove last newline
  1187. buf.deleteCharAt(buf.length() - 1);
  1188. return buf.toString();
  1189. }
  1190. /**
  1191. * A subclass of StateInfo adding additional GTK state information.
  1192. */
  1193. public static class GTKStateInfo extends StateInfo {
  1194. // <none>: fill in with background color
  1195. // <parent>: do nothing, parent will have handled it
  1196. // image: paint it.
  1197. private Object backgroundImage;
  1198. /**
  1199. * Creates a new GTKStateInfo with the specified properties
  1200. *
  1201. * @param state Component state that this StateInfo should be used
  1202. * for
  1203. * @param font Font for this state
  1204. * @param colors Colors for this state
  1205. * @param backgroundImage Background image
  1206. */
  1207. public GTKStateInfo(int state, Font font, Color[] colors,
  1208. Object backgroundImage) {
  1209. super(state, font, colors);
  1210. this.backgroundImage = backgroundImage;
  1211. }
  1212. /**
  1213. * Creates a GTKStateInfo that is a copy of the passed in
  1214. * <code>StateInfo</code>.
  1215. *
  1216. * @param info StateInfo to copy.
  1217. */
  1218. public GTKStateInfo(StateInfo info) {
  1219. super(info);
  1220. if (info instanceof GTKStateInfo) {
  1221. backgroundImage = ((GTKStateInfo)info).backgroundImage;
  1222. }
  1223. }
  1224. void setColor(ColorType type, Color color) {
  1225. Color[] colors = getColors();
  1226. if (colors == null) {
  1227. if (color == null) {
  1228. return;
  1229. }
  1230. colors = new Color[GTKColorType.MAX_COUNT];
  1231. setColors(colors);
  1232. }
  1233. colors[type.getID()] = color;
  1234. }
  1235. /**
  1236. * Returns the Color to used for the specified ColorType.
  1237. *
  1238. * @return Color.
  1239. */
  1240. public Color getColor(ColorType type) {
  1241. Color color = super.getColor(type);
  1242. if (color == null) {
  1243. Color[] colors = getColors();
  1244. Color bg;
  1245. if (colors != null && (bg = super.getColor(
  1246. GTKColorType.BACKGROUND)) != null) {
  1247. if (type == GTKColorType.LIGHT) {
  1248. color = colors[GTKColorType.LIGHT.getID()] =
  1249. calculateLightColor(bg);
  1250. }
  1251. else if (type == GTKColorType.MID) {
  1252. color = colors[GTKColorType.MID.getID()] =
  1253. calculateMidColor(bg);
  1254. }
  1255. else if (type == GTKColorType.DARK) {
  1256. color = colors[GTKColorType.DARK.getID()] =
  1257. calculateDarkColor(bg);
  1258. }
  1259. }
  1260. }
  1261. return color;
  1262. }
  1263. /**
  1264. * This returns the background image, and will be one of:
  1265. * the String "<none>", the String "<parent>" or an Image.
  1266. *
  1267. * @return the background.
  1268. */
  1269. Object getBackgroundImage() {
  1270. if (backgroundImage == null ||
  1271. (backgroundImage instanceof Image) ||
  1272. backgroundImage == "<none>" ||
  1273. backgroundImage == "<parent>") {
  1274. return backgroundImage;
  1275. }
  1276. ImageIcon ii = (ImageIcon)AccessController.doPrivileged(new PrivilegedAction() {
  1277. public Object run() {
  1278. return new ImageIcon((String)backgroundImage);
  1279. }
  1280. });
  1281. if (ii.getIconWidth() > 0 && ii.getIconHeight() > 0) {
  1282. backgroundImage = ii.getImage();
  1283. } else {
  1284. backgroundImage = null;
  1285. }
  1286. return backgroundImage;
  1287. }
  1288. /**
  1289. * Creates and returns a copy of this GTKStateInfo.
  1290. *
  1291. * @return Copy of this StateInfo.
  1292. */
  1293. public Object clone() {
  1294. return new GTKStateInfo(this);
  1295. }
  1296. /**
  1297. * Merges the contents of this GTKStateInfo with that of the passed in
  1298. * GTKStateInfo, returning the resulting merged StateInfo. Properties
  1299. * of this <code>GTKStateInfo</code> will take precedence over those
  1300. * of the
  1301. * passed in <code>GTKStateInfo</code>. For example, if this
  1302. * GTKStateInfo specifics a non-null font, the returned GTKStateInfo
  1303. * will have its font so to that regardless of the
  1304. * <code>GTKStateInfo</code>'s font.
  1305. *
  1306. * @param info StateInfo to add our styles to
  1307. * @return Merged StateInfo.
  1308. */
  1309. public StateInfo addTo(StateInfo info) {
  1310. if (!(info instanceof GTKStateInfo)) {
  1311. info = new GTKStateInfo(info);
  1312. }
  1313. else {
  1314. super.addTo(info);
  1315. }
  1316. GTKStateInfo gInfo = (GTKStateInfo)info;
  1317. if (backgroundImage != null) {
  1318. gInfo.backgroundImage = backgroundImage;
  1319. }
  1320. return gInfo;
  1321. }
  1322. public String toString() {
  1323. StringBuffer buf = new StringBuffer();
  1324. buf.append(getStateName(getComponentState(), "UNDEFINED")).append(":\n");
  1325. if (getColor(GTKColorType.FOREGROUND) != null) {
  1326. buf.append(" fg=").append(getColor(GTKColorType.FOREGROUND)).append('\n');
  1327. }
  1328. if (getColor(GTKColorType.BACKGROUND) != null) {
  1329. buf.append(" bg=").append(getColor(GTKColorType.BACKGROUND)).append('\n');
  1330. }
  1331. if (getColor(GTKColorType.TEXT_FOREGROUND) != null) {
  1332. buf.append(" text=").append(getColor(GTKColorType.TEXT_FOREGROUND)).append('\n');
  1333. }
  1334. if (getColor(GTKColorType.TEXT_BACKGROUND) != null) {
  1335. buf.append(" base=").append(getColor(GTKColorType.TEXT_BACKGROUND)).append('\n');
  1336. }
  1337. if (backgroundImage != null) {
  1338. buf.append(" pmn=").append(backgroundImage).append('\n');
  1339. }
  1340. // remove last newline
  1341. buf.deleteCharAt(buf.length() - 1);
  1342. return buf.toString();
  1343. }
  1344. }
  1345. // used by toString() in some of our inner classes
  1346. static String getStateName(int state, String undef) {
  1347. switch(state) {
  1348. case SynthConstants.ENABLED: return "NORMAL";
  1349. case SynthConstants.PRESSED: return "ACTIVE";
  1350. case SynthConstants.SELECTED: return "SELECTED";
  1351. case SynthConstants.MOUSE_OVER: return "PRELIGHT";
  1352. case SynthConstants.DISABLED: return "INSENSITIVE";
  1353. case UNDEFINED: return undef;
  1354. }
  1355. return "UNKNOWN";
  1356. }
  1357. /**
  1358. * A tagging interface indicating that a value coming from
  1359. * DATA should be added to the Style's data after invoking getValue.
  1360. * This is useful for lazy type properties that need to key off information
  1361. * kept in the style.
  1362. */
  1363. interface StyleSpecificValue {
  1364. public Object getValue(SynthContext context);
  1365. }
  1366. /**
  1367. * An Icon that is fetched using getStockIcon.
  1368. */
  1369. private static class GTKStockIcon extends SynthIcon implements Cloneable,
  1370. StyleSpecificValue {
  1371. private String key;
  1372. private int size;
  1373. private boolean loadedLTR;
  1374. private boolean loadedRTL;
  1375. private Icon ltrIcon;
  1376. private Icon rtlIcon;
  1377. private SynthStyle style;
  1378. GTKStockIcon(String key, int size) {
  1379. this.key = key;
  1380. this.size = size;
  1381. }
  1382. public void paintIcon(SynthContext context, Graphics g, int x,
  1383. int y, int w, int h) {
  1384. Icon icon = getIcon(context);
  1385. if (icon != null) {
  1386. if (context == null) {
  1387. icon.paintIcon(null, g, x, y);
  1388. }
  1389. else {
  1390. icon.paintIcon(context.getComponent(), g, x, y);
  1391. }
  1392. }
  1393. }
  1394. public int getIconWidth(SynthContext context) {
  1395. Icon icon = getIcon(context);
  1396. if (icon != null) {
  1397. return icon.getIconWidth();
  1398. }
  1399. return 0;
  1400. }
  1401. public int getIconHeight(SynthContext context) {
  1402. Icon icon = getIcon(context);
  1403. if (icon != null) {
  1404. return icon.getIconHeight();
  1405. }
  1406. return 0;
  1407. }
  1408. private Icon getIcon(SynthContext context) {
  1409. if (context != null) {
  1410. ComponentOrientation co = context.getComponent().
  1411. getComponentOrientation();
  1412. SynthStyle style = context.getStyle();
  1413. if (style != this.style) {
  1414. this.style = style;
  1415. loadedLTR = loadedRTL = false;
  1416. }
  1417. if (co == null || co.isLeftToRight()) {
  1418. if (!loadedLTR) {
  1419. loadedLTR = true;
  1420. ltrIcon = ((GTKStyle)context.getStyle()).
  1421. getStockIcon(context, key, size);
  1422. }
  1423. return ltrIcon;
  1424. }
  1425. else if (!loadedRTL) {
  1426. loadedRTL = true;
  1427. rtlIcon = ((GTKStyle)context.getStyle()).
  1428. getStockIcon(context, key,size);
  1429. }
  1430. return rtlIcon;
  1431. }
  1432. return ltrIcon;
  1433. }
  1434. public Object getValue(SynthContext context) {
  1435. try {
  1436. return clone();
  1437. } catch (CloneNotSupportedException cnse) {
  1438. }
  1439. return null;
  1440. }
  1441. }
  1442. /**
  1443. * MetalLazyValue is a slimmed down version of <code>ProxyLaxyValue</code>.
  1444. * The code is duplicate so that it can get at the package private
  1445. * classes in gtk.
  1446. */
  1447. static class GTKLazyValue implements UIDefaults.LazyValue {
  1448. /**
  1449. * Name of the class to create.
  1450. */
  1451. private String className;
  1452. private String methodName;
  1453. GTKLazyValue(String name) {
  1454. this(name, null);
  1455. }
  1456. GTKLazyValue(String name, String methodName) {
  1457. this.className = name;
  1458. this.methodName = methodName;
  1459. }
  1460. public Object createValue(UIDefaults table) {
  1461. try {
  1462. Class c = Class.forName(className, true,Thread.currentThread().
  1463. getContextClassLoader());
  1464. if (methodName == null) {
  1465. return c.newInstance();
  1466. }
  1467. Method m = c.getMethod(methodName, null);
  1468. return m.invoke(c, null);
  1469. } catch (ClassNotFoundException cnfe) {
  1470. } catch (IllegalAccessException iae) {
  1471. } catch (InvocationTargetException ite) {
  1472. } catch (NoSuchMethodException nsme) {
  1473. } catch (InstantiationException ie) {
  1474. }
  1475. return null;
  1476. }
  1477. }
  1478. static {
  1479. DEFAULT_COLOR_MAP = new int[] {
  1480. SynthConstants.PRESSED, SynthConstants.SELECTED,
  1481. SynthConstants.ENABLED, SynthConstants.MOUSE_OVER,
  1482. SynthConstants.DISABLED
  1483. };
  1484. DEFAULT_COLORS = new Color[5][];
  1485. // 2.0 colors
  1486. //
  1487. if (!GTKLookAndFeel.is2_2()) {
  1488. DEFAULT_COLORS[0] = getColorsFrom(
  1489. new ColorUIResource(195, 195, 195), BLACK_COLOR);
  1490. DEFAULT_COLORS[1] = getColorsFrom(
  1491. new ColorUIResource(0, 0, 156), WHITE_COLOR);
  1492. DEFAULT_COLORS[2] = getColorsFrom(
  1493. new ColorUIResource(214, 214, 214), BLACK_COLOR);
  1494. DEFAULT_COLORS[3] = getColorsFrom(
  1495. new ColorUIResource(233, 233, 233), BLACK_COLOR);
  1496. DEFAULT_COLORS[4] = getColorsFrom(
  1497. new ColorUIResource(214, 214, 214),
  1498. new ColorUIResource(117, 117, 117));
  1499. DEFAULT_COLORS[0][GTKColorType.TEXT_BACKGROUND.getID()] = new
  1500. ColorUIResource(188, 210, 238);
  1501. DEFAULT_COLORS[1][GTKColorType.TEXT_BACKGROUND.getID()] = new
  1502. ColorUIResource(164, 223, 255);
  1503. DEFAULT_COLORS[1][GTKColorType.TEXT_FOREGROUND.getID()] =
  1504. BLACK_COLOR;
  1505. DEFAULT_COLORS[2][GTKColorType.TEXT_FOREGROUND.getID()] =
  1506. BLACK_COLOR;
  1507. DEFAULT_COLORS[4][GTKColorType.TEXT_FOREGROUND.getID()] =
  1508. DEFAULT_COLORS[2][GTKColorType.TEXT_FOREGROUND.getID()];
  1509. }
  1510. else {
  1511. // 2.2 colors
  1512. DEFAULT_COLORS[0] = getColorsFrom(
  1513. new ColorUIResource(186, 181, 171), BLACK_COLOR);
  1514. DEFAULT_COLORS[1] = getColorsFrom(
  1515. new ColorUIResource(75, 105, 131), WHITE_COLOR);
  1516. DEFAULT_COLORS[2] = getColorsFrom(
  1517. new ColorUIResource(220, 218, 213), BLACK_COLOR);
  1518. DEFAULT_COLORS[3] = getColorsFrom(
  1519. new ColorUIResource(238, 235, 231), BLACK_COLOR);
  1520. DEFAULT_COLORS[4] = getColorsFrom(
  1521. new ColorUIResource(220, 218, 213),
  1522. new ColorUIResource(117, 117, 117));
  1523. DEFAULT_COLORS[0][GTKColorType.TEXT_BACKGROUND.getID()] = new
  1524. ColorUIResource(128, 125, 116);
  1525. DEFAULT_COLORS[1][GTKColorType.TEXT_BACKGROUND.getID()] = new
  1526. ColorUIResource(75, 105, 131);
  1527. DEFAULT_COLORS[2][GTKColorType.TEXT_BACKGROUND.getID()] =
  1528. WHITE_COLOR;
  1529. DEFAULT_COLORS[3][GTKColorType.TEXT_BACKGROUND.getID()] =
  1530. WHITE_COLOR;
  1531. DEFAULT_COLORS[4][GTKColorType.TEXT_BACKGROUND.getID()] = new
  1532. ColorUIResource(238, 235, 231);
  1533. DEFAULT_COLORS[0][GTKColorType.TEXT_FOREGROUND.getID()] =
  1534. WHITE_COLOR;
  1535. DEFAULT_COLORS[1][GTKColorType.TEXT_FOREGROUND.getID()] =
  1536. WHITE_COLOR;
  1537. DEFAULT_COLORS[2][GTKColorType.TEXT_FOREGROUND.getID()] =
  1538. BLACK_COLOR;
  1539. DEFAULT_COLORS[3][GTKColorType.TEXT_FOREGROUND.getID()] =
  1540. BLACK_COLOR;
  1541. DEFAULT_COLORS[4][GTKColorType.TEXT_FOREGROUND.getID()] = new
  1542. ColorUIResource(117, 117, 117);
  1543. }
  1544. CLASS_SPECIFIC_MAP = new HashMap();
  1545. CLASS_SPECIFIC_MAP.put("CheckBox.iconTextGap", "indicator-spacing");
  1546. CLASS_SPECIFIC_MAP.put("Slider.thumbHeight", "slider-width");
  1547. CLASS_SPECIFIC_MAP.put("Slider.trackBorder", "trough-border");
  1548. CLASS_SPECIFIC_MAP.put("SplitPane.size", "handle-size");
  1549. CLASS_SPECIFIC_MAP.put("Tree.expanderSize", "expander-size");
  1550. CLASS_SPECIFIC_MAP.put("ScrollBar.thumbHeight", "slider-width");
  1551. CLASS_SPECIFIC_MAP.put("TextArea.caretForeground", "cursor-color");
  1552. CLASS_SPECIFIC_MAP.put("TextArea.caretAspectRatio", "cursor-aspect-ratio");
  1553. CLASS_SPECIFIC_MAP.put("TextField.caretForeground", "cursor-color");
  1554. CLASS_SPECIFIC_MAP.put("TextField.caretAspectRatio", "cursor-aspect-ratio");
  1555. CLASS_SPECIFIC_MAP.put("PasswordField.caretForeground", "cursor-color");
  1556. CLASS_SPECIFIC_MAP.put("PasswordField.caretAspectRatio", "cursor-aspect-ratio");
  1557. CLASS_SPECIFIC_MAP.put("FormattedTextField.caretForeground", "cursor-color");
  1558. CLASS_SPECIFIC_MAP.put("FormattedTextField.caretAspectRatio", "cursor-aspect-");
  1559. CLASS_SPECIFIC_MAP.put("TextPane.caretForeground", "cursor-color");
  1560. CLASS_SPECIFIC_MAP.put("TextPane.caretAspectRatio", "cursor-aspect-ratio");
  1561. CLASS_SPECIFIC_MAP.put("EditorPane.caretForeground", "cursor-color");
  1562. CLASS_SPECIFIC_MAP.put("EditorPane.caretAspectRatio", "cursor-aspect-ratio");
  1563. Object[] defaults = {
  1564. "FileChooser.cancelIcon", new GTKStockIcon("gtk-cancel", 4),
  1565. "FileChooser.okIcon", new GTKStockIcon("gtk-ok", 4),
  1566. "OptionPane.errorIcon", new GTKStockIcon("gtk-dialog-error", 6),
  1567. "OptionPane.informationIcon", new GTKStockIcon("gtk-dialog-info", 6),
  1568. "OptionPane.warningIcon", new GTKStockIcon("gtk-dialog-warning", 6),
  1569. "OptionPane.questionIcon", new GTKStockIcon("gtk-dialog-question", 6),
  1570. "OptionPane.yesIcon", new GTKStockIcon("gtk-yes", 4),
  1571. "OptionPane.noIcon", new GTKStockIcon("gtk-no", 4),
  1572. "OptionPane.cancelIcon", new GTKStockIcon("gtk-cancel", 4),
  1573. "OptionPane.okIcon", new GTKStockIcon("gtk-ok", 4),
  1574. };
  1575. for (int counter = 0, max = defaults.length; counter < max;
  1576. counter++) {
  1577. DATA.put(defaults[counter], defaults[++counter]);
  1578. }
  1579. }
  1580. }