PageRenderTime 66ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/bundles/plugins-trunk/XInsert/src/XTree.java

#
Java | 545 lines | 426 code | 45 blank | 74 comment | 114 complexity | d07758823d52671cb98cf4875d15d976 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * 20:47:04 04/06/00
  3. *
  4. * XTree.java - A tree used for XInsert system
  5. * Original Version Copyright (C) 1999 Romain Guy - guy.romain@bigfoot.com
  6. * Potion copyright (C) 2000 Richard Lowe
  7. * Copyright (C) 2000 Dominic Stolerman - dominic@sspd.org.uk
  8. * www.chez.com/powerteam/jext
  9. * Changes (c) 2005 Martin Raspe - raspe@biblhertz.it
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24. */
  25. import java.io.*;
  26. import java.util.*;
  27. import java.awt.*;
  28. import java.awt.event.*;
  29. import javax.swing.*;
  30. import javax.swing.tree.*;
  31. import javax.swing.event.*;
  32. import org.gjt.sp.jedit.*;
  33. import org.gjt.sp.util.Log;
  34. import org.gjt.sp.jedit.textarea.*;
  35. public class XTree extends JPanel {
  36. // the following was replaced by a private class "TreeListener"
  37. // implements TreeSelectionListener, ActionListener
  38. private XTreeTree tree;
  39. private HashMap map;
  40. private TreeListener treeListener;
  41. private ActionListener escapeListener = new ActionListener() {
  42. public void actionPerformed(ActionEvent e) {
  43. view.getTextArea().requestFocus();
  44. view.toFront();
  45. }
  46. };
  47. private View view;
  48. private static Vector inserts;
  49. private DefaultTreeModel treeModel;
  50. private JButton expand, collapse, reload;
  51. private JCheckBox carriageReturn, executeScript;
  52. private boolean treeJustCollapsed = false;
  53. // nested submenus
  54. private int rootIndex;
  55. private XTreeNode root;
  56. private Stack menuStack = null;
  57. private XTreeObject xtreeObj = null;
  58. //private int clicks;
  59. private int lineNo;
  60. // get access to tree widget (needed for XInsertActions)
  61. public XTreeTree getTree() {
  62. return tree;
  63. }
  64. public void addMenu(String nodeName) {
  65. xtreeObj = new XTreeObject(new XTreeNode(nodeName), 0);
  66. if (menuStack.empty()) {
  67. treeModel.insertNodeInto(xtreeObj.getXTreeNode(), root, rootIndex);
  68. rootIndex++;
  69. }
  70. else {
  71. //Log.log(Log.DEBUG, this, "Adding menu on menu stack: " + nodeName);
  72. XTreeObject obj = (XTreeObject) menuStack.peek();
  73. treeModel.insertNodeInto(xtreeObj.getXTreeNode(), obj.getXTreeNode(), obj.getIndex());
  74. obj.incrementIndex();
  75. }
  76. menuStack.push(xtreeObj);
  77. }
  78. public void closeMenu() {
  79. try {
  80. xtreeObj = (XTreeObject) menuStack.pop();
  81. }
  82. catch (Exception e) {
  83. xtreeObj = null;
  84. }
  85. }
  86. public void addVariable(String key, String value) {
  87. XTreeObject obj = (XTreeObject)menuStack.peek();
  88. XTreeNode node;
  89. if (obj != null)
  90. node = obj.getXTreeNode();
  91. else
  92. node = root;
  93. node.addVariable(key, value);
  94. }
  95. public void addInsert(String nodeName, String content, int script) {
  96. inserts.addElement(new XTreeItem(content, script));
  97. XTreeNode node = new XTreeNode(nodeName, inserts.size());
  98. if (xtreeObj == null) {
  99. treeModel.insertNodeInto(node, root, rootIndex);
  100. ++rootIndex;
  101. }
  102. else {
  103. XTreeObject obj = (XTreeObject) menuStack.peek();
  104. treeModel.insertNodeInto(node, obj.getXTreeNode(), obj.getIndex());
  105. obj.incrementIndex();
  106. }
  107. }
  108. public XTree(View view) {
  109. super();
  110. this.view = view;
  111. setLayout(new BorderLayout());
  112. /* Use default icons
  113. UIManager.put("Tree.expandedIcon", Utilities.getIcon("images/down_arrow.gif"));
  114. UIManager.put("Tree.collapsedIcon", Utilities.getIcon("images/right_arrow.gif"));
  115. UIManager.put("Tree.leftChildIndent", new Integer(5));
  116. UIManager.put("Tree.rightChildIndent", new Integer(7));
  117. */
  118. root = new XTreeNode("XInsert");
  119. treeModel = new DefaultTreeModel(root);
  120. tree = new XTreeTree(treeModel);
  121. ToolTipManager.sharedInstance().registerComponent(tree);
  122. tree.putClientProperty("JTree.lineStyle", "Angled");
  123. // tree.addTreeSelectionListener(this);
  124. // add a special tree listener
  125. treeListener = new TreeListener();
  126. tree.addMouseListener(treeListener);
  127. // respond to keyboard events
  128. tree.registerKeyboardAction(
  129. treeListener,
  130. KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
  131. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
  132. );
  133. tree.registerKeyboardAction(
  134. treeListener,
  135. KeyStroke.getKeyStroke(' '),
  136. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
  137. );
  138. tree.registerKeyboardAction(
  139. escapeListener,
  140. KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
  141. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
  142. );
  143. tree.setCellRenderer(new XTreeCellRenderer());
  144. init();
  145. JPanel pane = new JPanel(new BorderLayout());
  146. //No mnemonics as they are confusing/ do not work when docked.
  147. pane.add(collapse = new JButton(Utilities.getIcon("images/button_collapse.gif")),BorderLayout.WEST);
  148. collapse.setToolTipText(jEdit.getProperty("xtree.collapse.button"));
  149. //collapse.setMnemonic(jEdit.getProperty("xtree.collapse.mnemonic").charAt(0));
  150. collapse.addActionListener(treeListener);
  151. pane.add(reload = new JButton(Utilities.getIcon("images/menu_reload.gif")),BorderLayout.CENTER);
  152. reload.setToolTipText(jEdit.getProperty("xtree.reload.button"));
  153. //reload.setMnemonic(jEdit.getProperty("xtree.reload.mnemonic").charAt(0));
  154. reload.addActionListener(treeListener);
  155. pane.add(expand = new JButton(Utilities.getIcon("images/button_expand.gif")),BorderLayout.EAST);
  156. expand.setToolTipText(jEdit.getProperty("xtree.expand.button"));
  157. //expand.setMnemonic(jEdit.getProperty("xtree.expand.mnemonic").charAt(0));
  158. expand.addActionListener(treeListener);
  159. add(pane, BorderLayout.NORTH);
  160. add(new JScrollPane(tree), BorderLayout.CENTER);
  161. JPanel optionPane = new JPanel(new BorderLayout());
  162. optionPane.add(carriageReturn = new JCheckBox(jEdit.getProperty("xtree.carriage.label")), BorderLayout.NORTH);
  163. carriageReturn.setSelected(jEdit.getBooleanProperty("xtree.carriage", false));
  164. carriageReturn.addActionListener(treeListener);
  165. optionPane.add(executeScript = new JCheckBox(jEdit.getProperty("xtree.execute.label")), BorderLayout.CENTER);
  166. executeScript.setSelected(jEdit.getBooleanProperty("xtree.execute", true));
  167. if (jEdit.getProperty("xtree.execute") == null)
  168. executeScript.setSelected(true);
  169. executeScript.addActionListener(treeListener);
  170. add(optionPane, BorderLayout.SOUTH);
  171. }
  172. private void init() {
  173. inserts = new Vector(200);
  174. menuStack = new Stack();
  175. rootIndex = 0;
  176. if(jEdit.getBooleanProperty("xinsert.display.all", true)) {
  177. int i =0;
  178. String current;
  179. while((current = jEdit.getProperty("xinsert.inserts." + i)) != null) {
  180. loadInternalInsert(current);
  181. i++;
  182. }
  183. }
  184. //Macros menu
  185. if(jEdit.getBooleanProperty("xinsert.display.macros", true)) {
  186. addMenu("Macros");
  187. Vector vec = Macros.getMacroHierarchy();
  188. Iterator iter = vec.iterator();
  189. while(iter.hasNext()) {
  190. Object o = iter.next();
  191. if(o instanceof Vector) {
  192. loadMacroVector((Vector)o);
  193. }
  194. else if(o instanceof String) {
  195. loadNamedMacro(Macros.getMacro((String)o));
  196. }
  197. else {
  198. loadNamedMacro((Macros.Macro)o);
  199. }
  200. }
  201. closeMenu();
  202. }
  203. build();
  204. fillMap();
  205. tree.expandRow(0);
  206. tree.setRootVisible(false);
  207. tree.setShowsRootHandles(true);
  208. }
  209. private void loadMacroVector(Vector v) {
  210. Iterator iter = v.iterator();
  211. addMenu(iter.next().toString());
  212. while(iter.hasNext()) {
  213. Object o = iter.next();
  214. if(o instanceof Vector) {
  215. loadMacroVector((Vector)o);
  216. }
  217. else {
  218. loadNamedMacro(Macros.getMacro(o.toString())); //NDT fix...
  219. }
  220. }
  221. closeMenu();
  222. }
  223. private void loadNamedMacro(Macros.Macro macro) {
  224. addInsert(macro.getLabel(), macro.getName(), XTreeItem.NAMED_MACRO_TYPE);
  225. }
  226. private void loadInternalInsert(String fileName) {
  227. try {
  228. if(jEdit.getBooleanProperty("xinsert.display." + fileName, true)) {
  229. if(!XInsertReader.read(
  230. this,
  231. XTree.class.getResourceAsStream("xml/" + fileName + ".insert.xml"),
  232. "xml/" + fileName + ".insert.xml"))
  233. Log.log(Log.ERROR,this,("Resource not found: " + fileName));
  234. else
  235. Log.log(Log.NOTICE,this,("Resource loaded: " + fileName));
  236. }
  237. }
  238. catch(NullPointerException e) {
  239. Log.log(Log.ERROR,this,("Resource not found: " + fileName));
  240. }
  241. }
  242. private void build() {
  243. String dir = jEdit.getProperty("xinsert.inserts-directory");
  244. if(!dir.endsWith(File.separator))
  245. dir = dir + File.separator;
  246. File f = new File(dir);
  247. if (!f.exists())
  248. f.mkdirs();
  249. String inserts[] = Utilities.getWildCardMatches(dir, "*.insert.xml", false);
  250. if (inserts == null)
  251. return;
  252. try {
  253. String fileName;
  254. for (int i = 0; i < inserts.length; i++) {
  255. fileName = dir + inserts[i];
  256. if (XInsertReader.read(this, new FileInputStream(fileName), fileName)) {
  257. String[] args = { inserts[i] };
  258. System.out.println(jEdit.getProperty("xtree.loaded", args));
  259. }
  260. }
  261. }
  262. catch (FileNotFoundException fnfe) {}
  263. }
  264. private void reload() {
  265. root.removeAllChildren();
  266. init();
  267. treeModel.reload();
  268. tree.repaint();
  269. }
  270. public void reload(DefaultTreeModel model) {
  271. tree.setModel(model);
  272. }
  273. // main action, called from treelistener.actionPerformed
  274. // insert selected item, shift focus to text area,
  275. // stay selected (for future keyboard inserts)
  276. public void treeAction() {
  277. if (tree.isSelectionEmpty()) return;
  278. XTreeNode node = (XTreeNode) tree.getSelectionPath().getLastPathComponent();
  279. if (node.getIndex() != -1) insert(node);
  280. view.getTextArea().requestFocus();
  281. view.toFront();
  282. // source.clearSelection();
  283. }
  284. private void insert(XTreeNode node) {
  285. if (!view.getTextArea().isEditable()) {
  286. view.getToolkit().beep();
  287. return;
  288. }
  289. XTreeItem item = (XTreeItem) inserts.elementAt(node.getIndex() - 1);
  290. String data = item.getContent();
  291. int type = item.getType();
  292. if(type == XTreeItem.TEXT_TYPE || !executeScript.isSelected())
  293. XScripter.insertText(view, data, node);
  294. else if(type == XTreeItem.MACRO_TYPE) {
  295. Log.log(Log.DEBUG, this, "Running Macro...");
  296. XScripter.runMacro(view, (String) node.getUserObject(), data);
  297. return;
  298. }
  299. else if(type == XTreeItem.XINSERT_SCRIPT_TYPE) {
  300. Log.log(Log.DEBUG, this, "Running XInsert Script ...");
  301. XScripter.runXInsertScript(view, data, node);
  302. }
  303. else if(type == XTreeItem.NAMED_MACRO_TYPE) {
  304. Log.log(Log.DEBUG, this, "Running Named Macro ...");
  305. XScripter.runNamedMacro(view, (String) node.getUserObject(), data);
  306. return;
  307. }
  308. else if(type == XTreeItem.ACTION_TYPE) {
  309. Log.log(Log.DEBUG, this, "Invoking Action " + data + " ...");
  310. XScripter.invokeAction(view, (String) node.getUserObject(), data);
  311. return;
  312. }
  313. else if(type == XTreeItem.REFERENCE_TYPE) {
  314. // new: lookup a referenced item by path [hertzhaft]
  315. Log.log(Log.DEBUG, this, "Resolving XInsert reference " + data + " ...");
  316. XTreeNode ref = getXTreeNodeByPath(data);
  317. if (ref == null) {
  318. Log.log(Log.DEBUG, this, "Could not resolve path: " + data);
  319. return;
  320. }
  321. // Log.log(Log.DEBUG, this, ref.toString());
  322. XTreeItem refitem = (XTreeItem) inserts.elementAt(ref.getIndex()-1);
  323. // Log.log(Log.DEBUG, this, refitem.toString());
  324. if (refitem.getType() == XTreeItem.REFERENCE_TYPE) {
  325. Log.log(Log.DEBUG, this, "Chained references are not allowed: " + data);
  326. return;
  327. }
  328. insert(ref);
  329. }
  330. else {
  331. // non of the known insert types. Insert the node content
  332. XScripter.insertText(view, data, node);
  333. Log.log(Log.DEBUG, this, node + ": Unknown insert type");
  334. }
  335. }
  336. // construct a reference lookup map for nodes by stringified tree paths
  337. public void fillMap() {
  338. map = new HashMap();
  339. fillItem("", root);
  340. }
  341. // visit all nodes and append their paths
  342. public void fillItem(String name, XTreeNode node) {
  343. String nodename = name + "/" + (String) node.getUserObject();
  344. // Log.log(Log.DEBUG, this, nodename + node.toString());
  345. map.put(nodename, node);
  346. Enumeration list = node.children();
  347. while (list.hasMoreElements())
  348. fillItem(nodename, (XTreeNode) list.nextElement());
  349. }
  350. // get a node by its path, needed for reference lookup
  351. public XTreeNode getXTreeNodeByPath(String path) {
  352. return (XTreeNode) map.get("/XInsert" + path);
  353. }
  354. private static final ImageIcon plainLeaf = Utilities.getIcon("images/tree_leaf.gif");
  355. private static final ImageIcon scriptLeaf = Utilities.getIcon("images/tree_leaf_script_x.gif");
  356. private static final ImageIcon macroLeaf = Utilities.getIcon("images/tree_leaf_macro.gif");
  357. private static final ImageIcon namedmacroLeaf = Utilities.getIcon("images/tree_leaf_namedmacro.gif");
  358. private static final ImageIcon referenceLeaf = Utilities.getIcon("images/tree_leaf_reference.gif");
  359. private static final ImageIcon actionLeaf = Utilities.getIcon("images/tree_leaf_action.gif");
  360. private static final ImageIcon errorLeaf = Utilities.getIcon("images/tree_leaf_error.gif");
  361. class XTreeCellRenderer extends DefaultTreeCellRenderer {
  362. XTreeCellRenderer() {
  363. super();
  364. /*openIcon = Utilities.getIcon("images/tree_open.gif");
  365. closedIcon = Utilities.getIcon("images/tree_close.gif");*/
  366. // commented out (MR): why change default behaviour here?
  367. // textSelectionColor = Color.red;
  368. // borderSelectionColor = tree.getBackground();
  369. // backgroundSelectionColor = tree.getBackground();
  370. }
  371. public Component getTreeCellRendererComponent(JTree source, Object value, boolean sel,
  372. boolean expanded, boolean leaf, int row,
  373. boolean hasFocus) {
  374. if (leaf) {
  375. TreePath path = source.getPathForRow(row);
  376. if (path != null) {
  377. XTreeNode node = (XTreeNode) path.getLastPathComponent();
  378. int index = node.getIndex();
  379. if (index != -1) {
  380. int type = ((XTreeItem) inserts.elementAt(index - 1)).getType();
  381. switch (type) {
  382. case XTreeItem.TEXT_TYPE:
  383. leafIcon = plainLeaf;
  384. break;
  385. case XTreeItem.XINSERT_SCRIPT_TYPE:
  386. leafIcon = scriptLeaf;
  387. break;
  388. case XTreeItem.MACRO_TYPE:
  389. leafIcon = macroLeaf;
  390. break;
  391. case XTreeItem.NAMED_MACRO_TYPE:
  392. leafIcon = namedmacroLeaf;
  393. break;
  394. case XTreeItem.ACTION_TYPE:
  395. leafIcon = actionLeaf;
  396. break;
  397. case XTreeItem.REFERENCE_TYPE:
  398. leafIcon = referenceLeaf;
  399. break;
  400. default:
  401. leafIcon = errorLeaf;
  402. }
  403. }
  404. }
  405. }
  406. return super.getTreeCellRendererComponent(source, value, sel, expanded, leaf, row, hasFocus);
  407. }
  408. }
  409. private class XTreeTree extends JTree {
  410. public XTreeTree(TreeModel model) {
  411. super(model);
  412. }
  413. public String getToolTipText(MouseEvent e) {
  414. if(e == null)
  415. return null;
  416. TreePath tPath = tree.getPathForLocation(e.getX(), e.getY());
  417. if(tPath != null) {
  418. XTreeNode node = (XTreeNode) tPath.getLastPathComponent();
  419. if(!node.isLeaf())
  420. return null;
  421. try {
  422. XTreeItem item = (XTreeItem) inserts.elementAt(node.getIndex()-1);
  423. int type = item.getType();
  424. String content = item.getContent();
  425. if(type == XTreeItem.TEXT_TYPE)
  426. return (content.length() > 30) ? content.substring(0, 30) + " ..." : content;
  427. else if(type == XTreeItem.MACRO_TYPE)
  428. return "Macro";
  429. else if(type == XTreeItem.XINSERT_SCRIPT_TYPE)
  430. return "Script";
  431. else if(type == XTreeItem.ACTION_TYPE)
  432. return "Action: " + content;
  433. else if(type == XTreeItem.NAMED_MACRO_TYPE)
  434. return "Named Macro";
  435. else if(type == XTreeItem.REFERENCE_TYPE)
  436. return "Ref => " + content;
  437. else
  438. return "Error: " + content;
  439. }
  440. catch( ArrayIndexOutOfBoundsException ex) {
  441. // Log.log(Log.ERROR, XTree.class, "getTreeToolTip() throws "
  442. // + ex.getClass().getName() + " exception.");
  443. // Log.log(Log.ERROR, XTree.class, "TreePath is " + tPath.toString());
  444. // Log.log(Log.ERROR, XTree.class, "TreeNode object is " +
  445. // node.toString());
  446. return null;
  447. }
  448. }
  449. else
  450. return null;
  451. }
  452. }
  453. /**
  454. * Private class that acts as a listener to the tree of clips.
  455. * Inserts a clip either on double-click or when space is hit.
  456. */
  457. private class TreeListener
  458. extends MouseAdapter
  459. implements ActionListener {
  460. public void mouseClicked(MouseEvent e) {
  461. // changed (MR): respond only to double clicks
  462. // todo: right click popup to add/change entries
  463. if ((e.getClickCount() == 2)
  464. && ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)) {
  465. Log.log(Log.DEBUG, XTree.this, "Mouse Clicked");
  466. int selRow = tree.getRowForLocation(e.getX(), e.getY());
  467. if(selRow == -1) return;
  468. TreePath tPath = tree.getPathForLocation(e.getX(), e.getY());
  469. if(tPath == null) return;
  470. XTreeNode node = (XTreeNode) tPath.getLastPathComponent();
  471. if(!node.isLeaf()) return;
  472. if(node.getChildCount() != 0) return;
  473. if(node.getIndex() != -1)
  474. insert(node);
  475. view.requestFocus();
  476. view.toFront();
  477. }
  478. }
  479. public void actionPerformed(ActionEvent evt) {
  480. Object o = evt.getSource();
  481. if (o == tree)
  482. treeAction();
  483. else if (o == expand) {
  484. for (int i = 0; i < tree.getRowCount(); i++)
  485. tree.expandRow(i);
  486. }
  487. else if (o == collapse) {
  488. for (int i = tree.getRowCount(); i >= 0; i--)
  489. tree.collapseRow(i);
  490. }
  491. else if (o == reload)
  492. reload();
  493. else if (o == carriageReturn)
  494. jEdit.setBooleanProperty("xtree.carriage", carriageReturn.isSelected());
  495. else if (o == executeScript)
  496. jEdit.setBooleanProperty("xtree.execute", executeScript.isSelected());
  497. }
  498. }
  499. }
  500. // End of XTree.java