/*
* HyperSearchResults.java - HyperSearch results
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 1998, 1999, 2000, 2001 Slava Pestov
* Portions copyright (C) 2002 Peter Cox
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit.search;
//{{{ Imports
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import org.gjt.sp.jedit.EditBus.EBHandler;
import org.gjt.sp.jedit.gui.DefaultFocusComponent;
import org.gjt.sp.jedit.gui.RolloverButton;
import org.gjt.sp.jedit.gui.StyleEditor;
import org.gjt.sp.jedit.msg.*;
import org.gjt.sp.jedit.search.SearchMatcher.Match;
import org.gjt.sp.jedit.syntax.SyntaxStyle;
import org.gjt.sp.jedit.*;
import org.gjt.sp.util.EnhancedTreeCellRenderer;
import org.gjt.sp.util.GenericGUIUtilities;
import org.gjt.sp.util.HtmlUtilities;
import org.gjt.sp.util.SyntaxUtilities;
import org.gjt.sp.util.TaskManager;
//}}}
/**
* HyperSearch results window.
* @author Slava Pestov
* @version $Id$
*/
public class HyperSearchResults extends JPanel implements DefaultFocusComponent
{
public static final String NAME = "hypersearch-results";
public static final String HIGHLIGHT_PROP = "hypersearch.results.highlight";
//{{{ HyperSearchResults constructor
public HyperSearchResults(View view)
{
super(new BorderLayout());
this.view = view;
caption = new JLabel();
Box toolBar = new Box(BoxLayout.X_AXIS);
toolBar.add(caption);
toolBar.add(Box.createGlue());
ActionHandler ah = new ActionHandler();
highlight = new RolloverButton();
highlight.setToolTipText(jEdit.getProperty(
"hypersearch-results.highlight.label"));
highlight.addActionListener(ah);
toolBar.add(highlight);
clear = new RolloverButton(GUIUtilities.loadIcon(
jEdit.getProperty("hypersearch-results.clear.icon")));
clear.setToolTipText(jEdit.getProperty(
"hypersearch-results.clear.label"));
clear.addActionListener(ah);
toolBar.add(clear);
multi = new RolloverButton();
multi.setToolTipText(jEdit.getProperty(
"hypersearch-results.multi.label"));
multi.addActionListener(ah);
toolBar.add(multi);
stop = new RolloverButton(GUIUtilities.loadIcon(
jEdit.getProperty("hypersearch-results.stop.icon")));
stop.setToolTipText(jEdit.getProperty(
"hypersearch-results.stop.label"));
stop.addActionListener(ah);
toolBar.add(stop);
stop.setEnabled(false);
add(BorderLayout.NORTH, toolBar);
resultTreeRoot = new DefaultMutableTreeNode();
resultTreeModel = new DefaultTreeModel(resultTreeRoot);
resultTree = new HighlightingTree(resultTreeModel);
resultTree.setRowHeight(0);
resultTree.setToolTipText(null);
resultTree.setCellRenderer(new ResultCellRenderer());
resultTree.setVisibleRowCount(16);
resultTree.setRootVisible(false);
resultTree.setShowsRootHandles(true);
//the ESCAPE keystroke is assigned to hideTip action by swing
//it breaks the action usually assigned to close-docking-area by jEdit,
//so we remove this keystroke binding bug #1955140
KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
resultTree.getInputMap().remove(keyStroke);
// looks bad with the OS X L&F, apparently...
if(!OperatingSystem.isMacOSLF())
resultTree.putClientProperty("JTree.lineStyle", "Angled");
resultTree.setEditable(false);
resultTree.addKeyListener(new KeyHandler());
resultTree.addMouseListener(new MouseHandler());
JScrollPane scrollPane = new JScrollPane(resultTree);
Dimension dim = scrollPane.getPreferredSize();
dim.width = 400;
scrollPane.setPreferredSize(dim);
add(BorderLayout.CENTER, scrollPane);
resultTree.setTransferHandler(new ResultTreeTransferHandler());
} //}}}
//{{{ focusOnDefaultComponent() method
@Override
public void focusOnDefaultComponent()
{
resultTree.requestFocus();
} //}}}
//{{{ addNotify() method
@Override
public void addNotify()
{
super.addNotify();
EditBus.addToBus(this);
multiStatus = jEdit.getBooleanProperty(
"hypersearch-results.multi");
updateHighlightStatus();
updateMultiStatus();
} //}}}
//{{{ removeNotify() method
@Override
public void removeNotify()
{
super.removeNotify();
EditBus.removeFromBus(this);
jEdit.setBooleanProperty("hypersearch-results.multi",multiStatus);
} //}}}
//{{{ visitBuffers() method
private void visitBuffers(final ResultVisitor visitor, final Buffer buffer)
{
// impl note: since multi-level hierarchies now allowed,
// use traverseNodes to process HyperSearchResult nodes
traverseNodes(resultTreeRoot, new TreeNodeCallbackAdapter()
{
@Override
public boolean processNode(DefaultMutableTreeNode node)
{
Object userObject = node.getUserObject();
if (!(userObject instanceof HyperSearchResult))
return true;
HyperSearchResult result = (HyperSearchResult) userObject;
if (result.pathEquals(buffer.getSymlinkPath()))
visitor.visit(buffer, result);
return true;
}
});
} //}}}
//{{{ handleBufferUpdate() method
@EBHandler
public void handleBufferUpdate(BufferUpdate bmsg)
{
Buffer buffer = bmsg.getBuffer();
Object what = bmsg.getWhat();
if(what == BufferUpdate.LOADED)
visitBuffers(new BufferLoadedVisitor(),buffer);
else if(what == BufferUpdate.CLOSED)
visitBuffers(new BufferClosedVisitor(),buffer);
} //}}}
//{{{ traverseNodes() method
public static boolean traverseNodes(DefaultMutableTreeNode node,
HyperSearchTreeNodeCallback callbackInterface)
{
if (!callbackInterface.processNode(node))
return false;
for (Enumeration e = node.children(); e.hasMoreElements();)
{
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)e.nextElement();
if (!traverseNodes(childNode, callbackInterface))
return false;
}
return true;
} //}}}
//{{{ getTreeModel() method
public DefaultTreeModel getTreeModel()
{
return resultTreeModel;
} //}}}
//{{{ getTree() method
/**
* Returns the result tree.
*
* @return the result tree
* @since jEdit 4.1pre9
*/
public JTree getTree()
{
return resultTree;
} //}}}
//{{{ searchStarted() method
public void searchStarted()
{
stop.setEnabled(true);
caption.setText(jEdit.getProperty("hypersearch-results.searching",
new String[] { trimSearchString() }));
} //}}}
//{{{ setSearchStatus() method
public void setSearchStatus(String status)
{
caption.setText(status);
} //}}}
//{{{ searchFailed() method
public void searchFailed()
{
caption.setText(jEdit.getProperty("hypersearch-results.no-results",
new String[] { trimSearchString() }));
// collapse all nodes, as suggested on user mailing list...
for(int i = 0; i < resultTreeRoot.getChildCount(); i++)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
resultTreeRoot.getChildAt(i);
resultTree.collapsePath(new TreePath(new Object[] {
resultTreeRoot, node }));
}
} //}}}
//{{{ searchDone() method
/**
* @param searchNode the result node
* @param selectNode the node that must be selected, or null
* @since jEdit 4.3pre12
*/
public void searchDone(final DefaultMutableTreeNode searchNode, final DefaultMutableTreeNode selectNode)
{
stop.setEnabled(false);
final int nodeCount = searchNode.getChildCount();
if (nodeCount < 1)
{
searchFailed();
return;
}
caption.setText(jEdit.getProperty("hypersearch-results.done",
new String [] { trimSearchString() }));
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
if(!multiStatus)
{
for(int i = 0; i < resultTreeRoot.getChildCount(); i++)
{
resultTreeRoot.remove(0);
}
}
resultTreeRoot.add(searchNode);
resultTreeModel.reload(resultTreeRoot);
for (int i = 0; i < nodeCount; i++)
{
TreePath lastNode = new TreePath(
((DefaultMutableTreeNode)
searchNode.getChildAt(i))
.getPath());
resultTree.expandPath(lastNode);
}
TreePath treePath;
if (selectNode == null)
{
treePath = new TreePath(new Object[]{
resultTreeRoot, searchNode});
}
else
{
treePath = new TreePath(selectNode.getPath());
}
resultTree.setSelectionPath(treePath);
resultTree.scrollPathToVisible(
treePath);
}
});
} //}}}
//{{{ searchDone() method
public void searchDone(DefaultMutableTreeNode searchNode)
{
searchDone(searchNode, null);
} //}}}
//{{{ Private members
private final View view;
private final JLabel caption;
private final JTree resultTree;
private final DefaultMutableTreeNode resultTreeRoot;
private final DefaultTreeModel resultTreeModel;
private final RolloverButton highlight;
private final RolloverButton clear;
private final RolloverButton multi;
private final RolloverButton stop;
private boolean multiStatus;
//{{{ updateHighlightStatus() method
private void updateHighlightStatus()
{
String prop = jEdit.getProperty(HIGHLIGHT_PROP);
if (prop != null && !prop.isEmpty())
highlight.setIcon(GUIUtilities.loadIcon(jEdit.getProperty("hypersearch-results.match.highlight.icon")));
else
highlight.setIcon(GUIUtilities.loadIcon(jEdit.getProperty("hypersearch-results.match.normal.icon")));
resultTree.repaint();
} //}}}
//{{{ updateMultiStatus() method
private void updateMultiStatus()
{
if(multiStatus)
multi.setIcon(GUIUtilities.loadIcon(jEdit.getProperty("hypersearch-results.multi.multiple.icon")));
else
multi.setIcon(GUIUtilities.loadIcon(jEdit.getProperty("hypersearch-results.multi.single.icon")));
} //}}}
//{{{ goToSelectedNode() method
public static final int M_OPEN = 0;
public static final int M_OPEN_NEW_VIEW = 1;
public static final int M_OPEN_NEW_PLAIN_VIEW = 2;
public static final int M_OPEN_NEW_SPLIT = 3;
private void goToSelectedNode(int mode)
{
TreePath path = resultTree.getSelectionPath();
if(path == null)
return;
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path
.getLastPathComponent();
Object value = node.getUserObject();
// do nothing if clicked "foo (showing n occurrences in m files)"
if(node.getParent() != resultTreeRoot && value instanceof HyperSearchNode)
{
HyperSearchNode n = (HyperSearchNode)value;
Buffer buffer = n.getBuffer(view);
if(buffer == null)
return;
EditPane pane;
switch(mode)
{
case M_OPEN:
pane = view.goToBuffer(buffer);
break;
case M_OPEN_NEW_VIEW:
pane = jEdit.newView(view,buffer,false).getEditPane();
break;
case M_OPEN_NEW_PLAIN_VIEW:
pane = jEdit.newView(view,buffer,true).getEditPane();
break;
case M_OPEN_NEW_SPLIT:
pane = view.splitHorizontally();
break;
default:
throw new IllegalArgumentException("Bad mode: " + mode);
}
n.goTo(pane);
}
} //}}}
//{{{ removeSelectedNode() method
private void removeSelectedNode()
{
TreePath path = resultTree.getSelectionPath();
if(path == null)
return;
MutableTreeNode value = (MutableTreeNode)path
.getLastPathComponent();
if(path.getPathCount() > 1)
{
// Adjust selection so that repeating some removals
// behave naturally.
TreePath parentPath = path.getParentPath();
MutableTreeNode parent = (MutableTreeNode)parentPath
.getLastPathComponent();
int removingIndex = parent.getIndex(value);
int nextIndex = removingIndex + 1;
if(nextIndex < parent.getChildCount())
{
TreeNode next = parent.getChildAt(nextIndex);
resultTree.setSelectionPath(
parentPath.pathByAddingChild(next));
}
else
{
resultTree.setSelectionPath(parentPath);
}
resultTreeModel.removeNodeFromParent(value);
}
HyperSearchOperationNode.removeNodeFromCache(value);
if (resultTreeRoot.getChildCount() == 0)
{
hideDockable();
}
} //}}}
//{{{ removeAllNodes() method
private void removeAllNodes()
{
resultTreeRoot.removeAllChildren();
resultTreeModel.reload(resultTreeRoot);
setSearchStatus(null);
hideDockable();
} //}}}
//{{{ hideDockable() method
private void hideDockable()
{
view.getDockableWindowManager().hideDockableWindow(NAME);
} //}}}
//{{{ trimSearchString() method
private static String trimSearchString()
{
String s = SearchAndReplace.getSearchString();
int length = jEdit.getIntegerProperty("hypersearch.displayQueryLength", 100);
if (s.length() > length)
{
return s.substring(0,length) + "...";
}
return s;
} //}}}
//{{{ parseHighlightStyle()
SyntaxStyle parseHighlightStyle(String style)
{
Font f = (resultTree != null) ? resultTree.getFont() :
UIManager.getFont("Tree.font");
return HtmlUtilities.parseHighlightStyle(style, f);
} //}}}
//}}}
//{{{ ActionHandler class
public class ActionHandler implements ActionListener
{
@Override
public void actionPerformed(ActionEvent evt)
{
Object source = evt.getSource();
if(source == highlight)
{
String prop = jEdit.getProperty(HIGHLIGHT_PROP);
Font f = (resultTree != null) ? resultTree.getFont() :
UIManager.getFont("Tree.font");
SyntaxStyle style = new StyleEditor(jEdit.getActiveView(),
HtmlUtilities.parseHighlightStyle(prop, f),
"hypersearch").getStyle();
if (style != null)
jEdit.setProperty(HIGHLIGHT_PROP, GUIUtilities.getStyleString(style));
updateHighlightStatus();
}
else if(source == clear)
{
removeAllNodes();
}
else if(source == multi)
{
multiStatus = !multiStatus;
updateMultiStatus();
if(!multiStatus)
{
for(int i = resultTreeRoot.getChildCount() - 2; i >= 0; i--)
{
resultTreeModel.removeNodeFromParent(
(MutableTreeNode)resultTreeRoot
.getChildAt(i));
}
}
}
else if(source == stop)
{
TaskManager.instance.cancelTasksByClass(HyperSearchRequest.class);
}
}
} //}}}
//{{{ HighlightingTree class
class HighlightingTree extends JTree
{
private String prop;
private String styleTag;
HighlightingTree(DefaultTreeModel model)
{
super(model);
prop = jEdit.getProperty(HIGHLIGHT_PROP);
if (prop != null && !prop.isEmpty())
{
Font f = (resultTree != null) ? resultTree.getFont() :
UIManager.getFont("Tree.font");
styleTag = HtmlUtilities.style2html(prop, f);
}
}
@Override
public String convertValueToText(Object value, boolean selected,
boolean expanded, boolean leaf, int row, boolean hasFocus)
{
String s = super.convertValueToText(value, selected, expanded, leaf,
row, hasFocus);
String newProp = jEdit.getProperty(HIGHLIGHT_PROP);
if (newProp == null || newProp.isEmpty())
return s;
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
while (node != null && !(node.getUserObject() instanceof HyperSearchOperationNode))
{
node = (DefaultMutableTreeNode) node.getParent();
}
if (node == null)
return s;
if (! newProp.equals(prop))
{
prop = newProp;
Font f = (resultTree != null) ? resultTree.getFont() :
UIManager.getFont("Tree.font");
styleTag = HtmlUtilities.style2html(prop, f);
}
SearchMatcher matcher =
((HyperSearchOperationNode) node.getUserObject()).getSearchMatcher();
int i = s.indexOf(": ");
if (i > 0)
i += 2;
else
i = 0;
Match m = null;
List<Integer> matches = new ArrayList<Integer>();
try {
m = matcher.nextMatch(s.substring(i), true, true, true, false);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
while (m != null)
{
matches.add(i + m.start);
matches.add(i + m.end);
i += m.end;
try {
m = matcher.nextMatch(s.substring(i), true, true, true, false);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
m = null;
}
}
return HtmlUtilities.highlightString(s, styleTag, matches);
}
} //}}}
//{{{ KeyHandler class
class KeyHandler extends KeyAdapter
{
@Override
public void keyPressed(KeyEvent evt)
{
switch(evt.getKeyCode())
{
case KeyEvent.VK_SPACE:
goToSelectedNode(M_OPEN);
// fuck me dead
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
resultTree.requestFocus();
}
});
evt.consume();
break;
case KeyEvent.VK_ENTER:
goToSelectedNode(M_OPEN);
evt.consume();
break;
case KeyEvent.VK_DELETE:
removeSelectedNode();
evt.consume();
break;
default:
break;
}
}
} //}}}
//{{{ MouseHandler class
class MouseHandler extends MouseAdapter
{
//{{{ mousePressed() method
@Override
public void mousePressed(MouseEvent evt)
{
if(evt.isConsumed())
return;
TreePath path1 = resultTree.getPathForLocation(
evt.getX(),evt.getY());
if(path1 == null)
return;
resultTree.setSelectionPath(path1);
if (GenericGUIUtilities.isPopupTrigger(evt))
showPopupMenu(evt);
else
{
goToSelectedNode(M_OPEN);
}
} //}}}
//{{{ Private members
private JPopupMenu popupMenu;
//{{{ showPopupMenu method
private void showPopupMenu(MouseEvent evt)
{
TreePath path = resultTree.getSelectionPath();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
popupMenu = new JPopupMenu();
Object userObj = node.getUserObject();
if (userObj instanceof HyperSearchNode)
{
popupMenu.add(new GoToNodeAction(
"hypersearch-results.open",
M_OPEN));
popupMenu.add(new GoToNodeAction(
"hypersearch-results.open-view",
M_OPEN_NEW_VIEW));
popupMenu.add(new GoToNodeAction(
"hypersearch-results.open-plain-view",
M_OPEN_NEW_PLAIN_VIEW));
popupMenu.add(new GoToNodeAction(
"hypersearch-results.open-split",
M_OPEN_NEW_SPLIT));
}
if (!(userObj instanceof HyperSearchFolderNode))
popupMenu.add(new RemoveTreeNodeAction());
if(!(userObj instanceof HyperSearchResult))
popupMenu.add(new ExpandChildTreeNodesAction());
if (userObj instanceof HyperSearchFolderNode
|| userObj instanceof HyperSearchOperationNode)
{
popupMenu.add(new CollapseChildTreeNodesAction());
if (userObj instanceof HyperSearchFolderNode)
{
popupMenu.add(new NewSearchAction());
}
else
{
popupMenu.add(new JPopupMenu.Separator());
HyperSearchOperationNode resultNode = (HyperSearchOperationNode)userObj;
JCheckBoxMenuItem chkItem =
new JCheckBoxMenuItem(jEdit.getProperty("hypersearch-results.tree-view"),
resultNode.isTreeViewDisplayed());
chkItem.addActionListener(new TreeDisplayAction());
popupMenu.add(chkItem);
popupMenu.add(new RedoSearchAction((HyperSearchOperationNode)userObj));
}
}
popupMenu.add(new CopyToClipboardAction());
GenericGUIUtilities.showPopupMenu(popupMenu,evt.getComponent(),
evt.getX(),evt.getY());
evt.consume();
} //}}}
//}}}
} //}}}
//{{{ RemoveTreeNodeAction class
class RemoveTreeNodeAction extends AbstractAction
{
RemoveTreeNodeAction()
{
super(jEdit.getProperty("hypersearch-results.remove-node"));
}
@Override
public void actionPerformed(ActionEvent evt)
{
removeSelectedNode();
}
}//}}}
//{{{ RemoveAllTreeNodesAction class
class RemoveAllTreeNodesAction extends AbstractAction
{
RemoveAllTreeNodesAction()
{
super(jEdit.getProperty("hypersearch-results.remove-all-nodes"));
}
@Override
public void actionPerformed(ActionEvent evt)
{
removeAllNodes();
}
}//}}}
//{{{ NewSearchAction class
class NewSearchAction extends AbstractAction
{
NewSearchAction()
{
super(jEdit.getProperty("hypersearch-results.new-search"));
}
@Override
public void actionPerformed(ActionEvent evt)
{
TreePath path = resultTree.getSelectionPath();
DefaultMutableTreeNode operNode = (DefaultMutableTreeNode)path.getLastPathComponent();
HyperSearchFolderNode nodeObj = (HyperSearchFolderNode)operNode.getUserObject();
String glob = "*";
SearchFileSet dirList = SearchAndReplace.getSearchFileSet();
if (dirList instanceof DirectoryListSet)
glob = ((DirectoryListSet)dirList).getFileFilter();
SearchAndReplace.setSearchFileSet(new DirectoryListSet(
nodeObj.getNodeFile().getAbsolutePath(),glob,true));
SearchDialog.showSearchDialog(view,null,SearchDialog.DIRECTORY);
}
}//}}}
//{{{ ExpandChildTreeNodesAction class
class ExpandChildTreeNodesAction extends AbstractAction
{
ExpandChildTreeNodesAction()
{
super(jEdit.getProperty("hypersearch-results.expand-child-nodes"));
}
@Override
public void actionPerformed(ActionEvent evt)
{
TreePath path = resultTree.getSelectionPath();
DefaultMutableTreeNode operNode = (DefaultMutableTreeNode)path.getLastPathComponent();
expandAllNodes(operNode);
}
}//}}}
//{{{ CopyToClipboardAction class
class CopyToClipboardAction extends AbstractAction
{
CopyToClipboardAction()
{
super(jEdit.getProperty("hypersearch-results.copy-to-clipboard"));
}
@Override
public void actionPerformed(ActionEvent evt)
{
TreePath path = resultTree.getSelectionPath();
DefaultMutableTreeNode operNode = (DefaultMutableTreeNode)
path.getLastPathComponent();
ToStringNodes toStringNodes = new ToStringNodes();
traverseNodes(operNode, toStringNodes);
StringSelection selection = new StringSelection(
toStringNodes.nodesString.toString());
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(selection, null);
}
}//}}}
//{{{ ToStringNodes class
static class ToStringNodes implements HyperSearchTreeNodeCallback
{
StringBuilder nodesString = new StringBuilder();
@Override
public boolean processNode(DefaultMutableTreeNode node)
{
Object userObject = node.getUserObject();
if (userObject instanceof HyperSearchFileNode)
nodesString.append(((HyperSearchFileNode)userObject).path);
else if (userObject instanceof HyperSearchResult)
{
HyperSearchResult hsr = (HyperSearchResult)userObject;
// Copy the ORIGINAL line from the buffer!
nodesString.append(hsr.buffer == null ? hsr.toString() : hsr.buffer.getLineText(hsr.line));
}
else
nodesString.append(userObject.toString());
nodesString.append('\n');
return true;
}
}//}}}
//{{{ CollapseChildTreeNodesAction class
class CollapseChildTreeNodesAction extends AbstractAction
{
CollapseChildTreeNodesAction()
{
super(jEdit.getProperty("hypersearch-results.collapse-child-nodes"));
}
@Override
public void actionPerformed(ActionEvent evt)
{
TreePath path = resultTree.getSelectionPath();
DefaultMutableTreeNode operNode = (DefaultMutableTreeNode)path.getLastPathComponent();
for (Enumeration e = operNode.children(); e.hasMoreElements();)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.nextElement();
resultTree.collapsePath(new TreePath(node.getPath()));
}
resultTree.scrollPathToVisible(
new TreePath(operNode.getPath()));
}
}//}}}
//{{{ RedoSearchAction class
class RedoSearchAction extends AbstractAction
{
private final HyperSearchOperationNode hyperSearchOperationNode;
RedoSearchAction(HyperSearchOperationNode hyperSearchOperationNode)
{
super(jEdit.getProperty("hypersearch-results.redo"));
this.hyperSearchOperationNode = hyperSearchOperationNode;
}
/**
* Invoked when an action occurs.
*/
@Override
public void actionPerformed(ActionEvent e)
{
SearchAndReplace.setSearchString(hyperSearchOperationNode.getSearchString());
SearchAndReplace.setSearchMatcher(hyperSearchOperationNode.getSearchMatcher());
removeSelectedNode();
SearchAndReplace.hyperSearch(view, false);
}
} //}}}
//{{{ TreeDisplayAction class
class TreeDisplayAction extends AbstractAction
{
@Override
public void actionPerformed(ActionEvent evt)
{
JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
boolean curState = menuItem.isSelected();
TreePath path = resultTree.getSelectionPath();
DefaultMutableTreeNode operNode = (DefaultMutableTreeNode)path.getLastPathComponent();
HyperSearchOperationNode operNodeObj = (HyperSearchOperationNode)operNode.getUserObject();
if (curState)
operNodeObj.cacheResultNodes(operNode);
operNode.removeAllChildren();
if (curState)
{
Exception excp = null;
try
{
operNodeObj.insertTreeNodes(resultTree, operNode);
}
catch (Exception ex)
{
operNodeObj.restoreFlatNodes(resultTree, operNode);
menuItem.setSelected(false);
excp = ex;
}
finally
{
((DefaultTreeModel)resultTree.getModel()).nodeStructureChanged(operNode);
expandAllNodes(operNode);
resultTree.scrollPathToVisible(
new TreePath(operNode.getPath()));
}
if (excp != null)
throw new RuntimeException(excp);
}
else
operNodeObj.restoreFlatNodes(resultTree, operNode);
operNodeObj.setTreeViewDisplayed(menuItem.isSelected());
}
}//}}}
//{{{ expandAllNodes() method
public void expandAllNodes(DefaultMutableTreeNode node)
{
traverseNodes(node, new TreeNodeCallbackAdapter()
{
@Override
public boolean processNode(DefaultMutableTreeNode node)
{
resultTree.expandPath(new TreePath(node.getPath()));
return true;
}
});
} //}}}
//{{{ GoToNodeAction class
class GoToNodeAction extends AbstractAction
{
private final int mode;
GoToNodeAction(String labelProp, int mode)
{
super(jEdit.getProperty(labelProp));
this.mode = mode;
}
@Override
public void actionPerformed(ActionEvent evt)
{
goToSelectedNode(mode);
}
}//}}}
//{{{ ResultCellRenderer class
static class ResultCellRenderer extends EnhancedTreeCellRenderer
{
//{{{ ResultCellRenderer constructor
ResultCellRenderer()
{
plainFont = UIManager.getFont("Tree.font");
if(plainFont == null)
plainFont = jEdit.getFontProperty("metal.secondary.font");
boldFont = new Font(plainFont.getName(),Font.BOLD,
plainFont.getSize());
} //}}}
@Override
protected TreeCellRenderer newInstance()
{
return new ResultCellRenderer();
}
//{{{ getTreeCellRendererComponent() method
@Override
protected void configureTreeCellRendererComponent(JTree tree,
Object value, boolean sel, boolean expanded,
boolean leaf, int row, boolean hasFocus)
{
setIcon(null);
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
if (node.getUserObject() instanceof HyperSearchOperationNode)
{
setFont(boldFont);
CountNodes countNodes = new CountNodes();
traverseNodes(node, countNodes);
setText(jEdit.getProperty("hypersearch-results.result-caption",
new Object[] {
node.toString(),
Integer.valueOf(countNodes.resultCount),
Integer.valueOf(countNodes.bufferCount)
}));
}
else if(node.getUserObject() instanceof HyperSearchFolderNode)
{
setFont(plainFont);
setText(node.toString() + " (" + node.getChildCount() + " files/folders)");
}
else if(node.getUserObject() instanceof HyperSearchFileNode)
{
// file name
setFont(boldFont);
HyperSearchFileNode hyperSearchFileNode = (HyperSearchFileNode) node.getUserObject();
setText(jEdit.getProperty("hypersearch-results.file-caption",
new Object[] {
hyperSearchFileNode,
Integer.valueOf(hyperSearchFileNode.getCount()),
Integer.valueOf(node.getChildCount())
}));
}
else
{
setFont(plainFont);
}
} //}}}
//{{{ CountNodes class
static class CountNodes implements HyperSearchTreeNodeCallback
{
int bufferCount;
int resultCount;
@Override
public boolean processNode(DefaultMutableTreeNode node)
{
Object userObject = node.getUserObject();
if (userObject instanceof HyperSearchFileNode)
{
resultCount += ((HyperSearchFileNode)userObject).getCount();
bufferCount++;
}
return true;
}
}//}}}
Font plainFont, boldFont;
} //}}}
//{{{
class ResultTreeTransferHandler extends TransferHandler
{
@Override
public void exportToClipboard(JComponent comp, Clipboard clip,
int action) throws IllegalStateException
{
TreePath [] paths = resultTree.getSelectionPaths();
ToStringNodes toStringNodes = new ToStringNodes();
for (TreePath path: paths)
{
DefaultMutableTreeNode operNode = (DefaultMutableTreeNode)
path.getLastPathComponent();
toStringNodes.processNode(operNode);
}
StringSelection selection = new StringSelection(
toStringNodes.nodesString.toString());
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(selection, null);
}
} //}}}
// these are used to eliminate code duplication. i don't normally use
// the visitor or "template method" pattern, but this code was contributed
// by Peter Cox and i don't feel like changing it around too much.
//{{{ ResultVisitor interface
interface ResultVisitor
{
void visit(Buffer buffer, HyperSearchResult result);
} //}}}
//{{{ BufferLoadedVisitor class
static class BufferLoadedVisitor implements ResultVisitor
{
@Override
public void visit(Buffer buffer, HyperSearchResult result)
{
result.bufferOpened(buffer);
}
} //}}}
//{{{ BufferClosedVisitor class
static class BufferClosedVisitor implements ResultVisitor
{
@Override
public void visit(Buffer buffer, HyperSearchResult result)
{
result.bufferClosed();
}
} //}}}
//{{{ TreeNodeCallbackAdapter class
static class TreeNodeCallbackAdapter implements HyperSearchTreeNodeCallback
{
@Override
public boolean processNode(DefaultMutableTreeNode node)
{
return false;
}
} //}}}
}