Trigger is a newly re-discovered SwingX problem : support is deep - it’s under the collapse nodes, and not just with visible nodes, that the current behavior is node search.
"Nichts leichter als das" with all my current SwingWorker exposure: go through the TreeModel in the background thread and update the ui in the process as shown in the snippet below. The Fest EDT checker is quite satisfied, but then it only checks the redraw (which is good for EDT here)
Just ... strictly speaking, this background thread must be EDT, as it scans (reading) the model. So the questions are:
- How to correctly implement the search stream?
- or we can live with this risk (largely documented, of course)
One possibility for a solution for a particular case would be to have a second (cloned or otherwise “the same”) model to search for, and then find the matching in the “real” model. This does not go well with general search support, because it does not know anything about any particular model, that is, it cannot create a clone, even if it wants to. Plus, it would be necessary to apply all sorting / filtering of the view (in the future) ...
// a crude worker (match hard-coded and directly coupled to the ui) public static class SearchWorker extends SwingWorker<Void, File> { private Enumeration enumer; private JXList list; private JXTree tree; public SearchWorker(Enumeration enumer, JXList list, JXTree tree) { this.enumer = enumer; this.list = list; this.tree = tree; } @Override protected Void doInBackground() throws Exception { int count = 0; while (enumer.hasMoreElements()) { count++; File file = (File) enumer.nextElement(); if (match(file)) { publish(file); } if (count > 100){ count = 0; Thread.sleep(50); } } return null; } @Override protected void process(List<File> chunks) { for (File file : chunks) { ((DefaultListModel) list.getModel()).addElement(file); TreePath path = createPathToRoot(file); tree.addSelectionPath(path); tree.scrollPathToVisible(path); } } private TreePath createPathToRoot(File file) { boolean result = false; List<File> path = new LinkedList<File>(); while(!result && file != null) { result = file.equals(tree.getModel().getRoot()); path.add(0, file); file = file.getParentFile(); } return new TreePath(path.toArray()); } private boolean match(File file) { return file.getName().startsWith("c"); } } // its usage in terms of SwingX test support public void interactiveDeepSearch() { final FileSystemModel files = new FileSystemModel(new File(".")); final JXTree tree = new JXTree(files); tree.setCellRenderer(new DefaultTreeRenderer(IconValues.FILE_ICON, StringValues.FILE_NAME)); final JXList list = new JXList(new DefaultListModel()); list.setCellRenderer(new DefaultListRenderer(StringValues.FILE_NAME)); list.setVisibleRowCount(20); JXFrame frame = wrapWithScrollingInFrame(tree, "search files"); frame.add(new JScrollPane(list), BorderLayout.SOUTH); Action traverse = new AbstractAction("worker") { @Override public void actionPerformed(ActionEvent e) { setEnabled(false); Enumeration fileEnum = new PreorderModelEnumeration(files); SwingWorker worker = new SearchWorker(fileEnum, list, tree); PropertyChangeListener l = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getNewValue() == SwingWorker.StateValue.DONE) { //T.imeOut("search end "); setEnabled(true); ((SwingWorker) evt.getSource()).removePropertyChangeListener(this); } } }; worker.addPropertyChangeListener(l); // T.imeOn("starting search ... "); worker.execute(); } }; addAction(frame, traverse); show(frame) }
FYI: cross-reference to OTN Swing forum and SwingLabs Forum - will try to publish a summary of all the input at the end (if any :-)
Adding
At the end of the day, it turned out that I had asked the wrong question (or the right question in the wrong context ;-): the “problem” arose due to the proposed solution, the real task for the solution is to support the hierarchical search algorithm (right now AbstractSearchable is severely distorted by linear search) .
Once this is resolved, the next question may be how much the framework can do to support specific hierarchical search queries. Given the variety of custom TreeModels implementations, this is perhaps only possible for the simplest.
Some thoughts that have appeared in discussions here and in other forums. In a specific context, first measure if the traversal is slow: most models in memory move quickly to go through, nothing needs to be done, except for the use of basic support.
Only if a workaround is a bottleneck (like fi in FileSystemModel SwingX implementations) does additional work be needed:
- in a truly immutable and immutable TreeModel we can get away with read-only access in the SwingWorker background thread.
- unmodifiable precondition violated in lazy load / uninstall scripts
- there may be a natural user data structure that supports a model that is actually “detached” from the real model, which allows you to synchronize with this support model (both in the crawl model and in the view).
- pass the actual search back to the database
- use a wrapper on top of this TreeModel, which guarantees access to the underlying model on EDT
- "fake" background search: actually do this in small blocks on EDT (fi in timer) so that the user does not notice any delay
Regardless of the technical feasibility of a slow search, there is the same usability problem: how to present a delay to the end user? And this is a completely different story, perhaps even more dependent on context / need :-)