All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.rsta.ac.xml.tree.XmlOutlineTree Maven / Gradle / Ivy

/*
 * 04/07/2012
 *
 * Copyright (C) 2012 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is distributed under a modified BSD license.  See the included
 * RSTALanguageSupport.License.txt file for details.
 */
package org.fife.rsta.ac.xml.tree;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BorderFactory;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import org.fife.rsta.ac.AbstractSourceTree;
import org.fife.rsta.ac.LanguageSupport;
import org.fife.rsta.ac.LanguageSupportFactory;
import org.fife.rsta.ac.xml.XmlLanguageSupport;
import org.fife.rsta.ac.xml.XmlParser;
import org.fife.ui.rsyntaxtextarea.DocumentRange;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;


/**
 * A tree view showing the outline of XML, similar to the "Outline" view in
 * Eclipse.  It also uses Eclipse's icons, just like the rest of this code
 * completion library.

* * You can get this tree automatically updating in response to edits in an * RSyntaxTextArea with {@link XmlLanguageSupport} installed by * calling {@link #listenTo(RSyntaxTextArea)}. Note that an instance of this * class can only listen to a single editor at a time, so if your application * contains multiple instances of RSyntaxTextArea, you'll either need a separate * XmlOutlineTree for each one, or call uninstall() * and listenTo(RSyntaxTextArea) each time a new RSTA receives * focus. * * @author Robert Futrell * @version 1.0 */ public class XmlOutlineTree extends AbstractSourceTree { private XmlParser parser; private XmlEditorListener listener; private DefaultTreeModel model; private XmlTreeCellRenderer xmlTreeCellRenderer; /** * Constructor. The tree created will not have its elements sorted * alphabetically. */ public XmlOutlineTree() { this(false); } /** * Constructor. * * @param sorted Whether the tree should sort its elements alphabetically. * Note that outline trees will likely group nodes by type before * sorting (i.e. methods will be sorted in one group, fields in * another group, etc.). */ public XmlOutlineTree(boolean sorted) { setSorted(sorted); setBorder(BorderFactory.createEmptyBorder(0,8,0,8)); setRootVisible(false); xmlTreeCellRenderer = new XmlTreeCellRenderer(); setCellRenderer(xmlTreeCellRenderer); model = new DefaultTreeModel(new XmlTreeNode("Nothing")); setModel(model); listener = new XmlEditorListener(); addTreeSelectionListener(listener); } /** * Refreshes listeners on the text area when its syntax style changes. */ private void checkForXmlParsing() { // Remove possible listener on old Java parser (in case they're just // changing syntax style AWAY from Java) if (parser!=null) { parser.removePropertyChangeListener(XmlParser.PROPERTY_AST, listener); parser = null; } // Get the Java language support (shared by all RSTA instances editing // Java that were registered with the LanguageSupportFactory). LanguageSupportFactory lsf = LanguageSupportFactory.get(); LanguageSupport support = lsf.getSupportFor(SyntaxConstants. SYNTAX_STYLE_XML); XmlLanguageSupport xls = (XmlLanguageSupport)support; // Listen for re-parsing of the editor, and update the tree accordingly parser = xls.getParser(textArea); if (parser!=null) { // Should always be true parser.addPropertyChangeListener(XmlParser.PROPERTY_AST, listener); // Populate with any already-existing AST. XmlTreeNode root = parser.getAst(); update(root); } else { update((XmlTreeNode)null); // Clear the tree } } //static int expandCount; /** * {@inheritDoc} */ @Override public void expandInitialNodes() { //long start = System.currentTimeMillis(); //expandCount = 0; fastExpandAll(new TreePath(getModel().getRoot()), true); //long end2 = System.currentTimeMillis(); //System.out.println("Expand all all: " + ((end2-start)/1000f) + " seconds (" + expandCount + ")"); //System.out.println("--- " + getRowCount()); } private void gotoElementAtPath(TreePath path) { Object node = path.getLastPathComponent(); if (node instanceof XmlTreeNode) { XmlTreeNode xtn = (XmlTreeNode)node; DocumentRange range = new DocumentRange(xtn.getStartOffset(), xtn.getEndOffset()); RSyntaxUtilities.selectAndPossiblyCenter(textArea, range, true); } } /** * {@inheritDoc} */ @Override public boolean gotoSelectedElement() { TreePath path = getLeadSelectionPath();//e.getNewLeadSelectionPath(); if (path != null) { gotoElementAtPath(path); return true; } return false; } /** * {@inheritDoc} */ @Override public void listenTo(RSyntaxTextArea textArea) { if (this.textArea!=null) { uninstall(); } // Nothing new to listen to if (textArea==null) { return; } // Listen for future language changes in the text editor this.textArea = textArea; textArea.addPropertyChangeListener( RSyntaxTextArea.SYNTAX_STYLE_PROPERTY, listener); checkForXmlParsing(); } /** * {@inheritDoc} */ @Override public void uninstall() { if (parser!=null) { parser.removePropertyChangeListener(XmlParser.PROPERTY_AST, listener); parser = null; } if (textArea!=null) { textArea.removePropertyChangeListener( RSyntaxTextArea.SYNTAX_STYLE_PROPERTY, listener); textArea = null; } } private void update(XmlTreeNode root) { if (root!=null) { root = (XmlTreeNode)root.cloneWithChildren(); } model.setRoot(root); if (root!=null) { root.setSorted(isSorted()); } refresh(); } /** * Overridden to update the cell renderer */ @Override public void updateUI() { super.updateUI(); xmlTreeCellRenderer = new XmlTreeCellRenderer(); setCellRenderer(xmlTreeCellRenderer); // So it picks up new LAF's properties } /** * Listens for events this tree is interested in (events in the associated * editor, for example), as well as events in this tree. */ private class XmlEditorListener implements PropertyChangeListener, TreeSelectionListener { /** * Called whenever the text area's syntax style changes, as well as * when it is re-parsed. */ @Override public void propertyChange(PropertyChangeEvent e) { String name = e.getPropertyName(); // If the text area is changing the syntax style it is editing if (RSyntaxTextArea.SYNTAX_STYLE_PROPERTY.equals(name)) { checkForXmlParsing(); } else if (XmlParser.PROPERTY_AST.equals(name)) { XmlTreeNode root = (XmlTreeNode)e.getNewValue(); update(root); } } /** * Selects the corresponding element in the text editor when a user * clicks on a node in this tree. */ @Override public void valueChanged(TreeSelectionEvent e) { if (getGotoSelectedElementOnClick()) { //gotoSelectedElement(); TreePath newPath = e.getNewLeadSelectionPath(); if (newPath!=null) { gotoElementAtPath(newPath); } } } } /** * Whenever the caret moves in the listened-to RSyntaxTextArea, this * class ensures that the XML element containing the caret position is * focused in the tree view after a small delay. */ /* * TODO: Make me work for any LanguageSupport (don't synchronize if waiting on * a pending parse) and pull me out and make me available for all languages. private class Synchronizer implements CaretListener, ActionListener { private Timer timer; private int dot; public Synchronizer() { timer = new Timer(650, this); timer.setRepeats(false); } public void actionPerformed(ActionEvent e) { recursivelyCheck(root); //System.out.println("Here"); } public void caretUpdate(CaretEvent e) { this.dot = e.getDot(); timer.restart(); } private boolean recursivelyCheck(XmlTreeNode node) { if (node.containsOffset(dot)) { for (int i=0; i





© 2015 - 2024 Weber Informatics LLC | Privacy Policy