| /*=============================================================================# |
| # Copyright (c) 2006, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.nico.ui.views; |
| |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IDebugEventSetListener; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.viewers.IStructuredContentProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITableLabelProvider; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.BusyIndicator; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DragSourceEvent; |
| import org.eclipse.swt.dnd.TableDragSourceEffect; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.graphics.Image; |
| 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.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IMemento; |
| import org.eclipse.ui.IViewSite; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.actions.ActionFactory; |
| import org.eclipse.ui.menus.CommandContributionItem; |
| import org.eclipse.ui.menus.CommandContributionItemParameter; |
| import org.eclipse.ui.part.ViewPart; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.ts.core.Tool; |
| import org.eclipse.statet.jcommons.ts.core.ToolRunnable; |
| |
| import org.eclipse.statet.ecommons.ts.ui.workbench.WorkbenchToolRegistry; |
| import org.eclipse.statet.ecommons.ts.ui.workbench.WorkbenchToolRegistryListener; |
| import org.eclipse.statet.ecommons.ts.ui.workbench.WorkbenchToolSessionData; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| |
| import org.eclipse.statet.internal.nico.ui.LocalTaskTransfer; |
| import org.eclipse.statet.internal.nico.ui.Messages; |
| import org.eclipse.statet.nico.core.runtime.Queue; |
| import org.eclipse.statet.nico.core.runtime.ToolProcess; |
| import org.eclipse.statet.nico.ui.NicoUI; |
| import org.eclipse.statet.nico.ui.NicoUITools; |
| import org.eclipse.statet.nico.ui.util.ToolProgressGroup; |
| |
| |
| /** |
| * A view for the queue of a tool process. |
| * |
| * Usage: This class is not intended to be subclassed. |
| */ |
| public class QueueView extends ViewPart { |
| |
| |
| private class ViewContentProvider implements IStructuredContentProvider, IDebugEventSetListener { |
| |
| private volatile boolean expectInfoEvent= false; |
| private ImList<ToolRunnable> refreshData; |
| |
| @Override |
| public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { |
| if (oldInput != null && newInput == null) { |
| unregister(); |
| } |
| if (newInput != null) { |
| final ToolProcess newProcess= (ToolProcess) newInput; |
| |
| final DebugPlugin manager= DebugPlugin.getDefault(); |
| if (manager != null) { |
| manager.addDebugEventListener(this); |
| } |
| } |
| } |
| |
| @Override |
| public Object[] getElements(final Object inputElement) { |
| ToolRunnable[] elements; |
| if (this.refreshData != null) { |
| elements= this.refreshData.toArray(new ToolRunnable[this.refreshData.size()]); |
| this.refreshData= null; |
| } |
| else { |
| elements= new ToolRunnable[0]; |
| final Queue queue= getQueue(); |
| if (queue != null) { |
| this.expectInfoEvent= true; |
| queue.sendElements(); |
| } |
| } |
| return elements; |
| } |
| |
| private void unregister() { |
| final DebugPlugin manager= DebugPlugin.getDefault(); |
| if (manager != null) { |
| manager.removeDebugEventListener(this); |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| unregister(); |
| } |
| |
| private void setElements(final ImList<ToolRunnable> elements) { |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) { |
| return; |
| } |
| ViewContentProvider.this.refreshData= elements; |
| QueueView.this.tableViewer.refresh(); |
| } |
| }); |
| } |
| |
| @Override |
| public void handleDebugEvents(final DebugEvent[] events) { |
| final ToolProcess process= QueueView.this.process; |
| if (process == null) { |
| return; |
| } |
| boolean updateProgress= false; |
| final Queue queue= process.getQueue(); |
| EVENT: for (int i= 0; i < events.length; i++) { |
| final DebugEvent event= events[i]; |
| final Object source= event.getSource(); |
| if (source == queue) { |
| switch (event.getKind()) { |
| |
| case DebugEvent.CHANGE: |
| if (event.getDetail() != DebugEvent.CONTENT) { |
| continue EVENT; |
| } |
| final Queue.TaskDelta taskDelta= (Queue.TaskDelta) event.getData(); |
| switch (taskDelta.type) { |
| case ToolRunnable.ADDING_TO: |
| case ToolRunnable.MOVING_TO: |
| if (!this.expectInfoEvent) { |
| if (events.length > i + 1 && taskDelta.data.size() == 1) { |
| // Added and removed in same set |
| final DebugEvent next= events[i + 1]; |
| if (next.getSource() == queue |
| && next.getKind() == DebugEvent.CHANGE |
| && next.getDetail() == DebugEvent.CONTENT) { |
| final Queue.TaskDelta nextDelta= (Queue.TaskDelta) next.getData(); |
| if (nextDelta.type == ToolRunnable.STARTING |
| && taskDelta.data.get(0) == nextDelta.data.get(0)) { |
| updateProgress= true; |
| i++; |
| continue EVENT; |
| } |
| } |
| } |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) { |
| return; |
| } |
| if (taskDelta.position >= 0) { |
| for (int j= 0; j < taskDelta.data.size(); j++) { |
| QueueView.this.tableViewer.insert(taskDelta.data.get(j), taskDelta.position + j); |
| } |
| } |
| else { |
| QueueView.this.tableViewer.add(taskDelta.data.toArray()); |
| } |
| } |
| }); |
| } |
| continue EVENT; |
| |
| case ToolRunnable.STARTING: |
| updateProgress= true; |
| //$FALL-THROUGH$ continue with delete |
| case ToolRunnable.REMOVING_FROM: |
| case ToolRunnable.MOVING_FROM: |
| if (!this.expectInfoEvent) { |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) { |
| return; |
| } |
| QueueView.this.tableViewer.remove(taskDelta.data.toArray()); |
| } |
| }); |
| } |
| continue EVENT; |
| |
| // case Queue.QUEUE_CHANGE: |
| // if (!fExpectInfoEvent) { |
| // setElements((IToolRunnable[]) event.getData()); |
| // } |
| // continue EVENT; |
| } |
| continue EVENT; |
| |
| case DebugEvent.MODEL_SPECIFIC: |
| if (event.getDetail() == Queue.QUEUE_INFO && this.expectInfoEvent) { |
| this.expectInfoEvent= false; |
| setElements((ImList<ToolRunnable>) event.getData()); |
| } |
| continue EVENT; |
| |
| case DebugEvent.TERMINATE: |
| disconnect(process); |
| continue EVENT; |
| |
| default: |
| continue EVENT; |
| } |
| } |
| } |
| if (updateProgress && QueueView.this.showProgress) { |
| final ToolProgressGroup progress= QueueView.this.progressControl; |
| if (progress != null) { |
| progress.refresh(false); |
| } |
| } |
| } |
| } |
| |
| private class TableLabelProvider extends LabelProvider implements ITableLabelProvider { |
| |
| @Override |
| public Image getColumnImage(final Object element, final int columnIndex) { |
| if (columnIndex == 0) { |
| return getImage(element); |
| } |
| return null; |
| } |
| |
| @Override |
| public Image getImage(final Object element) { |
| return NicoUITools.getImage((ToolRunnable) element); |
| } |
| |
| @Override |
| public String getColumnText(final Object element, final int columnIndex) { |
| if (columnIndex == 0) { |
| return getText(element); |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String getText(final Object element) { |
| final ToolRunnable runnable= (ToolRunnable) element; |
| return runnable.getLabel(); |
| } |
| } |
| |
| private class ShowDescriptionAction extends Action { |
| |
| public ShowDescriptionAction() { |
| setText(Messages.ShowToolDescription_name); |
| setToolTipText(Messages.ShowToolDescription_tooltip); |
| setChecked(QueueView.this.showDescription); |
| } |
| |
| @Override |
| public void run() { |
| QueueView.this.showDescription= isChecked(); |
| updateContentDescription(QueueView.this.process); |
| } |
| } |
| |
| private class ShowProgressAction extends Action { |
| |
| public ShowProgressAction() { |
| setText(Messages.ShowProgress_name); |
| setToolTipText(Messages.ShowProgress_tooltip); |
| setChecked(QueueView.this.showProgress); |
| } |
| |
| @Override |
| public void run() { |
| QueueView.this.showProgress= isChecked(); |
| if (QueueView.this.showProgress) { |
| createProgressControl(); |
| QueueView.this.progressControl.setTool(QueueView.this.process, true); |
| QueueView.this.progressControl.getControl().moveAbove(QueueView.this.tableViewer.getControl()); |
| } |
| else { |
| if (QueueView.this.progressControl != null) { |
| QueueView.this.progressControl.getControl().dispose(); |
| QueueView.this.progressControl= null; |
| } |
| } |
| QueueView.this.composite.layout(true); |
| } |
| } |
| |
| |
| private Composite composite; |
| private ToolProgressGroup progressControl; |
| private TableViewer tableViewer; |
| |
| private ToolProcess process; |
| private WorkbenchToolRegistryListener toolRegistryListener; |
| |
| private static final String M_SHOW_DESCRIPTION= "QueueView.ShowDescription"; //$NON-NLS-1$ |
| private boolean showDescription; |
| private Action showDescriptionAction; |
| |
| private static final String M_SHOW_PROGRESS= "QueueView.ShowProgress"; //$NON-NLS-1$ |
| private boolean showProgress; |
| private Action showProgressAction; |
| |
| private Action selectAllAction; |
| private Action deleteAction; |
| |
| |
| public QueueView() { |
| } |
| |
| |
| @Override |
| public void init(final IViewSite site, final IMemento memento) throws PartInitException { |
| super.init(site, memento); |
| |
| final String showDescription= (memento != null) ? memento.getString(M_SHOW_DESCRIPTION) : null; |
| if (showDescription == null || showDescription.equals("off")) { // default //$NON-NLS-1$ |
| this.showDescription= false; |
| } else { |
| this.showDescription= true; |
| } |
| |
| final String showProgress= (memento != null) ? memento.getString(M_SHOW_PROGRESS) : null; |
| if (showProgress== null || showProgress.equals("on")) { // default //$NON-NLS-1$ |
| this.showProgress= true; |
| } else { |
| this.showProgress= false; |
| } |
| } |
| |
| @Override |
| public void saveState(final IMemento memento) { |
| super.saveState(memento); |
| |
| memento.putString(M_SHOW_DESCRIPTION, (this.showDescription) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$ |
| memento.putString(M_SHOW_PROGRESS, (this.showProgress) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| @Override |
| public void createPartControl(final Composite parent) { |
| updateContentDescription(null); |
| |
| this.composite= parent; |
| final GridLayout layout= new GridLayout(); |
| layout.marginHeight= 0; |
| layout.marginWidth= 0; |
| layout.verticalSpacing= 0; |
| parent.setLayout(layout); |
| |
| if (this.showProgress) { |
| createProgressControl(); |
| } |
| |
| this.tableViewer= new TableViewer(parent, SWT.MULTI | SWT.V_SCROLL); |
| this.tableViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| this.tableViewer.getTable().setLinesVisible(false); |
| this.tableViewer.getTable().setHeaderVisible(false); |
| new TableColumn(this.tableViewer.getTable(), SWT.DEFAULT); |
| this.tableViewer.getTable().addControlListener(new ControlAdapter() { |
| @Override |
| public void controlResized(final ControlEvent e) { |
| // adapt the column width to the width of the table |
| final Table table= QueueView.this.tableViewer.getTable(); |
| final Rectangle area= table.getClientArea(); |
| final TableColumn column= table.getColumn(0); |
| column.setWidth(area.width-3); // it looks better with a small gap |
| } |
| }); |
| |
| this.tableViewer.setContentProvider(new ViewContentProvider()); |
| this.tableViewer.setLabelProvider(new TableLabelProvider()); |
| |
| createActions(); |
| contributeToActionBars(); |
| hookDND(); |
| |
| // listen on console changes |
| final WorkbenchToolRegistry toolRegistry= NicoUI.getToolRegistry(); |
| connect(toolRegistry.getActiveToolSession(getViewSite().getPage()).getTool()); |
| this.toolRegistryListener= new WorkbenchToolRegistryListener() { |
| @Override |
| public void toolSessionActivated(final WorkbenchToolSessionData sessionData) { |
| final Tool tool= sessionData.getTool(); |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| connect(tool); |
| } |
| }); |
| } |
| @Override |
| public void toolTerminated(final WorkbenchToolSessionData sessionData) { |
| // handled by debug events |
| } |
| }; |
| toolRegistry.addListener(this.toolRegistryListener, getViewSite().getPage()); |
| } |
| |
| |
| private void createProgressControl() { |
| this.progressControl= new ToolProgressGroup(this.composite); |
| this.progressControl.getControl().setLayoutData( |
| new GridData(SWT.FILL, SWT.FILL, true, false)); |
| } |
| |
| protected void updateContentDescription(final ToolProcess process) { |
| if (this.showDescription) { |
| setContentDescription(process != null ? process.getLabel(0) : " "); //$NON-NLS-1$ |
| } |
| else { |
| setContentDescription(""); //$NON-NLS-1$ |
| } |
| } |
| |
| private void createActions() { |
| this.showDescriptionAction= new ShowDescriptionAction(); |
| this.showProgressAction= new ShowProgressAction(); |
| |
| this.selectAllAction= new Action() { |
| @Override |
| public void run() { |
| QueueView.this.tableViewer.getTable().selectAll(); |
| } |
| }; |
| this.deleteAction= new Action() { |
| @Override |
| public void run() { |
| final Queue queue= getQueue(); |
| if (queue != null) { |
| final IStructuredSelection selection= (IStructuredSelection) QueueView.this.tableViewer.getSelection(); |
| queue.remove(ImCollections.toList(selection.toList())); |
| } |
| } |
| }; |
| } |
| |
| private void contributeToActionBars() { |
| final IActionBars bars= getViewSite().getActionBars(); |
| |
| bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), this.selectAllAction); |
| bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), this.deleteAction); |
| |
| fillLocalPullDown(bars.getMenuManager()); |
| fillLocalToolBar(bars.getToolBarManager()); |
| } |
| |
| private void fillLocalPullDown(final IMenuManager manager) { |
| manager.add(this.showDescriptionAction); |
| manager.add(this.showProgressAction); |
| } |
| |
| private void fillLocalToolBar(final IToolBarManager manager) { |
| manager.add(new CommandContributionItem(new CommandContributionItemParameter( |
| getSite(), null, NicoUI.PAUSE_COMMAND_ID, null, |
| null, null, null, |
| null, null, null, |
| CommandContributionItem.STYLE_CHECK, null, false))); |
| } |
| |
| private void hookDND() { |
| this.tableViewer.addDragSupport(DND.DROP_MOVE, |
| new Transfer[] { LocalTaskTransfer.getTransfer() }, |
| new TableDragSourceEffect(this.tableViewer.getTable()) { |
| @Override |
| public void dragStart(final DragSourceEvent event) { |
| if (QueueView.this.tableViewer.getTable().getSelectionCount() > 0) { |
| event.doit= true; |
| } else { |
| event.doit= false; |
| } |
| LocalTaskTransfer.getTransfer().init(QueueView.this.process); |
| super.dragStart(event); |
| } |
| @Override |
| public void dragSetData(final DragSourceEvent event) { |
| super.dragSetData(event); |
| final LocalTaskTransfer.Data data= LocalTaskTransfer.getTransfer().createData(); |
| if (data.process != QueueView.this.process) { |
| event.doit= false; |
| return; |
| } |
| data.runnables= ImCollections.toList( |
| ((IStructuredSelection) QueueView.this.tableViewer.getSelection()).toList() ); |
| event.data= data; |
| } |
| @Override |
| public void dragFinished(final DragSourceEvent event) { |
| super.dragFinished(event); |
| LocalTaskTransfer.getTransfer().finished(); |
| } |
| }); |
| } |
| |
| private void disconnect(final ToolProcess process) { |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (QueueView.this.process != null && QueueView.this.process == process) { |
| connect(null); |
| } |
| } |
| }); |
| } |
| |
| /** May only be called in UI thread */ |
| private void connect(final Tool tool) { |
| final ToolProcess process= (tool instanceof ToolProcess) ? (ToolProcess)tool : null; |
| final Runnable runnable= new Runnable() { |
| @Override |
| public void run() { |
| if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) { |
| return; |
| } |
| QueueView.this.process= process; |
| updateContentDescription(process); |
| if (QueueView.this.progressControl != null) { |
| QueueView.this.progressControl.setTool(process, true); |
| } |
| QueueView.this.tableViewer.setInput(process); |
| } |
| }; |
| BusyIndicator.showWhile(UIAccess.getDisplay(), runnable); |
| } |
| |
| /** |
| * Returns the tool process, which this view is connected to. |
| * |
| * @return a tool process or <code>null</code>, if no process is connected. |
| */ |
| public ToolProcess getProcess() { |
| return this.process; |
| } |
| |
| public Queue getQueue() { |
| if (this.process != null) { |
| return this.process.getQueue(); |
| } |
| return null; |
| } |
| |
| |
| @Override |
| public void setFocus() { |
| // Passing the focus request to the viewer's control. |
| this.tableViewer.getControl().setFocus(); |
| } |
| |
| @Override |
| public void dispose() { |
| super.dispose(); |
| } |
| |
| } |