1
28 package demo.view.graphexplorer;
29
30 import y.view.Graph2D;
31 import y.view.hierarchy.DefaultNodeChangePropagator;
32 import y.view.hierarchy.HierarchyJTree;
33 import y.view.hierarchy.HierarchyManager;
34 import y.view.hierarchy.HierarchyTreeModel;
35 import y.view.hierarchy.HierarchyTreeTransferHandler;
36
37 import java.awt.BorderLayout;
38 import java.awt.Color;
39 import java.awt.Dimension;
40 import java.awt.event.KeyAdapter;
41 import java.awt.event.KeyEvent;
42 import javax.swing.JLabel;
43 import javax.swing.JPanel;
44 import javax.swing.JScrollPane;
45 import javax.swing.JTextField;
46 import javax.swing.JTree;
47 import javax.swing.border.EmptyBorder;
48 import javax.swing.event.DocumentEvent;
49 import javax.swing.event.DocumentListener;
50 import javax.swing.tree.TreeModel;
51 import javax.swing.tree.TreePath;
52
53
56 class SearchableTreeViewPanel extends JPanel {
57 private JTree jTree;
58 private TreePath lastMatch;
59
60 SearchableTreeViewPanel( final Graph2D model ) {
61 setLayout(new BorderLayout());
62 jTree = configureHierarchyJTree(model);
63 jTree.setExpandsSelectedPaths(true);
64 jTree.setDragEnabled(false);
65
66 final JScrollPane scrollPane = new JScrollPane(jTree);
67 scrollPane.setPreferredSize(new Dimension(200, 0));
68 scrollPane.setMinimumSize(new Dimension(200, 0));
69 add(addSearchPanel(jTree), BorderLayout.NORTH);
70 add(scrollPane, BorderLayout.CENTER);
71 }
72
73 JTree getTree() {
74 return jTree;
75 }
76
77 private JPanel addSearchPanel(final JTree currentTree) {
78 final JPanel searchPanel = new JPanel(new BorderLayout(5, 5));
79 searchPanel.setBorder(new EmptyBorder(2, 5, 2, 2));
80 searchPanel.add(new JLabel("Search"), BorderLayout.WEST);
81 final JTextField searchField = new JTextField();
82
83 searchField.getDocument().addDocumentListener(new DocumentListener() {
85 public void insertUpdate(DocumentEvent e) {
86 updateSelection(searchField, currentTree);
87 }
88
89 public void removeUpdate(DocumentEvent e) {
90 updateSelection(searchField, currentTree);
91 }
92
93 public void changedUpdate(DocumentEvent e) {
94 updateSelection(searchField, currentTree);
95 }
96 });
97
98 searchField.addKeyListener(new KeyAdapter() {
101 public void keyPressed(KeyEvent e) {
102 int keyCode = e.getKeyCode();
103 if (keyCode == KeyEvent.VK_ESCAPE) {
104 clearSearchField(searchField);
106
107 e.consume();
110 } else if (keyCode == KeyEvent.VK_ENTER) {
111 String searchString = searchField.getText();
113 if (currentTree != null && currentTree.getModel() != null && searchString != null && searchString.length() > 0) {
114 TreePath match = findNextMatch(searchString, new TreePath(currentTree.getModel().getRoot()), currentTree);
115 if (match != null) {
116 selectMatch(match, currentTree);
117 } else {
118 lastMatch = null;
119 TreePath wrappedMatch = findNextMatch(searchString, new TreePath(currentTree.getModel().getRoot()),
120 currentTree);
121 if (wrappedMatch != null) {
122 selectMatch(wrappedMatch, currentTree);
123 } else {
124 noMatchExists(searchField);
125 }
126 }
127 }
128 }
129 }
130 });
131 searchPanel.add(searchField, BorderLayout.CENTER);
132 return searchPanel;
133 }
134
135 private void selectMatch(final TreePath match, final JTree actualTree) {
136 actualTree.setSelectionPath(match);
137 actualTree.scrollPathToVisible(match);
138 lastMatch = match;
139 }
140
141 private void clearSearchField(final JTextField searchField) {
142 searchField.setText("");
143 searchField.setBackground(Color.WHITE);
144 searchField.setForeground(Color.BLACK);
145 lastMatch = null;
146 }
147
148 private void noMatchExists(final JTextField searchField) {
149 searchField.setBackground(Color.RED);
150 searchField.setForeground(Color.WHITE);
151 lastMatch = null;
152 }
153
154 private void updateSelection(final JTextField searchField, final JTree actualTree) {
155 searchField.setBackground(Color.WHITE);
156 searchField.setForeground(Color.BLACK);
157 lastMatch = null;
158 if (actualTree != null && actualTree.getModel() != null) {
159 actualTree.clearSelection();
160 String searchString = searchField.getText();
161 if (searchString != null && searchString.length() > 0) {
162 TreePath match = findNextMatch(searchString, new TreePath(actualTree.getModel().getRoot()), actualTree);
163 if (match != null) {
164 selectMatch(match, actualTree);
165 } else {
166 noMatchExists(searchField);
167 }
168 }
169 }
170 }
171
172 private TreePath findNextMatch(String searchString, TreePath parent, final JTree actualTree) {
174 TreeModel model = actualTree.getModel();
175 Object treeRoot = parent.getLastPathComponent();
176 for (int i = 0; i < model.getChildCount(treeRoot); i++) {
177 Object treeNode = model.getChild(treeRoot, i);
178 TreePath path = parent.pathByAddingChild(treeNode);
179 if (isMatch(treeNode, searchString)) {
180 if (lastMatch != null) {
181 if (path.equals(lastMatch)) {
182 lastMatch = null;
183 }
184 } else {
185 return path;
186 }
187 }
188 TreePath match = findNextMatch(searchString, path, actualTree);
189 if (match != null) {
190 return match;
191 }
192 }
193 return null;
194 }
195
196 private boolean isMatch(Object treeNode, String searchString) {
197 String text = treeNode.toString().toUpperCase();
200 return text.startsWith(searchString.toUpperCase());
201 }
202
203 private JTree configureHierarchyJTree(Graph2D model) {
204 model.addGraph2DListener(new DefaultNodeChangePropagator());
206
207 HierarchyManager hierarchy = model.getHierarchyManager();
209 HierarchyTreeModel treeModel = new HierarchyTreeModel(hierarchy);
210
211 treeModel.setChildComparator(HierarchyTreeModel.createNodeStateComparator(hierarchy));
214
215 final JTree tree = new HierarchyJTree(hierarchy, treeModel);
217 tree.setEditable(false);
218
219 tree.setDragEnabled(true);
222 tree.setTransferHandler(new HierarchyTreeTransferHandler(hierarchy));
223 return tree;
224 }
225 }
226