Lines 1127-1207
Link Here
|
1127 |
} |
1127 |
} |
1128 |
setupSearch(); |
1128 |
setupSearch(); |
1129 |
} |
1129 |
} |
1130 |
|
1130 |
|
1131 |
private JTextField searchTextField = new JTextField(); |
1131 |
private JTextField searchTextField = new JTextField(); |
1132 |
final private int heightOfTextField = searchTextField.getPreferredSize().height; |
|
|
1133 |
|
1132 |
|
|
|
1133 |
final private int heightOfTextField = searchTextField.getPreferredSize().height; |
1134 |
|
1134 |
private void setupSearch() { |
1135 |
private void setupSearch() { |
|
|
1136 |
// Remove the default key listeners |
1135 |
KeyListener keyListeners[] = (KeyListener[]) (getListeners(KeyListener.class)); |
1137 |
KeyListener keyListeners[] = (KeyListener[]) (getListeners(KeyListener.class)); |
1136 |
for (int i = 0; i < keyListeners.length; i++) { |
1138 |
for (int i = 0; i < keyListeners.length; i++) { |
1137 |
removeKeyListener(keyListeners[i]); |
1139 |
removeKeyListener(keyListeners[i]); |
1138 |
} |
1140 |
} |
1139 |
// Invoked when the search field should be dismissed by using |
1141 |
// Add new key listeners |
1140 |
// the escape key |
|
|
1141 |
ActionListener escapeListener = new ActionListener() { |
1142 |
public void actionPerformed(ActionEvent e) { |
1143 |
removeSearchField(); |
1144 |
ExplorerTree.this.requestFocus(); |
1145 |
} |
1146 |
}; |
1147 |
searchTextField.registerKeyboardAction(escapeListener, |
1148 |
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), 0); |
1149 |
// An action in the textfield will expand the selected node |
1150 |
searchTextField.addActionListener(new ActionListener() { |
1151 |
public void actionPerformed(ActionEvent e) { |
1152 |
removeSearchField(); |
1153 |
ExplorerTree.this.requestFocus(); |
1154 |
expandPath(getSelectionPath()); |
1155 |
} |
1156 |
}); |
1157 |
// When the search field looses focus, hide it |
1158 |
searchTextField.addFocusListener(new FocusAdapter() { |
1159 |
public void focusLost(FocusEvent e) { |
1160 |
removeSearchField(); |
1161 |
} |
1162 |
}); |
1163 |
// Every change in the search fields document adds invokes a new search |
1164 |
// action |
1165 |
searchTextField.getDocument().addDocumentListener(new DocumentListener() { |
1166 |
public void insertUpdate(DocumentEvent e) { |
1167 |
searchForNode(); |
1168 |
} |
1169 |
public void removeUpdate(DocumentEvent e) { |
1170 |
searchForNode(); |
1171 |
} |
1172 |
public void changedUpdate(DocumentEvent e) { |
1173 |
searchForNode(); |
1174 |
} |
1175 |
|
1176 |
private void searchForNode() { |
1177 |
String text = searchTextField.getText().toUpperCase(); |
1178 |
if (text.length() == 0) { |
1179 |
return; |
1180 |
} |
1181 |
|
1182 |
TreePath selectionPath = getSelectionPath(); |
1183 |
TreePath rootPath = new TreePath(getModel().getRoot()); |
1184 |
TreePath newPath = null; |
1185 |
if (selectionPath != null) { |
1186 |
newPath = doSearch(text, selectionPath); |
1187 |
} |
1188 |
if (newPath == null) { |
1189 |
newPath = doSearch(text, rootPath); |
1190 |
} |
1191 |
if (newPath != null) { |
1192 |
setSelectionPath(newPath); |
1193 |
scrollPathToVisible(newPath); |
1194 |
} else { |
1195 |
clearSelection(); |
1196 |
} |
1197 |
} |
1198 |
}); |
1199 |
// The first key press in the tree must be dispatched to the text field |
1200 |
addKeyListener(new KeyAdapter() { |
1142 |
addKeyListener(new KeyAdapter() { |
1201 |
public void keyPressed(KeyEvent e) { |
1143 |
public void keyPressed(KeyEvent e) { |
1202 |
if (e.getModifiers () > 0 || |
1144 |
int modifiers = e.getModifiers(); |
1203 |
KeyEvent.VK_DELETE == e.getKeyCode () || |
1145 |
int keyCode = e.getKeyCode(); |
1204 |
KeyEvent.VK_ESCAPE == e.getKeyCode ()) |
1146 |
// If the only modifier is SHIFT, allow it |
|
|
1147 |
if ((modifiers > 0 && modifiers != KeyEvent.SHIFT_MASK) || |
1148 |
KeyEvent.VK_DELETE == keyCode || |
1149 |
KeyEvent.VK_ESCAPE == keyCode) |
1205 |
return ; |
1150 |
return ; |
1206 |
char c = e.getKeyChar(); |
1151 |
char c = e.getKeyChar(); |
1207 |
if (Character.isJavaIdentifierPart(c) || (c == File.separatorChar)) { |
1152 |
if (Character.isJavaIdentifierPart(c) || (c == File.separatorChar)) { |
Lines 1210-1256
Link Here
|
1210 |
} |
1155 |
} |
1211 |
} |
1156 |
} |
1212 |
}); |
1157 |
}); |
1213 |
// Any key press in the text field triggers the addition of the |
1158 |
// Create a the "multi-event" listener for the text field. Instead of |
1214 |
// search field to the tree |
1159 |
// adding separate instances of each needed listener, we're using a |
1215 |
ActionListener keyPressListener = new ActionListener() { |
1160 |
// class which implements them all. This approach is used in order |
1216 |
public void actionPerformed(ActionEvent e) { |
1161 |
// to avoid the creation of 4 instances which takes some time |
1217 |
displaySearchField(); |
1162 |
SearchFieldListener searchFieldListener = new SearchFieldListener(); |
|
|
1163 |
searchTextField.addKeyListener(searchFieldListener); |
1164 |
searchTextField.addFocusListener(searchFieldListener); |
1165 |
searchTextField.getDocument().addDocumentListener(searchFieldListener); |
1166 |
} |
1167 |
|
1168 |
private class SearchFieldListener extends KeyAdapter |
1169 |
implements DocumentListener, FocusListener { |
1170 |
/** The last search results */ |
1171 |
private ArrayList results = new ArrayList(); |
1172 |
/** The last selected index from the search results. */ |
1173 |
private int currentSelectionIndex; |
1174 |
|
1175 |
public void changedUpdate(DocumentEvent e) { |
1176 |
searchForNode(); |
1177 |
} |
1178 |
|
1179 |
public void insertUpdate(DocumentEvent e) { |
1180 |
searchForNode(); |
1181 |
} |
1182 |
|
1183 |
public void removeUpdate(DocumentEvent e) { |
1184 |
searchForNode(); |
1185 |
} |
1186 |
|
1187 |
public void keyPressed(KeyEvent e) { |
1188 |
int keyCode = e.getKeyCode(); |
1189 |
if (keyCode == KeyEvent.VK_ESCAPE) { |
1190 |
removeSearchField(); |
1191 |
} else if (keyCode == KeyEvent.VK_UP) { |
1192 |
currentSelectionIndex--; |
1193 |
displaySearchResult(); |
1194 |
// Stop processing the event here. Otherwise it's dispatched |
1195 |
// to the tree too (which scrolls) |
1196 |
e.consume(); |
1197 |
} else if (keyCode == KeyEvent.VK_DOWN) { |
1198 |
currentSelectionIndex++; |
1199 |
displaySearchResult(); |
1200 |
// Stop processing the event here. Otherwise it's dispatched |
1201 |
// to the tree too (which scrolls) |
1202 |
e.consume(); |
1203 |
} else if (keyCode == KeyEvent.VK_ENTER) { |
1204 |
removeSearchField(); |
1205 |
expandPath(getSelectionPath()); |
1206 |
ExplorerTree.this.requestFocus(); |
1207 |
ExplorerTree.this.dispatchEvent(e); |
1218 |
} |
1208 |
} |
1219 |
}; |
1209 |
} |
|
|
1210 |
|
1211 |
/** Searches for a node in the tree. */ |
1212 |
private void searchForNode() { |
1213 |
currentSelectionIndex = 0; |
1214 |
results.clear(); |
1215 |
String text = searchTextField.getText().toUpperCase(); |
1216 |
if (text.length() > 0) { |
1217 |
doSearch(text, results); |
1218 |
displaySearchResult(); |
1219 |
} |
1220 |
} |
1220 |
|
1221 |
|
|
|
1222 |
private void displaySearchResult() { |
1223 |
int sz = results.size(); |
1224 |
if (sz > 0) { |
1225 |
if (currentSelectionIndex < 0) { |
1226 |
currentSelectionIndex = sz - 1; |
1227 |
} else if (currentSelectionIndex >= sz) { |
1228 |
currentSelectionIndex = 0; |
1229 |
} |
1230 |
TreePath path = (TreePath) results.get(currentSelectionIndex); |
1231 |
setSelectionPath(path); |
1232 |
scrollPathToVisible(path); |
1233 |
} else { |
1234 |
clearSelection(); |
1235 |
} |
1236 |
} |
1237 |
|
1238 |
public void focusGained(FocusEvent e) { |
1239 |
// Do nothing |
1240 |
} |
1241 |
|
1242 |
public void focusLost(FocusEvent e) { |
1243 |
removeSearchField(); |
1244 |
} |
1221 |
} |
1245 |
} |
1222 |
|
1246 |
|
1223 |
private int originalScrollMode; |
1247 |
private int originalScrollMode; |
1224 |
|
1248 |
|
1225 |
/** |
1249 |
private void doSearch(String str, ArrayList results) { |
1226 |
* Method used to recursively search for a named node in a tree path. |
1250 |
int rows[] = getSelectionRows(); |
1227 |
*/ |
1251 |
int row = (rows == null || rows.length == 0) ? 0 : rows[0]; |
1228 |
private TreePath doSearch(String str, TreePath path) { |
1252 |
int rowCount = getRowCount(); |
1229 |
TreeNode node = (TreeNode) path.getLastPathComponent(); |
1253 |
for (int i = row; i < getRowCount(); i++) { |
1230 |
if (node.toString().toUpperCase().startsWith(str)) { |
1254 |
addPathIfMatches(str, i, results); |
1231 |
// Return if the current path matches |
|
|
1232 |
return path; |
1233 |
} |
1255 |
} |
1234 |
// It's a collapsed path, so return |
1256 |
for (int i = 0; i < row && i < rowCount; i++) { |
1235 |
if (isCollapsed(path)) { |
1257 |
addPathIfMatches(str, i, results); |
1236 |
return null; |
|
|
1237 |
} |
1238 |
Enumeration children = node.children(); |
1239 |
while (children.hasMoreElements()) { |
1240 |
TreeNode child = (TreeNode) children.nextElement(); |
1241 |
TreePath newPath = path.pathByAddingChild(child); |
1242 |
if (child.toString().toUpperCase().startsWith(str)) { |
1243 |
return newPath; |
1244 |
} |
1245 |
// Recursive search |
1246 |
newPath = doSearch(str, newPath); |
1247 |
if (newPath != null) { |
1248 |
return newPath; |
1249 |
} |
1250 |
} |
1258 |
} |
1251 |
return null; |
|
|
1252 |
} |
1259 |
} |
1253 |
|
1260 |
|
|
|
1261 |
private void addPathIfMatches(String str, int row, ArrayList results) { |
1262 |
TreePath path = getPathForRow(row); |
1263 |
TreeNode node = (TreeNode) path.getLastPathComponent(); |
1264 |
if (node.toString().toUpperCase().startsWith(str)) { |
1265 |
results.add(path); |
1266 |
} |
1267 |
} |
1268 |
|
1254 |
/** |
1269 |
/** |
1255 |
* Adds the search field to the tree. |
1270 |
* Adds the search field to the tree. |
1256 |
*/ |
1271 |
*/ |
Lines 1265-1279
Link Here
|
1265 |
searchTextField.requestFocus(); |
1280 |
searchTextField.requestFocus(); |
1266 |
} |
1281 |
} |
1267 |
} |
1282 |
} |
1268 |
|
1283 |
|
1269 |
public void paint(Graphics g) { |
1284 |
public void paint(Graphics g) { |
1270 |
Rectangle visibleRect = getVisibleRect(); |
1285 |
Rectangle visibleRect = getVisibleRect(); |
1271 |
if (searchTextField.isDisplayable()) { |
1286 |
if (searchTextField.isDisplayable()) { |
1272 |
searchTextField.setBounds( |
1287 |
searchTextField.setBounds( |
1273 |
Math.max(3, visibleRect.x + visibleRect.width - 163), |
1288 |
Math.max(3, visibleRect.x + visibleRect.width - 163), |
1274 |
visibleRect.y + 3, |
1289 |
visibleRect.y + 3, |
1275 |
Math.min(getPreferredSize().width - 6, 160), |
1290 |
Math.min(getPreferredSize().width - 6, 160), |
1276 |
heightOfTextField); |
1291 |
heightOfTextField); |
1277 |
} |
1292 |
} |
1278 |
super.paint(g); |
1293 |
super.paint(g); |
1279 |
} |
1294 |
} |
Lines 1283-1293
Link Here
|
1283 |
private void removeSearchField() { |
1298 |
private void removeSearchField() { |
1284 |
remove(searchTextField); |
1299 |
remove(searchTextField); |
1285 |
TreeView.this.getViewport().setScrollMode(originalScrollMode); |
1300 |
TreeView.this.getViewport().setScrollMode(originalScrollMode); |
|
|
1301 |
ExplorerTree.this.requestFocus(); |
1286 |
this.repaint(); |
1302 |
this.repaint(); |
1287 |
} |
1303 |
} |
1288 |
|
1304 |
|
1289 |
|
1305 |
/** notify the Component to autoscroll */ |
1290 |
/** notify the Component to autoscroll */ |
|
|
1291 |
public void autoscroll (Point cursorLoc) { |
1306 |
public void autoscroll (Point cursorLoc) { |
1292 |
getSupport().autoscroll(cursorLoc); |
1307 |
getSupport().autoscroll(cursorLoc); |
1293 |
} |
1308 |
} |