1. /*
  2. * @(#)GTKParser.java 1.87 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 java.util.*;
  9. import java.io.*;
  10. import java.awt.*;
  11. import java.util.regex.PatternSyntaxException;
  12. import javax.swing.plaf.ColorUIResource;
  13. import java.security.AccessController;
  14. import sun.security.action.GetPropertyAction;
  15. import javax.swing.plaf.synth.SynthConstants;
  16. /**
  17. * @author Shannon Hickey
  18. * @version 1.87 06/24/04
  19. */
  20. class GTKParser {
  21. private ArrayList freeScanners = new ArrayList();
  22. private HashMap namedStyles = new HashMap();
  23. private ArrayList assignments = new ArrayList();
  24. private HashMap settings = new HashMap();
  25. private File[] pixmapPaths = null;
  26. private ArrayList dirStack = new ArrayList();
  27. private HashMap engineParsers = new HashMap();
  28. // Register parsers here for now. Later we can add methods to register
  29. // new parser classes.
  30. {
  31. engineParsers.put("pixmap", "com.sun.java.swing.plaf.gtk.PixmapEngineParser");
  32. engineParsers.put("bluecurve", "com.sun.java.swing.plaf.gtk.BluecurveEngineParser");
  33. engineParsers.put("wonderland", "com.sun.java.swing.plaf.gtk.BluecurveEngineParser");
  34. engineParsers.put("blueprint", "com.sun.java.swing.plaf.gtk.BlueprintEngineParser");
  35. }
  36. private GTKScanner scanner;
  37. private final String CWD = (String)AccessController.doPrivileged(
  38. new GetPropertyAction("user.dir"));
  39. static class Symbol {
  40. public String name;
  41. public int val;
  42. public Symbol(String name, int val) {
  43. this.name = name;
  44. this.val = val;
  45. }
  46. }
  47. private static final Symbol SYMBOL_INVALID = new Symbol("invalid", GTKScanner.TOKEN_LAST);
  48. private static final Symbol SYMBOL_INCLUDE = new Symbol("include", SYMBOL_INVALID.val + 1);
  49. private static final Symbol SYMBOL_NORMAL = new Symbol("NORMAL", SYMBOL_INCLUDE.val + 1);
  50. private static final Symbol SYMBOL_ACTIVE = new Symbol("ACTIVE", SYMBOL_NORMAL.val + 1);
  51. private static final Symbol SYMBOL_PRELIGHT = new Symbol("PRELIGHT", SYMBOL_ACTIVE.val + 1);
  52. private static final Symbol SYMBOL_SELECTED = new Symbol("SELECTED", SYMBOL_PRELIGHT.val + 1);
  53. private static final Symbol SYMBOL_INSENSITIVE = new Symbol("INSENSITIVE", SYMBOL_SELECTED.val + 1);
  54. private static final Symbol SYMBOL_FG = new Symbol("fg", SYMBOL_INSENSITIVE.val + 1);
  55. private static final Symbol SYMBOL_BG = new Symbol("bg", SYMBOL_FG.val + 1);
  56. private static final Symbol SYMBOL_TEXT = new Symbol("text", SYMBOL_BG.val + 1);
  57. private static final Symbol SYMBOL_BASE = new Symbol("base", SYMBOL_TEXT.val + 1);
  58. private static final Symbol SYMBOL_XTHICKNESS = new Symbol("xthickness", SYMBOL_BASE.val + 1);
  59. private static final Symbol SYMBOL_YTHICKNESS = new Symbol("ythickness", SYMBOL_XTHICKNESS.val + 1);
  60. private static final Symbol SYMBOL_FONT = new Symbol("font", SYMBOL_YTHICKNESS.val + 1);
  61. private static final Symbol SYMBOL_FONTSET = new Symbol("fontset", SYMBOL_FONT.val + 1);
  62. private static final Symbol SYMBOL_FONT_NAME = new Symbol("font_name", SYMBOL_FONTSET.val + 1);
  63. private static final Symbol SYMBOL_BG_PIXMAP = new Symbol("bg_pixmap", SYMBOL_FONT_NAME.val + 1);
  64. private static final Symbol SYMBOL_PIXMAP_PATH = new Symbol("pixmap_path", SYMBOL_BG_PIXMAP.val + 1);
  65. private static final Symbol SYMBOL_STYLE = new Symbol("style", SYMBOL_PIXMAP_PATH.val + 1);
  66. private static final Symbol SYMBOL_BINDING = new Symbol("binding", SYMBOL_STYLE.val + 1);
  67. private static final Symbol SYMBOL_BIND = new Symbol("bind", SYMBOL_BINDING.val + 1);
  68. private static final Symbol SYMBOL_WIDGET = new Symbol("widget", SYMBOL_BIND.val + 1);
  69. private static final Symbol SYMBOL_WIDGET_CLASS = new Symbol("widget_class", SYMBOL_WIDGET.val + 1);
  70. private static final Symbol SYMBOL_CLASS = new Symbol("class", SYMBOL_WIDGET_CLASS.val + 1);
  71. private static final Symbol SYMBOL_LOWEST = new Symbol("lowest", SYMBOL_CLASS.val + 1);
  72. private static final Symbol SYMBOL_GTK = new Symbol("gtk", SYMBOL_LOWEST.val + 1);
  73. private static final Symbol SYMBOL_APPLICATION = new Symbol("application", SYMBOL_GTK.val + 1);
  74. private static final Symbol SYMBOL_THEME = new Symbol("theme", SYMBOL_APPLICATION.val + 1);
  75. private static final Symbol SYMBOL_RC = new Symbol("rc", SYMBOL_THEME.val + 1);
  76. private static final Symbol SYMBOL_HIGHEST = new Symbol("highest", SYMBOL_RC.val + 1);
  77. private static final Symbol SYMBOL_ENGINE = new Symbol("engine", SYMBOL_HIGHEST.val + 1);
  78. private static final Symbol SYMBOL_MODULE_PATH = new Symbol("module_path", SYMBOL_ENGINE.val + 1);
  79. private static final Symbol SYMBOL_IM_MODULE_PATH = new Symbol("im_module_path", SYMBOL_MODULE_PATH.val + 1);
  80. private static final Symbol SYMBOL_IM_MODULE_FILE = new Symbol("im_module_file", SYMBOL_IM_MODULE_PATH.val + 1);
  81. private static final Symbol SYMBOL_STOCK = new Symbol("stock", SYMBOL_IM_MODULE_FILE.val + 1);
  82. private static final Symbol SYMBOL_LTR = new Symbol("LTR", SYMBOL_STOCK.val + 1);
  83. private static final Symbol SYMBOL_RTL = new Symbol("RTL", SYMBOL_LTR.val + 1);
  84. private static final Symbol SYMBOL_LAST = new Symbol("last", SYMBOL_RTL.val + 1);
  85. private static final Symbol[] symbols = {
  86. SYMBOL_INCLUDE, SYMBOL_NORMAL, SYMBOL_ACTIVE, SYMBOL_PRELIGHT,
  87. SYMBOL_SELECTED, SYMBOL_INSENSITIVE, SYMBOL_FG, SYMBOL_BG,
  88. SYMBOL_TEXT, SYMBOL_BASE, SYMBOL_XTHICKNESS, SYMBOL_YTHICKNESS,
  89. SYMBOL_FONT, SYMBOL_FONTSET, SYMBOL_FONT_NAME, SYMBOL_BG_PIXMAP,
  90. SYMBOL_PIXMAP_PATH, SYMBOL_STYLE, SYMBOL_BINDING, SYMBOL_BIND,
  91. SYMBOL_WIDGET, SYMBOL_WIDGET_CLASS, SYMBOL_CLASS, SYMBOL_LOWEST,
  92. SYMBOL_GTK, SYMBOL_APPLICATION, SYMBOL_THEME, SYMBOL_RC,
  93. SYMBOL_HIGHEST, SYMBOL_ENGINE, SYMBOL_MODULE_PATH,
  94. SYMBOL_IM_MODULE_FILE, SYMBOL_STOCK, SYMBOL_LTR, SYMBOL_RTL
  95. };
  96. private static class StyleInfo {
  97. String name;
  98. static final int NUM_STATES = 5;
  99. static final int NORMAL = 0;
  100. static final int PRELIGHT = 1;
  101. static final int ACTIVE = 2;
  102. static final int INSENSITIVE = 3;
  103. static final int SELECTED = 4;
  104. Color[] fg = new Color[NUM_STATES];
  105. Color[] bg = new Color[NUM_STATES];
  106. Color[] text = new Color[NUM_STATES];
  107. Color[] base = new Color[NUM_STATES];
  108. String[] bgPixmapName = new String[NUM_STATES];
  109. Font font = null;
  110. int xThickness = GTKStyle.UNDEFINED_THICKNESS;
  111. int yThickness = GTKStyle.UNDEFINED_THICKNESS;
  112. // An array of HashMaps. The first HashMap is for stock
  113. // icons defined in this style. The other elements are
  114. // those inherited from parent.
  115. ArrayList stocks = null;
  116. CircularIdentityList props = null;
  117. EngineInfo engineInfo = null;
  118. private GTKStyle cachedStyle = null;
  119. private static GTKStyle EMPTY_STYLE = new GTKStyle();
  120. StyleInfo(String name) {
  121. this.name = name;
  122. }
  123. private void initStocksIfNecessary() {
  124. if (stocks == null) {
  125. stocks = new ArrayList();
  126. // for stock icons defined in this style
  127. stocks.add(new HashMap());
  128. }
  129. }
  130. void addStockItem(String id, GTKStyle.GTKIconSource[] sources) {
  131. initStocksIfNecessary();
  132. GTKStyle.GTKStockIconInfo iconInfo = new GTKStyle.GTKStockIconInfo(id, sources);
  133. HashMap map = (HashMap)stocks.get(0);
  134. map.put(id, iconInfo);
  135. }
  136. void addProperty(String klass, String prop, Object value) {
  137. if (props == null) {
  138. props = new CircularIdentityList();
  139. }
  140. CircularIdentityList subList = (CircularIdentityList)props.get(klass);
  141. if (subList == null) {
  142. subList = new CircularIdentityList();
  143. props.set(klass, subList);
  144. }
  145. subList.set(prop, value);
  146. }
  147. void copyDataFrom(StyleInfo other) {
  148. for (int i = 0; i < NUM_STATES; i++) {
  149. fg[i] = other.fg[i];
  150. bg[i] = other.bg[i];
  151. text[i] = other.text[i];
  152. base[i] = other.base[i];
  153. bgPixmapName[i] = other.bgPixmapName[i];
  154. }
  155. xThickness = other.xThickness;
  156. yThickness = other.yThickness;
  157. font = other.font;
  158. if (other.stocks != null) {
  159. initStocksIfNecessary();
  160. stocks.addAll(other.stocks);
  161. }
  162. if (props == null) {
  163. props = GTKStyle.cloneClassSpecificValues(other.props);
  164. } else {
  165. GTKStyle.addClassSpecificValues(other.props, props);
  166. }
  167. }
  168. GTKStyle toGTKStyle() {
  169. if (cachedStyle != null) {
  170. return cachedStyle;
  171. }
  172. ArrayList stateInfos = new ArrayList();
  173. for (int i = 0; i < NUM_STATES; i++) {
  174. Color[] colors = null;
  175. if (fg[i] != null
  176. || bg[i] != null
  177. || text[i] != null
  178. || base[i] != null) {
  179. colors = new Color[GTKColorType.MAX_COUNT];
  180. colors[GTKColorType.FOREGROUND.getID()] = fg[i];
  181. colors[GTKColorType.BACKGROUND.getID()] = bg[i];
  182. colors[GTKColorType.TEXT_FOREGROUND.getID()] = text[i];
  183. colors[GTKColorType.TEXT_BACKGROUND.getID()] = base[i];
  184. }
  185. if (colors != null || bgPixmapName[i] != null) {
  186. GTKStyle.GTKStateInfo stateInfo =
  187. new GTKStyle.GTKStateInfo(toSynthState(i),
  188. null, colors, bgPixmapName[i]);
  189. stateInfos.add(stateInfo);
  190. }
  191. }
  192. GTKStyle.GTKStateInfo[] infoArray = null;
  193. if (stateInfos.size() != 0) {
  194. infoArray = new GTKStyle.GTKStateInfo[stateInfos.size()];
  195. infoArray = (GTKStyle.GTKStateInfo[])stateInfos.toArray(infoArray);
  196. }
  197. GTKStyle.GTKStockIconInfo[] stockArray = stocksToArray();
  198. // if this style has engine information, delegate the creation
  199. if (engineInfo != null) {
  200. cachedStyle = engineInfo.constructGTKStyle(infoArray,
  201. props,
  202. font,
  203. xThickness,
  204. yThickness,
  205. stockArray);
  206. // otherwise, create a regular GTKStyle
  207. } else if (infoArray != null
  208. || stockArray != null
  209. || props != null
  210. || font != null
  211. || xThickness != GTKStyle.UNDEFINED_THICKNESS
  212. || yThickness != GTKStyle.UNDEFINED_THICKNESS) {
  213. cachedStyle = new GTKStyle(infoArray,
  214. props,
  215. font,
  216. xThickness,
  217. yThickness,
  218. stockArray);
  219. } else {
  220. cachedStyle = EMPTY_STYLE;
  221. }
  222. return cachedStyle;
  223. }
  224. private GTKStyle.GTKStockIconInfo[] stocksToArray() {
  225. if (stocks == null) {
  226. return null;
  227. }
  228. ArrayList tmpList = new ArrayList();
  229. HashMap[] maps = new HashMap[stocks.size()];
  230. maps = (HashMap[])stocks.toArray(maps);
  231. for (int i = 0; i < maps.length; i++) {
  232. tmpList.addAll(maps[i].values());
  233. }
  234. GTKStyle.GTKStockIconInfo[] retVal = new GTKStyle.GTKStockIconInfo[tmpList.size()];
  235. retVal = (GTKStyle.GTKStockIconInfo[])tmpList.toArray(retVal);
  236. return retVal;
  237. }
  238. private static int toSynthState(int ourState) {
  239. switch(ourState) {
  240. case NORMAL: return SynthConstants.ENABLED;
  241. case PRELIGHT: return SynthConstants.MOUSE_OVER;
  242. case ACTIVE: return SynthConstants.PRESSED;
  243. case INSENSITIVE: return SynthConstants.DISABLED;
  244. case SELECTED: return SynthConstants.SELECTED;
  245. }
  246. // should not happen
  247. return SynthConstants.ENABLED;
  248. }
  249. }
  250. static abstract class EngineInfo {
  251. private String engineName;
  252. abstract GTKStyle constructGTKStyle(GTKStyle.GTKStateInfo[] infoArray,
  253. CircularIdentityList props,
  254. Font font,
  255. int xThickness,
  256. int yThickness,
  257. GTKStyle.GTKStockIconInfo[] stockArray);
  258. }
  259. private static class Assignment {
  260. int type;
  261. String pattern;
  262. StyleInfo info;
  263. Assignment(int type, String pattern, StyleInfo info) {
  264. this.type = type;
  265. this.pattern = pattern;
  266. this.info = info;
  267. }
  268. public String toString() {
  269. String sVal = "";
  270. switch(type) {
  271. case GTKStyleFactory.WIDGET: sVal = "widget, "; break;
  272. case GTKStyleFactory.WIDGET_CLASS: sVal = "widget_class, "; break;
  273. case GTKStyleFactory.CLASS: sVal = "class, "; break;
  274. }
  275. sVal += pattern + ", ";
  276. sVal += info.name;
  277. return sVal;
  278. }
  279. }
  280. private static Symbol getSymbol(int symbol) {
  281. if (symbol > SYMBOL_INVALID.val && symbol < SYMBOL_LAST.val) {
  282. for (int i = 0; i < symbols.length; i++) {
  283. if (symbols[i].val == symbol) {
  284. return symbols[i];
  285. }
  286. }
  287. }
  288. return null;
  289. }
  290. public GTKParser() {
  291. freeScanners.add(createScanner());
  292. }
  293. public void parseString(String str) throws IOException {
  294. StringReader reader = new StringReader(str);
  295. parseReader(reader, "-");
  296. }
  297. public void parseFile(File file, String name) throws IOException {
  298. if (!file.canRead() || !file.isFile()) {
  299. return;
  300. }
  301. File parent = file.getParentFile();
  302. if (parent == null) {
  303. parent = new File(CWD);
  304. }
  305. dirStack.add(parent);
  306. try {
  307. BufferedReader reader = new BufferedReader(new FileReader(file));
  308. parseReader(reader, name);
  309. } finally {
  310. dirStack.remove(dirStack.size() - 1);
  311. }
  312. // PENDING(shannonh) - This is where we should look up and parse
  313. // the locale-specific version of the file.
  314. }
  315. private void parseReader(Reader reader, String name) throws IOException {
  316. int len = freeScanners.size();
  317. if (len == 0) {
  318. scanner = createScanner();
  319. } else {
  320. scanner = (GTKScanner)freeScanners.remove(len - 1);
  321. }
  322. scanner.scanReader(reader, name);
  323. try {
  324. parseCurrent();
  325. } finally {
  326. scanner.clearScanner();
  327. freeScanners.add(scanner);
  328. }
  329. }
  330. private static GTKScanner createScanner() {
  331. GTKScanner scanner = new GTKScanner();
  332. // configure scanner for GTK rc files
  333. scanner.caseSensitive = true;
  334. scanner.scanBinary = true;
  335. scanner.scanHexDollar = true;
  336. scanner.symbol2Token = true;
  337. for (int i = 0; i < symbols.length; i++) {
  338. scanner.addSymbol(symbols[i].name, symbols[i].val);
  339. }
  340. return scanner;
  341. }
  342. public void loadStylesInto(GTKStyleFactory factory) {
  343. Assignment[] assigns = new Assignment[assignments.size()];
  344. assigns = (Assignment[])assignments.toArray(assigns);
  345. for (int i = 0; i < assigns.length; i++) {
  346. Assignment assign = assigns[i];
  347. GTKStyle style = assign.info.toGTKStyle();
  348. if (style != StyleInfo.EMPTY_STYLE) {
  349. try {
  350. factory.addStyle(style, assign.pattern, assign.type);
  351. } catch (PatternSyntaxException pse) {
  352. // should not happen
  353. }
  354. }
  355. }
  356. }
  357. public HashMap getGTKSettings() {
  358. return settings;
  359. }
  360. public void clearParser() {
  361. namedStyles.clear();
  362. settings.clear();
  363. assignments.clear();
  364. dirStack.clear();
  365. pixmapPaths = null;
  366. }
  367. //------------------------- Parsing Methods ------------------------------//
  368. private void parseCurrent() throws IOException {
  369. while (true) {
  370. if (scanner.peekNextToken() == GTKScanner.TOKEN_EOF) {
  371. break;
  372. }
  373. int expected = parseStatement();
  374. if (expected != GTKScanner.TOKEN_NONE) {
  375. String symbolName = null;
  376. String msg = null;
  377. if (scanner.currScope == 0) {
  378. Symbol lookup;
  379. lookup = getSymbol(expected);
  380. if (lookup != null) {
  381. msg = "e.g. `" + lookup.name + "'";
  382. }
  383. lookup = getSymbol(scanner.currToken);
  384. if (lookup != null) {
  385. symbolName = lookup.name;
  386. }
  387. }
  388. scanner.unexpectedToken(expected, symbolName, msg, true);
  389. break;
  390. }
  391. }
  392. }
  393. private int parseStatement() throws IOException {
  394. int token;
  395. token = scanner.peekNextToken();
  396. if (token == SYMBOL_INCLUDE.val) {
  397. return parseInclude();
  398. } else if (token == SYMBOL_STYLE.val) {
  399. return parseStyle();
  400. } else if (token == SYMBOL_BINDING.val) {
  401. return parseBinding();
  402. } else if (token == SYMBOL_PIXMAP_PATH.val) {
  403. return parsePixmapPath();
  404. } else if (token == SYMBOL_WIDGET.val
  405. || token == SYMBOL_WIDGET_CLASS.val
  406. || token == SYMBOL_CLASS.val) {
  407. return parseAssignment(token);
  408. } else if (token == SYMBOL_MODULE_PATH.val) {
  409. return parseModulePath();
  410. } else if (token == SYMBOL_IM_MODULE_FILE.val) {
  411. return parseIMModuleFile();
  412. } else if (token == GTKScanner.TOKEN_IDENTIFIER) {
  413. return parseIdentifier();
  414. }
  415. scanner.getToken();
  416. return SYMBOL_STYLE.val;
  417. }
  418. private int parseInclude() throws IOException {
  419. int token;
  420. token = scanner.getToken();
  421. if (token != SYMBOL_INCLUDE.val) {
  422. return SYMBOL_INCLUDE.val;
  423. }
  424. token = scanner.getToken();
  425. if (token != GTKScanner.TOKEN_STRING) {
  426. return GTKScanner.TOKEN_STRING;
  427. }
  428. File parseFile = null;
  429. String name = scanner.currValue.stringVal;
  430. File file = new File(name);
  431. if (file.isAbsolute()) {
  432. parseFile = file;
  433. } else {
  434. File[] dirs = new File[dirStack.size()];
  435. dirs = (File[])dirStack.toArray(dirs);
  436. for (int i = dirs.length - 1; i >= 0; i--) {
  437. file = new File(dirs[i], name);
  438. if (file.exists()) {
  439. parseFile = file;
  440. break;
  441. }
  442. }
  443. }
  444. if (parseFile == null) {
  445. scanner.printMessage("Unable to find include file: \"" + name + "\"", false);
  446. } else {
  447. // save the current scanner and recurse
  448. GTKScanner savedScanner = scanner;
  449. try {
  450. parseFile(file, name);
  451. } catch (IOException ioe) {
  452. savedScanner.printMessage("(" + ioe.toString()
  453. + ") while parsing include file: \""
  454. + name
  455. + "\"", false);
  456. }
  457. // restore the scanner
  458. scanner = savedScanner;
  459. }
  460. return GTKScanner.TOKEN_NONE;
  461. }
  462. private int parseStyle() throws IOException {
  463. int token;
  464. token = scanner.getToken();
  465. if (token != SYMBOL_STYLE.val) {
  466. return SYMBOL_STYLE.val;
  467. }
  468. token = scanner.getToken();
  469. if (token != GTKScanner.TOKEN_STRING) {
  470. return GTKScanner.TOKEN_STRING;
  471. }
  472. StyleInfo info = (StyleInfo)namedStyles.get(scanner.currValue.stringVal);
  473. if (info == null) {
  474. info = new StyleInfo(scanner.currValue.stringVal);
  475. }
  476. token = scanner.peekNextToken();
  477. if (token == GTKScanner.TOKEN_EQUAL_SIGN) {
  478. token = scanner.getToken();
  479. token = scanner.getToken();
  480. if (token != GTKScanner.TOKEN_STRING) {
  481. return GTKScanner.TOKEN_STRING;
  482. }
  483. StyleInfo parent = (StyleInfo)namedStyles.get(scanner.currValue.stringVal);
  484. if (parent != null) {
  485. info.copyDataFrom(parent);
  486. }
  487. }
  488. token = scanner.getToken();
  489. if (token != GTKScanner.TOKEN_LEFT_CURLY) {
  490. return GTKScanner.TOKEN_LEFT_CURLY;
  491. }
  492. token = scanner.peekNextToken();
  493. while (token != GTKScanner.TOKEN_RIGHT_CURLY) {
  494. if (token == SYMBOL_FG.val
  495. || token == SYMBOL_BG.val
  496. || token == SYMBOL_TEXT.val
  497. || token == SYMBOL_BASE.val) {
  498. token = parseColorSetting(token, info);
  499. } else if (token == SYMBOL_XTHICKNESS.val
  500. || token == SYMBOL_YTHICKNESS.val) {
  501. token = parseThickness(token, info);
  502. } else if (token == SYMBOL_BG_PIXMAP.val) {
  503. token = parseBGPixmap(info);
  504. } else if (token == SYMBOL_FONT.val
  505. || token == SYMBOL_FONTSET.val
  506. || token == SYMBOL_FONT_NAME.val) {
  507. token = parseFont(token, info);
  508. } else if (token == SYMBOL_ENGINE.val) {
  509. token = parseEngine(info);
  510. } else if (token == SYMBOL_STOCK.val) {
  511. token = parseStock(info);
  512. } else if (token == GTKScanner.TOKEN_IDENTIFIER) {
  513. token = parseIdentifierInStyle(info);
  514. } else {
  515. scanner.getToken();
  516. token = GTKScanner.TOKEN_RIGHT_CURLY;
  517. }
  518. if (token != GTKScanner.TOKEN_NONE) {
  519. return token;
  520. }
  521. token = scanner.peekNextToken();
  522. }
  523. token = scanner.getToken();
  524. if (token != GTKScanner.TOKEN_RIGHT_CURLY) {
  525. return GTKScanner.TOKEN_RIGHT_CURLY;
  526. }
  527. namedStyles.put(info.name, info);
  528. return GTKScanner.TOKEN_NONE;
  529. }
  530. private int parseBinding() throws IOException {
  531. int token;
  532. token = scanner.getToken();
  533. if (token != SYMBOL_BINDING.val) {
  534. return SYMBOL_BINDING.val;
  535. }
  536. token = scanner.getToken();
  537. if (token != GTKScanner.TOKEN_STRING) {
  538. return GTKScanner.TOKEN_STRING;
  539. }
  540. token = ignoreBlock();
  541. if (token != GTKScanner.TOKEN_NONE) {
  542. return token;
  543. }
  544. scanner.printMessage("Binding specification is unsupported, ignoring", false);
  545. return GTKScanner.TOKEN_NONE;
  546. }
  547. private int parsePixmapPath() throws IOException {
  548. int token;
  549. token = scanner.getToken();
  550. if (token != SYMBOL_PIXMAP_PATH.val) {
  551. return SYMBOL_PIXMAP_PATH.val;
  552. }
  553. token = scanner.getToken();
  554. if (token != GTKScanner.TOKEN_STRING) {
  555. return GTKScanner.TOKEN_STRING;
  556. }
  557. pixmapPaths = null;
  558. ArrayList tempPaths = new ArrayList();
  559. StringTokenizer tok = new StringTokenizer(scanner.currValue.stringVal, File.pathSeparator);
  560. while (tok.hasMoreTokens()) {
  561. String path = tok.nextToken();
  562. File file = new File(path);
  563. if (file.isAbsolute()) {
  564. tempPaths.add(file);
  565. } else {
  566. scanner.printMessage("Pixmap path element: \"" + path + "\" must be absolute", false);
  567. }
  568. }
  569. if (tempPaths.size() > 0) {
  570. pixmapPaths = new File[tempPaths.size()];
  571. pixmapPaths = (File[])tempPaths.toArray(pixmapPaths);
  572. }
  573. return GTKScanner.TOKEN_NONE;
  574. }
  575. private int parseAssignment(int expVal) throws IOException {
  576. int token;
  577. token = scanner.getToken();
  578. if (token != expVal) {
  579. return expVal;
  580. }
  581. int type;
  582. String pattern;
  583. StyleInfo info;
  584. boolean isBinding;
  585. if (token == SYMBOL_WIDGET.val) {
  586. type = GTKStyleFactory.WIDGET;
  587. } else if (token == SYMBOL_WIDGET_CLASS.val) {
  588. type = GTKStyleFactory.WIDGET_CLASS;
  589. } else if (token == SYMBOL_CLASS.val) {
  590. type = GTKStyleFactory.CLASS;
  591. } else {
  592. return SYMBOL_WIDGET_CLASS.val;
  593. }
  594. token = scanner.getToken();
  595. if (token != GTKScanner.TOKEN_STRING) {
  596. return GTKScanner.TOKEN_STRING;
  597. }
  598. pattern = scanner.currValue.stringVal;
  599. token = scanner.getToken();
  600. if (token == SYMBOL_STYLE.val) {
  601. isBinding = false;
  602. } else if (token == SYMBOL_BINDING.val) {
  603. isBinding = true;
  604. } else {
  605. return SYMBOL_STYLE.val;
  606. }
  607. token = scanner.peekNextToken();
  608. if (token == ':') {
  609. token = scanner.getToken();
  610. token = scanner.getToken();
  611. if (token != SYMBOL_LOWEST.val
  612. && token != SYMBOL_GTK.val
  613. && token != SYMBOL_APPLICATION.val
  614. && token != SYMBOL_THEME.val
  615. && token != SYMBOL_RC.val
  616. && token != SYMBOL_HIGHEST.val) {
  617. return SYMBOL_APPLICATION.val;
  618. }
  619. scanner.printMessage("Priority specification is unsupported, ignoring", false);
  620. }
  621. token = scanner.getToken();
  622. if (token != GTKScanner.TOKEN_STRING) {
  623. return GTKScanner.TOKEN_STRING;
  624. }
  625. // PENDING(shannonh) - When we start handling priority, the information will
  626. // probably be stored as part of an Assignment here
  627. if (isBinding) {
  628. // PENDING(shannonh) - Key binding support
  629. scanner.printMessage("Binding assignment is unsupported, ignoring", false);
  630. } else {
  631. info = (StyleInfo)namedStyles.get(scanner.currValue.stringVal);
  632. if (info == null) {
  633. return GTKScanner.TOKEN_STRING;
  634. }
  635. Assignment assignment = new Assignment(type, pattern, info);
  636. assignments.add(assignment);
  637. }
  638. return GTKScanner.TOKEN_NONE;
  639. }
  640. private int parseModulePath() throws IOException {
  641. int token;
  642. token = scanner.getToken();
  643. if (token != SYMBOL_MODULE_PATH.val) {
  644. return SYMBOL_MODULE_PATH.val;
  645. }
  646. token = scanner.getToken();
  647. if (token != GTKScanner.TOKEN_STRING) {
  648. return GTKScanner.TOKEN_STRING;
  649. }
  650. scanner.printMessage("module_path directive is now ignored", false);
  651. return GTKScanner.TOKEN_NONE;
  652. }
  653. private int parseIMModuleFile() throws IOException {
  654. int token;
  655. token = scanner.getToken();
  656. if (token != SYMBOL_IM_MODULE_FILE.val) {
  657. return SYMBOL_IM_MODULE_FILE.val;
  658. }
  659. token = scanner.getToken();
  660. if (token != GTKScanner.TOKEN_STRING) {
  661. return GTKScanner.TOKEN_STRING;
  662. }
  663. scanner.printMessage("im_module_file directive is unsupported, ignoring", false);
  664. return GTKScanner.TOKEN_NONE;
  665. }
  666. private int parseIdentifier() throws IOException {
  667. int token;
  668. token = scanner.getToken();
  669. if (token != GTKScanner.TOKEN_IDENTIFIER) {
  670. return GTKScanner.TOKEN_IDENTIFIER;
  671. }
  672. String prop;
  673. Object[] value = new Object[1];
  674. StringBuffer buf = new StringBuffer(scanner.currValue.stringVal);
  675. String validChars = GTKScanner.CHARS_A_2_Z
  676. + GTKScanner.CHARS_a_2_z
  677. + GTKScanner.CHARS_DIGITS
  678. + "-";
  679. // some weird logic that GTK does
  680. int len = buf.length();
  681. for (int i = 0; i < len; i++) {
  682. if (validChars.indexOf(buf.charAt(i)) == -1) {
  683. buf.setCharAt(i, '-');
  684. }
  685. }
  686. prop = buf.toString().intern();
  687. token = parsePropertyAssignment(value);
  688. if (token != GTKScanner.TOKEN_NONE) {
  689. return token;
  690. }
  691. settings.put(prop, value[0]);
  692. return GTKScanner.TOKEN_NONE;
  693. }
  694. private int parseColorSetting(int expVal, StyleInfo info) throws IOException {
  695. int token;
  696. token = scanner.getToken();
  697. if (token != expVal) {
  698. return expVal;
  699. }
  700. Color[] cols = null;
  701. if (token == SYMBOL_FG.val) {
  702. cols = info.fg;
  703. } else if (token == SYMBOL_BG.val) {
  704. cols = info.bg;
  705. } else if (token == SYMBOL_TEXT.val) {
  706. cols = info.text;
  707. } else if (token == SYMBOL_BASE.val) {
  708. cols = info.base;
  709. } else {
  710. return SYMBOL_FG.val;
  711. }
  712. int[] state = new int[1];
  713. token = parseState(state);
  714. if (token != GTKScanner.TOKEN_NONE) {
  715. return token;
  716. }
  717. token = scanner.getToken();
  718. if (token != GTKScanner.TOKEN_EQUAL_SIGN) {
  719. return GTKScanner.TOKEN_EQUAL_SIGN;
  720. }
  721. return parseColor(scanner, cols, state[0]);
  722. }
  723. private int parseState(int[] retVal) throws IOException {
  724. int token;
  725. token = scanner.getToken();
  726. if (token != GTKScanner.TOKEN_LEFT_BRACE) {
  727. return GTKScanner.TOKEN_LEFT_BRACE;
  728. }
  729. token = scanner.getToken();
  730. if (token == SYMBOL_NORMAL.val) {
  731. retVal[0] = StyleInfo.NORMAL;
  732. } else if (token == SYMBOL_ACTIVE.val) {
  733. retVal[0] = StyleInfo.ACTIVE;
  734. } else if (token == SYMBOL_PRELIGHT.val) {
  735. retVal[0] = StyleInfo.PRELIGHT;
  736. } else if (token == SYMBOL_SELECTED.val) {
  737. retVal[0] = StyleInfo.SELECTED;
  738. } else if (token == SYMBOL_INSENSITIVE.val) {
  739. retVal[0] = StyleInfo.INSENSITIVE;
  740. } else {
  741. return SYMBOL_NORMAL.val;
  742. }
  743. token = scanner.getToken();
  744. if (token != GTKScanner.TOKEN_RIGHT_BRACE) {
  745. return GTKScanner.TOKEN_RIGHT_BRACE;
  746. }
  747. return GTKScanner.TOKEN_NONE;
  748. }
  749. // this is static, and takes a scanner as a parameter, so that it can
  750. // be used in other places that need to parse colors
  751. static int parseColor(GTKScanner scanner, Color[] colors, int index)
  752. throws IOException {
  753. int token;
  754. long lVal;
  755. double dVal;
  756. float red;
  757. float green;
  758. float blue;
  759. token = scanner.getToken();
  760. switch(token) {
  761. case GTKScanner.TOKEN_LEFT_CURLY:
  762. token = scanner.getToken();
  763. if (token == GTKScanner.TOKEN_INT) {
  764. red = javaColorVal(scanner.currValue.longVal);
  765. } else if (token == GTKScanner.TOKEN_FLOAT) {
  766. red = javaColorVal(scanner.currValue.doubleVal);
  767. } else {
  768. return GTKScanner.TOKEN_FLOAT;
  769. }
  770. token = scanner.getToken();
  771. if (token != GTKScanner.TOKEN_COMMA) {
  772. return GTKScanner.TOKEN_COMMA;
  773. }
  774. token = scanner.getToken();
  775. if (token == GTKScanner.TOKEN_INT) {
  776. green = javaColorVal(scanner.currValue.longVal);
  777. } else if (token == GTKScanner.TOKEN_FLOAT) {
  778. green = javaColorVal(scanner.currValue.doubleVal);
  779. } else {
  780. return GTKScanner.TOKEN_FLOAT;
  781. }
  782. token = scanner.getToken();
  783. if (token != GTKScanner.TOKEN_COMMA) {
  784. return GTKScanner.TOKEN_COMMA;
  785. }
  786. token = scanner.getToken();
  787. if (token == GTKScanner.TOKEN_INT) {
  788. blue = javaColorVal(scanner.currValue.longVal);
  789. } else if (token == GTKScanner.TOKEN_FLOAT) {
  790. blue = javaColorVal(scanner.currValue.doubleVal);
  791. } else {
  792. return GTKScanner.TOKEN_FLOAT;
  793. }
  794. token = scanner.getToken();
  795. if (token != GTKScanner.TOKEN_RIGHT_CURLY) {
  796. return GTKScanner.TOKEN_RIGHT_CURLY;
  797. }
  798. colors[index] = new ColorUIResource(red, green, blue);
  799. break;
  800. case GTKScanner.TOKEN_STRING:
  801. Color color = parseColorString(scanner.currValue.stringVal);
  802. if (color == null) {
  803. scanner.printMessage("Invalid color constant '" +
  804. scanner.currValue.stringVal
  805. + "'", false);
  806. return GTKScanner.TOKEN_STRING;
  807. }
  808. colors[index] = color;
  809. break;
  810. default:
  811. return GTKScanner.TOKEN_STRING;
  812. }
  813. return GTKScanner.TOKEN_NONE;
  814. }
  815. static Color parseColorString(String str) {
  816. if (str.charAt(0) == '#') {
  817. str = str.substring(1);
  818. int i = str.length();
  819. if (i < 3 || i > 12 || (i % 3) != 0) {
  820. return null;
  821. }
  822. i /= 3;
  823. int r;
  824. int g;
  825. int b;
  826. try {
  827. r = Integer.parseInt(str.substring(0, i), 16);
  828. g = Integer.parseInt(str.substring(i, i * 2), 16);
  829. b = Integer.parseInt(str.substring(i * 2, i * 3), 16);
  830. } catch (NumberFormatException nfe) {
  831. return null;
  832. }
  833. if (i == 4) {
  834. return new ColorUIResource(r / 65535.0f, g / 65535.0f, b / 65535.0f);
  835. } else if (i == 1) {
  836. return new ColorUIResource(r / 15.0f, g / 15.0f, b / 15.0f);
  837. } else if (i == 2) {
  838. return new ColorUIResource(r, g, b);
  839. } else {
  840. return new ColorUIResource(r / 4095.0f, g / 4095.0f, b / 4095.0f);
  841. }
  842. } else {
  843. return XColors.lookupColor(str);
  844. }
  845. }
  846. private static float javaColorVal(long col) {
  847. int color = (int)Math.max(Math.min(col, 65535), 0);
  848. return color / 65535.0f;
  849. }
  850. private static float javaColorVal(double col) {
  851. float color = (float)Math.max(Math.min(col, 1.0f), 0.0f);
  852. return color;
  853. }
  854. private int parseThickness(int expVal, StyleInfo info) throws IOException {
  855. int token;
  856. boolean isXThickness;
  857. token = scanner.getToken();
  858. if (token != expVal) {
  859. return expVal;
  860. }
  861. if (token == SYMBOL_XTHICKNESS.val) {
  862. isXThickness = true;
  863. } else if (token == SYMBOL_YTHICKNESS.val) {
  864. isXThickness = false;
  865. } else {
  866. return SYMBOL_XTHICKNESS.val;
  867. }
  868. token = scanner.getToken();
  869. if (token != GTKScanner.TOKEN_EQUAL_SIGN) {
  870. return GTKScanner.TOKEN_EQUAL_SIGN;
  871. }
  872. token = scanner.getToken();
  873. if (token != GTKScanner.TOKEN_INT) {
  874. return GTKScanner.TOKEN_INT;
  875. }
  876. int thickness = (int)scanner.currValue.longVal;
  877. if (isXThickness) {
  878. info.xThickness = thickness;
  879. } else {
  880. info.yThickness = thickness;
  881. }
  882. return GTKScanner.TOKEN_NONE;
  883. }
  884. private int parseBGPixmap(StyleInfo info) throws IOException {
  885. int token;
  886. token = scanner.getToken();
  887. if (token != SYMBOL_BG_PIXMAP.val) {
  888. return SYMBOL_BG_PIXMAP.val;
  889. }
  890. int[] state = new int[1];
  891. token = parseState(state);
  892. if (token != GTKScanner.TOKEN_NONE) {
  893. return token;
  894. }
  895. token = scanner.getToken();
  896. if (token != GTKScanner.TOKEN_EQUAL_SIGN) {
  897. return GTKScanner.TOKEN_EQUAL_SIGN;
  898. }
  899. token = scanner.getToken();
  900. if (token != GTKScanner.TOKEN_STRING) {
  901. return GTKScanner.TOKEN_STRING;
  902. }
  903. String pixmapStr = null;
  904. String str = scanner.currValue.stringVal;
  905. if (str.equals("<none>") || str.equals("<parent>")) {
  906. pixmapStr = str.intern();
  907. } else {
  908. pixmapStr = resolvePixmapPath(str);
  909. }
  910. if (pixmapStr == null) {
  911. scanner.printMessage("Unable to locate image file in pixmap_path: \"" + str + "\"", false);
  912. } else {
  913. info.bgPixmapName[state[0]] = pixmapStr;
  914. }
  915. return GTKScanner.TOKEN_NONE;
  916. }
  917. String resolvePixmapPath(String str) {
  918. // search in pixmap path
  919. if (pixmapPaths != null) {
  920. for (int i = 0; i < pixmapPaths.length; i++) {
  921. File file = new File(pixmapPaths[i], str);
  922. if (file.canRead()) {
  923. return file.getAbsolutePath();
  924. }
  925. }
  926. }
  927. // search in rc directory stack
  928. File[] dirs = new File[dirStack.size()];
  929. dirs = (File[])dirStack.toArray(dirs);
  930. for (int i = dirs.length - 1; i >= 0; i--) {
  931. File file = new File(dirs[i], str);
  932. if (file.canRead()) {
  933. return file.getAbsolutePath();
  934. }
  935. }
  936. return null;
  937. }
  938. private int parseFont(int expVal, StyleInfo info) throws IOException {
  939. int token;
  940. boolean isPango;
  941. token = scanner.getToken();
  942. if (token != expVal) {
  943. return expVal;
  944. }
  945. if (token == SYMBOL_FONT_NAME.val) {
  946. isPango = true;
  947. } else if (token == SYMBOL_FONT.val
  948. || token == SYMBOL_FONTSET.val) {
  949. isPango = false;
  950. } else {
  951. return SYMBOL_FONT_NAME.val;
  952. }
  953. token = scanner.getToken();
  954. if (token != GTKScanner.TOKEN_EQUAL_SIGN) {
  955. return GTKScanner.TOKEN_EQUAL_SIGN;
  956. }
  957. token = scanner.getToken();
  958. if (token != GTKScanner.TOKEN_STRING) {
  959. return GTKScanner.TOKEN_STRING;
  960. }
  961. // only need to parse pango font names
  962. if (isPango) {
  963. String pangoName = scanner.currValue.stringVal;
  964. info.font = PangoFonts.lookupFont(pangoName);
  965. }
  966. return GTKScanner.TOKEN_NONE;
  967. }
  968. private GTKEngineParser getParser(String engineName) {
  969. Object o = engineParsers.get(engineName);
  970. if (o == null) {
  971. return null;
  972. }
  973. if (o instanceof GTKEngineParser) {
  974. return (GTKEngineParser)o;
  975. }
  976. GTKEngineParser parser = null;
  977. try {
  978. parser = (GTKEngineParser)Class.forName((String)o).newInstance();
  979. } catch (ClassNotFoundException e) {
  980. } catch (InstantiationException e) {
  981. } catch (IllegalAccessException e) {
  982. }
  983. if (parser == null) {
  984. // no need to keep trying to load it every time
  985. engineParsers.remove(engineName);
  986. } else {
  987. // replace the name with an instance of a parser
  988. engineParsers.put(engineName, parser);
  989. }
  990. return parser;
  991. }
  992. private int parseEngine(StyleInfo info) throws IOException {
  993. int token;
  994. token = scanner.getToken();
  995. if (token != SYMBOL_ENGINE.val) {
  996. return SYMBOL_ENGINE.val;
  997. }
  998. token = scanner.getToken();
  999. if (token != GTKScanner.TOKEN_STRING) {
  1000. return GTKScanner.TOKEN_STRING;
  1001. }
  1002. String engineName = scanner.currValue.stringVal;
  1003. // engine "" {} means to use the default engine
  1004. if (engineName.length() == 0) {
  1005. token = scanner.getToken();
  1006. if (token != GTKScanner.TOKEN_LEFT_CURLY) {
  1007. return GTKScanner.TOKEN_LEFT_CURLY;
  1008. }
  1009. token = scanner.getToken();
  1010. if (token != GTKScanner.TOKEN_RIGHT_CURLY) {
  1011. return GTKScanner.TOKEN_RIGHT_CURLY;
  1012. }
  1013. info.engineInfo = null;
  1014. return GTKScanner.TOKEN_NONE;
  1015. }
  1016. GTKEngineParser parser = getParser(engineName);
  1017. if (parser == null) {
  1018. token = ignoreBlock();
  1019. if (token != GTKScanner.TOKEN_NONE) {
  1020. return token;
  1021. }
  1022. scanner.printMessage("Engine \"" + engineName + "\" is unsupported, ignoring", false);
  1023. } else {
  1024. token = scanner.getToken();
  1025. if (token != GTKScanner.TOKEN_LEFT_CURLY) {
  1026. return GTKScanner.TOKEN_LEFT_CURLY;
  1027. }
  1028. EngineInfo[] engineInfo = new EngineInfo[1];
  1029. // only pass in the existing engine info if it came from this parser
  1030. if (info.engineInfo != null && engineName.equals(info.engineInfo.engineName)) {
  1031. engineInfo[0] = info.engineInfo;
  1032. }
  1033. token = parser.parse(scanner, this, engineInfo);
  1034. if (token != GTKScanner.TOKEN_NONE) {
  1035. return token;
  1036. }
  1037. // tag the returned engine info with the engine name
  1038. if (engineInfo[0] != null) {
  1039. engineInfo[0].engineName = engineName;
  1040. }
  1041. info.engineInfo = engineInfo[0];
  1042. }
  1043. return GTKScanner.TOKEN_NONE;
  1044. }
  1045. private int parseStock(StyleInfo info) throws IOException {
  1046. String id;
  1047. int token;
  1048. token = scanner.getToken();
  1049. if (token != SYMBOL_STOCK.val) {
  1050. return SYMBOL_STOCK.val;
  1051. }
  1052. token = scanner.getToken();
  1053. if (token != GTKScanner.TOKEN_LEFT_BRACE) {
  1054. return GTKScanner.TOKEN_LEFT_BRACE;
  1055. }
  1056. token = scanner.getToken();
  1057. if (token != GTKScanner.TOKEN_STRING) {
  1058. return GTKScanner.TOKEN_STRING;
  1059. }
  1060. id = scanner.currValue.stringVal;
  1061. token = scanner.getToken();
  1062. if (token != GTKScanner.TOKEN_RIGHT_BRACE) {
  1063. return GTKScanner.TOKEN_RIGHT_BRACE;
  1064. }
  1065. token = scanner.getToken();
  1066. if (token != GTKScanner.TOKEN_EQUAL_SIGN) {
  1067. return GTKScanner.TOKEN_EQUAL_SIGN;
  1068. }
  1069. token = scanner.getToken();
  1070. if (token != GTKScanner.TOKEN_LEFT_CURLY) {
  1071. return GTKScanner.TOKEN_LEFT_CURLY;
  1072. }
  1073. ArrayList iconSources = new ArrayList();
  1074. // This array will be used first to hold the return value from
  1075. // parseIconSource and then the variable will be used in
  1076. // converting iconSources to an array.
  1077. GTKStyle.GTKIconSource[] sources = new GTKStyle.GTKIconSource[1];
  1078. token = scanner.peekNextToken();
  1079. while (token != GTKScanner.TOKEN_RIGHT_CURLY) {
  1080. token = parseIconSource(sources);
  1081. if (token != GTKScanner.TOKEN_NONE) {
  1082. return token;
  1083. }
  1084. token = scanner.getToken();
  1085. if (token != GTKScanner.TOKEN_COMMA
  1086. && token != GTKScanner.TOKEN_RIGHT_CURLY) {
  1087. return GTKScanner.TOKEN_RIGHT_CURLY;
  1088. }
  1089. if (sources[0] != null) {
  1090. iconSources.add(sources[0]);
  1091. }
  1092. }
  1093. if (iconSources.size() != 0) {
  1094. sources = new GTKStyle.GTKIconSource[iconSources.size()];
  1095. sources = (GTKStyle.GTKIconSource[])iconSources.toArray(sources);
  1096. info.addStockItem(id, sources);
  1097. }
  1098. return GTKScanner.TOKEN_NONE;
  1099. }
  1100. private GTKStyle.GTKIconSource createIconSource(String path,
  1101. int direction,
  1102. int state,
  1103. String size) {
  1104. String resolvedPath = resolvePixmapPath(path);
  1105. if (resolvedPath != null) {
  1106. return new GTKStyle.GTKIconSource(resolvedPath, direction, state, size);
  1107. }
  1108. return null;
  1109. }
  1110. private int parseIconSource(GTKStyle.GTKIconSource[] retVal) throws IOException {
  1111. int token;
  1112. String pixmapStr = null;
  1113. int direction = GTKConstants.UNDEFINED;
  1114. int state = GTKConstants.UNDEFINED;
  1115. String size = null;
  1116. token = scanner.getToken();
  1117. if (token != GTKScanner.TOKEN_LEFT_CURLY) {
  1118. return GTKScanner.TOKEN_LEFT_CURLY;
  1119. }
  1120. token = scanner.getToken();
  1121. if (token != GTKScanner.TOKEN_STRING) {
  1122. return GTKScanner.TOKEN_STRING;
  1123. }
  1124. pixmapStr = scanner.currValue.stringVal;
  1125. token = scanner.getToken();
  1126. if (token == GTKScanner.TOKEN_RIGHT_CURLY) {
  1127. retVal[0] = createIconSource(pixmapStr, direction, state, size);
  1128. return GTKScanner.TOKEN_NONE;
  1129. } else if (token != GTKScanner.TOKEN_COMMA) {
  1130. return GTKScanner.TOKEN_COMMA;
  1131. }
  1132. token = scanner.getToken();
  1133. if (token == SYMBOL_RTL.val) {
  1134. direction = GTKConstants.RTL;
  1135. } else if (token == SYMBOL_LTR.val) {
  1136. direction = GTKConstants.LTR;
  1137. } else if (token == '*') {
  1138. // nothing
  1139. } else {
  1140. return SYMBOL_RTL.val;
  1141. }
  1142. token = scanner.getToken();
  1143. if (token == GTKScanner.TOKEN_RIGHT_CURLY) {
  1144. retVal[0] = createIconSource(pixmapStr, direction, state, size);
  1145. return GTKScanner.TOKEN_NONE;
  1146. } else if (token != GTKScanner.TOKEN_COMMA) {
  1147. return GTKScanner.TOKEN_COMMA;
  1148. }
  1149. token = scanner.getToken();
  1150. if (token == SYMBOL_NORMAL.val) {
  1151. state = SynthConstants.ENABLED;
  1152. } else if (token == SYMBOL_ACTIVE.val) {
  1153. state = SynthConstants.PRESSED;
  1154. } else if (token == SYMBOL_PRELIGHT.val) {
  1155. state = SynthConstants.MOUSE_OVER;
  1156. } else if (token == SYMBOL_SELECTED.val) {
  1157. state = SynthConstants.SELECTED;
  1158. } else if (token == SYMBOL_INSENSITIVE.val) {
  1159. state = SynthConstants.DISABLED;
  1160. } else if (token == '*') {
  1161. // nothing
  1162. } else {
  1163. return SYMBOL_PRELIGHT.val;
  1164. }
  1165. token = scanner.getToken();
  1166. if (token == GTKScanner.TOKEN_RIGHT_CURLY) {
  1167. retVal[0] = createIconSource(pixmapStr, direction, state, size);
  1168. return GTKScanner.TOKEN_NONE;
  1169. } else if (token != GTKScanner.TOKEN_COMMA) {
  1170. return GTKScanner.TOKEN_COMMA;
  1171. }
  1172. token = scanner.getToken();
  1173. if (token != '*') {
  1174. if (token != GTKScanner.TOKEN_STRING) {
  1175. return GTKScanner.TOKEN_STRING;
  1176. }
  1177. size = scanner.currValue.stringVal;
  1178. }
  1179. token = scanner.getToken();
  1180. if (token != GTKScanner.TOKEN_RIGHT_CURLY) {
  1181. return GTKScanner.TOKEN_RIGHT_CURLY;
  1182. }
  1183. retVal[0] = createIconSource(pixmapStr, direction, state, size);
  1184. return GTKScanner.TOKEN_NONE;
  1185. }
  1186. private int parseIdentifierInStyle(StyleInfo info) throws IOException {
  1187. int token;
  1188. token = scanner.getToken();
  1189. if (token != GTKScanner.TOKEN_IDENTIFIER
  1190. || scanner.currValue.stringVal.charAt(0) < 'A'
  1191. || scanner.currValue.stringVal.charAt(0) > 'Z') {
  1192. return GTKScanner.TOKEN_IDENTIFIER;
  1193. }
  1194. String klass;
  1195. String prop;
  1196. Object[] value = new Object[1];
  1197. klass = scanner.currValue.stringVal.intern();
  1198. // check the next two tokens to make sure they're both ':'
  1199. if (scanner.getToken() != ':' || scanner.getToken() != ':') {
  1200. return ':';
  1201. }
  1202. token = scanner.getToken();
  1203. if (token != GTKScanner.TOKEN_IDENTIFIER) {
  1204. return GTKScanner.TOKEN_IDENTIFIER;
  1205. }
  1206. StringBuffer buf = new StringBuffer(scanner.currValue.stringVal);
  1207. String validChars = GTKScanner.CHARS_A_2_Z
  1208. + GTKScanner.CHARS_a_2_z
  1209. + GTKScanner.CHARS_DIGITS
  1210. + "-";
  1211. // some weird logic that GTK does
  1212. int len = buf.length();
  1213. for (int i = 0; i < len; i++) {
  1214. if (validChars.indexOf(buf.charAt(i)) == -1) {
  1215. buf.setCharAt(i, '-');
  1216. }
  1217. }
  1218. prop = buf.toString().intern();
  1219. token = parsePropertyAssignment(value);
  1220. if (token != GTKScanner.TOKEN_NONE) {
  1221. return token;
  1222. }
  1223. // for Strings or StringBuffers (representing complex values) we check
  1224. // for a parser that might want to parse the value
  1225. if (value[0] instanceof String || value[0] instanceof StringBuffer) {
  1226. Object val = value[0];
  1227. PropertyParser pp = PropertyParser.getParserFor(prop);
  1228. if (pp == null) {
  1229. // just add the property (but convert to String first if StringBuffer)
  1230. info.addProperty(klass, prop,
  1231. val instanceof String ? val : val.toString());
  1232. } else {
  1233. String toParse = val instanceof String ?
  1234. '"' + escapeString((String) val) + '"' :
  1235. val.toString();
  1236. Object parsedVal = pp.parse(toParse);
  1237. if (parsedVal == null) {
  1238. scanner.printMessage("Failed to parse property value \"" + toParse + "\" for `"
  1239. + klass + "::" + prop + "'", false);
  1240. } else {
  1241. info.addProperty(klass, prop, parsedVal);
  1242. }
  1243. }
  1244. } else {
  1245. info.addProperty(klass, prop, value[0]);
  1246. }
  1247. return GTKScanner.TOKEN_NONE;
  1248. }
  1249. private int parsePropertyAssignment(Object[] retVal) throws IOException {
  1250. int token;
  1251. token = scanner.getToken();
  1252. if (token != '=') {
  1253. return '=';
  1254. }
  1255. // save the scanner mode
  1256. boolean scanIdentifier = scanner.scanIdentifier;
  1257. boolean scanSymbols = scanner.scanSymbols;
  1258. boolean identifier2String = scanner.identifier2String;
  1259. boolean char2Token = scanner.char2Token;
  1260. boolean scanIdentifierNULL = scanner.scanIdentifierNULL;
  1261. boolean numbers2Int = scanner.numbers2Int;
  1262. // modify the scanner mode for our purposes
  1263. scanner.scanIdentifier = true;
  1264. scanner.scanSymbols = false;
  1265. scanner.identifier2String = false;
  1266. scanner.char2Token = true;
  1267. scanner.scanIdentifierNULL = false;
  1268. scanner.numbers2Int = true;
  1269. boolean negate = false;
  1270. if (scanner.peekNextToken() == '-') {
  1271. scanner.getToken();
  1272. negate = true;
  1273. }
  1274. token = scanner.peekNextToken();
  1275. switch(token) {
  1276. case GTKScanner.TOKEN_INT:
  1277. scanner.getToken();
  1278. retVal[0] = new Long(negate ? -scanner.currValue.longVal : scanner.currValue.longVal);
  1279. token = GTKScanner.TOKEN_NONE;
  1280. break;
  1281. case GTKScanner.TOKEN_FLOAT:
  1282. scanner.getToken();
  1283. retVal[0] = new Double(negate ? -scanner.currValue.doubleVal : scanner.currValue.doubleVal);
  1284. token = GTKScanner.TOKEN_NONE;
  1285. break;
  1286. case GTKScanner.TOKEN_STRING:
  1287. scanner.getToken();
  1288. if (negate) {
  1289. token = GTKScanner.TOKEN_INT;
  1290. } else {
  1291. retVal[0] = scanner.currValue.stringVal;
  1292. token = GTKScanner.TOKEN_NONE;
  1293. }
  1294. break;
  1295. case GTKScanner.TOKEN_IDENTIFIER:
  1296. case GTKScanner.TOKEN_LEFT_PAREN:
  1297. case GTKScanner.TOKEN_LEFT_CURLY:
  1298. case GTKScanner.TOKEN_LEFT_BRACE:
  1299. if (negate) {
  1300. token = GTKScanner.TOKEN_INT;
  1301. } else {
  1302. StringBuffer result = new StringBuffer();
  1303. token = parseComplexPropVal(result, GTKScanner.TOKEN_EOF);
  1304. if (token == GTKScanner.TOKEN_NONE) {
  1305. result.append(' ');
  1306. // return the StringBuffer directly to indicate a complex value
  1307. retVal[0] = result;
  1308. }
  1309. }
  1310. break;
  1311. default:
  1312. scanner.getToken();
  1313. token = GTKScanner.TOKEN_INT;
  1314. break;
  1315. }
  1316. // restore the scanner mode
  1317. scanner.scanIdentifier = scanIdentifier;
  1318. scanner.scanSymbols = scanSymbols;
  1319. scanner.identifier2String = identifier2String;
  1320. scanner.char2Token = char2Token;
  1321. scanner.scanIdentifierNULL = scanIdentifierNULL;
  1322. scanner.numbers2Int = numbers2Int;
  1323. return token;
  1324. }
  1325. private int parseComplexPropVal(StringBuffer into, int delim) throws IOException {
  1326. int token;
  1327. token = scanner.getToken();
  1328. switch(token) {
  1329. case GTKScanner.TOKEN_INT:
  1330. into.append(" 0x");
  1331. into.append(Long.toHexString(scanner.currValue.longVal));
  1332. break;
  1333. case GTKScanner.TOKEN_FLOAT:
  1334. into.append(' ');
  1335. into.append(scanner.currValue.doubleVal);
  1336. break;
  1337. case GTKScanner.TOKEN_STRING:
  1338. into.append(" \"");
  1339. into.append(escapeString(scanner.currValue.stringVal));
  1340. into.append('"');
  1341. break;
  1342. case GTKScanner.TOKEN_IDENTIFIER:
  1343. into.append(' ');
  1344. into.append(scanner.currValue.stringVal);
  1345. break;
  1346. case GTKScanner.TOKEN_LEFT_PAREN:
  1347. into.append(' ');
  1348. into.append((char)token);
  1349. token = parseComplexPropVal(into, GTKScanner.TOKEN_RIGHT_PAREN);
  1350. if (token != GTKScanner.TOKEN_NONE) {
  1351. return token;
  1352. }
  1353. break;
  1354. case GTKScanner.TOKEN_LEFT_CURLY:
  1355. into.append(' ');
  1356. into.append((char)token);
  1357. token = parseComplexPropVal(into, GTKScanner.TOKEN_RIGHT_CURLY);
  1358. if (token != GTKScanner.TOKEN_NONE) {
  1359. return token;
  1360. }
  1361. break;
  1362. case GTKScanner.TOKEN_LEFT_BRACE:
  1363. into.append(' ');
  1364. into.append((char)token);
  1365. token = parseComplexPropVal(into, GTKScanner.TOKEN_RIGHT_BRACE);
  1366. if (token != GTKScanner.TOKEN_NONE) {
  1367. return token;
  1368. }
  1369. break;
  1370. default:
  1371. if (token >= GTKScanner.TOKEN_NONE || token <= GTKScanner.TOKEN_EOF) {
  1372. return delim != GTKScanner.TOKEN_EOF ? delim : GTKScanner.TOKEN_STRING;
  1373. }
  1374. into.append(' ');
  1375. into.append((char)token);
  1376. if (token == delim) {
  1377. return GTKScanner.TOKEN_NONE;
  1378. }
  1379. }
  1380. if (delim == GTKScanner.TOKEN_EOF) {
  1381. return GTKScanner.TOKEN_NONE;
  1382. } else {
  1383. return parseComplexPropVal(into, delim);
  1384. }
  1385. }
  1386. private String escapeString(String source) {
  1387. int len = source.length();
  1388. StringBuffer result = new StringBuffer(len * 4);
  1389. for (int i = 0; i < len; i++) {
  1390. char ch = source.charAt(i);
  1391. switch(ch) {
  1392. case '\b':
  1393. result.append("\\b");
  1394. break;
  1395. case '\f':
  1396. result.append("\\f");
  1397. break;
  1398. case '\n':
  1399. result.append("\\n");
  1400. break;
  1401. case '\r':
  1402. result.append("\\r");
  1403. break;
  1404. case '\t':
  1405. result.append("\\t");
  1406. break;
  1407. case '\\':
  1408. result.append("\\\\");
  1409. break;
  1410. case '"':
  1411. result.append("\\\"");
  1412. break;
  1413. default:
  1414. if (ch < ' ' || ch > '~') {
  1415. result.append('\\');
  1416. result.append(Integer.toOctalString(ch));
  1417. } else {
  1418. result.append((char)ch);
  1419. }
  1420. break;
  1421. }
  1422. }
  1423. return result.toString();
  1424. }
  1425. private int ignoreBlock() throws IOException {
  1426. int token;
  1427. token = scanner.getToken();
  1428. if (token != GTKScanner.TOKEN_LEFT_CURLY) {
  1429. return GTKScanner.TOKEN_LEFT_CURLY;
  1430. }
  1431. int curlys = 1;
  1432. while (curlys > 0) {
  1433. token = scanner.getToken();
  1434. switch(token) {
  1435. case GTKScanner.TOKEN_EOF:
  1436. return GTKScanner.TOKEN_RIGHT_CURLY;
  1437. case GTKScanner.TOKEN_LEFT_CURLY:
  1438. curlys++;
  1439. break;
  1440. case GTKScanner.TOKEN_RIGHT_CURLY:
  1441. curlys--;
  1442. break;
  1443. default:
  1444. // ignore
  1445. }
  1446. }
  1447. return GTKScanner.TOKEN_NONE;
  1448. }
  1449. // for testing
  1450. public static void main(String[] args) {
  1451. if (args.length == 0) {
  1452. System.err.println("Usage: java GTKParser <gtkrc file> <gtkrc file>....");
  1453. System.exit(1);
  1454. }
  1455. GTKParser parser = new GTKParser();
  1456. try {
  1457. for (int i = 0; i < args.length; i++) {
  1458. parser.parseFile(new File(args[i]), args[i]);
  1459. }
  1460. } catch (IOException ioe) {
  1461. ioe.printStackTrace();
  1462. }
  1463. parser.printNamedStyles();
  1464. System.out.println();
  1465. parser.printSettings();
  1466. System.out.println();
  1467. parser.printAssignments();
  1468. }
  1469. // for testing
  1470. private void printNamedStyles() {
  1471. System.out.println("===== Named Styles =====");
  1472. StyleInfo[] infos = new StyleInfo[namedStyles.size()];
  1473. infos = (StyleInfo[])namedStyles.values().toArray(infos);
  1474. for (int i = 0; i < infos.length; i++) {
  1475. StyleInfo info = infos[i];
  1476. System.out.println("NAME: " + info.name);
  1477. GTKStyle style = info.toGTKStyle();
  1478. System.out.println(style == StyleInfo.EMPTY_STYLE ? "EMPTY_STYLE" : style.toString());
  1479. System.out.println("---------------------------");
  1480. }
  1481. }
  1482. // for testing
  1483. private void printSettings() {
  1484. System.out.println("===== GTK Settings =====");
  1485. Iterator iter = settings.entrySet().iterator();
  1486. while (iter.hasNext()) {
  1487. Map.Entry entry = (Map.Entry)iter.next();
  1488. System.out.println(entry.getKey() + "=" + entry.getValue());
  1489. }
  1490. }
  1491. // for testing
  1492. private void printAssignments() {
  1493. System.out.println("===== Assignments =====");
  1494. Assignment[] assigns = new Assignment[assignments.size()];
  1495. assigns = (Assignment[])assignments.toArray(assigns);
  1496. for (int i = 0; i < assigns.length; i++) {
  1497. System.out.println(assigns[i]);
  1498. }
  1499. }
  1500. }