content: /*
   * 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.indexing.SearchQuery;
  import it.unibz.instasearch.indexing.SearchResultDoc;
  
  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.Job;
  import org.eclipse.core.runtime.jobs.JobChangeAdapter;
  import org.eclipse.jface.action.IStatusLineManager;
  import org.eclipse.jface.viewers.TreeViewer;
  import org.eclipse.swt.widgets.Display;
  import org.eclipse.swt.widgets.TreeItem;
  
  /**
   * A search job that runs search in UI thread.
   * Is used to start search after user has stopped typing
   */
  public class SearchJob extends Job {
  	
  	/**
  	 * 
  	 */
  	private TreeViewer resultViewer = null;
  	private SearchQuery searchQuery = null;
  	private boolean selectLast = false;
  	private InstaSearchView searchView;
  	private IStatusLineManager statusLineManager;
  	
  	public SearchJob(InstaSearchView searchView)
  	{
  		super("Search");
  		
  		this.searchView = searchView;
  		this.resultViewer = searchView.getResultViewer();
  		this.statusLineManager = searchView.getViewSite().getActionBars().getStatusLineManager();
  		
  		this.addJobChangeListener(new JobChangeAdapter(){
  			public void done(IJobChangeEvent event) {
  				if( event.getResult() == Status.CANCEL_STATUS )
  					cancelSearch();
  			}
  		});
  		
  		setPriority(Job.INTERACTIVE);
  	}
  	
  	public void schedule(SearchQuery searchQuery, boolean selectLast, long delay) {
  		cancelSearch();
  		
  		this.searchQuery = searchQuery;
  		this.selectLast = selectLast;
  		
  		if( this.searchQuery != null ) {
  			this.searchQuery.setCanceled(false);
  			this.searchQuery.setCurrentProject( InstaSearchUI.getActiveProject() );
  			
  			schedule(delay);
  		}
  	}
  	
  	/**
  	 * Cancel current search
  	 */
  	private void cancelSearch() {
  		if( searchQuery != null )
  			searchQuery.setCanceled(true);
  	}
  	
  	protected IStatus run(IProgressMonitor monitor)
  	{
  		ResultContentProvider prov = (ResultContentProvider) resultViewer.getContentProvider();
  		
  		if(  prov == null || monitor.isCanceled() || searchQuery == null || searchQuery.isCanceled() ) return Status.CANCEL_STATUS;
  		
  		prov.getElements(searchQuery); // runs query and caches result. running in this job to avoid UI freeze
  		
  		if( monitor.isCanceled() || searchQuery.isCanceled() ) return Status.CANCEL_STATUS; // perhaps we cancelled while executing query
  		
  		Display display = resultViewer.getControl().getDisplay();
  		
  		display.syncExec(new Runnable() {	
  			public void run() {
  				search(); // run in UI
  			}
  		});
  		
  		return Status.OK_STATUS;
  	}
  	
  	private void search() {
  		int items = resultViewer.getTree().getItemCount();
  		
  		resultViewer.setInput(searchQuery); // run in UI, displays cached results immediately
  		
  		int newItems = resultViewer.getTree().getItemCount();
  		
  		if( selectLast && items != 0 && newItems >= items )
  			resultViewer.getTree().setSelection(resultViewer.getTree().getItem(items-1)); // show previous selection
  		
  		int resultCount = countSearchResults(newItems);
  		String resultsDesc = " " + (searchQuery.isExact() ? "exact" : "similar") + " results";
  		
  		if( searchQuery.isLimited() ) // limited
  		{
  			if( resultCount >= searchQuery.getMaxResults() )
  				statusLineManager.setMessage(searchView.getTitleImage(), searchQuery.getMaxResults() + "+" + resultsDesc + " (Ctrl+Enter to see all)");
  			else 
  				statusLineManager.setMessage(searchView.getTitleImage(), resultCount + resultsDesc);
  		}
  		else { // not limited
  			statusLineManager.setMessage(searchView.getTitleImage(), resultCount + resultsDesc);
  		}
  	}
  
  	/**
  	 * @param newItems
  	 * @return
  	 */
  	private int countSearchResults(int newItems) {
  		
  		int i, allItems = resultViewer.getTree().getItemCount();
  		
  		for(i = allItems-1; i >= 0; i--)
  		{
  			TreeItem treeItem = resultViewer.getTree().getItem(i);
  			
  			if( treeItem == null || treeItem.getData() == null )
  				continue;
  			
  			if( treeItem.getData() instanceof SearchResultDoc ) // from now on we have results
  				return i+1;
  		}
  		
  		return 0;
  	}
  
  	
  }