Package it.unibz.instasearch.ui

Source Code of it.unibz.instasearch.ui.InstaSearchView$ExpandCollapseJob

/*
* 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.actions.CheckUpdatesActionDelegate;
import it.unibz.instasearch.actions.ShowExceptionAction;
import it.unibz.instasearch.indexing.Field;
import it.unibz.instasearch.indexing.SearchQuery;
import it.unibz.instasearch.indexing.SearchResultDoc;
import it.unibz.instasearch.jobs.CheckUpdatesJob;
import it.unibz.instasearch.prefs.PreferenceConstants;
import it.unibz.instasearch.ui.ResultContentProvider.MatchLine;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;

import org.eclipse.core.runtime.ILogListener;
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.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;

/**
*
*/
public class InstaSearchView extends ViewPart implements ModifyListener, ILogListener, ITreeViewerListener, IPropertyChangeListener {

  /** The view ID */
  public static final String ID = InstaSearchView.class.getName(); // we have just one view
 
  /** Tree for results */
  private TreeViewer resultViewer;
  /** Textbox for search query */
  private StyledText searchText;
 
  private IAction openAction;
  private SearchJob searchJob;
  private ExpandCollapseJob expandCollapseJob;

  private ResultContentProvider contentProvider;
  private int lastIncrementalSearchPos = 0;
 
  // preferences
  private int maxResults;
  private int typingSearchDelay;
  private boolean incrementalSearchEnabled;
 
  private SearchViewControl searchViewControl;
 
  /**
   *
   */
  public InstaSearchView() {
  }
 
  @Override
  public void init(IViewSite site) throws PartInitException
  {
    super.init(site);
   
    scheduleUpdateCheck()// schedule update check if view is opened
   
    InstaSearchPlugin.getDefault().getLog().addLogListener(this); // listen for exceptions
   
    initPrefs();
   
    InstaSearchPlugin.addPreferenceChangeListener(this);
  }

  private void initPrefs() {
    typingSearchDelay = InstaSearchPlugin.getIntPref(PreferenceConstants.P_TYPING_SEARCH_DELAY);
    maxResults = InstaSearchPlugin.getIntPref(PreferenceConstants.P_SHOWN_FILES_COUNT);
    incrementalSearchEnabled = InstaSearchPlugin.getBoolPref(PreferenceConstants.P_INCREMENTAL_SEARCH);
  }
 
  @Override
  public void createPartControl(Composite parent) {
   
    this.searchViewControl = new SearchViewControl(parent, this);
   
    searchText = searchViewControl.getSearchText();
    resultViewer = searchViewControl.getResultViewer();
   
    searchText.addModifyListener(this);
   
    contentProvider = new ResultContentProvider();
    IStyledLabelProvider labelProvider = new ResultLabelProvider(contentProvider);
    IBaseLabelProvider decoratedLabelProvider = new DecoratingStyledCellLabelProvider(labelProvider, null, null);
   
    configureResultViewer(contentProvider, decoratedLabelProvider);
    searchViewControl.setContentProposalAdapter(new SearchContentProposalProvider(contentProvider));
   
    searchJob = new SearchJob(this);
    expandCollapseJob = new ExpandCollapseJob();
   
    makeActions();
    hookContextMenu();
    hookDoubleClickAction();
  }
 
  public void modifyText(ModifyEvent e)
  {
    searchJob.cancel();
   
    StyleRange[] styleRanges = createStyledSearchString(searchText.getText());
    searchText.setStyleRanges(styleRanges);
   
    if( searchViewControl.isShowingSearchTip() ) return; // showing search tip
   
    searchJob.schedule(getSearchQuery(), false, typingSearchDelay); // start with a delay since user might still be typing
   
    if( incrementalSearchEnabled )
      doIncrementalSearch();
  }

  private void doIncrementalSearch()
  {
    IEditorPart editor = InstaSearchUI.getActiveEditor();
   
    if( editor != null )
    {
      IFindReplaceTarget target = (IFindReplaceTarget) editor.getAdapter(IFindReplaceTarget.class);
     
      if( target != null )
        lastIncrementalSearchPos = target.findAndSelect(lastIncrementalSearchPos, searchText.getText(), true, false, false) + searchText.getText().length();
    }
  }
 
  /**
   * Highlight fields
   *
   * @param text
   * @return bold ranges
   */
  private static StyleRange[] createStyledSearchString(String text)
  {
    //TODO: use parser
    ArrayList<StyleRange> styleRanges = new ArrayList<StyleRange>();
    String lcaseText = text.toLowerCase(Locale.ENGLISH);
   
    ArrayList<String> fieldsToHighlight = new ArrayList<String>(Field.values().length);
    for(Field field: Field.values()) // add all field names
      fieldsToHighlight.add(field.toString());
   
    for(String fieldName: fieldsToHighlight)
    {
      int pos = lcaseText.indexOf(fieldName + ':'); // should use a parser here
     
      while( pos != -1 ) {
        styleRanges.add(new StyleRange(pos, fieldName.length(), null, null, SWT.BOLD));
        pos = lcaseText.indexOf(fieldName + ':', pos+fieldName.length()-1); // find next
      }
    }
   
    // ranges must be sorted by start position
    Collections.sort(styleRanges, new Comparator<StyleRange>() {
      public int compare(StyleRange sr1, StyleRange sr2) {
        return sr1.start - sr2.start;
      }
    });
   
    //TODO: highlight AND, OR
    return styleRanges.toArray(new StyleRange[styleRanges.size()]);
  }

  void setSearchString(String searchString)
  {
    searchText.setText(searchString);
  }
 
  /**
   * Starts the search by giving search string as input to the viewer
   *
   * @param searchQuery
   * @param selectLast whether to select the item which is currently last
   */
  void search(SearchQuery searchQuery, boolean selectLast) {
    searchJob.cancel(); // cancel previous search
    searchQuery.setFilter( searchViewControl.getFilter() );
    searchJob.schedule(searchQuery, selectLast, 0);
  }
 
  private SearchQuery getSearchQuery()
  {
    SearchQuery sq = new SearchQuery(getSearchText(), maxResults );
    sq.setFilter( searchViewControl.getFilter() );
    return sq;
  }
 
  String getSearchText() {
    return searchText.getText().trim();
  }

  TreeViewer getResultViewer() {
    return resultViewer;
  }
 
  public void treeExpanded(TreeExpansionEvent event) {
    
     if( event.getElement() instanceof SearchQuery ) {
       search((SearchQuery)event.getElement(), true);
     }
  }
 
  public void treeCollapsed(TreeExpansionEvent event) {
  }
 
  private void configureResultViewer(ResultContentProvider contentProvider, IBaseLabelProvider decoratedLabelProvider) {
   
    resultViewer.setContentProvider(contentProvider);
    resultViewer.setLabelProvider(decoratedLabelProvider);
    resultViewer.setSorter(null);
   
    getViewSite().setSelectionProvider(resultViewer);
    resultViewer.addTreeListener(this);
   
    resultViewer.getControl().addMouseTrackListener(new MouseTrackAdapter()
    {
      public void mouseHover(MouseEvent e)
      {
        ViewerCell cell = resultViewer.getCell(new Point(e.x, e.y));
       
        if( cell != null && cell.getElement() instanceof SearchResultDoc )
        {
          SearchResultDoc doc = (SearchResultDoc) cell.getElement();
          resultViewer.getTree().setToolTipText(doc.getFilePath());
        }
        else
        {
          resultViewer.getTree().setToolTipText("");
        }
      }
    });
   
    KeyAdapter keyListener = new KeyAdapter()
    {
      public void keyReleased(KeyEvent e)
      {
        onSearchTextKeyPress(e);
      }
    };
   
    resultViewer.getControl().addKeyListener(keyListener);
    searchText.addKeyListener(keyListener);
  }
 
  private void hookContextMenu()
  {
    MenuManager menuMgr = new MenuManager("#PopupMenu");
   
    menuMgr.setRemoveAllWhenShown(true);
    menuMgr.addMenuListener(new IMenuListener() {
      public void menuAboutToShow(IMenuManager manager) {
        fillContextMenu(manager);
      }
    });
   
    Menu menu = menuMgr.createContextMenu(resultViewer.getControl());
    resultViewer.getControl().setMenu(menu);
    getSite().registerContextMenu(menuMgr, resultViewer);
  }

  private void onSearchTextKeyPress(KeyEvent e)
  {
    if( e.keyCode == SWT.F5 )
    {
      refreshSearch();
    }
    if( e.keyCode == SWT.DEL )
    {
      deleteSelectedMatch();
    }
    if( e.keyCode == (int)'j' && (e.stateMask & SWT.CTRL)!=0 )
    {
      doIncrementalSearch();
    }
    else if( e.keyCode == SWT.TAB )
    {
      resultViewer.getTree().setFocus();
    }
    else if( e.keyCode == SWT.ESC )
    {
      if( expandCollapseJob.getState() == Job.RUNNING )
      {
        expandCollapseJob.cancel();
      }
      else
      {
        if( searchText.getSelectionText().equals(searchText.getText()) )
        {
          searchText.setText("");
        }
        else
        {
          searchText.setFocus();
          searchText.selectAll();
        }
      }
    }
    else if( e.getSource() == searchText && e.keyCode == SWT.CR && (e.stateMask & SWT.CTRL)!=0 )
    {
      showAllResults();
    }
  }
 
  private void fillContextMenu(IMenuManager manager)
  {
    boolean haveSelection = ! resultViewer.getSelection().isEmpty();
    SearchQuery sq = (SearchQuery) resultViewer.getInput();
   
    openAction.setEnabled( haveSelection  );
    manager.add(openAction);
   
    boolean showingItems = resultViewer.getTree().getItemCount() > 0;
   
    Action expandAll = new Action("Expand All", InstaSearchPlugin.getImageDescriptor("expandall")) {
      public void run() {
        expandAll();
      }
    };
    expandAll.setEnabled( showingItems  );
    manager.add(expandAll);
   
    Action collapseAll = new Action("Collapse All", InstaSearchPlugin.getImageDescriptor("collapseall")) {
      public void run() {
        collapseAll();
      }
    };
    collapseAll.setEnabled( showingItems  );
    manager.add(collapseAll);
   
    Action refresh = new Action("Refresh") {
      public void run() {
        refreshSearch();
      }
    };
    refresh.setAccelerator(SWT.F5);
    manager.add(refresh);
   
    Action delete = new Action("Delete Match") {
      public void run() {
        deleteSelectedMatch();
      }
    };
    delete.setAccelerator(SWT.DEL);
    manager.add(delete);
   
    Action moreResults = new Action("More Results...") {
      public void run() {
        showAllResults();
      }
    };
    moreResults.setEnabled( showingItems );
    manager.add(moreResults);
   
    if( sq == null || !sq.isLimited() )
      moreResults.setEnabled(false);
       
  }
 

  private void deleteSelectedMatch() {
    if( getResultViewer().getSelection() == null )
      return;
    IStructuredSelection selection = (IStructuredSelection)resultViewer.getSelection();
    getResultViewer().remove(selection.toArray());
  }
 
  /**
   *
   */
  public void showAllResults() {
    SearchQuery sq = (SearchQuery)resultViewer.getInput();
    SearchQuery newSq = new SearchQuery(sq);
    newSq.setMaxResults(SearchQuery.UNLIMITED_RESULTS);
    search(newSq, false);
  }
 
  /**
   *
   */
  public void expandAll() {
    expandCollapseJob.schedule(true);
  }
 
  /**
   *
   */
  public void collapseAll() {
    expandCollapseJob.schedule(false);
  }
 
  private void openSelection() throws Exception {
    IStructuredSelection selection = (IStructuredSelection)resultViewer.getSelection();
    Object obj = selection.getFirstElement();
   
    SearchResultDoc doc = null;
    MatchLine selectedLineMatches = null;
   
    if(obj instanceof SearchResultDoc) {
      doc = (SearchResultDoc) obj;
    } else if(obj instanceof MatchLine) {
      selectedLineMatches = (MatchLine) obj;
      doc = selectedLineMatches.getResultDoc();
    } else if(obj instanceof Exception) {
      InstaSearchUI.showError((Exception)obj);
      return;
    } else if(obj instanceof SearchQuery ) {
      search( (SearchQuery) obj, true );
      return;
    } else
      return;
   
   
    new MatchHighlightJob(doc, selectedLineMatches, contentProvider, searchJob, getSite().getPage()).schedule();
  }

 
 
  private void hookDoubleClickAction() {
    resultViewer.addDoubleClickListener(new IDoubleClickListener() {
      public void doubleClick(DoubleClickEvent event) {
        openAction.run();
      }
    });
  }
   
  public void setFocus() {
    searchText.setFocus();
    //searchText.selectAll();
  }
 
  private void makeActions() {
    openAction = new Action("Open") {
      public void run() {
        try {
          openSelection();
        } catch (Exception e) {
          InstaSearchPlugin.log(e);
        }
      }
    };
  }
 
  private void scheduleUpdateCheck()
  {
    boolean checkUpdates = InstaSearchPlugin.getBoolPref(PreferenceConstants.P_CHECK_UPDATES);
    if( !checkUpdates )
      return;
   
    CheckUpdatesJob checkUpdatesJob = new CheckUpdatesJob();
    checkUpdatesJob.setSystem(true);
    checkUpdatesJob.addJobChangeListener(new UpdateJobChangeListener());
    checkUpdatesJob.schedule(InstaSearchPlugin.getIntPref(PreferenceConstants.P_UPDATE_CHECK_DELAY));
  }

 
  /**
   * Logging an error in the plugin
   * Create an action that allows reporting it
   */
  public void logging(IStatus status, String plugin)
  {
    IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
   
    IContributionItem item = menuManager.find(ShowExceptionAction.ID);
   
    if( item != null )
      menuManager.remove(item);
   
    ShowExceptionAction action = new ShowExceptionAction(status);
    action.setText("Report Bug");
   
    menuManager.add(action);
  }
 
  @Override
  public void dispose()
  {   
    super.dispose();
   
    if( InstaSearchPlugin.getDefault() != null )
    {
      InstaSearchPlugin.getDefault().getLog().removeLogListener(this);
      InstaSearchPlugin.removePreferenceChangeListener(this);
    }
  }
 
  private void refreshSearch()
  {
    InstaSearchPlugin.getInstaSearch().updateIndex();
   
    SearchQuery input = (SearchQuery) resultViewer.getInput();
    if( input == null ) return;
    resultViewer.setInput(null); // clear cached search results
   
    searchJob.cancel();
    searchJob.schedule(input, false, typingSearchDelay);
  }

  public void propertyChange(PropertyChangeEvent event)
  {
    typingSearchDelay = InstaSearchPlugin.getIntPref(PreferenceConstants.P_TYPING_SEARCH_DELAY);
    maxResults = InstaSearchPlugin.getIntPref(PreferenceConstants.P_SHOWN_FILES_COUNT);
    incrementalSearchEnabled = InstaSearchPlugin.getBoolPref(PreferenceConstants.P_INCREMENTAL_SEARCH);
  }
 
  /**
   * Waits for {@link CheckUpdatesJob} to finish and notifies if update is available
   * by placing an Update button in the view's toolbar
   */
  private class UpdateJobChangeListener extends JobChangeAdapter {
 
    public void done(IJobChangeEvent event)
    {
      IStatus status = event.getResult();
     
      if( status.getSeverity() == IStatus.OK )
      {
        boolean updateAvailable = (status.getCode() == CheckUpdatesJob.UPDATE_AVAILABLE_CODE);
        if( updateAvailable )
        {
          getViewSite().getShell().getDisplay().asyncExec(new Runnable() {
            public void run()
            {
              addUpdateAction();
              setTitleToolTip("New version available");
              getViewSite().getActionBars().getStatusLineManager().setMessage(getTitleImage(), "New version available");
            }         
          });
        }
       
       
      }
    }
   
    private void addUpdateAction() {
      IAction updateAction = CheckUpdatesJob.createUpdateNotificationAction();
      updateAction.setImageDescriptor( InstaSearchPlugin.getImageDescriptor("lightbulb") );
     
      IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager();
      mgr.add(updateAction);
      mgr.update(true);
     
      IMenuManager menuMgr = getViewSite().getActionBars().getMenuManager();
     
      menuMgr.add(updateAction);
     
      IContributionItem checkUpdatesItem = mgr.find(CheckUpdatesActionDelegate.ID);
      if( checkUpdatesItem != null )
        checkUpdatesItem.setVisible(false); // hide Check for Updates action
    }
  }
 
  /**
   * Background job that expands/collapses all entries
   */
  private class ExpandCollapseJob extends Job implements ISchedulingRule {
   
    /**
     */
    public ExpandCollapseJob() {
      super("Expand All");
     
      setRule(this);
      //setUser(true);
     
      // listen to searchJob changes. stop expanding on new search
      searchJob.addJobChangeListener(new JobChangeAdapter() {
        public void scheduled(IJobChangeEvent event) {
          cancel(); // new search
        }
        public void done(IJobChangeEvent event) {
          cancel(); // canceled search
        }
      });
     
    }
 
    public void schedule(boolean expandAll) {
     
      this.cancel();
     
      if( !expandAll ) {
        resultViewer.collapseAll();
        return;
      }
     
      if( resultViewer.getTree().getItemCount() == 0 ) {
        return;
      }
     
      this.schedule();
    }
   
    protected IStatus run(IProgressMonitor monitor) {
     
      Display display = getViewSite().getShell().getDisplay();
     
      Object[] elements = contentProvider.getElements();
      monitor.beginTask("InstaSearch Expanding", elements.length);
     
      for(int i = 0; i < elements.length && !monitor.isCanceled(); i++) {
        final Object curDoc = elements[i];
        if( curDoc == null ) continue;
        if( !(curDoc instanceof SearchResultDoc) ) continue;
       
        contentProvider.getChildren(curDoc); // get lines from file (they become cached)
       
        Runnable expander = new Runnable() {
          public void run() {
            resultViewer.setExpandedState(curDoc, true);
          }
        };
        display.syncExec(expander); // expand in UI thread
       
        monitor.worked(1);
      }
     
      monitor.done();
     
      return Status.OK_STATUS;
    }

    public boolean contains(ISchedulingRule rule) {
      return rule.getClass() == this.getClass();
    }

    public boolean isConflicting(ISchedulingRule rule) {
      return rule.getClass() == this.getClass();
    }
   
  }

}
TOP

Related Classes of it.unibz.instasearch.ui.InstaSearchView$ExpandCollapseJob

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.