/** * Copyright (c) 2014 by JP Moresmau * This code is made available under the terms of the Eclipse Public License, * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html */ package net.sf.eclipsefp.haskell.ui.internal.views.worksheet; import java.util.ArrayList; import java.util.Collections; import java.util.List; import net.sf.eclipsefp.haskell.buildwrapper.BWFacade; import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin; import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.HaskellEditor; import net.sf.eclipsefp.haskell.ui.internal.util.UITexts; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IActionBars; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.widgets.SharedScrolledComposite; import org.eclipse.ui.part.Page; /** * A page of expressions to evaluae on the linked haskell editor * @author JP Moresmau * */ public class WorkSheetViewPage extends Page { /** * the type of markers * we use markers to save expressions on files, since they're persistent and unlimited in size */ public static String MARKER_TYPE="net.sf.eclipsefp.haskell.ui.worksheet"; /** * the expression attribute */ public static String MARKER_EXPRESSION="expression"; /** * the index attribute, to ensure consistent ordering over restarts */ public static String MARKER_INDEX="index"; /** * type of result */ public static String MARKER_RESULT_TYPE="resultType"; /** * the hooked editor */ private HaskellEditor editor; /** * the main composite */ private Composite mainComposite; /** * the composite for scrolling */ private SharedScrolledComposite sc1; /** * the list of evaluation composite, one for each expression, in index order */ private final List<EvalComposite> evalComposites=new ArrayList<>(); private final AddExpressionAction addAction=new AddExpressionAction(); private final RemoveAllExpressionsAction removeAllAction=new RemoveAllExpressionsAction(); /** * */ public WorkSheetViewPage() { } /* (non-Javadoc) * @see org.eclipse.ui.part.Page#createControl(org.eclipse.swt.widgets.Composite) */ @Override public void createControl( final Composite parent ) { Composite mainComp= new Composite(parent, SWT.NONE); mainComp.setFont(parent.getFont()); GridData gd=new GridData(SWT.FILL,SWT.FILL,true,true); mainComp.setLayoutData( gd ); //GridLayout layout= new GridLayout(1,true); FillLayout layout=new FillLayout(); layout.marginHeight= 0; layout.marginWidth= 0; mainComp.setLayout(layout); sc1 = new SharedScrolledComposite(mainComp,SWT.V_SCROLL | SWT.H_SCROLL){ // no implementation required }; //gd=new GridData(SWT.FILL,SWT.FILL,true,true); sc1.setLayoutData(gd); mainComposite=new Composite(sc1,SWT.NONE); sc1.setExpandHorizontal(true); sc1.setExpandVertical(true); sc1.setContent( mainComposite ); //RowLayout l=new RowLayout(SWT.VERTICAL); //l.fill=true; //l.pack=true; GridLayout l=new GridLayout( 1, true ); l.marginBottom=0; l.marginHeight=0; l.marginLeft=0; l.marginRight=0; l.marginTop=0; l.marginWidth=0; mainComposite.setLayout( l ); mainComposite.setBackground( mainComposite.getDisplay().getSystemColor( SWT.COLOR_LIST_BACKGROUND ) ); //mainComposite.setBackground( parent.getBackground() ); removeAllAction.setEnabled(false); build(); layout(); //sc1.reflow( true ); addAction.setEnabled(false); IActionBars actionBars= getSite().getActionBars(); registerToolbarActions(actionBars); } private void registerToolbarActions( final IActionBars actionBars ) { IToolBarManager toolBarManager= actionBars.getToolBarManager(); toolBarManager.add(addAction); toolBarManager.add( removeAllAction ); } /** * relayout with scrolling */ public void layout(){ sc1.reflow( true ); mainComposite.layout(); } /** * @return the evalComposites */ public List<EvalComposite> getEvalComposites() { return evalComposites; } /* (non-Javadoc) * @see org.eclipse.ui.part.Page#getControl() */ @Override public Control getControl() { if (sc1!=null && !sc1.isDisposed()){ return sc1.getParent(); } return null; } /* (non-Javadoc) * @see org.eclipse.ui.part.Page#setFocus() */ @Override public void setFocus() { if (mainComposite!=null && !mainComposite.isDisposed()){ mainComposite.setFocus(); } } public HaskellEditor getEditor() { return editor; } public void setEditor( final HaskellEditor editor ) { this.editor = editor; addAction.setEnabled( this.editor!=null ); build(); } /** * build UI from file markers */ private void build(){ if (mainComposite!=null && !mainComposite.isDisposed()){ for (Control c:mainComposite.getChildren()){ c.dispose(); } evalComposites.clear(); if (this.editor!=null){ final IFile f=this.editor.findFile(); if (f!=null){ try { IMarker[] mks=f.findMarkers( MARKER_TYPE, true, IResource.DEPTH_ZERO ); List<EvalExpression> exprs=new ArrayList<>(); for (IMarker mk:mks){ EvalExpression expr=new EvalExpression(mk); if (expr.isValid()){ exprs.add( expr ); } } Collections.sort( exprs ); for (EvalExpression expr:exprs){ addComposite(expr); } eval(); } catch (CoreException ce){ HaskellUIPlugin.log( ce ); } } } } } /** * save expression to file markers */ public void save(){ final IFile f=this.editor.findFile(); if (f!=null){ try { // delete existing f.deleteMarkers( MARKER_TYPE, true, IResource.DEPTH_ZERO ); // recreate for (EvalComposite ec:evalComposites){ ec.getEvalExpression().addMarker( f ); } } catch (CoreException ce){ HaskellUIPlugin.log( ce ); } } } /** * remove a given expression from the list * remove the associated marker * ensure indices are kept consistent * @param exprToRemove */ private void remove(final EvalExpression exprToRemove){ final IFile f=this.editor.findFile(); if (f!=null){ try { IMarker[] mks=f.findMarkers( MARKER_TYPE, true, IResource.DEPTH_ZERO ); List<EvalExpression> exprs=new ArrayList<>(); int remove=0; for (IMarker mk:mks){ EvalExpression expr=new EvalExpression(mk); if (expr.getExpression().equals(exprToRemove.getExpression()) && expr.getIndex()==exprToRemove.getIndex()){ mk.delete(); remove++; } if (expr.isValid()){ exprs.add( expr ); } } for (EvalExpression e:exprs){ if (e.getIndex()>exprToRemove.getIndex()){ e.setIndex( e.getIndex()-remove ); } } } catch (CoreException ce){ HaskellUIPlugin.log( ce ); } } } /** * remove a evaluation composite and the associated expression marker * @param comp */ public void remove(final EvalComposite comp){ evalComposites.remove( comp ); remove(comp.getEvalExpression()); save(); comp.dispose(); removeAllAction.setEnabled( evalComposites.size()>0 ); layout(); } /** * @return the mainComposite */ public Composite getMainComposite() { return mainComposite; } /** * add an expression in a new composite * @param expr the expression * @return the created composite */ private EvalComposite addComposite(final EvalExpression expr){ EvalComposite ec=new EvalComposite( this, expr ); GridData gd=new GridData(GridData.FILL_HORIZONTAL); ec.setLayoutData( gd ); evalComposites.add(ec); removeAllAction.setEnabled( true ); return ec; } /** * launch evaluation of all expressions in a job */ public void eval(){ if (this.editor!=null){ final IFile f=this.editor.findFile(); if (f!=null){ BWFacade bwf=BuildWrapperPlugin.getFacade( f.getProject() ); if (bwf!=null){ // clone the list to avoid potential concurrent modifications BuildWrapperPlugin.getJobFacade( f.getProject() ).eval( f, new ArrayList<>(evalComposites) ); } } } } /** * action: add a new expression * * @author JP Moresmau * */ private class AddExpressionAction extends Action{ public AddExpressionAction(){ super(); PlatformUI.getWorkbench().getHelpSystem().setHelp(this, "WorkSheet.AddExpressionAction"); //$NON-NLS-1$ setText(UITexts.worksheet_addexpression); setToolTipText(UITexts.worksheet_addexpression); setDescription(UITexts.worksheet_addexpression); setImageDescriptor( PlatformUI.getWorkbench().getSharedImages().getImageDescriptor( ISharedImages.IMG_OBJ_ADD ) ); } /* (non-Javadoc) * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { EvalExpressionDialog id=new EvalExpressionDialog( getSite().getShell(), UITexts.worksheet_addexpression_title, UITexts.worksheet_addexpression_message, new EvalExpression() ); if (id.open()==Window.OK){ EvalExpression expr=new EvalExpression(); expr.setExpression( id.getValue() ); expr.setIndex( evalComposites.size() ); expr.setResultType( id.getResultType() ); EvalComposite ec= addComposite( expr ); final IFile f=editor.findFile(); if (f!=null){ try { expr.addMarker( f); }catch (CoreException ce){ HaskellUIPlugin.log( ce ); } BWFacade bwf=BuildWrapperPlugin.getFacade( f.getProject() ); if (bwf!=null){ BuildWrapperPlugin.getJobFacade( f.getProject() ).eval( f, Collections.singletonList( ec ) ); } } layout(); } } } /** * action: remove all expressions * * @author JP Moresmau * */ private class RemoveAllExpressionsAction extends Action{ /** * */ public RemoveAllExpressionsAction() { super(); PlatformUI.getWorkbench().getHelpSystem().setHelp(this, "WorkSheet.RemoveAllExpressionsAction"); //$NON-NLS-1$ setText(UITexts.worksheet_removeallexpressions_tooltip); setToolTipText(UITexts.worksheet_removeallexpressions_tooltip); setDescription(UITexts.worksheet_removeallexpressions_tooltip); setImageDescriptor( PlatformUI.getWorkbench().getSharedImages().getImageDescriptor( ISharedImages.IMG_ELCL_REMOVEALL ) ); } /* (non-Javadoc) * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { if (MessageDialog.openConfirm( getSite().getShell(), UITexts.worksheet_removeallexpressions_title, UITexts.worksheet_removeallexpressions_message )){ for (EvalComposite c:evalComposites){ c.dispose(); } evalComposites.clear(); save(); layout(); removeAllAction.setEnabled( false ); } } } }