package Schmortopf.SearchResults;
/**
* This tree displays the results of text searches.
* It contains nodes for all files containing the search text
* and leafs, which force the editor to display the associated
* file and jump to the line and position of the found text,
* when the user clicks that leaf.
*/
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import Schmortopf.Main.IDE_ProjectFrameProvider;
import Schmortopf.Main.GCMemoryChecker;
import Schmortopf.Main.FilesTreeProvider;
import Schmortopf.ProjectFiles.ProjectFilesTree.*;
import Schmortopf.Utility.ThreadEngine.ThreadEngine;
import Schmortopf.Utility.Text.*;
import Schmortopf.Utility.IniFile.IniFile;
import Schmortopf.Utility.gui.dialogs.ProgressWindow;
import Schmortopf.Utility.gui.dialogs.TextSearchDialog;
import Schmortopf.Utility.SchmortopfConstants;
import Schmortopf.FileComponents.Model.EditableLeafObject;
import Schmortopf.Libraries.LibrariesTree;
import Schmortopf.Libraries.LibrariesTreeLeafObject;
import Schmortopf.FileStructure.FileStructureDescriptionFactory;
import Schmortopf.FileStructure.Descriptions.FileStructureDescription;
import Language.Language;
public class SearchResultsTree extends JTree
{
private IDE_ProjectFrameProvider projectFrame;
private SearchResultsTreeModel model;
private boolean readyForSpecialUpdates = false;
// Needed, as some updateUI() calls are too early
// for the special UI updates, cause some components
// could not be existing yet.
private long lastGCCallTime = 0; // used by checkMemory()
private String treeName = "undefined";
/**
* Constructor
* @param _projectFrame the project frame as controller
* @param _treeName the name displayed in the search tabs
*/
public SearchResultsTree( final IDE_ProjectFrameProvider _projectFrame,
final String _treeName )
{
this.projectFrame = _projectFrame;
this.treeName = _treeName;
this.model = new SearchResultsTreeModel();
this.setModel( this.model );
this.setCellRenderer( new SearchResultsTreeCellRenderer() );
final SearchResultsTree thisTree = this;
this.addTreeSelectionListener( new TreeSelectionListener()
{
public void valueChanged(TreeSelectionEvent e)
{
final DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
thisTree.getLastSelectedPathComponent();
if( selectedNode != null )
{
final Object userObject = selectedNode.getUserObject();
if( userObject instanceof SearchResultsUserObject )
{
final SearchResultsUserObject searchObject = (SearchResultsUserObject)userObject;
showSearchHit( searchObject ); // does all what's needed :
}
else
if( userObject instanceof SearchResultsFileNodeUserObject )
{
// This is true for results of filename searches, where the file as whole
// is the subject and should just be displayed, without jumping to a line in it:
final SearchResultsFileNodeUserObject searchObject = (SearchResultsFileNodeUserObject)userObject;
showFileNodeHit( searchObject ); // does all what's needed :
}
} // if node != null
}
});
// The mouse listener :
MouseListener mouseListener = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
processLeftMouseClick( e.getX(), e.getY() );
}
};
this.addMouseListener( mouseListener );
EventQueue.invokeLater( new Runnable()
{
public void run()
{
readyForSpecialUpdates = true;
}
});
} // Constructor
public String getTreeName() {
return this.treeName;
}
/**
* Called when the user leftclicks on a tree element.
*/
public void processLeftMouseClick( int xMouseCoord, int yMouseCoord )
{
if( this.getPathForLocation( xMouseCoord, yMouseCoord ) != null )
{
this.setSelectionPath( this.getPathForLocation( xMouseCoord, yMouseCoord ) );
// If all this worked, we now should get the selectedNode as valid object :
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)this.getLastSelectedPathComponent();
if( selectedNode != null )
{
Object userObject = selectedNode.getUserObject();
if( userObject instanceof SearchResultsUserObject )
{
final SearchResultsUserObject searchObject = (SearchResultsUserObject)userObject;
showSearchHit( searchObject ); // does all what's needed :
}
else
if( userObject instanceof SearchResultsFileNodeUserObject )
{
// This is true for results of filename searches, where the file as whole
// is the subject and should just be displayed, without jumping to a line in it:
final SearchResultsFileNodeUserObject searchObject = (SearchResultsFileNodeUserObject)userObject;
showFileNodeHit( searchObject ); // does all what's needed :
}
}
}
} // popupRequest
/**
* Called when the user selects a search leaf entry.
* This method then calls the editor and tells it to display
* the associated document and jump to the linenumber stored
* in the passed userobject.
*/
private void showSearchHit( SearchResultsUserObject searchObject )
{
final EditableLeafObject leaf = searchObject.getEditableLeafObject();
final int lineNumber = searchObject.getLineNumber();
// Call the projectfilestree or the libraries tree.
// Decouple it from the EDT using aThreadEngine task.
// The called methods will switch back to the EDT, so its no risk to call
// them outside the EDT here.
Runnable searchHitSelectorTask = new Runnable()
{
public void run()
{
// Give Swing some cleanup time:
try{ Thread.sleep(44); } catch( Exception anythingGoes ){}
if( leaf instanceof ProjectFilesTreeLeafObject )
{
projectFrame.selectProjectFilesTreeNodeFor( leaf.getFilePathName(),lineNumber,0,true );
}
if( leaf instanceof LibrariesTreeLeafObject )
{
projectFrame.selectLibrariesTreeNodeFor( leaf.getJarFilePath(),leaf.jarFileEntryName(),lineNumber,0 );
}
}
};
ThreadEngine.getInstance().addRunnable( searchHitSelectorTask, "searchHitSelectorTask");
} // showSearchHit
/**
* This is used, when the user clicks a filenode after a filename search,
* where the tree has depth one.
*/
private void showFileNodeHit( SearchResultsFileNodeUserObject searchObject )
{
// Call the projectfilestree or the libraries tree :
if( searchObject.getBelongsToProject() )
{
this.projectFrame.selectProjectFilesTreeNodeFor( searchObject.getFilePathName(),-1,-1,false );
}
else
{
final EditableLeafObject leaf = searchObject.getEditableLeafObject();
if( leaf != null )
{
this.projectFrame.selectLibrariesTreeNodeFor( leaf.getJarFilePath(),
leaf.jarFileEntryName(),
-1,-1 );
// For an editable leaf, we must have:
// searchObject.getFilePathName() = leaf.getJarFilePath()
// and
// searchObject.getFileName() = leaf.jarFileEntryName(),lineNumber );
}
}
} // showFileNodeHit
public void initializeTreeWithSingleNode( final String nodeTitle )
{
EventQueue.invokeLater( new Runnable()
{
public void run()
{
model.initializeModel(nodeTitle);
}
});
}
/**
* Overwritten method. Additionally updates special components.
*/
public void updateUI()
{
super.updateUI();
if( this.readyForSpecialUpdates )
{
// Only thing to do: Recreate the renderer.
this.setCellRenderer( new SearchResultsTreeCellRenderer() );
}
}
/**
* GC assistance, before this object is decoupled from the application.
* It should release reference connections.
* Called by the projectframe when it's closed.
* Calling method is projectframe.doFinalCleanUp() [ in a ThreadEngine thread ]
*/
public void terminate()
{
this.projectFrame = null;
this.model.initializeModel(""); // has GC assistance
this.model = null;
} // terminate
/**
* This is called from the SymbolUsageLocationSearch, which passes
* its results to this tree.
*/
public void setTreeContentWithObjectSearchResults( final SearchResultsUserObject[] searchResultsUserObjects )
{
this.model.initializeModel("Usage Search Results");
this.model.initializeTreeWith( searchResultsUserObjects );
// A small netiquette: If there is only one node, open it automatically:
DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)this.model.getRoot();
if( rootNode != null ) {
if( rootNode.getChildCount() == 1 ) {
DefaultMutableTreeNode firstChildNode = (DefaultMutableTreeNode)rootNode.getChildAt(0);
if( firstChildNode.getChildCount() > 0 ) {
DefaultMutableTreeNode firstLeaf = (DefaultMutableTreeNode)firstChildNode.getChildAt(0);
this.expandPath( new TreePath( firstChildNode.getPath() ) );
}
}
}
} // setTreeContentWithObjectSearchResults
} // SearchResultsTree
|