/*
* Copyright (c) 2009 Andrejs Jermakovics.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrejs Jermakovics - initial implementation
*/
package it.unibz.instasearch.ui;
import it.unibz.instasearch.InstaSearchPlugin;
import it.unibz.instasearch.indexing.SearchResultDoc;
import it.unibz.instasearch.ui.ResultContentProvider.MatchLine;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.Match;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.MarkerUtilities;
class MatchHighlightJob extends Job implements ISchedulingRule, IPartListener
{
private SearchResultDoc doc;
private MatchLine selectedLineMatches;
private ResultContentProvider contentProvider;
private ITextEditor editor;
private Job searchJob;
private IDocument document;
private IWorkbenchPage workbenchPage;
private IFile file;
/**
* @param doc
* @param selectedLineMatches
* @param contentProvider
* @param workbenchPage
* @throws Exception
*/
public MatchHighlightJob(SearchResultDoc doc, MatchLine selectedLineMatches, ResultContentProvider contentProvider, Job searchJob, IWorkbenchPage workbenchPage) throws Exception
{
super("Highlight Matches");
this.doc = doc;
this.selectedLineMatches = selectedLineMatches;
this.contentProvider = contentProvider;
this.searchJob = searchJob;
this.workbenchPage = workbenchPage;
IEditorInput input = contentProvider.getEditorInput(doc);
IEditorDescriptor editorDesc = IDE.getEditorDescriptor(doc.getFileName());
IEditorPart editorPart = IDE.openEditor(workbenchPage, input, editorDesc.getId());
this.editor = getTextEditor(input, editorPart);
this.document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
IStorage storage = contentProvider.getStorage(doc);
this.file = null;
if( storage instanceof IFile )
file = (IFile) storage;
}
@Override
protected IStatus run(IProgressMonitor monitor)
{
try {
if( editor != null )
{
workbenchPage.addPartListener(this);
findAndHighlightMatches(doc, monitor);
}
} catch (Exception e) {
InstaSearchPlugin.log(e);
} finally {
workbenchPage.removePartListener(this);
}
return Status.OK_STATUS;
}
private void findAndHighlightMatches(SearchResultDoc doc, final IProgressMonitor monitor)
throws Exception, BadLocationException, CoreException
{
if( file != null )
{
deleteMarkers();
addMarkerRemover(file);
}
MatchHighliter highlighter = new MatchHighliter(monitor);
if( selectedLineMatches != null )
highlighter.highlightMatchLine(file, selectedLineMatches);
contentProvider.getMatchLines(doc, false, highlighter);
//highlightMatches(file, selectedLineMatches, lineMatchesList, monitor);
}
private class MatchHighliter implements ResultContentProvider.MatchFindCallback
{
private IProgressMonitor monitor;
private boolean matchSelected = false;
/**
* @param monitor
*/
public MatchHighliter(IProgressMonitor monitor)
{
this.monitor = monitor;
}
public void matchFound(MatchLine line)
{
try
{
highlightMatchLine(file, line);
}
catch (Exception e)
{
InstaSearchPlugin.log(e);
monitor.setCanceled(true);
}
}
public boolean isCanceled()
{
return monitor.isCanceled();
}
/**
* @param file
* @throws CoreException
* @throws BadLocationException
*/
protected void highlightMatchLine(IFile file, MatchLine lineMatches) throws CoreException, BadLocationException
{
int lineNum = lineMatches.getLineNumber();
if( document.getNumberOfLines() < lineNum )
return;
if( selectedLineMatches == null ) selectedLineMatches = lineMatches; // pick first
int pos = document.getLineOffset(lineNum-1);
for(Match match: lineMatches.getMatches())
{
if( monitor.isCanceled() ) break;
editor.setHighlightRange(pos+match.getOffset(), match.getLength(), false);
IMarker marker = createMarker(pos, lineNum, match, file, editor);
if(selectedLineMatches != null && lineNum == selectedLineMatches.getLineNumber())
{
if( marker != null && ! matchSelected )
{
gotoEditorMarker(marker);
matchSelected = true;
}
if( !matchSelected ) {
int selectedOffset = pos+match.getOffset();
int selectedLength = match.getLength();
editor.setHighlightRange(selectedOffset, selectedLength, true);
matchSelected = true;
}
}
}
}
private IMarker createMarker(int pos, int lineNumber, Match match, IFile file, ITextEditor textEditor) throws CoreException {
if( file == null )
return null;
IMarker marker = null;
marker = file.createMarker(NewSearchUI.SEARCH_MARKER);
marker.setAttribute(IMarker.TRANSIENT, true);
marker.setAttribute(IMarker.MESSAGE, match.getElement());
MarkerUtilities.setLineNumber(marker, lineNumber);
MarkerUtilities.setCharStart(marker, pos + match.getOffset());
MarkerUtilities.setCharEnd(marker, pos + match.getOffset() + match.getLength());
return marker;
}
private void gotoEditorMarker(final IMarker selectedMarker)
{
if( selectedMarker == null )
return;
Runnable runnable = new Runnable()
{
public void run()
{
IDE.gotoMarker(editor, selectedMarker);
}
};
editor.getSite().getShell().getDisplay().asyncExec(runnable);
}
}
private ITextEditor getTextEditor(IEditorInput input, IEditorPart editor)
{
if( editor instanceof MultiPageEditorPart )
{
MultiPageEditorPart multiPageEditor = (MultiPageEditorPart) editor;
IEditorPart[] editors = multiPageEditor.findEditors(input);
for(IEditorPart ed: editors)
{
if( ed instanceof ITextEditor )
{
multiPageEditor.setActiveEditor(ed);
return (ITextEditor) ed;
}
}
}
else if( editor instanceof ITextEditor )
{
return (ITextEditor) editor;
}
return null;
}
/**
* Remove markers when new search is made
* @param file
*/
private void addMarkerRemover(final IFile file)
{
searchJob.addJobChangeListener(new JobChangeAdapter(){
public void done(IJobChangeEvent event) {
try {
cancel();
deleteMarkers();
} finally {
searchJob.removeJobChangeListener(this);
}
}
});
}
public boolean contains(ISchedulingRule rule) {
return rule.getClass() == this.getClass();
}
public boolean isConflicting(ISchedulingRule rule) {
return rule.getClass() == this.getClass();
}
public void partClosed(IWorkbenchPart part)
{
if( part == editor ) // if closing editor
{
this.cancel(); // cancel highlight job
deleteMarkers();
}
}
private void deleteMarkers()
{
try
{
if( file != null )
file.deleteMarkers(NewSearchUI.SEARCH_MARKER, true, IResource.DEPTH_INFINITE);
}
catch (CoreException e)
{
InstaSearchPlugin.log(e);
}
}
public void partDeactivated(IWorkbenchPart part)
{
}
public void partOpened(IWorkbenchPart part)
{
}
public void partActivated(IWorkbenchPart part)
{
}
public void partBroughtToTop(IWorkbenchPart part)
{
}
}