001: /*
002: * Copyright 2006 Ethan Nicholas. All rights reserved.
003: * Use is subject to license terms.
004: */
005: package jaxx.runtime.swing;
006:
007: import java.awt.*;
008: import java.beans.*;
009: import java.util.*;
010: import java.util.List;
011: import javax.swing.*;
012: import javax.swing.event.*;
013: import javax.swing.tree.*;
014:
015: public class JAXXTree extends JTree {
016: private static final String SYNTHETIC = "<synthetic root node>";
017:
018: public class JAXXTreeModel implements TreeModel {
019: private Item root;
020: private List/*<TreeModelListener>*/listeners = new ArrayList/*<TreeModelListener>*/();
021:
022: public JAXXTreeModel(List/*<Item>*/items) {
023: if (items.size() == 1)
024: this .root = (Item) items.get(0);
025: else {
026: this .root = new Item(null, null, SYNTHETIC, false);
027: for (int i = 0; i < items.size(); i++)
028: root.addChild((Item) items.get(i));
029: }
030:
031: PropertyChangeListener listener = new PropertyChangeListener() {
032: public void propertyChange(PropertyChangeEvent e) {
033: if (e.getPropertyName().equals(
034: Item.SELECTED_PROPERTY)) {
035: Item item = (Item) e.getSource();
036: if (item.isSelected())
037: addSelectionPath(getTreePath(item));
038: else
039: removeSelectionPath(getTreePath(item));
040: } else {
041: Item item = (Item) e.getSource();
042: boolean root = item.getParent() == null;
043: TreePath path = !root ? getTreePath(item
044: .getParent()) : null;
045: fireTreeNodesChanged(new TreeModelEvent(
046: JAXXTreeModel.this , path,
047: !root ? new int[] { item.getParent()
048: .getChildren().indexOf(item) }
049: : null, new Object[] { item
050: .getValue() }));
051: }
052: }
053: };
054: addPropertyChangeListener(root, listener);
055: }
056:
057: private void addPropertyChangeListener(Item item,
058: PropertyChangeListener listener) {
059: item.addPropertyChangeListener(listener);
060: List/*<Item>*/children = item.getChildren();
061: for (int i = 0; i < children.size(); i++)
062: addPropertyChangeListener((Item) children.get(i),
063: listener);
064: }
065:
066: public void addTreeModelListener(TreeModelListener listener) {
067: listeners.add(listener);
068: }
069:
070: /* This is an inefficient implementation, but hand-coded tree structures are unlikely to contain
071: enough nodes for that to really matter. This could be sped up with caching. */
072: private Item findItem(Object value) {
073: return findItem(root, value);
074: }
075:
076: private Item findItem(Item node, Object value) {
077: if (node.getValue() == value)
078: return node;
079: else {
080: List/*<Item>*/children = node.getChildren();
081: for (int i = 0; i < children.size(); i++) {
082: Item result = findItem((Item) children.get(i),
083: value);
084: if (result != null)
085: return result;
086: }
087: return null;
088: }
089: }
090:
091: private TreePath getTreePath(Item node) {
092: List/*<Object>*/path = new ArrayList/*<Object>*/();
093: while (node != null) {
094: path.add(0, node.getValue());
095: node = node.getParent();
096: }
097: return new TreePath(path.toArray());
098: }
099:
100: public Object getChild(Object parent, int index) {
101: Item node = findItem(parent);
102: return ((Item) node.getChildren().get(index)).getValue();
103: }
104:
105: public int getChildCount(Object parent) {
106: Item node = findItem(parent);
107: return node.getChildren().size();
108: }
109:
110: public int getIndexOfChild(Object parent, Object child) {
111: Item node = findItem(parent);
112: List/*<Item>*/children = node.getChildren();
113: for (int i = 0; i < children.size(); i++)
114: if (((Item) children.get(i)).getValue() == child)
115: return i;
116: return -1;
117: }
118:
119: public Object getRoot() {
120: return root.getValue();
121: }
122:
123: public boolean isLeaf(Object node) {
124: Item item = findItem(node);
125: return item != null && item.getChildren().size() == 0;
126: }
127:
128: public void removeTreeModelListener(TreeModelListener listener) {
129: listeners.remove(listener);
130: }
131:
132: public void fireTreeNodesChanged(TreeModelEvent e) {
133: for (int i = 0; i < listeners.size(); i++)
134: ((TreeModelListener) listeners.get(i))
135: .treeNodesChanged(e);
136: }
137:
138: public void valueForPathChanged(TreePath path, Object newValue) {
139: }
140: };
141:
142: public JAXXTree() {
143: setCellRenderer(new DefaultTreeCellRenderer() {
144: public Component getTreeCellRendererComponent(JTree tree,
145: Object value, boolean sel, boolean expanded,
146: boolean leaf, int row, boolean hasFocus) {
147: TreeModel model = tree.getModel();
148: if (model instanceof JAXXTreeModel) {
149: Item item = ((JAXXTreeModel) model).findItem(value);
150: if (item != null) {
151: String label = item.getLabel();
152: if (label != null)
153: value = label;
154: }
155: }
156: return super .getTreeCellRendererComponent(tree, value,
157: sel, expanded, leaf, row, hasFocus);
158: }
159: });
160:
161: addTreeSelectionListener(new TreeSelectionListener() {
162: public void valueChanged(TreeSelectionEvent e) {
163: TreeModel model = getModel();
164: if (model instanceof JAXXTreeModel)
165: scan((JAXXTreeModel) model,
166: ((JAXXTreeModel) model).root);
167: }
168:
169: private void scan(JAXXTreeModel model, Item item) {
170: TreePath path = model.getTreePath(item);
171: if (item.isSelected() != isPathSelected(path))
172: item.setSelected(!item.isSelected());
173: List/*<Item>*/children = item.getChildren();
174: for (int i = 0; i < children.size(); i++)
175: scan(model, (Item) children.get(i));
176: }
177: });
178: }
179:
180: public void setItems(List/*<Item>*/items) {
181: JAXXTreeModel model = new JAXXTreeModel(items);
182: if (model.getRoot() != null)
183: setRootVisible(model.getRoot() != SYNTHETIC);
184: setModel(model);
185: }
186:
187: public Object getSelectionValue() {
188: TreePath selectionPath = getSelectionPath();
189: return selectionPath != null ? selectionPath
190: .getLastPathComponent() : null;
191: }
192: }
|