1. /*
  2. * @(#)GTKStyleFactory.java 1.29 04/03/18
  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 javax.swing.plaf.synth.*;
  9. import java.awt.*;
  10. import java.util.*;
  11. import java.util.regex.*;
  12. import javax.swing.*;
  13. import javax.swing.plaf.*;
  14. import sun.swing.BakedArrayList;
  15. import sun.swing.plaf.synth.DefaultSynthStyle;
  16. import sun.swing.plaf.synth.StyleAssociation;
  17. /**
  18. * GTKStyleFactory extends DefaultSynthStyleFactory providing a mapping that
  19. * mirrors the gtk name space. Styles registered for REGION are mapped to
  20. * the corresponding gtk class name. Similarly styles registered for
  21. * CLASS are mapped to the corresponding gtk class name, including the
  22. * corresponding gtk class hierarchy.
  23. *
  24. * @version 1.29, 03/18/04
  25. * @author Scott Violet
  26. */
  27. class GTKStyleFactory extends SynthStyleFactory {
  28. /**
  29. * Indicates lookup should be done using the name of the Component.
  30. * If the name is null this fallsback to the name of the region.
  31. */
  32. public static final int WIDGET = 0;
  33. /**
  34. * Indicates lookup should be done using the region name.
  35. */
  36. public static final int WIDGET_CLASS = 1;
  37. /**
  38. * Indicates lookup should be done using the class name
  39. */
  40. public static final int CLASS = 2;
  41. /**
  42. * Maps from a GTK class name to its super class.
  43. */
  44. private static final Map GTK_CLASS_MAP;
  45. /**
  46. * Maps from Region to gtk class.
  47. */
  48. private static final Map REGION_MAP;
  49. /**
  50. * List of StyleAssociations for WIDGET.
  51. */
  52. private java.util.List _widgetStyles;
  53. /**
  54. * List of StyleAssociations for WIDGET_CLASS.
  55. */
  56. private java.util.List _widgetClassStyles;
  57. /**
  58. * List of StyleAssociations for CLASS.
  59. */
  60. private java.util.List _classStyles;
  61. /**
  62. * Labels are special cased. This is set to true when a Style for
  63. * a label bearing widget is asked for and triggers massaging of
  64. * the path and class name lookup.
  65. */
  66. private boolean _isLabel;
  67. /**
  68. * Will hold the label style and label bearing components style. This
  69. * is used as the key to mergedStyleMap.
  70. */
  71. private BakedArrayList _labelStyleList;
  72. /**
  73. * Maps from a List containing the label style and label bearing components
  74. * style to the merged style.
  75. */
  76. private Map _mergedStyleMap;
  77. /**
  78. * All lookups will at least get this style.
  79. */
  80. private SynthStyle _defaultStyle;
  81. /**
  82. * Default style for tooltips.
  83. */
  84. private GTKStyle _tooltipStyle;
  85. /**
  86. * Default style for progressbars.
  87. */
  88. private GTKStyle _pbStyle;
  89. /**
  90. * Default style for menu items.
  91. */
  92. private GTKStyle _menuItemStyle;
  93. /**
  94. * Maps from List to the resolved DefaultSynthStyle.
  95. */
  96. private Map _resolvedStyles;
  97. /**
  98. * Used for Style lookup to avoid garbage.
  99. */
  100. private BakedArrayList _tmpList;
  101. /**
  102. * StringBuffer used in building paths.
  103. */
  104. private StringBuffer _tmpPath;
  105. /**
  106. * Used in matching class styles, will contain the class depth of the
  107. * matches.
  108. */
  109. private int[] _depth;
  110. static {
  111. REGION_MAP = new HashMap();
  112. REGION_MAP.put(Region.ARROW_BUTTON, "GtkButton");
  113. REGION_MAP.put(Region.BUTTON, "GtkButton");
  114. REGION_MAP.put(Region.CHECK_BOX, "GtkCheckButton");
  115. REGION_MAP.put(Region.CHECK_BOX_MENU_ITEM, "GtkCheckMenuItem");
  116. REGION_MAP.put(Region.COLOR_CHOOSER, "GtkColorSelectionDialog");
  117. REGION_MAP.put(Region.COMBO_BOX, "GtkCombo");
  118. REGION_MAP.put(Region.DESKTOP_ICON, "GtkLabel");
  119. REGION_MAP.put(Region.DESKTOP_PANE, "GtkContainer");
  120. REGION_MAP.put(Region.EDITOR_PANE, "GtkTextView");
  121. REGION_MAP.put(Region.FORMATTED_TEXT_FIELD, "GtkEntry");
  122. REGION_MAP.put(GTKRegion.HANDLE_BOX, "GtkHandleBox");
  123. REGION_MAP.put(Region.INTERNAL_FRAME, "GtkFrame");
  124. REGION_MAP.put(Region.INTERNAL_FRAME_TITLE_PANE, "GtkLabel");
  125. REGION_MAP.put(Region.LABEL, "GtkLabel");
  126. REGION_MAP.put(Region.LIST, "GtkTreeView");
  127. // GTK doesn't use menu's as swing does, Swing's menus better
  128. // map to a GTKMenuItem.
  129. REGION_MAP.put(Region.MENU, "GtkMenuItem");
  130. REGION_MAP.put(Region.MENU_BAR, "GtkMenuBar");
  131. REGION_MAP.put(Region.MENU_ITEM, "GtkMenuItem");
  132. REGION_MAP.put(Region.MENU_ITEM_ACCELERATOR, "GtkLabel");
  133. REGION_MAP.put(Region.OPTION_PANE, "GtkMessageDialog");
  134. REGION_MAP.put(Region.PANEL, "GtkContainer");
  135. REGION_MAP.put(Region.PASSWORD_FIELD, "GtkEntry");
  136. // GTK does not have a distinct class for popups.
  137. REGION_MAP.put(Region.POPUP_MENU, "GtkMenu");
  138. REGION_MAP.put(Region.POPUP_MENU_SEPARATOR, "GtkSeparatorMenuItem");
  139. REGION_MAP.put(Region.PROGRESS_BAR, "GtkProgressBar");
  140. REGION_MAP.put(Region.RADIO_BUTTON, "GtkRadioButton");
  141. REGION_MAP.put(Region.RADIO_BUTTON_MENU_ITEM, "GtkRadioMenuItem");
  142. REGION_MAP.put(Region.ROOT_PANE, "GtkContainer");
  143. // GTK has two subclasses for the two directions.
  144. REGION_MAP.put(Region.SCROLL_BAR, "GtkScrollbar");
  145. REGION_MAP.put(Region.SCROLL_BAR_TRACK, "GtkScrollbar");
  146. REGION_MAP.put(Region.SCROLL_BAR_THUMB, "GtkScrollbar");
  147. REGION_MAP.put(Region.SCROLL_PANE, "GtkScrolledWindow");
  148. // GTK has two subclasses of GtkSeparator for the two directions
  149. REGION_MAP.put(Region.SEPARATOR, "GtkSeparator");
  150. // GTK has two subclasses of GtkScale for the two directions
  151. REGION_MAP.put(Region.SLIDER, "GtkScale");
  152. REGION_MAP.put(Region.SLIDER_TRACK, "GtkScale");
  153. REGION_MAP.put(Region.SLIDER_THUMB, "GtkScale");
  154. REGION_MAP.put(Region.SPINNER, "GtkSpinButton");
  155. // GTK has two subclasses of GtkPaned for the two diretions.
  156. REGION_MAP.put(Region.SPLIT_PANE, "GtkPaned");
  157. REGION_MAP.put(Region.SPLIT_PANE_DIVIDER, "GtkPaned");
  158. REGION_MAP.put(Region.TABBED_PANE, "GtkNotebook");
  159. REGION_MAP.put(Region.TABBED_PANE_TAB_AREA, "GtkNotebook");
  160. REGION_MAP.put(Region.TABBED_PANE_CONTENT, "GtkNotebook");
  161. REGION_MAP.put(Region.TABBED_PANE_TAB, "GtkNotebook");
  162. REGION_MAP.put(Region.TABLE, "GtkTreeView");
  163. // It would appear the headers are drawn as buttons.
  164. REGION_MAP.put(Region.TABLE_HEADER, "GtkButton");
  165. REGION_MAP.put(Region.TEXT_AREA, "GtkTextView");
  166. REGION_MAP.put(Region.TEXT_FIELD, "GtkEntry");
  167. REGION_MAP.put(Region.TEXT_PANE, "GtkTextView");
  168. REGION_MAP.put(Region.TOGGLE_BUTTON, "GtkToggleButton");
  169. REGION_MAP.put(Region.TOOL_BAR, "GtkToolbar");
  170. REGION_MAP.put(Region.TOOL_BAR_DRAG_WINDOW, "GtkToolbar");
  171. // GTK does not define a distinct class for the toolbar separator
  172. REGION_MAP.put(Region.TOOL_BAR_SEPARATOR, "GtkSeparator");
  173. REGION_MAP.put(Region.TOOL_TIP, "GtkWindow");
  174. REGION_MAP.put(Region.TREE, "GtkTreeView");
  175. REGION_MAP.put(Region.TREE_CELL, "GtkTreeView");
  176. REGION_MAP.put(Region.VIEWPORT, "GtkViewport");
  177. GTK_CLASS_MAP = new HashMap();
  178. GTK_CLASS_MAP.put("GtkHandleBox", "GtkBin");
  179. GTK_CLASS_MAP.put("GtkFrame", "GtkBin");
  180. GTK_CLASS_MAP.put("GtkProgress", "GtkWidget");
  181. GTK_CLASS_MAP.put("GtkViewport", "GtkBin");
  182. GTK_CLASS_MAP.put("GtkMessageDialog", "GtkDialog");
  183. GTK_CLASS_MAP.put("GtkCombo", "GtkHBox");
  184. GTK_CLASS_MAP.put("GtkHBox", "GtkBox");
  185. GTK_CLASS_MAP.put("GtkBox", "GtkContainer");
  186. GTK_CLASS_MAP.put("GtkTooltips", "GtkObject");
  187. GTK_CLASS_MAP.put("GtkToolbar", "GtkContainer");
  188. GTK_CLASS_MAP.put("GtkLabel", "GtkMisc");
  189. GTK_CLASS_MAP.put("GtkMisc", "GtkWidget");
  190. GTK_CLASS_MAP.put("GtkTreeView", "GtkContainer");
  191. GTK_CLASS_MAP.put("GtkTextView", "GtkContainer");
  192. GTK_CLASS_MAP.put("GtkNotebook", "GtkContainer");
  193. GTK_CLASS_MAP.put("GtkSeparatorMenuItem", "GtkMenuItem");
  194. GTK_CLASS_MAP.put("GtkSpinButton", "GtkEntry");
  195. GTK_CLASS_MAP.put("GtkSeparator", "GtkWidget");
  196. GTK_CLASS_MAP.put("GtkScale", "GtkRange");
  197. GTK_CLASS_MAP.put("GtkRange", "GtkWidget");
  198. GTK_CLASS_MAP.put("GtkPaned", "GtkContainer");
  199. GTK_CLASS_MAP.put("GtkScrolledWindow", "GtkBin");
  200. GTK_CLASS_MAP.put("GtkScrollbar", "GtkRange");
  201. GTK_CLASS_MAP.put("GtkProgressBar", "GtkProgress");
  202. GTK_CLASS_MAP.put("GtkRadioButton", "GtkCheckButton");
  203. GTK_CLASS_MAP.put("GtkRadioMenuItem", "GtkCheckMenuItem");
  204. GTK_CLASS_MAP.put("GtkCheckMenuItem", "GtkMenuItem");
  205. GTK_CLASS_MAP.put("GtkMenuItem", "GtkItem");
  206. GTK_CLASS_MAP.put("GtkItem", "GtkBin");
  207. GTK_CLASS_MAP.put("GtkMenu", "GtkMenuShell");
  208. GTK_CLASS_MAP.put("GtkMenuBar", "GtkMenuShell");
  209. GTK_CLASS_MAP.put("GtkMenuShell", "GtkContainer");
  210. GTK_CLASS_MAP.put("GtkEntry", "GtkWidget");
  211. GTK_CLASS_MAP.put("GtkColorSelectionDialog", "GtkDialog");
  212. GTK_CLASS_MAP.put("GtkDialog", "GtkWindow");
  213. GTK_CLASS_MAP.put("GtkWindow", "GtkBin");
  214. GTK_CLASS_MAP.put("GtkCheckButton", "GtkToggleButton");
  215. GTK_CLASS_MAP.put("GtkToggleButton", "GtkButton");
  216. GTK_CLASS_MAP.put("GtkButton", "GtkBin");
  217. GTK_CLASS_MAP.put("GtkBin", "GtkContainer");
  218. GTK_CLASS_MAP.put("GtkContainer", "GtkWidget");
  219. GTK_CLASS_MAP.put("GtkWidget", "GtkObject");
  220. GTK_CLASS_MAP.put("GtkObject", "GObject");
  221. }
  222. /**
  223. * In GTK Button and other widgets that display text are actually
  224. * implemented as two separate widgets, one for the text and one for
  225. * the button, this method returns true if in GTK the Region would
  226. * contain a Label to draw the text.
  227. */
  228. static final boolean isLabelBearing(Region id) {
  229. return (id == Region.BUTTON || id == Region.CHECK_BOX ||
  230. id == Region.CHECK_BOX_MENU_ITEM || id == Region.MENU ||
  231. id == Region.MENU_ITEM || id == Region.RADIO_BUTTON ||
  232. id == Region.RADIO_BUTTON_MENU_ITEM ||
  233. id == Region.TABBED_PANE_TAB ||
  234. id == Region.TOGGLE_BUTTON || id == Region.TOOL_TIP);
  235. }
  236. /**
  237. * Returns the gtk class that corresponds to the passed in region.
  238. */
  239. static String gtkClassFor(Region region) {
  240. String name = (String)REGION_MAP.get(region);
  241. if (name == null) {
  242. // There are no GTK equivalents for some GTK classes, force
  243. // a match.
  244. return "XXX";
  245. }
  246. return name;
  247. }
  248. /**
  249. * Returns the super class of the passed in gtk class, or null if
  250. * <code>gtkClass</code> is the root class.
  251. */
  252. static String gtkSuperclass(String gtkClass) {
  253. return (String)GTK_CLASS_MAP.get(gtkClass);
  254. }
  255. GTKStyleFactory() {
  256. this(null);
  257. }
  258. GTKStyleFactory(GTKStyle baseStyle) {
  259. _tmpList = new BakedArrayList(5);
  260. _resolvedStyles = new HashMap();
  261. _tmpPath = new StringBuffer();
  262. _mergedStyleMap = new HashMap();
  263. _defaultStyle = (baseStyle == null ? new GTKStyle() : baseStyle);
  264. _labelStyleList = new BakedArrayList(2);
  265. }
  266. public synchronized void addStyle(DefaultSynthStyle style,
  267. String path, int type) throws PatternSyntaxException {
  268. // GTK only supports * and ?, escape everything else.
  269. int length = path.length();
  270. StringBuffer buffer = new StringBuffer(length * 2);
  271. for (int counter = 0; counter < length; counter++) {
  272. char aChar = path.charAt(counter);
  273. if (aChar == '*') {
  274. buffer.append(".*");
  275. }
  276. else if (aChar == '?') {
  277. buffer.append('.');
  278. }
  279. else if (Character.isLetterOrDigit(aChar)) {
  280. buffer.append(aChar);
  281. }
  282. else {
  283. buffer.append('\\');
  284. buffer.append(aChar);
  285. }
  286. }
  287. path = buffer.toString();
  288. switch (type) {
  289. case WIDGET:
  290. if (_widgetStyles == null) {
  291. _widgetStyles = new ArrayList(1);
  292. }
  293. _widgetStyles.add(StyleAssociation.createStyleAssociation(
  294. path, style));
  295. break;
  296. case WIDGET_CLASS:
  297. if (_widgetClassStyles == null) {
  298. _widgetClassStyles = new ArrayList(1);
  299. }
  300. _widgetClassStyles.add(StyleAssociation.createStyleAssociation(
  301. path, style));
  302. break;
  303. case CLASS:
  304. if (_classStyles == null) {
  305. _classStyles = new ArrayList(1);
  306. }
  307. _classStyles.add(StyleAssociation.createStyleAssociation(
  308. path, style));
  309. break;
  310. default:
  311. throw new IllegalArgumentException("type must be one of " +
  312. "CLASS, WIDGET_CLASS or WIDGET");
  313. }
  314. }
  315. /**
  316. * Returns the <code>SynthStyle</code> to use based on the
  317. * class name of a GtkWidget. This will throw an
  318. * <code>IllegalArgumentException</code> if
  319. * <code>gtkWidgetClassName</code> is not a valid Gtk class name.
  320. *
  321. * @param gtkWidget Class name of a GtkWidget.
  322. * @throws IllegalArgumentException if <code>gtkWidgetClassName</code> is
  323. * not a valid class name.
  324. */
  325. synchronized SynthStyle getStyle(String gtkWidgetClassName)
  326. throws IllegalArgumentException {
  327. if (!GTK_CLASS_MAP.containsKey(gtkWidgetClassName)) {
  328. throw new IllegalArgumentException("Invalid class name: " +
  329. gtkWidgetClassName);
  330. }
  331. BakedArrayList matches = _tmpList;
  332. matches.clear();
  333. if (_classStyles != null) {
  334. getClassMatches(matches, gtkWidgetClassName);
  335. }
  336. matches.add(_defaultStyle);
  337. return getStyle(matches);
  338. }
  339. /**
  340. * Returns the style for the specified Component.
  341. *
  342. * @param c Component asking for
  343. * @param id ID of the Component
  344. */
  345. public synchronized SynthStyle getStyle(JComponent c, Region id) {
  346. if ((id == Region.FORMATTED_TEXT_FIELD &&
  347. c.getName() == "Spinner.formattedTextField") ||
  348. (id == Region.ARROW_BUTTON &&
  349. (c.getName() == "Spinner.previousButton" ||
  350. c.getName() == "Spinner.nextButton"))){
  351. // Force all the widgets of a spinner to be treated like a spinner
  352. id = Region.SPINNER;
  353. Container parent = c.getParent();
  354. if (parent != null) {
  355. parent = parent.getParent();
  356. if (parent instanceof JSpinner) {
  357. c = (JComponent)parent;
  358. }
  359. }
  360. }
  361. else if (id == Region.LABEL && c.getName() == "ComboBox.renderer") {
  362. id = Region.TEXT_FIELD;
  363. }
  364. SynthStyle style = _getStyle(c, id);
  365. if (isLabelBearing(id)) {
  366. style = getMergedStyle(c, id, style);
  367. }
  368. return style;
  369. }
  370. private SynthStyle _getStyle(JComponent c, Region id) {
  371. BakedArrayList matches = _tmpList;
  372. matches.clear();
  373. getMatchingStyles(matches, c, id);
  374. return getStyle(matches);
  375. }
  376. private SynthStyle getStyle(BakedArrayList matches) {
  377. // Use a cached Style if possible, otherwise create a new one.
  378. matches.cacheHashCode();
  379. SynthStyle style = getCachedStyle(matches);
  380. if (style == null) {
  381. style = mergeStyles(matches);
  382. if (style != null) {
  383. cacheStyle(matches, style);
  384. }
  385. }
  386. return style;
  387. }
  388. /**
  389. * Fetches any styles that match the passed into arguments into
  390. * <code>matches</code>.
  391. */
  392. private void getMatchingStyles(java.util.List matches, JComponent c,
  393. Region id) {
  394. // TableHeaderer.renderer is special cased as it descends from
  395. // DefaultTableCellRenderer which does NOT pass along the property
  396. // change that would trigger the style to be refetched.
  397. if (c != null && (c.getParent() != null ||
  398. c.getName() == "TableHeader.renderer" ||
  399. c.getName() == "Slider.label") ||
  400. c.getName() == "ComboBox.list") {
  401. // First match on WIDGET
  402. if (_widgetStyles != null) {
  403. getMatches(getPath(WIDGET, c, id), _widgetStyles, matches, c,
  404. id);
  405. }
  406. // Then match on WIDGET_CLASS
  407. if (_widgetClassStyles != null) {
  408. getMatches(getPath(WIDGET_CLASS, c, id), _widgetClassStyles,
  409. matches, c, id);
  410. }
  411. // Lastly match on CLASS
  412. if (_classStyles != null) {
  413. getClassMatches(matches, c, id);
  414. }
  415. }
  416. if (id == Region.TOOL_TIP) {
  417. matches.add(getToolTipStyle());
  418. }
  419. else if (id == Region.PROGRESS_BAR && GTKLookAndFeel.is2_2()) {
  420. matches.add(getProgressBarStyle());
  421. }
  422. else if ((id == Region.MENU || id == Region.MENU_ITEM ||
  423. id == Region.POPUP_MENU_SEPARATOR ||
  424. id == Region.CHECK_BOX_MENU_ITEM ||
  425. id == Region.RADIO_BUTTON_MENU_ITEM ||
  426. id == Region.MENU_ITEM_ACCELERATOR) &&
  427. GTKLookAndFeel.is2_2()) {
  428. matches.add(getMenuItemStyle());
  429. }
  430. matches.add(_defaultStyle);
  431. }
  432. private void getMatches(CharSequence path, java.util.List styles,
  433. java.util.List matches, JComponent c, Region id) {
  434. for (int counter = styles.size() - 1; counter >= 0; counter--){
  435. StyleAssociation sa = (StyleAssociation)styles.get(counter);
  436. if (sa.matches(path) && matches.indexOf(sa.getStyle()) == -1) {
  437. matches.add(sa.getStyle());
  438. }
  439. }
  440. }
  441. private void getClassMatches(java.util.List matches, JComponent c,
  442. Region id) {
  443. getClassMatches(matches, getClass(c, id));
  444. }
  445. private void getClassMatches(java.util.List matches, Object gtkClassName){
  446. if (_depth == null) {
  447. _depth = new int[4];
  448. }
  449. int[] sDepth = _depth;
  450. int matched = 0;
  451. int startMatchLength = matches.size();
  452. for (int counter = _classStyles.size() - 1; counter >= 0; counter--){
  453. StyleAssociation sa = (StyleAssociation)_classStyles.get(counter);
  454. Object klass = gtkClassName;
  455. while (klass != null) {
  456. if (sa.matches(getClassName(klass))) {
  457. int depth = 0;
  458. while ((klass = getSuperclass(klass)) != null) {
  459. depth++;
  460. }
  461. if (matched == 0) {
  462. sDepth[0] = depth;
  463. matches.add(sa.getStyle());
  464. }
  465. else {
  466. int sCounter = 0;
  467. while (sCounter < matched && depth < sDepth[sCounter]){
  468. sCounter++;
  469. }
  470. matches.add(sCounter + startMatchLength,
  471. sa.getStyle());
  472. if (matched + 1 == sDepth.length) {
  473. int[] newDepth = new int[sDepth.length * 2];
  474. System.arraycopy(sDepth, 0, newDepth, 0,
  475. sDepth.length);
  476. _depth = newDepth;
  477. sDepth = newDepth;
  478. }
  479. if (sCounter < matched) {
  480. System.arraycopy(sDepth, 0, sDepth, 0, sCounter);
  481. System.arraycopy(sDepth, sCounter, sDepth,
  482. sCounter + 1, matched - sCounter);
  483. }
  484. sDepth[sCounter] = depth;
  485. }
  486. matched++;
  487. break;
  488. }
  489. klass = getSuperclass(klass);
  490. }
  491. }
  492. }
  493. /**
  494. * Caches the specified style.
  495. */
  496. private void cacheStyle(java.util.List styles, SynthStyle style) {
  497. BakedArrayList cachedStyles = new BakedArrayList(styles);
  498. _resolvedStyles.put(cachedStyles, style);
  499. }
  500. /**
  501. * Returns the cached style from the passed in arguments.
  502. */
  503. private SynthStyle getCachedStyle(java.util.List styles) {
  504. if (styles.size() == 0) {
  505. return null;
  506. }
  507. return (SynthStyle)_resolvedStyles.get(styles);
  508. }
  509. /**
  510. * Creates a single Style from the passed in styles. The passed in List
  511. * is reverse sorted, that is the most recently added style found to
  512. * match will be first.
  513. */
  514. private SynthStyle mergeStyles(java.util.List styles) {
  515. int size = styles.size();
  516. if (size == 0) {
  517. return null;
  518. }
  519. else if (size == 1) {
  520. return (SynthStyle)((DefaultSynthStyle)styles.get(0)).clone();
  521. }
  522. // NOTE: merging is done backwards as DefaultSynthStyleFactory reverses
  523. // order, that is, the most specific style is first.
  524. DefaultSynthStyle style = (DefaultSynthStyle)styles.get(size - 1);
  525. style = (DefaultSynthStyle)style.clone();
  526. for (int counter = size - 2; counter >= 0; counter--) {
  527. style = ((DefaultSynthStyle)styles.get(counter)).addTo(style);
  528. }
  529. return style;
  530. }
  531. /**
  532. * Builds the path returning a CharSequence describing the path. A
  533. * temporary StringBuffer is provided that should NOT be cached.
  534. */
  535. private CharSequence getPath(int type, Component c, Region id) {
  536. _tmpPath.setLength(0);
  537. if (type == WIDGET && id == Region.TOOL_TIP) {
  538. if (c.getName() == null) {
  539. _tmpPath.append("gtk-tooltips");
  540. }
  541. else {
  542. _tmpPath.append(c.getName());
  543. }
  544. }
  545. else {
  546. _getPath(_tmpPath, type, c, id);
  547. }
  548. if (_isLabel) {
  549. if (_tmpPath.length() > 0) {
  550. _tmpPath.append('.');
  551. }
  552. _tmpPath.append(getName(c, Region.LABEL));
  553. }
  554. return _tmpPath;
  555. }
  556. private void _getPath(StringBuffer path, int type, Component c,Region id) {
  557. if (c instanceof JComponent) {
  558. boolean isSubregion = (id != null && id.isSubregion());
  559. if (isSubregion) {
  560. _getPath(path, type, c, null);
  561. }
  562. else {
  563. _getPath(path, type, c.getParent(), id);
  564. }
  565. String key = null;
  566. if (type == WIDGET && !isSubregion) {
  567. key = c.getName();
  568. }
  569. if (key == null) {
  570. if (isSubregion) {
  571. key = getName(c, id);
  572. }
  573. else {
  574. Region region = SynthLookAndFeel.getRegion((JComponent)c);
  575. if (region != null) {
  576. key = getName(c, region);
  577. }
  578. }
  579. }
  580. if (path.length() > 0) {
  581. path.append('.');
  582. }
  583. path.append(key);
  584. }
  585. }
  586. /**
  587. * Returns a class identifer for <code>c</code>.
  588. */
  589. protected Object getClass(JComponent c, Region id) {
  590. if (_isLabel) {
  591. id = Region.LABEL;
  592. }
  593. else if (id == Region.ROOT_PANE) {
  594. Object name = getRootPaneParentType(c);
  595. if (name != null) {
  596. return name;
  597. }
  598. }
  599. String klass = gtkClassFor(id);
  600. if (klass == "GtkLabel") {
  601. if (c.getName() == "TableHeader.renderer") {
  602. return "GtkButton";
  603. }
  604. }
  605. return klass;
  606. }
  607. private SynthStyle getMergedStyle(JComponent c, Region id,
  608. SynthStyle style) {
  609. SynthStyle labelStyle;
  610. try {
  611. _isLabel = true;
  612. labelStyle = (GTKStyle)_getStyle(c, id);
  613. } finally {
  614. _isLabel = false;
  615. }
  616. _labelStyleList.clear();
  617. _labelStyleList.add(style);
  618. _labelStyleList.add(labelStyle);
  619. _labelStyleList.cacheHashCode();
  620. GTKStyle mergedStyle = (GTKStyle)_mergedStyleMap.get(_labelStyleList);
  621. if (mergedStyle == null) {
  622. mergedStyle = (GTKStyle)((DefaultSynthStyle)style).clone();
  623. mergedStyle.addLabelProperties((GTKStyle)labelStyle);
  624. _mergedStyleMap.put(_labelStyleList, mergedStyle);
  625. _labelStyleList = new BakedArrayList(2);
  626. }
  627. return mergedStyle;
  628. }
  629. /**
  630. * Returns the super class of a klass as returned from
  631. * <code>getClass</code>, or null if <code>klass</code> has no
  632. * super classes.
  633. */
  634. private Object getSuperclass(Object klass) {
  635. return gtkSuperclass((String)klass);
  636. }
  637. /**
  638. * Returns the name of a class returned from <code>getClass</code>.
  639. */
  640. private String getClassName(Object klass) {
  641. return (String)klass;
  642. }
  643. /**
  644. * Returns the name of the Region.
  645. */
  646. private String getName(Component c, Region region) {
  647. if (region == Region.ROOT_PANE && c != null) {
  648. String name = getRootPaneParentType(c);
  649. if (name != null) {
  650. return name;
  651. }
  652. }
  653. return gtkClassFor(region);
  654. }
  655. private String getRootPaneParentType(Component c) {
  656. Component parent = c.getParent();
  657. if (parent instanceof Frame) {
  658. return "GtkWindow";
  659. }
  660. else if (parent instanceof Dialog) {
  661. return "GtkDialog";
  662. }
  663. else if (parent instanceof Window) {
  664. return "GtkWindow";
  665. }
  666. else if (parent instanceof JInternalFrame) {
  667. return "GtkFrame";
  668. }
  669. return null;
  670. }
  671. private GTKStyle getProgressBarStyle() {
  672. if (_pbStyle == null) {
  673. Color[] moColors = new Color[GTKColorType.MAX_COUNT];
  674. Color[] normalColors = new Color[GTKColorType.MAX_COUNT];
  675. moColors[GTKColorType.BACKGROUND.getID()] = new ColorUIResource(
  676. 0x4B6983);
  677. normalColors[GTKColorType.BACKGROUND.getID()] =
  678. new ColorUIResource(0xBAB5AB);
  679. _pbStyle = new GTKStyle(new GTKStyle.GTKStateInfo[]
  680. { new GTKStyle.GTKStateInfo(SynthConstants.ENABLED,
  681. null, normalColors, null),
  682. new GTKStyle.GTKStateInfo(SynthConstants.MOUSE_OVER,
  683. null, moColors, null)
  684. }, null, null, GTKStyle.UNDEFINED_THICKNESS,
  685. GTKStyle.UNDEFINED_THICKNESS, null);
  686. }
  687. return _pbStyle;
  688. }
  689. private GTKStyle getMenuItemStyle() {
  690. if (_menuItemStyle == null) {
  691. Color[] moColors = new Color[GTKColorType.MAX_COUNT];
  692. Color[] selectedColors = new Color[GTKColorType.MAX_COUNT];
  693. moColors[GTKColorType.BACKGROUND.getID()] = new ColorUIResource(
  694. 0x9db8d2);
  695. moColors[GTKColorType.FOREGROUND.getID()] = GTKStyle.WHITE_COLOR;
  696. moColors[GTKColorType.TEXT_FOREGROUND.getID()] =
  697. new ColorUIResource(0xFFFFFF);
  698. selectedColors[GTKColorType.TEXT_FOREGROUND.getID()] =
  699. new ColorUIResource(0xFFFFFF);
  700. _menuItemStyle = new GTKStyle(new GTKStyle.GTKStateInfo[]
  701. {
  702. new GTKStyle.GTKStateInfo(SynthConstants.MOUSE_OVER,
  703. null, moColors, null),
  704. new GTKStyle.GTKStateInfo(SynthConstants.SELECTED,
  705. null, selectedColors, null),
  706. }, null, null, GTKStyle.UNDEFINED_THICKNESS,
  707. GTKStyle.UNDEFINED_THICKNESS, null);
  708. }
  709. return _menuItemStyle;
  710. }
  711. private GTKStyle getToolTipStyle() {
  712. if (_tooltipStyle == null) {
  713. Color[] ttColors = new Color[GTKColorType.MAX_COUNT];
  714. if (GTKLookAndFeel.is2_2()) {
  715. ttColors[GTKColorType.BACKGROUND.getID()] =
  716. new ColorUIResource(0xEEE1B3);
  717. ttColors[GTKColorType.FOREGROUND.getID()] =
  718. new ColorUIResource(0x000000);
  719. }
  720. else {
  721. ttColors[GTKColorType.BACKGROUND.getID()] =
  722. new ColorUIResource(0xFFFFC0);
  723. ttColors[GTKColorType.FOREGROUND.getID()] =
  724. new ColorUIResource(0x000000);
  725. }
  726. _tooltipStyle = new GTKStyle(new GTKStyle.GTKStateInfo[] {
  727. new GTKStyle.GTKStateInfo(SynthConstants.ENABLED,
  728. null, ttColors, null)}, null, null,
  729. GTKStyle.UNDEFINED_THICKNESS, GTKStyle.UNDEFINED_THICKNESS,
  730. null);
  731. }
  732. return _tooltipStyle;
  733. }
  734. }