/** * (c) 2011, Alejandro Serrano & JP Moresmau * Released under the terms of the EPL. */ package net.sf.eclipsefp.haskell.browser.views.hoogle; import java.util.ArrayList; import java.util.Map; import net.sf.eclipsefp.haskell.browser.BrowserEvent; import net.sf.eclipsefp.haskell.browser.BrowserPlugin; import net.sf.eclipsefp.haskell.browser.DatabaseLoadedEvent; import net.sf.eclipsefp.haskell.browser.DatabaseType; import net.sf.eclipsefp.haskell.browser.IDatabaseLoadedListener; import net.sf.eclipsefp.haskell.browser.IHoogleLoadedListener; import net.sf.eclipsefp.haskell.browser.items.DeclarationType; import net.sf.eclipsefp.haskell.browser.items.HaskellPackage; import net.sf.eclipsefp.haskell.browser.items.HoogleResult; import net.sf.eclipsefp.haskell.browser.items.HoogleResultConstructor; import net.sf.eclipsefp.haskell.browser.items.HoogleResultDeclaration; import net.sf.eclipsefp.haskell.browser.items.HoogleResultModule; import net.sf.eclipsefp.haskell.browser.items.HoogleResultPackage; import net.sf.eclipsefp.haskell.browser.util.HtmlUtil; import net.sf.eclipsefp.haskell.browser.views.SpecialRoot; import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin; import net.sf.eclipsefp.haskell.ui.handlers.OpenDefinitionHandler; import net.sf.eclipsefp.haskell.ui.internal.util.UITexts; 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.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; /** * View part for Hoogle search. * @author Alejandro Serrano & JP Moresmau * */ public class HoogleView extends ViewPart implements SelectionListener, ISelectionChangedListener, IDoubleClickListener, IHoogleLoadedListener, IDatabaseLoadedListener, ISchedulingRule { /** * search for the given text in Hoogle as if it was typed in the view * @param text the text to show in the view */ public static void searchHoogle(final String text){ // remove prefix if qualified int ix=text.lastIndexOf( '.' ); final String txt=(ix>-1) ?text.substring( ix+1 ) :text; Display.getDefault().asyncExec( new Runnable() { @Override public void run() { try { IWorkbench w=PlatformUI.getWorkbench(); IViewPart p=null; for (IWorkbenchWindow iww : w.getWorkbenchWindows()) { for (IWorkbenchPage page : iww.getPages()) { p=page.findView( ID ); if (p!=null){ iww.setActivePage( page ); page.activate( p ); break; } } } if (p==null){ p=w.getActiveWorkbenchWindow().getActivePage().showView( ID ); final HoogleView hv=(HoogleView)p; // will run after the notifications hoogleLoaded Display.getDefault().asyncExec( new Runnable() { @Override public void run() { hv.text.setText( txt ); Event evt=new Event(); evt.widget=hv.text; hv.widgetDefaultSelected(new SelectionEvent( evt )); } }); return; //PlatformUI.getWorkbench().getViewRegistry().find( ID ).createView(); } final HoogleView hv=(HoogleView)p; hv.text.setText( txt ); Event evt=new Event(); evt.widget=hv.text; hv.widgetDefaultSelected(new SelectionEvent( evt )); } catch (CoreException ce){ HaskellUIPlugin.log( ce ); } } }); } /** * The ID of the view as specified by the extension. */ public static final String ID = "net.sf.eclipsefp.haskell.browser.views.hoogle.HoogleView"; Text text; TreeViewer viewer; Browser doc; Button localDb; Button hackageDb; @Override public void createPartControl( final Composite parent ) { GridLayout layout = new GridLayout(); layout.numColumns = 1; layout.verticalSpacing = layout.horizontalSpacing = 0; layout.marginBottom = layout.marginHeight = layout.marginLeft = layout.marginRight = layout.marginTop = layout.marginWidth = 0; parent.setLayout( layout ); Composite dbSelection = new Composite( parent, SWT.NULL ); GridLayout innerLayout = new GridLayout(); innerLayout.numColumns = 3; innerLayout.makeColumnsEqualWidth= true; GridData innerData = new GridData(); innerData.horizontalAlignment = SWT.FILL; innerData.grabExcessHorizontalSpace = true; dbSelection.setLayoutData( innerData ); dbSelection.setLayout( innerLayout ); Label searchInLabel = new Label( dbSelection, SWT.NULL ); searchInLabel.setText( UITexts.browser_hoogleSearchIn ); BrowserPlugin.getDefault().addDatabaseLoadedListener( this ); localDb = new Button( dbSelection, SWT.CHECK ); localDb.setText( UITexts.browser_localDatabase ); localDb.setSelection( true ); localDb.setEnabled( BrowserPlugin.getDefault().isLocalDatabaseLoaded() ); localDb.setBackground( dbSelection.getBackground() ); hackageDb = new Button( dbSelection, SWT.CHECK ); hackageDb.setText( UITexts.browser_hackageDatabase ); hackageDb.setSelection( false ); hackageDb.setEnabled( BrowserPlugin.getDefault().isHackageDatabaseLoaded() ); hackageDb.setBackground( dbSelection.getBackground() ); text = new Text( parent, SWT.SINGLE | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL ); GridData textData = new GridData(); textData.horizontalAlignment = SWT.FILL; textData.grabExcessHorizontalSpace = true; text.setLayoutData( textData ); text.addSelectionListener( this ); text.setEnabled( false ); SashForm form = new SashForm( parent, SWT.VERTICAL ); GridData formData = new GridData(); formData.horizontalAlignment = SWT.FILL; formData.verticalAlignment = SWT.FILL; formData.grabExcessVerticalSpace = true; formData.grabExcessHorizontalSpace = true; form.setLayoutData( formData ); viewer = new TreeViewer( form ); viewer.setLabelProvider( new HoogleLabelProvider() ); // Load if needed if (BrowserPlugin.getDefault().isHoogleLoaded()) { hoogleLoaded( null ); } else { hoogleUnloaded( null ); } doc = new Browser( form, SWT.NONE ); form.setWeights( new int[] { 70, 30 } ); // Hook for double clicking viewer.addDoubleClickListener( this ); // Hook for changes in selection viewer.addPostSelectionChangedListener( this ); viewer.setContentProvider( new HoogleContentProvider() ); // Wait the Hoogle database to be ready BrowserPlugin.getDefault().addHoogleLoadedListener( this ); } @Override public void hoogleLoaded( final BrowserEvent e ) { Display.getDefault().asyncExec( new Runnable() { @Override public void run() { //viewer.setLabelProvider( new HoogleLabelProvider() ); if (!viewer.getTree().isDisposed()){ if (!text.isDisposed() && text.getText().length()>0){ search( text.getText() ); } else { viewer.setInput( null ); } viewer.refresh(); } if (!text.isDisposed()){ text.setEnabled( true ); } } } ); } private void search(final String text){ viewer.setInput( SpecialRoot.SEARCHING ); viewer.refresh(); final HoogleSearchResult r=new HoogleSearchResult( localDb.getSelection(),hackageDb.getSelection() ); final Display d=Display.getCurrent(); Job job=new Job(UITexts.browser_hoogleSearching){ /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IStatus run( final IProgressMonitor monitor ) { r.search( text ); return Status.OK_STATUS; } }; job.addJobChangeListener( new JobChangeAdapter(){ /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent) */ @Override public void done( final IJobChangeEvent event ) { d.asyncExec( new Runnable(){ /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { if (r.getResults().isEmpty()){ viewer.setInput( SpecialRoot.EMPTY ); viewer.refresh(); } else { viewer.setInput( r ); viewer.refresh(); Object first = ((HoogleContentProvider)viewer.getContentProvider()).getFirstElement(); if( first != null ) { viewer.setSelection( new StructuredSelection( first ), true ); viewer.getControl().setFocus(); } } } } ); } } ); job.setRule( this ); job.setPriority( Job.INTERACTIVE ); job.schedule(); } @Override public void hoogleUnloaded( final BrowserEvent e ) { Display.getDefault().asyncExec( new Runnable() { @Override public void run() { if (!text.isDisposed()){ text.setEnabled( false ); } viewer.setInput( SpecialRoot.NO_DATABASE ); viewer.refresh(); } } ); } @Override public void setFocus() { if (text!=null && !text.isDisposed()){ text.setFocus(); } if (hackageDb!=null && !hackageDb.isDisposed()){ hackageDb.setBackground( hackageDb.getParent().getBackground() ); } if (localDb!=null && !localDb.isDisposed()){ localDb.setBackground( localDb.getParent().getBackground() ); } } @Override public void widgetSelected( final SelectionEvent e ) { // Do nothing } @Override public void widgetDefaultSelected( final SelectionEvent e ) { if( e.detail == SWT.CANCEL ) { viewer.setInput( "" ); viewer.refresh(); } else { search( text.getText() ); } } @Override @SuppressWarnings ( "unchecked" ) public void selectionChanged( final SelectionChangedEvent event ) { TreeSelection selection = ( TreeSelection )event.getSelection(); Object o = selection.getFirstElement(); if( o == null || o instanceof SpecialRoot ) { doc.setText( "" ); return; } // Try to find element to show HoogleResult result = null; if( o instanceof HoogleResult ) { result = ( HoogleResult )o; } else { Map.Entry<String, Object> entry = ( Map.Entry<String, Object> )o; if( entry.getValue() instanceof HoogleResult ) { result = ( HoogleResult )entry.getValue(); } } if( result != null ) { String text = ""; switch( result.getType() ) { case KEYWORD: text = HtmlUtil.generateDocument( "keyword " + result.getName(), "" ); break; case PACKAGE: HaskellPackage pkg = ( ( HoogleResultPackage )result ).getPackage(); text = HtmlUtil.generateDocument( "package " + pkg.getIdentifier().toString(), pkg.getDoc() ); break; case MODULE: HoogleResultModule mod = ( HoogleResultModule )result; text = HtmlUtil.generateDocument( "module " + mod.getName(), mod .getPackageIdentifiers(), null, false, mod.getModule().getDoc() ); break; case DECLARATION: HoogleResultDeclaration decl = ( HoogleResultDeclaration )result; text = HtmlUtil.generateDocument( decl.getDeclaration() .getCompleteDefinition(), decl.getPackageIdentifiers(), decl .getModule(), false, decl.getDeclaration().getDoc() ); break; case CONSTRUCTOR: HoogleResultConstructor con = ( HoogleResultConstructor )result; text = HtmlUtil.generateDocument( con.getConstructor() .getCompleteDefinition(), con.getPackageIdentifiers(), con .getModule(), false, con.getDeclaration().getDoc() ); break; case WARNING: // not in tree break; } doc.setText( text ); } else { doc.setText( HtmlUtil .generateText( UITexts.browser_definedInSeveralLocations ) ); } } @Override @SuppressWarnings ( "unchecked" ) public void doubleClick( final DoubleClickEvent event ) { TreeSelection selection = ( TreeSelection )event.getSelection(); Object o = selection.getFirstElement(); if( o == null || o instanceof SpecialRoot ) { return; } // Try to find element to show HoogleResult result = null; if( o instanceof HoogleResult ) { result = ( HoogleResult )o; } else { Map.Entry<String, Object> entry = ( Map.Entry<String, Object> )o; if( entry.getValue() instanceof HoogleResult ) { result = ( HoogleResult )entry.getValue(); } else { // Show the first one (better than nothing) result = ( ( ArrayList<HoogleResult> )entry.getValue() ).get( 0 ); } } IWorkbenchPage page=getSite().getPage(); switch( result.getType() ) { case KEYWORD: OpenDefinitionHandler.openExternalDefinition( page, null, null,null,result.getName(), null ); break; case PACKAGE: HoogleResultPackage pkg = ( HoogleResultPackage )result; OpenDefinitionHandler.openExternalDefinition( page, null, pkg.getPackage().getIdentifier().toString(),null,null, null ); break; case MODULE: HoogleResultModule mod = ( HoogleResultModule )result; OpenDefinitionHandler.openExternalDefinition( page, null, mod.getPackageIdentifiers().get( 0 ).toString(),mod.getName(),null, null ); break; case CONSTRUCTOR: HoogleResultConstructor con = ( HoogleResultConstructor )result; OpenDefinitionHandler.openExternalDefinition( page, null, con.getPackageIdentifiers().get( 0 ).toString(),con.getModule(), con.getName(), "v" ); break; case DECLARATION: HoogleResultDeclaration decl = ( HoogleResultDeclaration )result; OpenDefinitionHandler.openExternalDefinition( page, null, decl.getPackageIdentifiers().get( 0 ).toString(),decl.getModule(), decl.getName(), decl .getDeclaration().getType() == DeclarationType.FUNCTION?"v":"t"); break; case WARNING: // not in tree break; } } /* (non-Javadoc) * @see net.sf.eclipsefp.haskell.browser.IDatabaseLoadedListener#databaseLoaded(net.sf.eclipsefp.haskell.browser.DatabaseLoadedEvent) */ @Override public void databaseLoaded( final DatabaseLoadedEvent e ) { final Display display = Display.getDefault(); display.asyncExec( new Runnable() { @Override public void run() { if (e.getType() == DatabaseType.LOCAL) { localDb.setEnabled( true ); } else if (e.getType() == DatabaseType.HACKAGE) { hackageDb.setEnabled( true ); } } } ); } /* (non-Javadoc) * @see net.sf.eclipsefp.haskell.browser.IDatabaseLoadedListener#databaseUnloaded(net.sf.eclipsefp.haskell.browser.BrowserEvent) */ @Override public void databaseUnloaded( final BrowserEvent e ) { final Display display = Display.getDefault(); display.asyncExec( new Runnable() { @Override public void run() { localDb.setSelection( false ); localDb.setEnabled( false ); hackageDb.setSelection( false ); hackageDb.setEnabled( false ); } } ); } @Override public boolean isConflicting(final ISchedulingRule arg0) { return this==arg0; } @Override public boolean contains(final ISchedulingRule arg0) { return this==arg0; } }