001: package prefuse.util.ui;
002:
003: import javax.swing.JFrame;
004: import javax.swing.JScrollPane;
005: import javax.swing.JTree;
006: import javax.swing.event.TreeModelEvent;
007: import javax.swing.event.TreeModelListener;
008: import javax.swing.tree.TreeModel;
009: import javax.swing.tree.TreePath;
010:
011: import prefuse.data.Graph;
012: import prefuse.data.Node;
013: import prefuse.data.Tree;
014: import prefuse.data.event.EventConstants;
015: import prefuse.data.event.GraphListener;
016: import prefuse.util.StringLib;
017: import prefuse.util.collections.CopyOnWriteArrayList;
018: import prefuse.visual.VisualTree;
019:
020: /**
021: * Swing component that displays a prefuse Tree instance in a Swing
022: * JTree component. Graph instances can also be displayed by first
023: * getting a Tree instance with the
024: * {@link prefuse.data.Graph#getSpanningTree()} method.
025: *
026: * @author <a href="http://jheer.org">jeffrey heer</a>
027: * @see javax.swing.JTree
028: */
029: public class JPrefuseTree extends JTree {
030:
031: private Tree m_tree;
032: private String m_field;
033:
034: /**
035: * Create a new JPrefuseTree.
036: * @param t the Tree to display
037: * @param labelField the data field used to privde labels
038: */
039: public JPrefuseTree(Tree t, String labelField) {
040: super ();
041: m_tree = t;
042: m_field = labelField;
043:
044: PrefuseTreeModel model = new PrefuseTreeModel();
045: super .setModel(model);
046: m_tree.addGraphModelListener(model);
047: }
048:
049: /**
050: * Return the backing Tree instance.
051: * @return the backing Tree
052: */
053: public Tree getTree() {
054: return m_tree;
055: }
056:
057: /**
058: * Returns a String label for Node instances by looking up the
059: * label data field specified in the constructor of this class.
060: * @see javax.swing.JTree#convertValueToText(java.lang.Object, boolean, boolean, boolean, int, boolean)
061: */
062: public String convertValueToText(Object value, boolean selected,
063: boolean expanded, boolean leaf, int row, boolean hasFocus) {
064: if (value == null)
065: return "";
066:
067: if (value instanceof Node) {
068: Object o = ((Node) value).get(m_field);
069: if (o.getClass().isArray()) {
070: return StringLib.getArrayString(o);
071: } else {
072: return o.toString();
073: }
074: } else {
075: return value.toString();
076: }
077: }
078:
079: // ------------------------------------------------------------------------
080:
081: /**
082: * TreeModel implementation that provides an adapter to a backing prefuse
083: * Tree instance.
084: */
085: public class PrefuseTreeModel implements TreeModel, GraphListener {
086:
087: private CopyOnWriteArrayList m_listeners = new CopyOnWriteArrayList();
088:
089: /**
090: * @see javax.swing.tree.TreeModel#getRoot()
091: */
092: public Object getRoot() {
093: return m_tree.getRoot();
094: }
095:
096: /**
097: * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int)
098: */
099: public Object getChild(Object node, int idx) {
100: Node c = ((Node) node).getChild(idx);
101: if (c == null) {
102: throw new IllegalArgumentException(
103: "Index out of range: " + idx);
104: }
105: return c;
106: }
107:
108: /**
109: * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object)
110: */
111: public int getChildCount(Object node) {
112: return ((Node) node).getChildCount();
113: }
114:
115: /**
116: * @see javax.swing.tree.TreeModel#isLeaf(java.lang.Object)
117: */
118: public boolean isLeaf(Object node) {
119: return ((Node) node).getChildCount() == 0;
120: }
121:
122: /**
123: * @see javax.swing.tree.TreeModel#valueForPathChanged(javax.swing.tree.TreePath, java.lang.Object)
124: */
125: public void valueForPathChanged(TreePath path, Object newValue) {
126: // do nothing?
127: }
128:
129: /**
130: * @see javax.swing.tree.TreeModel#getIndexOfChild(java.lang.Object, java.lang.Object)
131: */
132: public int getIndexOfChild(Object parent, Object child) {
133: return ((Node) parent).getChildIndex(((Node) child));
134: }
135:
136: /**
137: * @see javax.swing.tree.TreeModel#addTreeModelListener(javax.swing.event.TreeModelListener)
138: */
139: public void addTreeModelListener(TreeModelListener tml) {
140: if (!m_listeners.contains(tml))
141: m_listeners.add(tml);
142: }
143:
144: /**
145: * @see javax.swing.tree.TreeModel#removeTreeModelListener(javax.swing.event.TreeModelListener)
146: */
147: public void removeTreeModelListener(TreeModelListener tml) {
148: m_listeners.remove(tml);
149: }
150:
151: /**
152: * @see prefuse.data.event.GraphListener#graphChanged(prefuse.data.Graph, java.lang.String, int, int, int, int)
153: */
154: public void graphChanged(Graph g, String table, int start,
155: int end, int col, int type) {
156: if (m_listeners == null || m_listeners.size() == 0)
157: return; // nothing to do
158:
159: boolean nodeTable = table.equals(Graph.NODES);
160: if (type != EventConstants.UPDATE && nodeTable)
161: return;
162: else if (type == EventConstants.UPDATE && !nodeTable)
163: return;
164:
165: for (int row = start; row <= end; ++row) {
166: // create the event
167: Node n = null;
168: if (nodeTable)
169: n = g.getNode(row);
170: else
171: n = g.getEdge(row).getTargetNode();
172: Object[] path = new Object[n.getDepth() + 1];
173: for (int i = path.length; --i >= 0; n = n.getParent()) {
174: path[i] = n;
175: }
176: TreeModelEvent e = new TreeModelEvent(this , path);
177:
178: // fire it
179: Object[] lstnrs = m_listeners.getArray();
180: for (int i = 0; i < lstnrs.length; ++i) {
181: TreeModelListener tml = (TreeModelListener) lstnrs[i];
182:
183: switch (type) {
184: case EventConstants.INSERT:
185: tml.treeNodesInserted(e);
186: break;
187: case EventConstants.DELETE:
188: tml.treeNodesRemoved(e);
189: break;
190: case EventConstants.UPDATE:
191: tml.treeNodesChanged(e);
192: }
193: }
194: }
195: }
196:
197: } // end of inner class PrefuseTreeModel
198:
199: // ------------------------------------------------------------------------
200:
201: /**
202: * Create a new window displaying the contents of the input Tree as
203: * a Swing JTree.
204: * @param t the Tree instance to display
205: * @param labelField the data field to use for labeling nodes
206: * @return a reference to the JFrame holding the tree view
207: */
208: public static JFrame showTreeWindow(Tree t, String labelField) {
209: JPrefuseTree tree = new JPrefuseTree(t, labelField);
210: String title = t.toString();
211: if (t instanceof VisualTree) {
212: title = ((VisualTree) t).getGroup() + " " + title;
213: }
214: JFrame frame = new JFrame(title);
215: frame.getContentPane().add(new JScrollPane(tree));
216: frame.pack();
217: frame.setVisible(true);
218: return frame;
219: }
220:
221: } // end of class JPrefuseTree
|