<commit_before>/*
 * Copyright (c) 2012 Diamond Light Source Ltd.
 *
 * 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
 */
package org.eclipse.scanning.event.ui.view;

import java.io.File;
import java.net.URI;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.scanning.api.event.EventException;
import org.eclipse.scanning.api.event.IEventService;
import org.eclipse.scanning.api.event.bean.BeanEvent;
import org.eclipse.scanning.api.event.bean.IBeanListener;
import org.eclipse.scanning.api.event.core.ConsumerConfiguration;
import org.eclipse.scanning.api.event.core.IPublisher;
import org.eclipse.scanning.api.event.core.ISubmitter;
import org.eclipse.scanning.api.event.core.ISubscriber;
import org.eclipse.scanning.api.event.status.AdministratorMessage;
import org.eclipse.scanning.api.event.status.StatusBean;
import org.eclipse.scanning.api.ui.IModifyHandler;
import org.eclipse.scanning.api.ui.IRerunHandler;
import org.eclipse.scanning.api.ui.IResultHandler;
import org.eclipse.scanning.event.ui.Activator;
import org.eclipse.scanning.event.ui.ServiceHolder;
import org.eclipse.scanning.event.ui.dialog.PropertiesDialog;
import org.eclipse.scanning.event.ui.preference.CommandConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A view for which the secondary id MUST be set and provides the queueName
 * and optionally the queue view name if a custom one is required. Syntax of
 * these parameters in the secondary id are key1=value1;key2=value2...
 * 
 * The essential keys are: beanBundleName, beanClassName, queueName, topicName, submissionQueueName
 * You can use createId(...) to generate a legal id from them.
 * 
 * The optional keys are: partName, 
 *                        uri (default CommandConstants.JMS_URI),
 *                        userName (default is user.name system property)
 * 
 * Example id for this view would be:
 * org.eclipse.scanning.event.ui.queueView:beanClassName=org.dawnsci.commandserver.mx.beans.ProjectBean;beanBundleName=org.dawnsci.commandserver.mx
 * 
 * You can optionally extend this class to provide a table which is displayed for your
 * queue of custom objects. For instance for a queue showing xia2 reruns, the 
 * extra columns for this could be defined. However by default the 
 * 
 * @author <NAME>
 *
 */
public class StatusQueueView extends ViewPart {
	
	public static final String ID = "org.eclipse.scanning.event.ui.queueView";
	
	private static final Logger logger = LoggerFactory.getLogger(StatusQueueView.class);
	
	// UI
	private TableViewer                       viewer;
	
	// Data
	private Properties                        idProperties;
	private Map<String, StatusBean>           queue;
	private boolean                           showEntireQueue = false;

	private ISubscriber<IBeanListener<StatusBean>>           topicMonitor;
	private ISubscriber<IBeanListener<AdministratorMessage>> adminMonitor;
	private ISubmitter<StatusBean>                           queueConnection;

	private Action rerun, edit, remove, up, down, pause;
	private IEventService service;
	
	public StatusQueueView() {
		this.service = ServiceHolder.getEventService();
	}

	@Override
	public void createPartControl(Composite content) {
		
		content.setLayout(new GridLayout(1, false));
		Util.removeMargins(content);

		this.viewer   = new TableViewer(content, SWT.FULL_SELECTION | SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
		viewer.setUseHashlookup(true);
		viewer.getTable().setHeaderVisible(true);
		viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		
		createColumns();
		viewer.setContentProvider(createContentProvider());
		
        try {
    		queueConnection = service.createSubmitter(getUri(), getSubmissionQueueName());
    		updateQueue(getUri());
    		
    		String name = getSecondaryIdAttribute("partName");
            if (name!=null) setPartName(name);
    		
            createActions();

    		// We just use this submitter to read the queue
            createTopicListener(getUri());
            
		} catch (Exception e) {
			logger.error("Cannot listen to topic of command server!", e);
		}
        
		getViewSite().setSelectionProvider(viewer);
		viewer.addSelectionChangedListener(new ISelectionChangedListener() {	
			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				updateSelected();
			}
		});

	}
	
	protected void updateSelected() {
		StatusBean bean = getSelection();
		if (bean == null) bean = new StatusBean();
		
		remove.setEnabled(bean.getStatus()!=null);
		rerun.setEnabled(true);
		
		boolean isSubmitted = bean.getStatus()==org.eclipse.scanning.api.event.status.Status.SUBMITTED;
		up.setEnabled(isSubmitted);
		edit.setEnabled(isSubmitted);
		down.setEnabled(isSubmitted);
		pause.setEnabled(bean.getStatus().isRunning()||bean.getStatus().isPaused());
		pause.setChecked(bean.getStatus().isPaused());
		pause.setText(bean.getStatus().isPaused()?"Resume job":"Pause job");
	}

	/**
	 * Listens to a topic
	 */
	private void createTopicListener(final URI uri) throws Exception {
		
		// Use job because connection might timeout.
		final Job topicJob = new Job("Create topic listener") {

			@Override
			protected IStatus run(IProgressMonitor monitor) {
				try {
					topicMonitor = service.createSubscriber(uri, getTopicName());
					topicMonitor.addListener(new IBeanListener<StatusBean>() {
						@Override
						public void beanChangePerformed(BeanEvent<StatusBean> evt) {
							final StatusBean bean = evt.getBean();
	                        try {
								mergeBean(bean);
							} catch (Exception e) {
								logger.error("Cannot merge changed bean!");
							}
						}
					});

					adminMonitor = service.createSubscriber(uri, IEventService.ADMIN_MESSAGE_TOPIC);
					adminMonitor.addListener(new IBeanListener<AdministratorMessage>() {
						@Override
						public void beanChangePerformed(BeanEvent<AdministratorMessage> evt) {
							final AdministratorMessage bean = evt.getBean();
	        				getSite().getShell().getDisplay().syncExec(new Runnable() {
	        					public void run() {
	                                   MessageDialog.openError(getViewSite().getShell(), 
	                                		                   bean.getTitle(), 
	                                		                   bean.getMessage());
	                                   
	                                   viewer.refresh();
	        					}
	        				});
						}
					});
					
			        return Status.OK_STATUS;
			        
				} catch (Exception ne) {
					logger.error("Cannot listen to topic changes because command server is not there", ne);
			        return Status.CANCEL_STATUS;
				}
			}
			
			
		};
		
		topicJob.setPriority(Job.INTERACTIVE);
		topicJob.setSystem(true);
		topicJob.setUser(false);
		topicJob.schedule();
	}
	
	public void dispose() {
		super.dispose();
		try {
			if (topicMonitor!=null) topicMonitor.disconnect();
			if (adminMonitor!=null) adminMonitor.disconnect();
		} catch (Exception ne) {
			logger.warn("Problem stopping topic listening for "+getTopicName(), ne);
		}
	}

	/**
	 * Updates the bean if it is found in the list, otherwise
	 * refreshes the whole list because a bean we are not reporting
	 * has been(bean?) encountered.
	 * 
	 * @param bean
	 */
	protected void mergeBean(final StatusBean bean) throws Exception {
		
		getSite().getShell().getDisplay().asyncExec(new Runnable() {
			public void run(){
				if (queue.containsKey(bean.getUniqueId())) {
					queue.get(bean.getUniqueId()).merge(bean);
					viewer.refresh();
					updateSelected();
				} else {
					reconnect();
				}
			}
		});
	}

	private void createActions() {
		
		final IContributionManager toolMan  = getViewSite().getActionBars().getToolBarManager();
		final IContributionManager dropDown = getViewSite().getActionBars().getMenuManager();
		final MenuManager          menuMan = new MenuManager();
	
		final Action openResults = new Action("Open results for selected run", Activator.getDefault().getImageDescriptor("icons/results.png")) {
			public void run() {
				openResults(getSelection());
			}
		};
		
		toolMan.add(openResults);
		toolMan.add(new Separator());
		menuMan.add(openResults);
		menuMan.add(new Separator());
		dropDown.add(openResults);
		dropDown.add(new Separator());
		
		this.up = new Action("Less urgent (-1)", Activator.getDefault().getImageDescriptor("icons/arrow-090.png")) {
			@SuppressWarnings("unused")
			public void run() {
				final StatusBean bean = getSelection();
				try {
					queueConnection.reorder(bean, -1);
				} catch (EventException e) {
					ErrorDialog.openError(getViewSite().getShell(), "Cannot move "+bean.getName(), "'"+bean.getName()+"' cannot be moved in the submission queue.",
							new Status(IStatus.ERROR, "org.eclipse.scanning.event.ui", e.getMessage()));
				}
				refresh();
			}
		};
		up.setEnabled(false);
		toolMan.add(up);
		menuMan.add(up);
		dropDown.add(up);
		
		this.down = new Action("More urgent (+1)", Activator.getDefault().getImageDescriptor("icons/arrow-270.png")) {
			@SuppressWarnings("unused")
			public void run() {
				final StatusBean bean = getSelection();
				try {
					queueConnection.reorder(getSelection(), +1);
				} catch (EventException e) {
					ErrorDialog.openError(getViewSite().getShell(), "Cannot move "+bean.getName(), e.getMessage(),
							new Status(IStatus.ERROR, "org.eclipse.scanning.event.ui", e.getMessage()));
				}
				refresh();
			}
		};
		down.setEnabled(false);
		toolMan.add(down);
		menuMan.add(down);
		dropDown.add(down);

		this.pause = new Action("Pause job", IAction.AS_CHECK_BOX) {
			public void run() {
				pauseJob();
			}
		};
		pause.setImageDescriptor(Activator.getDefault().getImageDescriptor("icons/control-pause.png"));
		pause.setEnabled(false);
		pause.setChecked(false);
		toolMan.add(pause);
		menuMan.add(pause);
		dropDown.add(pause);
		
		this.remove = new Action("Stop job or remove if finished", Activator.getDefault().getImageDescriptor("icons/control-stop-square.png")) {
			public void run() {
				stopJob();
			}
		};
		remove.setEnabled(false);
		toolMan.add(remove);
		menuMan.add(remove);
		dropDown.add(remove);
		
		this.rerun = new Action("Rerun...", Activator.getDefault().getImageDescriptor("icons/rerun.png")) {
			public void run() {
				rerunSelection();
			}
		};
		rerun.setEnabled(false);
		toolMan.add(rerun);
		menuMan.add(rerun);
		dropDown.add(rerun);
		
		this.edit = new Action("Edit...", Activator.getDefault().getImageDescriptor("icons/edit.png")) {
			public void run() {
				editSelection();
			}
		};
		edit.setEnabled(false);
		toolMan.add(edit);
		menuMan.add(edit);
		dropDown.add(edit);


		toolMan.add(new Separator());
		menuMan.add(new Separator());
		
		final Action showAll = new Action("Show other users results", IAction.AS_CHECK_BOX) {
			public void run() {
				showEntireQueue = isChecked();
				viewer.refresh();
			}
		};
		showAll.setImageDescriptor(Activator.getDefault().getImageDescriptor("icons/spectacle-lorgnette.png"));
		
		toolMan.add(showAll);
		menuMan.add(showAll);
		dropDown.add(showAll);<commit_msg>Fix to image of edit action.<commit_after>/*
 * Copyright (c) 2012 Diamond Light Source Ltd.
 *
 * 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
 */
package org.eclipse.scanning.event.ui.view;

import java.io.File;
import java.net.URI;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.scanning.api.event.EventException;
import org.eclipse.scanning.api.event.IEventService;
import org.eclipse.scanning.api.event.bean.BeanEvent;
import org.eclipse.scanning.api.event.bean.IBeanListener;
import org.eclipse.scanning.api.event.core.ConsumerConfiguration;
import org.eclipse.scanning.api.event.core.IPublisher;
import org.eclipse.scanning.api.event.core.ISubmitter;
import org.eclipse.scanning.api.event.core.ISubscriber;
import org.eclipse.scanning.api.event.status.AdministratorMessage;
import org.eclipse.scanning.api.event.status.StatusBean;
import org.eclipse.scanning.api.ui.IModifyHandler;
import org.eclipse.scanning.api.ui.IRerunHandler;
import org.eclipse.scanning.api.ui.IResultHandler;
import org.eclipse.scanning.event.ui.Activator;
import org.eclipse.scanning.event.ui.ServiceHolder;
import org.eclipse.scanning.event.ui.dialog.PropertiesDialog;
import org.eclipse.scanning.event.ui.preference.CommandConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A view for which the secondary id MUST be set and provides the queueName
 * and optionally the queue view name if a custom one is required. Syntax of
 * these parameters in the secondary id are key1=value1;key2=value2...
 * 
 * The essential keys are: beanBundleName, beanClassName, queueName, topicName, submissionQueueName
 * You can use createId(...) to generate a legal id from them.
 * 
 * The optional keys are: partName, 
 *                        uri (default CommandConstants.JMS_URI),
 *                        userName (default is user.name system property)
 * 
 * Example id for this view would be:
 * org.eclipse.scanning.event.ui.queueView:beanClassName=org.dawnsci.commandserver.mx.beans.ProjectBean;beanBundleName=org.dawnsci.commandserver.mx
 * 
 * You can optionally extend this class to provide a table which is displayed for your
 * queue of custom objects. For instance for a queue showing xia2 reruns, the 
 * extra columns for this could be defined. However by default the 
 * 
 * @author <NAME>
 *
 */
public class StatusQueueView extends ViewPart {
	
	public static final String ID = "org.eclipse.scanning.event.ui.queueView";
	
	private static final Logger logger = LoggerFactory.getLogger(StatusQueueView.class);
	
	// UI
	private TableViewer                       viewer;
	
	// Data
	private Properties                        idProperties;
	private Map<String, StatusBean>           queue;
	private boolean                           showEntireQueue = false;

	private ISubscriber<IBeanListener<StatusBean>>           topicMonitor;
	private ISubscriber<IBeanListener<AdministratorMessage>> adminMonitor;
	private ISubmitter<StatusBean>                           queueConnection;

	private Action rerun, edit, remove, up, down, pause;
	private IEventService service;
	
	public StatusQueueView() {
		this.service = ServiceHolder.getEventService();
	}

	@Override
	public void createPartControl(Composite content) {
		
		content.setLayout(new GridLayout(1, false));
		Util.removeMargins(content);

		this.viewer   = new TableViewer(content, SWT.FULL_SELECTION | SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
		viewer.setUseHashlookup(true);
		viewer.getTable().setHeaderVisible(true);
		viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		
		createColumns();
		viewer.setContentProvider(createContentProvider());
		
        try {
    		queueConnection = service.createSubmitter(getUri(), getSubmissionQueueName());
    		updateQueue(getUri());
    		
    		String name = getSecondaryIdAttribute("partName");
            if (name!=null) setPartName(name);
    		
            createActions();

    		// We just use this submitter to read the queue
            createTopicListener(getUri());
            
		} catch (Exception e) {
			logger.error("Cannot listen to topic of command server!", e);
		}
        
		getViewSite().setSelectionProvider(viewer);
		viewer.addSelectionChangedListener(new ISelectionChangedListener() {	
			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				updateSelected();
			}
		});

	}
	
	protected void updateSelected() {
		StatusBean bean = getSelection();
		if (bean == null) bean = new StatusBean();
		
		remove.setEnabled(bean.getStatus()!=null);
		rerun.setEnabled(true);
		
		boolean isSubmitted = bean.getStatus()==org.eclipse.scanning.api.event.status.Status.SUBMITTED;
		up.setEnabled(isSubmitted);
		edit.setEnabled(isSubmitted);
		down.setEnabled(isSubmitted);
		pause.setEnabled(bean.getStatus().isRunning()||bean.getStatus().isPaused());
		pause.setChecked(bean.getStatus().isPaused());
		pause.setText(bean.getStatus().isPaused()?"Resume job":"Pause job");
	}

	/**
	 * Listens to a topic
	 */
	private void createTopicListener(final URI uri) throws Exception {
		
		// Use job because connection might timeout.
		final Job topicJob = new Job("Create topic listener") {

			@Override
			protected IStatus run(IProgressMonitor monitor) {
				try {
					topicMonitor = service.createSubscriber(uri, getTopicName());
					topicMonitor.addListener(new IBeanListener<StatusBean>() {
						@Override
						public void beanChangePerformed(BeanEvent<StatusBean> evt) {
							final StatusBean bean = evt.getBean();
	                        try {
								mergeBean(bean);
							} catch (Exception e) {
								logger.error("Cannot merge changed bean!");
							}
						}
					});

					adminMonitor = service.createSubscriber(uri, IEventService.ADMIN_MESSAGE_TOPIC);
					adminMonitor.addListener(new IBeanListener<AdministratorMessage>() {
						@Override
						public void beanChangePerformed(BeanEvent<AdministratorMessage> evt) {
							final AdministratorMessage bean = evt.getBean();
	        				getSite().getShell().getDisplay().syncExec(new Runnable() {
	        					public void run() {
	                                   MessageDialog.openError(getViewSite().getShell(), 
	                                		                   bean.getTitle(), 
	                                		                   bean.getMessage());
	                                   
	                                   viewer.refresh();
	        					}
	        				});
						}
					});
					
			        return Status.OK_STATUS;
			        
				} catch (Exception ne) {
					logger.error("Cannot listen to topic changes because command server is not there", ne);
			        return Status.CANCEL_STATUS;
				}
			}
			
			
		};
		
		topicJob.setPriority(Job.INTERACTIVE);
		topicJob.setSystem(true);
		topicJob.setUser(false);
		topicJob.schedule();
	}
	
	public void dispose() {
		super.dispose();
		try {
			if (topicMonitor!=null) topicMonitor.disconnect();
			if (adminMonitor!=null) adminMonitor.disconnect();
		} catch (Exception ne) {
			logger.warn("Problem stopping topic listening for "+getTopicName(), ne);
		}
	}

	/**
	 * Updates the bean if it is found in the list, otherwise
	 * refreshes the whole list because a bean we are not reporting
	 * has been(bean?) encountered.
	 * 
	 * @param bean
	 */
	protected void mergeBean(final StatusBean bean) throws Exception {
		
		getSite().getShell().getDisplay().asyncExec(new Runnable() {
			public void run(){
				if (queue.containsKey(bean.getUniqueId())) {
					queue.get(bean.getUniqueId()).merge(bean);
					viewer.refresh();
					updateSelected();
				} else {
					reconnect();
				}
			}
		});
	}

	private void createActions() {
		
		final IContributionManager toolMan  = getViewSite().getActionBars().getToolBarManager();
		final IContributionManager dropDown = getViewSite().getActionBars().getMenuManager();
		final MenuManager          menuMan = new MenuManager();
	
		final Action openResults = new Action("Open results for selected run", Activator.getDefault().getImageDescriptor("icons/results.png")) {
			public void run() {
				openResults(getSelection());
			}
		};
		
		toolMan.add(openResults);
		toolMan.add(new Separator());
		menuMan.add(openResults);
		menuMan.add(new Separator());
		dropDown.add(openResults);
		dropDown.add(new Separator());
		
		this.up = new Action("Less urgent (-1)", Activator.getDefault().getImageDescriptor("icons/arrow-090.png")) {
			@SuppressWarnings("unused")
			public void run() {
				final StatusBean bean = getSelection();
				try {
					queueConnection.reorder(bean, -1);
				} catch (EventException e) {
					ErrorDialog.openError(getViewSite().getShell(), "Cannot move "+bean.getName(), "'"+bean.getName()+"' cannot be moved in the submission queue.",
							new Status(IStatus.ERROR, "org.eclipse.scanning.event.ui", e.getMessage()));
				}
				refresh();
			}
		};
		up.setEnabled(false);
		toolMan.add(up);
		menuMan.add(up);
		dropDown.add(up);
		
		this.down = new Action("More urgent (+1)", Activator.getDefault().getImageDescriptor("icons/arrow-270.png")) {
			@SuppressWarnings("unused")
			public void run() {
				final StatusBean bean = getSelection();
				try {
					queueConnection.reorder(getSelection(), +1);
				} catch (EventException e) {
					ErrorDialog.openError(getViewSite().getShell(), "Cannot move "+bean.getName(), e.getMessage(),
							new Status(IStatus.ERROR, "org.eclipse.scanning.event.ui", e.getMessage()));
				}
				refresh();
			}
		};
		down.setEnabled(false);
		toolMan.add(down);
		menuMan.add(down);
		dropDown.add(down);

		this.pause = new Action("Pause job", IAction.AS_CHECK_BOX) {
			public void run() {
				pauseJob();
			}
		};
		pause.setImageDescriptor(Activator.getDefault().getImageDescriptor("icons/control-pause.png"));
		pause.setEnabled(false);
		pause.setChecked(false);
		toolMan.add(pause);
		menuMan.add(pause);
		dropDown.add(pause);
		
		this.remove = new Action("Stop job or remove if finished", Activator.getDefault().getImageDescriptor("icons/control-stop-square.png")) {
			public void run() {
				stopJob();
			}
		};
		remove.setEnabled(false);
		toolMan.add(remove);
		menuMan.add(remove);
		dropDown.add(remove);
		
		this.rerun = new Action("Rerun...", Activator.getDefault().getImageDescriptor("icons/rerun.png")) {
			public void run() {
				rerunSelection();
			}
		};
		rerun.setEnabled(false);
		toolMan.add(rerun);
		menuMan.add(rerun);
		dropDown.add(rerun);
		
		this.edit = new Action("Edit...", Activator.getDefault().getImageDescriptor("icons/modify.png")) {
			public void run() {
				editSelection();
			}
		};
		edit.setEnabled(false);
		toolMan.add(edit);
		menuMan.add(edit);
		dropDown.add(edit);


		toolMan.add(new Separator());
		menuMan.add(new Separator());
		
		final Action showAll = new Action("Show other users results", IAction.AS_CHECK_BOX) {
			public void run() {
				showEntireQueue = isChecked();
				viewer.refresh();
			}
		};
		showAll.setImageDescriptor(Activator.getDefault().getImageDescriptor("icons/spectacle-lorgnette.png"));
		
		toolMan.add(showAll);
		menuMan.add(showAll);
		dropDown.add(showAll);
{'fraction_non_alphanumeric': 0.11935281297329245, 'fraction_numerical': 0.0019208747368032212, 'mean_word_length': 3.673226307612636, 'pattern_counts': {'":': 2, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3145425', 'n_tokens_mistral': 9047, 'n_tokens_neox': 7976, 'n_words': 1644}