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