PageRenderTime 143ms CodeModel.GetById 117ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

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

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