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