/**
* Copyright (c) 2012 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.debug.ui.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.sf.eclipsefp.haskell.core.HaskellCorePlugin;
import net.sf.eclipsefp.haskell.debug.core.test.TestResult;
import net.sf.eclipsefp.haskell.debug.core.test.TestSuite;
import net.sf.eclipsefp.haskell.debug.ui.internal.util.UITexts;
import net.sf.eclipsefp.haskell.ui.handlers.OpenDefinitionHandler;
import net.sf.eclipsefp.haskell.ui.util.HaskellUIImages;
import net.sf.eclipsefp.haskell.ui.util.IImageNames;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
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.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
/**
* Haskell test results view
* @author JP Moresmau
*
*/
public class TestResultView extends ViewPart {
private TreeViewer testTree;
private StyledText testText;
private Text tRuns;
private Text tErrors;
private Text tFailures;
private final List<TestSuite> history=new ArrayList<>();
private int historyIndex=-1;
// private final List<Resource> resources=new ArrayList<Resource>();
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createPartControl( final Composite parent ) {
parent.setLayout( new GridLayout(6,false) );
CLabel clRuns=new CLabel( parent, SWT.LEFT );
clRuns.setText( UITexts.test_view_runs );
tRuns=new Text(parent,SWT.NONE);
tRuns.setText( "0" ); //$NON-NLS-1$
tRuns.setEditable( false );
tRuns.setBackground( parent.getBackground() );
tRuns.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, true, false ) );
CLabel clErrors=new CLabel( parent, SWT.LEFT );
clErrors.setText( UITexts.test_view_errors );
clErrors.setImage( HaskellUIImages.getImage( IImageNames.ERROR_OVERLAY) );
tErrors=new Text(parent,SWT.NONE);
tErrors.setText( "0" ); //$NON-NLS-1$
tErrors.setEditable( false );
tErrors.setBackground( parent.getBackground() );
tErrors.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, true, false ) );
CLabel clFailures=new CLabel( parent, SWT.LEFT );
clFailures.setText( UITexts.test_view_failures );
clFailures.setImage( HaskellUIImages.getImage( IImageNames.FAILURE_OVERLAY) );
tFailures=new Text(parent,SWT.NONE);
tFailures.setText( "0" ); //$NON-NLS-1$
tFailures.setEditable( false );
tFailures.setBackground( parent.getBackground() );
tFailures.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, true, false ) );
final Composite mainComposite=new Composite(parent,SWT.NONE);
final GridData gd=new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH);
gd.horizontalSpan=6;
mainComposite.setLayoutData( gd );
FormLayout fl=new FormLayout();
mainComposite.setLayout( fl );
testTree=new TreeViewer( mainComposite,SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
final Sash sash=new Sash( mainComposite, SWT.HORIZONTAL);
FormData fd=new FormData();
fd.top=new FormAttachment( 0);
fd.left=new FormAttachment(0);
fd.right=new FormAttachment(100);
fd.bottom=new FormAttachment(sash,0);
testTree.getTree().setLayoutData( fd );
testTree.setContentProvider( new TestResultCP());
testTree.setLabelProvider( new TestResultLP() );
CLabel clOutput=new CLabel( mainComposite, SWT.LEFT );
clOutput.setText( UITexts.test_view_text);
final FormData sashData=new FormData();
sashData.top=new FormAttachment( 50, 0 );
sashData.left=new FormAttachment(0);
sashData.right=new FormAttachment(100);
sash.setLayoutData( sashData );
fd=new FormData();
fd.top=new FormAttachment( sash, 0 );
fd.left=new FormAttachment(0);
fd.right=new FormAttachment(100);
clOutput.setLayoutData( fd );
testText=new StyledText( mainComposite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
testText.setEditable( false );
fd=new FormData();
fd.top=new FormAttachment( clOutput, 0 );
fd.left=new FormAttachment(0,0);
fd.right=new FormAttachment(100);
fd.bottom=new FormAttachment(100);
testText.setLayoutData( fd );
testTree.addSelectionChangedListener( new ISelectionChangedListener() {
@Override
public void selectionChanged( final SelectionChangedEvent paramSelectionChangedEvent ) {
Object o=((IStructuredSelection)paramSelectionChangedEvent.getSelection()).getFirstElement();
if (o instanceof TestResult){
String txt=((TestResult)o).getText();
if (txt==null){
txt=""; //$NON-NLS-1$
}
testText.setText( txt );
}
}
} );
testTree.addDoubleClickListener( new IDoubleClickListener() {
@Override
public void doubleClick( final DoubleClickEvent paramDoubleClickEvent ) {
Object o=((IStructuredSelection)paramDoubleClickEvent.getSelection()).getFirstElement();
if (o instanceof TestResult){
TestResult tr=(TestResult)o;
if (tr.getLocation()!=null && tr.getProject()!=null){
try {
OpenDefinitionHandler.openInEditor( tr.getLocation(), tr.getProject() );
} catch (Throwable t){
HaskellCorePlugin.log( t );
}
}
}
}
} );
sash.addListener( SWT.Selection, new Listener() {
@Override
public void handleEvent( final Event event ) {
Rectangle clientArea = mainComposite.getClientArea();
Rectangle sashRect = sash.getBounds ();
int top=clientArea.height - 70; // bottom minimum
event.y=Math.max (Math.min (event.y, top), 30); // top minimum
if (event.y!=sashRect.y){
sashData.top=new FormAttachment(0,event.y);
mainComposite.layout();
}
}
});
IToolBarManager tmgr=getViewSite().getActionBars().getToolBarManager();
HistoryAction act=new HistoryAction();
tmgr.add( act );
}
public void clear(){
testTree.setInput( Collections.emptySet() );
tRuns.setText( String.valueOf( 0 ) );
tErrors.setText( String.valueOf( 0 ) );
tFailures.setText( String.valueOf( 0 ) );
tRuns.getParent().layout( new Control[]{tRuns,tErrors,tFailures} );
}
/**
* set the given test suite as input
* @param ts the test suite
* @param add add to history?
*/
public void setInput(final TestSuite ts,final boolean add){
Object[] expanded=testTree.getExpandedElements();
/** avoid flicker by only refreshing if we have the proper input already **/
boolean needInput=true;
if (!add){
Object i=testTree.getInput();
if (i!=null && i instanceof Collection<?>){
Collection<?> c=(Collection<?>)i;
if (c.size()==1 && ts.getRoot()==c.iterator().next() ){
testTree.refresh();
needInput=false;
}
}
}
if (needInput){
testTree.setInput( Collections.singleton( ts.getRoot() ));
}
tRuns.setText( String.valueOf( ts.getRuns() ) );
tErrors.setText( String.valueOf( ts.getErrors() ) );
tFailures.setText( String.valueOf( ts.getFailures() ) );
tRuns.getParent().layout( new Control[]{tRuns,tErrors,tFailures} );
if (add){
testText.setText( "" ); //$NON-NLS-1$
history.add( ts );
historyIndex=history.size()-1;
testTree.expandToLevel( 2 );
} else {
testTree.setExpandedElements( expanded );
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#setFocus()
*/
@Override
public void setFocus() {
if (testTree!=null && !testTree.getTree().isDisposed()){
testTree.getTree().setFocus();
}
if (tRuns!=null && !tRuns.isDisposed()){
tRuns.setBackground( tRuns.getParent().getBackground() );
}
if (tFailures!=null && !tFailures.isDisposed()){
tFailures.setBackground( tFailures.getParent().getBackground() );
}
if (tErrors!=null && !tErrors.isDisposed()){
tErrors.setBackground( tErrors.getParent().getBackground() );
}
}
/**
* dynamic history menu
* @author JP Moresmau
*
*/
private class HistoryMenuCreator implements IMenuCreator{
private Menu menu;
/* (non-Javadoc)
* @see org.eclipse.jface.action.IMenuCreator#getMenu(org.eclipse.swt.widgets.Control)
*/
@Override
public Menu getMenu( final Control paramControl ) {
dispose();
menu=new Menu(paramControl);
fillMenu(menu);
return menu;
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IMenuCreator#getMenu(org.eclipse.swt.widgets.Menu)
*/
@Override
public Menu getMenu( final Menu paramMenu ) {
dispose();
menu=new Menu(paramMenu);
fillMenu(menu);
return menu;
}
private void fillMenu(final Menu m){
if(history.size()>0){
TestResultLP lp=new TestResultLP();
for (int a=history.size()-1;a>=0;a--){
final TestSuite ts=history.get( a );
MenuItem mi=new MenuItem(m,SWT.RADIO);
mi.setText( lp.getText( ts ) );
mi.setImage( lp.getImage( ts ) );
if (historyIndex!=a){
final int idx=a;
mi.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
historyIndex=idx;
setInput( ts,false );
}
} );
} else {
mi.setSelection( true );
}
}
new MenuItem(m,SWT.SEPARATOR);
/** clear all terminated **/
MenuItem mi=new MenuItem(m,SWT.PUSH);
mi.setText( UITexts.test_history_clear);
mi.setImage( PlatformUI.getWorkbench().getSharedImages().getImage( ISharedImages.IMG_ELCL_REMOVEALL ) );
mi.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
int a=0;
for (Iterator<TestSuite> it=history.iterator();it.hasNext();){
final TestSuite ts=it.next();
/** terminated= !PENDING **/
if (ts.getRoot().isFinished()){
it.remove();
/** history index moves too **/
if (historyIndex>=a){
historyIndex--;
}
}
}
/** defensive **/
if (historyIndex<0){
historyIndex=history.size()-1;
}
if (historyIndex>=0){
setInput( history.get( historyIndex ), false );
} else {
clear(); /** remove **/
}
}
} );
} else {
/** nothing to show **/
MenuItem mi=new MenuItem(m,SWT.PUSH);
mi.setText( UITexts.test_history_none);
mi.setEnabled( false );
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.IMenuCreator#dispose()
*/
@Override
public void dispose() {
if (menu!=null){
menu.dispose();
}
}
}
/**
* simple action for test run history, delegates to menu creator
* @author JP Moresmau
*
*/
private class HistoryAction extends Action {
private HistoryAction(){
super(UITexts.test_history, SWT.DROP_DOWN);
this.setImageDescriptor( HaskellUIImages.getImageDescriptor( IImageNames.HISTORY_LIST ) );
setMenuCreator( new HistoryMenuCreator() );
}
}
}