/** * 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.List; import net.sf.eclipsefp.haskell.buildwrapper.types.EvalHandler; import net.sf.eclipsefp.haskell.buildwrapper.types.EvalResult; import net.sf.eclipsefp.haskell.ui.internal.util.UITexts; import net.sf.eclipsefp.haskell.ui.util.HaskellUIImages; import net.sf.eclipsefp.haskell.ui.util.IImageNames; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.browser.ProgressEvent; import org.eclipse.swt.browser.ProgressListener; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.graphics.Color; 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.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * This composite displays one evaluation expression and it result or error * Double clicking allow edition of the expression * There is also a button to delete the expression * @author JP Moresmau * */ public class EvalComposite extends Composite implements EvalHandler{ /** * the expression to evaluate */ private final EvalExpression expression; /** * the current result UI */ private IControlProvider lResult; /** * result icon: ok/error */ private final Label lResultIcon; /** * the listener to edit expression */ private final MouseListener dblListener; /** * the parent page */ private final WorkSheetViewPage page; public EvalComposite(final WorkSheetViewPage page, final EvalExpression expression) { super( page.getMainComposite(), SWT.NONE ); this.page=page; this.expression=expression; GridLayout gl=new GridLayout(3,false); this.setLayout( gl ); final Text lExpr=new Text(this,SWT.WRAP | SWT.MULTI); lExpr.setEditable( false ); lExpr.setBackground( getBackground() ); lExpr.setFont(JFaceResources.getFontRegistry().getItalic(lExpr.getFont().getFontData()[0].getName())); GridData gdExpr=new GridData(GridData.FILL_HORIZONTAL) ; gdExpr.horizontalSpan=2; gdExpr.widthHint=getBounds().width-20; lExpr.setLayoutData(gdExpr); lExpr.setText( getExpression() ); ToolBar tb=new ToolBar( this, SWT.FLAT ); ToolItem tiRemove=new ToolItem( tb, SWT.PUSH ); tiRemove.setImage( PlatformUI.getWorkbench().getSharedImages().getImage( ISharedImages.IMG_ELCL_REMOVE ) ); tiRemove.setToolTipText( UITexts.worksheet_removeexpression_tooltip ); lResultIcon=new Label( this, SWT.NONE ); lResultIcon.setBackground( getBackground() ); lResultIcon.setLayoutData( new GridData(GridData.VERTICAL_ALIGN_BEGINNING) ); dblListener=new MouseAdapter() { @Override public void mouseDoubleClick( final MouseEvent event ) { EvalExpressionDialog id=new EvalExpressionDialog( getShell(), UITexts.worksheet_editexpression_title, UITexts.worksheet_addexpression_message, expression ); if (id.open()==Window.OK){ expression.setExpression( id.getValue() ); expression.setResultType( id.getResultType() ); expression.setLastResult( null ); lExpr.setText( getExpression() ); page.save(); page.eval(); } } }; buildEmptyControl(); tiRemove.addSelectionListener( new SelectionAdapter() { @Override public void widgetSelected(final org.eclipse.swt.events.SelectionEvent arg0) { String msg=NLS.bind( UITexts.worksheet_removeexpression_message,expression.getExpression()); if (MessageDialog.openConfirm( getShell(), UITexts.worksheet_removeexpression_title, msg )){ page.remove( EvalComposite.this ); } } } ); lExpr.addMouseListener( dblListener); lResultIcon.addMouseListener( dblListener ); this.addMouseListener( dblListener ); setBackground( getDisplay().getSystemColor( SWT.COLOR_LIST_BACKGROUND ) ); } public EvalExpression getEvalExpression() { return expression; } /* (non-Javadoc) * @see org.eclipse.swt.widgets.Control#setBackground(org.eclipse.swt.graphics.Color) */ @Override public void setBackground( final Color arg0 ) { super.setBackground( arg0 ); for (Control c:getChildren()){ c.setBackground( arg0 ); } } /** * @return the expression */ @Override public String getExpression() { return expression.getExpression(); } /* (non-Javadoc) * @see org.eclipse.swt.widgets.Control#setToolTipText(java.lang.String) */ @Override public void setToolTipText( final String arg0 ) { super.setToolTipText( arg0 ); for (Control c:getChildren()){ if (!(c instanceof ToolBar)){ c.setToolTipText( arg0 ); } } } /* (non-Javadoc) * @see net.sf.eclipsefp.haskell.buildwrapper.types.EvalHandler#handleResult(net.sf.eclipsefp.haskell.buildwrapper.types.EvalResult) */ @Override public void handleResult( final EvalResult er ) { if (expression.getLastResult()!=null && expression.getLastResult().equals( er )){ return; } expression.setLastResult( er ); if (this.isDisposed()){ return; } getDisplay().syncExec( new Runnable(){ /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { setToolTipText(""); buildControl( er ); if (er.getType()!=null && er.getType().length()>0){ setToolTipText( er.getType() ); } layout(true); page.layout(); } } ); } private void buildControl(final EvalResult er){ if (er.getResult()!=null && er.getResult().length()>0){ buildResultControl(er.getResult()); } else if (er.getError()!=null && er.getError().length()>0){ buildErrorControl( er.getError() ); } else { buildEmptyControl(); } } private void buildResultControl(final String re){ switch (expression.getResultType()){ case HTML: buildHTML( re ); break; case JSON: buildJSON( re ); break; default: buildText( re ); } lResultIcon.setImage(HaskellUIImages.getImage( IImageNames.WORKSHEET_OK )); } private void setCurrentControl(final IControlProvider c){ lResult=c; GridData gdResult=new GridData(GridData.FILL_HORIZONTAL); gdResult.horizontalSpan=2; gdResult.widthHint=getBounds().width-40; lResult.getControl().setLayoutData( gdResult ); lResult.getControl().setBackground( getBackground() ); } private void buildHTML(final String html){ IControlProvider cp=null; Browser b=null; if (lResult==null || !(lResult.getControl() instanceof Browser)){ if (lResult!=null){ lResult.getControl().dispose(); lResult=null; } final Browser fb=new Browser(this,SWT.TOP | SWT.RESIZE); b=fb; b.addProgressListener(new ProgressListener() { @Override public void completed(final ProgressEvent event) { int contentHeight = ((Double)fb.evaluate("return document.body.scrollHeight")).intValue(); int contentWidth = ((Double)fb.evaluate("return document.body.scrollWidth")).intValue(); GridData gdResult=new GridData(GridData.FILL_HORIZONTAL); gdResult.horizontalSpan=2; gdResult.heightHint=contentHeight; gdResult.widthHint=contentWidth; fb.setLayoutData( gdResult ); EvalComposite.this.layout(true); EvalComposite.this.getParent().layout(true); } @Override public void changed(final ProgressEvent arg0) { EvalComposite.this.layout(true); EvalComposite.this.getParent().layout(true); } }); b.addMouseListener( dblListener ); cp=new ControlProvider( b ); } else{ cp=lResult; b=(Browser)lResult.getControl(); } setCurrentControl(cp); b.setText(html); } private void buildText(final String txt){ IControlProvider cp=null; StyledText t=null; if (lResult==null || !(lResult.getControl() instanceof StyledText)){ if (lResult!=null){ lResult.getControl().dispose(); lResult=null; } // styled text wraps even if no text t=new StyledText(this,SWT.MULTI | SWT.WRAP); t.setEditable( false ); t.addMouseListener( dblListener ); cp=new ControlProvider(t); } else { cp=lResult; t=(StyledText)lResult.getControl(); } setCurrentControl(cp); t.setText( txt ); } private void buildErrorControl(final String error){ buildText(error); lResultIcon.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( ISharedImages.IMG_OBJS_ERROR_TSK ) ); } private void buildEmptyControl(){ lResultIcon.setImage( null ); buildText( "" ); } private void buildJSON(final String json){ Object root=null; try { root=new JSONObject(json); } catch (JSONException je){ try { root=new JSONArray( json ); } catch (JSONException je2){ root=JSONObject.stringToValue( json ); } } if (root==null || root instanceof String){ buildText(json); } else { TreeControlProvider tcp; List<List<String>> names=null; if (lResult==null || !(lResult instanceof TreeControlProvider)){ if (lResult!=null){ lResult.getControl().dispose(); lResult=null; } TreeViewer tv=new TreeViewer(this,SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL); tcp=new TreeControlProvider( tv ); tcp.viewer.setContentProvider( new JSONContentProvider() ); tcp.viewer.setComparator( new JSONContentProvider.JSONComparator() ); Listener l=new Listener(){ /* (non-Javadoc) * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event) */ @Override public void handleEvent( final Event arg0 ) { // let the tree expand, then layout getDisplay().asyncExec( new Runnable() { @Override public void run() { EvalComposite.this.layout(true); EvalComposite.this.page.layout(); } } ); } }; tcp.viewer.getTree().addListener( SWT.Expand, l ); tcp.viewer.getTree().addListener( SWT.Collapse, l ); tcp.getControl().addMouseListener( dblListener ); } else { tcp=(TreeControlProvider)lResult; // converts tree path to simple string paths TreePath[] exp=tcp.viewer.getExpandedTreePaths(); if (exp!=null && exp.length>0){ names=new ArrayList<>(exp.length); LabelProvider lp=new LabelProvider(); for (TreePath tp:exp){ if (tp.getSegmentCount()>0){ List<String> names1=new ArrayList<>(tp.getSegmentCount()); for (int a=0;a<tp.getSegmentCount();a++){ names1.add(lp.getText( tp.getSegment( a ) )); } names.add(names1); } } } } tcp.viewer.setInput( root ); setCurrentControl(tcp); // expand the tree by reconverting name paths into tree paths if (names!=null){ LabelProvider lp=new LabelProvider(); JSONContentProvider cp=(JSONContentProvider)tcp.viewer.getContentProvider(); List<TreePath> tps=new ArrayList<>(); for (Object r:cp.getElements( root )){ String rn=lp.getText( r ); for (List<String> names1 :names){ if (names1.size()>0 && names1.get( 0 ).equals(rn)){ List<Object> path=new ArrayList<>(); path.add( r ); addToPath( path, r, names1.subList( 1, names1.size() ), lp, cp ); tps.add( new TreePath( path.toArray() ) ); } } } if (tps.size()>0){ tcp.viewer.setExpandedTreePaths( tps.toArray( new TreePath[tps.size()] ) ); } } } } /** * converts one level name path into a path of proper objects * @param path the path of objects as given by the content provider * @param parent the parent objects * @param names1 the current name path * @param lp the label provider * @param cp the content provider */ private void addToPath(final List<Object> path,final Object parent,final List<String> names1,final LabelProvider lp,final JSONContentProvider cp){ if (names1.size()>0){ Object[] cs=cp.getChildren( parent ); if (cs!=null && cs.length>0){ for (Object c:cs){ String cn=lp.getText( c); if (names1.get( 0 ).equals(cn)){ path.add( c ); addToPath( path, c, names1.subList( 1, names1.size() ), lp, cp ); } } } } } /** * Simple interface to use as wrapper around a control * @author JP Moresmau * */ private interface IControlProvider{ Control getControl(); } /** * Simple wrapper * @author JP Moresmau * */ private class ControlProvider implements IControlProvider { private final Control ctrl; public ControlProvider( final Control ctrl ) { super(); this.ctrl = ctrl; } @Override public Control getControl(){ return ctrl; } } /** * we need to do things on the tree viewer, so wrap the viewer instead * * @author JP Moresmau * */ private class TreeControlProvider implements IControlProvider { private final TreeViewer viewer; public TreeControlProvider(final TreeViewer viewer){ this.viewer=viewer; } /* (non-Javadoc) * @see net.sf.eclipsefp.haskell.ui.internal.views.worksheet.EvalComposite.IControlProvider#getControl() */ @Override public Control getControl() { return viewer.getControl(); } } }