Skip to content

Commit 5706af4

Browse files
authored
Merge pull request #35 from acardona/filter-listed-files
New text/regex filter to choose which files are to be listed in the FileSystemTree of the Script Editor.
2 parents 60dec9c + f9e15f4 commit 5706af4

File tree

2 files changed

+127
-9
lines changed

2 files changed

+127
-9
lines changed

src/main/java/org/scijava/ui/swing/script/FileSystemTree.java

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,13 @@ public boolean isDirectory() {
103103
return new File(this.path).isDirectory();
104104
}
105105

106-
public File[] updatedChildrenFiles(final boolean sort) {
106+
/**
107+
*
108+
* @param sort
109+
* @param file_filter Applies to leafs, not to directories.
110+
* @return
111+
*/
112+
public File[] updatedChildrenFiles(final boolean sort, final FileFilter file_filter) {
107113
final File file = new File(this.path);
108114
if (!file.isDirectory()) {
109115
return new File[0];
@@ -112,7 +118,8 @@ public File[] updatedChildrenFiles(final boolean sort) {
112118
@Override
113119
public boolean accept(final File f) {
114120
return !f.isHidden() && !f.getName().endsWith("~")
115-
&& !re_ignored_extensions.matcher(f.getName()).matches();
121+
&& !re_ignored_extensions.matcher(f.getName()).matches()
122+
&& (f.isDirectory() || file_filter.accept(f));
116123
}
117124
});
118125
if (sort) Arrays.sort(files);
@@ -122,11 +129,11 @@ public boolean accept(final File f) {
122129
/**
123130
* If it's a directory, add a Node for each of its visible files.
124131
*/
125-
public synchronized void populateChildren(final DefaultTreeModel model) {
132+
public synchronized void populateChildren(final DefaultTreeModel model, final FileFilter file_filter) {
126133
try {
127134
if (isLeaf()) return;
128135
int index = 0;
129-
for (final File file : updatedChildrenFiles(true)) {
136+
for (final File file : updatedChildrenFiles(true, file_filter)) {
130137
// Can't use add: would try to insert at getChildCount(), which is the wrong value here
131138
model.insertNodeInto(new Node(file.getAbsolutePath()), this, index++);
132139
}
@@ -207,9 +214,9 @@ public synchronized void removeAllChildren(final DefaultTreeModel model) {
207214
}
208215
}
209216

210-
public synchronized void updateChildrenList(final DefaultTreeModel model) {
217+
public synchronized void updateChildrenList(final DefaultTreeModel model, final FileFilter file_filter) {
211218
removeAllChildren(model);
212-
populateChildren(model);
219+
populateChildren(model, file_filter);
213220
}
214221
}
215222

@@ -225,6 +232,7 @@ public interface LeafListener {
225232

226233
private final HashSet<String> ignored_extensions = new HashSet<>();
227234
private Pattern re_ignored_extensions = Pattern.compile("^.*$", Pattern.CASE_INSENSITIVE); // match all
235+
private FileFilter file_filter = ((f) -> true);
228236

229237
public FileSystemTree(final Logger log)
230238
{
@@ -238,7 +246,7 @@ public FileSystemTree(final Logger log)
238246
@Override
239247
public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
240248
final Node node = ((Node)event.getPath().getLastPathComponent());
241-
node.populateChildren(getModel());
249+
node.populateChildren(getModel(), file_filter);
242250
dir_watcher.register(node);
243251
}
244252

@@ -329,6 +337,46 @@ private void updateIgnoreExtensionPattern() {
329337
this.re_ignored_extensions = Pattern.compile(s.toString(), Pattern.CASE_INSENSITIVE);
330338
}
331339
}
340+
341+
public void setFileFilter(final FileFilter file_filter) {
342+
this.file_filter = file_filter;
343+
updateRecursively(file_filter);
344+
}
345+
346+
private void updateRecursively(final FileFilter file_filter) {
347+
// Find expanded directories
348+
final ArrayList<Node> stack = new ArrayList<>();
349+
final Node root = (Node) this.getModel().getRoot();
350+
for (int i=root.getChildCount() -1; i>-1; --i) {
351+
stack.add((Node)root.getChildAt(i));
352+
}
353+
final ArrayList<Node> stack2 = new ArrayList<>(stack); // copy for second phase
354+
final HashSet<String> expanded = new HashSet<>();
355+
while (!stack.isEmpty()) {
356+
final Node node = stack.remove(0);
357+
if (this.isExpanded(new TreePath(node.getPath()))) {
358+
expanded.add(node.path);
359+
for (int i=node.getChildCount() -1; i>-1; --i) {
360+
final Node child = node.getChildAt(i);
361+
if (child.isDirectory()) stack.add(child);
362+
}
363+
}
364+
}
365+
// Re-list all files, filtering
366+
while (!stack2.isEmpty()) {
367+
final Node node = stack2.remove(0);
368+
if (expanded.contains(node.path)) {
369+
node.removeAllChildren(this.getModel());
370+
node.populateChildren(this.getModel(), file_filter);
371+
final int count = node.getChildCount();
372+
if (count > 0) expandPath(new TreePath(node.getChildAt(0).getPath())); // awkward way to ensure parent is expanded
373+
for (int i=0; i<count; ++i) {
374+
final Node child = node.getChildAt(i);
375+
if (expanded.contains(child.path)) stack2.add(child);
376+
}
377+
}
378+
}
379+
}
332380

333381
synchronized public void addLeafListener(final LeafListener l) {
334382
this.leaf_listeners.add(l);
@@ -508,7 +556,7 @@ public void run() {
508556

509557
SwingUtilities.invokeLater(() -> {
510558
for (final Node node : nodes) {
511-
node.updateChildrenList(FileSystemTree.this.getModel());
559+
node.updateChildrenList(FileSystemTree.this.getModel(), file_filter);
512560
FileSystemTree.this.expandPath(new TreePath(node.getPath()));
513561
}
514562
});

src/main/java/org/scijava/ui/swing/script/TextEditor.java

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import java.awt.event.ActionListener;
5353
import java.awt.event.ComponentAdapter;
5454
import java.awt.event.ComponentEvent;
55+
import java.awt.event.FocusEvent;
56+
import java.awt.event.FocusListener;
5557
import java.awt.event.ItemEvent;
5658
import java.awt.event.KeyAdapter;
5759
import java.awt.event.KeyEvent;
@@ -94,6 +96,8 @@
9496
import java.util.concurrent.ExecutionException;
9597
import java.util.jar.JarEntry;
9698
import java.util.jar.JarOutputStream;
99+
import java.util.regex.Pattern;
100+
import java.util.regex.PatternSyntaxException;
97101
import java.util.zip.ZipException;
98102

99103
import javax.script.ScriptEngine;
@@ -116,6 +120,7 @@
116120
import javax.swing.JSplitPane;
117121
import javax.swing.JTabbedPane;
118122
import javax.swing.JTextArea;
123+
import javax.swing.JTextField;
119124
import javax.swing.KeyStroke;
120125
import javax.swing.SwingUtilities;
121126
import javax.swing.event.ChangeEvent;
@@ -618,6 +623,11 @@ public TextEditor(final Context context) {
618623
add_directory.setToolTipText("Add a directory");
619624
final JButton remove_directory = new JButton("[-]");
620625
remove_directory.setToolTipText("Remove a top-level directory");
626+
627+
final JTextField filter = new JTextField("filter...");
628+
filter.setForeground(Color.gray);
629+
filter.setToolTipText("Use leading '/' for regular expressions");
630+
621631
tree = new FileSystemTree(log);
622632
tree.ignoreExtension("class");
623633
dragSource = new DragSource();
@@ -683,6 +693,63 @@ public TextEditor(final Context context) {
683693
"Can only remove top-level folders.");
684694
}
685695
});
696+
filter.addFocusListener(new FocusListener() {
697+
@Override
698+
public void focusLost(FocusEvent e) {
699+
if (0 == filter.getText().length()) {
700+
filter.setForeground(Color.gray);
701+
filter.setText("filter...");
702+
}
703+
}
704+
705+
@Override
706+
public void focusGained(FocusEvent e) {
707+
if (filter.getForeground() == Color.gray) {
708+
filter.setText("");
709+
filter.setForeground(Color.black);
710+
}
711+
}
712+
});
713+
filter.addKeyListener(new KeyAdapter() {
714+
Pattern pattern = null;
715+
@Override
716+
public void keyPressed(final KeyEvent ke) {
717+
if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
718+
final String text = filter.getText();
719+
if (0 == text.length()) {
720+
tree.setFileFilter(((f) -> true)); // any
721+
return;
722+
}
723+
if ('/' == text.charAt(0)) {
724+
// Interpret as a regular expression
725+
// Attempt to compile the pattern
726+
try {
727+
String regex = text.substring(1);
728+
if ('^' != regex.charAt(1)) regex = "^.*" + regex;
729+
if ('$' != regex.charAt(regex.length() -1)) regex += ".*$";
730+
pattern = Pattern.compile(regex);
731+
filter.setForeground(Color.black);
732+
} catch (final PatternSyntaxException pse) {
733+
log.warn(pse.getLocalizedMessage());
734+
filter.setForeground(Color.red);
735+
pattern = null;
736+
return;
737+
}
738+
if (null != pattern) {
739+
tree.setFileFilter((f) -> pattern.matcher(f.getName()).matches());
740+
}
741+
} else {
742+
// Interpret as a literal match
743+
tree.setFileFilter((f) -> -1 != f.getName().indexOf(text));
744+
}
745+
} else {
746+
// Upon re-typing something
747+
if (filter.getForeground() == Color.red) {
748+
filter.setForeground(Color.black);
749+
}
750+
}
751+
}
752+
});
686753

687754
// Restore top-level directories
688755
tree.addTopLevelFoldersFrom(getEditorPane().loadFolders());
@@ -699,8 +766,11 @@ public TextEditor(final Context context) {
699766
tree_panel.add(add_directory, bc);
700767
bc.gridx = 1;
701768
tree_panel.add(remove_directory, bc);
769+
bc.gridx = 2;
770+
bc.fill = GridBagConstraints.BOTH;
771+
tree_panel.add(filter, bc);
702772
bc.gridx = 0;
703-
bc.gridwidth = 2;
773+
bc.gridwidth = 3;
704774
bc.gridy = 1;
705775
bc.weightx = 1.0;
706776
bc.weighty = 1.0;

0 commit comments

Comments
 (0)