blob: 75452310380e80c1f3b98245a980a334ae8658c6 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2019 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 java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import com.ibm.icu.text.DateFormat;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler2;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.window.DefaultToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.dialogs.SearchPattern;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.ts.core.ActiveToolListener;
import org.eclipse.statet.jcommons.ts.core.ActiveToolListener.ActiveToolEvent;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.jcommons.ts.core.ToolProvider;
import org.eclipse.statet.ecommons.collections.FastList;
import org.eclipse.statet.ecommons.preferences.core.Preference.EnumSetPref;
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.SharedMessages;
import org.eclipse.statet.ecommons.ui.SharedUIResources;
import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
import org.eclipse.statet.ecommons.ui.actions.SearchContributionItem;
import org.eclipse.statet.ecommons.ui.actions.SimpleContributionItem;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.internal.nico.ui.Messages;
import org.eclipse.statet.internal.nico.ui.NicoUIPlugin;
import org.eclipse.statet.internal.nico.ui.actions.HistoryCopyAction;
import org.eclipse.statet.internal.nico.ui.actions.HistoryDragAdapter;
import org.eclipse.statet.internal.nico.ui.actions.HistorySubmitAction;
import org.eclipse.statet.nico.core.runtime.History;
import org.eclipse.statet.nico.core.runtime.History.Entry;
import org.eclipse.statet.nico.core.runtime.IHistoryListener;
import org.eclipse.statet.nico.core.runtime.SubmitType;
import org.eclipse.statet.nico.core.runtime.ToolProcess;
import org.eclipse.statet.nico.ui.NicoUI;
import org.eclipse.statet.nico.ui.actions.LoadHistoryAction;
import org.eclipse.statet.nico.ui.actions.SaveHistoryAction;
import org.eclipse.statet.nico.ui.console.ScrollLockAction;
import org.eclipse.statet.nico.ui.console.ScrollLockAction.Receiver;
/**
* A view for the history of a tool process.
*
* Usage: This class is not intend to be subclassed.
*/
public class HistoryView extends ViewPart implements ToolProvider {
public static interface EntryFilter {
public boolean select(Entry e);
}
/**
* Converts the selection of this view/viewer into a commmand text block.
*
* @param selection a selection with history entries.
* @return command block.
*/
public static String createTextBlock(final Entry[] selection) {
final StringBuilder text = new StringBuilder(selection.length * 8);
for (int i = 0; i < selection.length; i++) {
text.append(selection[i].getCommand());
text.append('\n');
}
return text.toString();
}
public static List<String> createCommandList(final Entry[] selection) {
final String[] array = new String[selection.length];
for (int i = 0; i < selection.length; i++) {
array[i] = selection[i].getCommand();
}
return ImCollections.newList(array);
}
private static final EntryFilter EMPTY_FILTER = new EntryFilter() {
@Override
public boolean select(final Entry e) {
return (e.getCommandMarker() >= 0);
}
};
private static final class SubmitTypeFilter implements EntryFilter {
private final EnumSet<SubmitType> fSubmitTypes;
public SubmitTypeFilter(final EnumSet<SubmitType> types) {
this.fSubmitTypes = types;
}
@Override
public boolean select(final Entry e) {
final SubmitType type = e.getSubmitType();
return (type == null
|| this.fSubmitTypes.contains(type));
}
}
private static final EnumSetPref<SubmitType> SOURCE_ENCODER= new EnumSetPref<>(null, null, SubmitType.class);
private class ViewReloadJob extends Job {
ViewReloadJob() {
super("Update History View"); //$NON-NLS-1$
setPriority(SHORT);
setUser(false);
}
@Override
protected IStatus run(final IProgressMonitor monitor) {
final ToolProcess process = HistoryView.this.fProcess;
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (process == null) {
HistoryView.this.fContentProvider.setNewSource(null, new Entry[0]);
return Status.OK_STATUS;
}
final History history = process.getHistory();
history.getReadLock().lock();
final Entry[] entries;
try {
entries = history.toArray();
}
finally {
history.getReadLock().unlock();
}
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
HistoryView.this.fContentProvider.setNewSource(history, entries);
return Status.OK_STATUS;
}
}
private class ViewContentProvider implements IHistoryListener, Runnable {
private static final int REMOVE_THRESHOLD = 25;
private History fCurrentSource;
private boolean fIsScheduled;
private final List<Entry> fToAdd= new ArrayList<>(16);
private final List<Entry> fToRemove= new ArrayList<>(16);
private Entry[] fNewEntrys;
public synchronized void setNewSource(final History source, final Entry[] es) {
this.fCurrentSource = source;
this.fNewEntrys = es;
this.fToAdd.clear();
this.fToRemove.clear();
if (!this.fIsScheduled) {
this.fIsScheduled = true;
UIAccess.getDisplay().asyncExec(this);
}
}
@Override
public synchronized void completeChange(final History source, final Entry[] es) {
if (this.fCurrentSource != source) {
return;
}
this.fNewEntrys = es;
this.fToAdd.clear();
this.fToRemove.clear();
if (!this.fIsScheduled) {
this.fIsScheduled = true;
UIAccess.getDisplay().asyncExec(this);
}
}
@Override
public synchronized void entryAdded(final History source, final Entry e) {
if (this.fCurrentSource != source) {
return;
}
this.fToAdd.add(e);
if (!this.fIsScheduled) {
this.fIsScheduled = true;
UIAccess.getDisplay().asyncExec(this);
}
}
@Override
public synchronized void entryRemoved(final History source, final Entry e) {
if (this.fCurrentSource != source) {
return;
}
this.fToRemove.add(e);
if (!this.fIsScheduled) {
this.fIsScheduled = true;
UIAccess.getDisplay().asyncExec(this);
}
}
@Override
public void run() {
final Entry[] newEntries;
final int toAdd;
final ImList<Entry> toAddEntries;
final int toRemove;
final ImList<Entry> toRemoveEntries;
final EntryFilter[] filter;
synchronized (this) {
this.fIsScheduled = false;
if (!UIAccess.isOkToUse(HistoryView.this.fTable)) {
return;
}
if ((HistoryView.this.fProcess != null && this.fCurrentSource != HistoryView.this.fProcess.getHistory())
|| (HistoryView.this.fProcess == null && this.fCurrentSource != null)) {
return;
}
newEntries = this.fNewEntrys;
this.fNewEntrys = null;
toAdd = this.fToAdd.size();
toAddEntries = (toAdd > 0) ? ImCollections.clearToList(this.fToAdd) : null;
toRemove = this.fToRemove.size();
toRemoveEntries = (toRemove > REMOVE_THRESHOLD) ? ImCollections.clearToList(this.fToRemove) : null;
filter = HistoryView.this.fFilter.toArray();
}
HistoryView.this.fTable.setRedraw(false);
TableItem addedItem = null;
if (newEntries != null) {
HistoryView.this.fTable.deselectAll();
final int reusableItemCount = HistoryView.this.fTable.getItemCount();
int reuseItemIdx = 0;
final int n = newEntries.length;
ITER_ENTRY : for (int i = 0; i < n; i++) {
final Entry e = newEntries[i];
for (int f = 0; f < filter.length; f++) {
if (!filter[f].select(e)) {
continue ITER_ENTRY;
}
}
if (reuseItemIdx < reusableItemCount) {
addedItem = HistoryView.this.fTable.getItem(reuseItemIdx++);
}
else {
addedItem = new TableItem(HistoryView.this.fTable, SWT.NONE);
}
addedItem.setData(e);
updateItem(addedItem);
}
if (reuseItemIdx < reusableItemCount) {
HistoryView.this.fTable.remove(reuseItemIdx, reusableItemCount-1);
}
if (addedItem != null) {
HistoryView.this.fTable.showItem(addedItem);
}
}
if (toAdd > 0) {
ITER_ENTRY : for (int i = 0; i < toAdd; i++) {
final Entry e = toAddEntries.get(i);
for (int f = 0; f < filter.length; f++) {
if (!filter[f].select(e)) {
continue ITER_ENTRY;
}
}
addedItem = new TableItem(HistoryView.this.fTable, SWT.NONE);
addedItem.setData(e);
updateItem(addedItem);
}
}
if (toRemove > REMOVE_THRESHOLD) {
final int itemCount = HistoryView.this.fTable.getItemCount();
int[] removeIdxs = new int[toRemove];
int count = 0;
for (int i = 0; i < toRemove; i++) {
for (int j = 0; j < itemCount; j++) {
final TableItem removedItem = HistoryView.this.fTable.getItem(j);
if (removedItem.getData() == toRemoveEntries.get(i)) {
removedItem.setData(null);
removeIdxs[count++] = j;
}
}
}
if (count > 0) {
if (count < removeIdxs.length) {
System.arraycopy(removeIdxs, 0, removeIdxs = new int[count], 0, count);
}
HistoryView.this.fTable.remove(removeIdxs);
}
}
if (HistoryView.this.fDoAutoscroll && addedItem != null) {
HistoryView.this.fTable.showItem(addedItem);
}
HistoryView.this.fTable.setRedraw(true);
}
}
private class FilterEmptyAction extends Action {
FilterEmptyAction() {
setText(Messages.FilterEmptyAction_name);
setToolTipText(Messages.FilterEmptyAction_tooltip);
setImageDescriptor(SharedUIResources.getImages().getDescriptor(SharedUIResources.LOCTOOL_FILTER_IMAGE_ID));
setChecked(HistoryView.this.fDoFilterEmpty);
}
@Override
public void run() {
final boolean switchOn = isChecked();
HistoryView.this.fDoFilterEmpty = switchOn;
if (switchOn) {
addFilter(EMPTY_FILTER);
}
else {
removeFilter(EMPTY_FILTER);
}
}
}
private class FilterBySourceAction extends SimpleContributionItem {
private final SubmitType fType;
public FilterBySourceAction(final SubmitType type) {
super(type.getLabel(), null, SimpleContributionItem.STYLE_CHECK);
this.fType = type;
setChecked(HistoryView.this.fFilterBySource.fSubmitTypes.contains(type));
}
@Override
protected void execute() throws ExecutionException {
final SubmitTypeFilter currentFilter = HistoryView.this.fFilterBySource;
final EnumSet<SubmitType> types = EnumSet.copyOf(currentFilter.fSubmitTypes);
if (types.contains(this.fType)) {
types.remove(this.fType);
setChecked(false);
}
else {
types.add(this.fType);
setChecked(true);
}
final SubmitTypeFilter newFilter = new SubmitTypeFilter(types);
HistoryView.this.fFilterBySource = newFilter;
replaceFilter(currentFilter, newFilter);
}
}
private volatile ToolProcess fProcess; // note: we write only in ui thread
private WorkbenchToolRegistryListener fToolRegistryListener;
private final ViewContentProvider fContentProvider;
private Table fTable;
private Clipboard fClipboard;
private static final String M_FILTER_EMPTY = "FilterEmpty.enabled"; //$NON-NLS-1$
private boolean fDoFilterEmpty;
private Action fFilterEmptyAction;
private final FastList<EntryFilter> fFilter= new FastList<>(EntryFilter.class, FastList.IDENTITY);
private static final String M_AUTOSCROLL = "Autoscroll.enabled"; //$NON-NLS-1$
private boolean fDoAutoscroll;
private Action fScrollLockAction;
private static final String M_FILTER_BY_SOURCE = "FilterBySource.include"; //$NON-NLS-1$
private SubmitTypeFilter fFilterBySource;
private final CopyOnWriteIdentityListSet<ActiveToolListener> fToolListeners= new CopyOnWriteIdentityListSet<>();
private Action fSelectAllAction;
private Action fCopyAction;
private Action fSubmitAction;
private IHandler2 fSearchStartHandler;
private IHandler2 fSearchNextHandler;
private IHandler2 fSearchPrevHandler;
private SearchContributionItem fSearchTextItem;
private final SearchPattern fSearchPattern = new SearchPattern(SearchPattern.RULE_EXACT_MATCH
| SearchPattern.RULE_PREFIX_MATCH | SearchPattern.RULE_CAMELCASE_MATCH
| SearchPattern.RULE_PATTERN_MATCH | SearchPattern.RULE_BLANK_MATCH);
private LoadHistoryAction fLoadHistoryAction;
private SaveHistoryAction fSaveHistoryAction;
private final ViewReloadJob fReloadJob;
/**
* The constructor.
*/
public HistoryView() {
this.fReloadJob = new ViewReloadJob();
this.fContentProvider = new ViewContentProvider();
}
@Override
public void init(final IViewSite site, final IMemento memento) throws PartInitException {
super.init(site, memento);
final String autoscroll = (memento != null) ? memento.getString(M_AUTOSCROLL) : null;
if (autoscroll == null || autoscroll.equals("on")) { // default //$NON-NLS-1$
this.fDoAutoscroll = true;
}
else {
this.fDoAutoscroll = false;
}
final String filterBySource = (memento != null) ? memento.getString(M_FILTER_BY_SOURCE) : null;
if (filterBySource == null) {
this.fFilterBySource = new SubmitTypeFilter(EnumSet.range(SubmitType.CONSOLE, SubmitType.TOOLS));
}
else {
this.fFilterBySource = new SubmitTypeFilter(SOURCE_ENCODER.store2Usage(filterBySource));
}
this.fFilter.add(this.fFilterBySource);
final String filterEmpty = (memento != null) ? memento.getString(M_FILTER_EMPTY) : null;
if (filterEmpty == null || filterEmpty.equals("off")) { // default //$NON-NLS-1$
this.fDoFilterEmpty = false;
}
else {
this.fDoFilterEmpty = true;
this.fFilter.add(EMPTY_FILTER);
}
}
@Override
public void saveState(final IMemento memento) {
super.saveState(memento);
memento.putString(M_AUTOSCROLL, (this.fDoAutoscroll) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
memento.putString(M_FILTER_BY_SOURCE, SOURCE_ENCODER.usage2Store(this.fFilterBySource.fSubmitTypes));
memento.putString(M_FILTER_EMPTY, (this.fDoFilterEmpty) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
public void createPartControl(final Composite parent) {
parent.setLayout(LayoutUtils.newSashGrid());
this.fTable = new Table(parent, SWT.MULTI | SWT.V_SCROLL | SWT.FULL_SELECTION);
this.fTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
this.fTable.setLinesVisible(false);
this.fTable.setHeaderVisible(false);
new DefaultToolTip(this.fTable) {
private final DateFormat fFormat = DateFormat.getDateTimeInstance();
@Override
protected boolean shouldCreateToolTip(final Event event) {
return (super.shouldCreateToolTip(event)
&& HistoryView.this.fTable.getItem(new Point(event.x, event.y)) != null );
}
@Override
protected String getText(final Event event) {
final TableItem item = HistoryView.this.fTable.getItem(new Point(event.x, event.y));
if (item != null) {
final Entry e = (Entry) item.getData();
if (e.getTimeStamp() < 0) {
return "[-]\n"+e.getCommand(); //$NON-NLS-1$
}
else {
return "["+this.fFormat.format(new Date(e.getTimeStamp()))+"]\n" + e.getCommand(); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return null;
}
};
final TableColumn column = new TableColumn(this.fTable, SWT.DEFAULT);
this.fTable.addListener(SWT.Resize, new Listener() {
@Override
public void handleEvent(final Event event) {
// adapt the column width to the width of the table
final int tableWidth = HistoryView.this.fTable.getClientArea().width;
if (tableWidth == 0) {
return;
}
column.setWidth(tableWidth);
}
});
this.fTable.addKeyListener(new KeyListener() {
@Override
public void keyPressed(final KeyEvent e) {
if (e.keyCode == SWT.ARROW_UP &&
HistoryView.this.fTable.getSelectionCount() == 1 && HistoryView.this.fTable.getSelectionIndex() == 0) {
HistoryView.this.fSearchTextItem.show();
HistoryView.this.fTable.deselectAll();
e.doit = false;
}
}
@Override
public void keyReleased(final KeyEvent e) {
}
});
createActions();
hookContextMenu();
contributeToActionBars();
final DragSource dragSource = new DragSource(this.fTable, DND.DROP_COPY);
dragSource.setTransfer(new Transfer[] { TextTransfer.getInstance() });
dragSource.addDragListener(new HistoryDragAdapter(this));
// listen on console changes
final WorkbenchToolRegistry toolRegistry = NicoUI.getToolRegistry();
this.fToolRegistryListener = 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) {
final Tool tool = sessionData.getTool();
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (HistoryView.this.fProcess != null && HistoryView.this.fProcess == tool) {
connect(null);
}
}
});
}
};
toolRegistry.addListener(this.fToolRegistryListener, getViewSite().getPage());
connect(toolRegistry.getActiveToolSession(getViewSite().getPage()).getTool());
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.statet.nico.ui.cmd_history_view"); //$NON-NLS-1$
}
private void updateItem(final TableItem item) {
final Entry e = (Entry) item.getData();
item.setImage(NicoUIPlugin.getInstance().getImageRegistry().get(NicoUI.OBJ_CONSOLECOMMAND_IMAGE_ID));
item.setText(e.getCommand());
}
private void createActions() {
final IHandlerService handlerService = getSite().getService(IHandlerService.class);
this.fFilterEmptyAction = new FilterEmptyAction();
this.fScrollLockAction = new ScrollLockAction(new Receiver() {
@Override
public void setAutoScroll(final boolean enabled) {
HistoryView.this.fDoAutoscroll = enabled;
}
}, !this.fDoAutoscroll);
this.fSelectAllAction = new Action() {
@Override
public void run() {
HistoryView.this.fTable.selectAll();
}
};
this.fCopyAction = new HistoryCopyAction(this);
this.fSubmitAction = new HistorySubmitAction(this);
enabledSelectionActions(false);
this.fTable.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(final SelectionEvent e) {
if (HistoryView.this.fTable.getSelectionCount() > 0) {
enabledSelectionActions(true);
}
else {
enabledSelectionActions(false);
}
}
@Override
public void widgetDefaultSelected(final SelectionEvent e) {
HistoryView.this.fSubmitAction.run();
}
} );
this.fLoadHistoryAction = new LoadHistoryAction(this);
this.fSaveHistoryAction = new SaveHistoryAction(this);
this.fSearchStartHandler = new AbstractHandler() {
@Override
public Object execute(final ExecutionEvent arg0) {
HistoryView.this.fSearchTextItem.show();
return null;
}
};
handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE, this.fSearchStartHandler);
this.fSearchNextHandler = new AbstractHandler() {
@Override
public Object execute(final ExecutionEvent arg0) {
search(true, -1);
return null;
}
};
handlerService.activateHandler(SharedUIResources.FIND_NEXT_COMMAND_ID, this.fSearchNextHandler);
handlerService.activateHandler("org.eclipse.ui.navigate.next", this.fSearchNextHandler); //$NON-NLS-1$
this.fSearchPrevHandler = new AbstractHandler() {
@Override
public Object execute(final ExecutionEvent arg0) {
search(false, -1);
return null;
}
};
handlerService.activateHandler(SharedUIResources.FIND_PREVIOUS_COMMAND_ID, this.fSearchPrevHandler);
handlerService.activateHandler("org.eclipse.ui.navigate.previous", this.fSearchPrevHandler); //$NON-NLS-1$
}
protected void enabledSelectionActions(final boolean enable) {
this.fCopyAction.setEnabled(enable);
this.fSubmitAction.setEnabled(enable);
}
private void hookContextMenu() {
final MenuManager menuMgr = new MenuManager("ContextMenu"); //$NON-NLS-1$
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(final IMenuManager manager) {
HistoryView.this.fillContextMenu(manager);
}
});
final Menu menu = menuMgr.createContextMenu(this.fTable);
this.fTable.setMenu(menu);
// getSite().registerContextMenu(menuMgr, fTableViewer);
}
private void contributeToActionBars() {
final IActionBars bars = getViewSite().getActionBars();
bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), this.fSelectAllAction);
bars.setGlobalActionHandler(ActionFactory.COPY.getId(), this.fCopyAction);
fillLocalPullDown(bars.getMenuManager());
fillLocalToolBar(bars.getToolBarManager());
}
private void fillLocalPullDown(final IMenuManager manager) {
manager.add(this.fLoadHistoryAction);
manager.add(this.fSaveHistoryAction);
manager.add(new Separator());
final IMenuManager filterBySourceMenu = new MenuManager("Include Commands &From", SharedUIResources.getImages().getDescriptor(SharedUIResources.LOCTOOL_FILTER_IMAGE_ID), null);
// final IMenuManager filterBySourceMenu = new MenuManager("Hide Commands &From", StatetImages.getDescriptor(StatetImages.LOCTOOL_FILTER), null);
final EnumSet<SubmitType> types = SubmitType.getDefaultSet();
for (final SubmitType submitType : types) {
filterBySourceMenu.add(new FilterBySourceAction(submitType));
}
manager.add(filterBySourceMenu);
manager.add(this.fFilterEmptyAction);
manager.add(new Separator());
manager.add(this.fScrollLockAction);
manager.add(new Separator());
}
private void fillContextMenu(final IMenuManager manager) {
manager.add(this.fCopyAction);
manager.add(this.fSubmitAction);
// Other plug-ins can contribute there actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void fillLocalToolBar(final IToolBarManager manager) {
this.fSearchTextItem = new SearchContributionItem("search.text", //$NON-NLS-1$
SearchContributionItem.VIEW_TOOLBAR ) {
@Override
protected void search() {
HistoryView.this.search(true, -1);
}
};
this.fSearchTextItem.setToolTip(Messages.HistorySearch_Pattern_tooltip);
this.fSearchTextItem.setSizeControl(this.fTable.getParent());
this.fSearchTextItem.setResultControl(this.fTable);
manager.add(this.fSearchTextItem);
final ImageRegistry ecommonsImages = SharedUIResources.getImages();
manager.add(new HandlerContributionItem(new CommandContributionItemParameter(
getSite(), "search.next", SharedUIResources.FIND_NEXT_COMMAND_ID, null, //$NON-NLS-1$
ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_DOWN_IMAGE_ID), null, ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_DOWN_H_IMAGE_ID),
SharedMessages.FindNext_tooltip, null, null, SWT.PUSH, null, false), this.fSearchNextHandler));
manager.add(new HandlerContributionItem(new CommandContributionItemParameter(
getSite(), "search.previous", SharedUIResources.FIND_PREVIOUS_COMMAND_ID, null, //$NON-NLS-1$
ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_UP_IMAGE_ID), null, ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_UP_H_IMAGE_ID),
SharedMessages.FindPrevious_tooltip, null, null, SWT.PUSH, null, false), this.fSearchPrevHandler));
manager.add(new Separator());
manager.add(this.fScrollLockAction);
}
/** May only be called in UI thread */
private void connect(final Tool tool) {
final ToolProcess process= (tool instanceof ToolProcess) ? (ToolProcess)tool : null;
if (this.fProcess == process) {
return;
}
if (this.fProcess != null) {
this.fProcess.getHistory().removeListener(this.fContentProvider);
}
this.fProcess = process;
if (this.fProcess != null) {
this.fProcess.getHistory().addListener(this.fContentProvider);
}
scheduleRefresh(true);
final ActiveToolEvent event= new ActiveToolEvent(ActiveToolEvent.TOOL_ACTIVATED, this.fProcess);
for (final ActiveToolListener listener : this.fToolListeners) {
listener.onToolChanged(event);
}
}
private void scheduleRefresh(final boolean change) {
final IWorkbenchSiteProgressService context = getSite().getAdapter(IWorkbenchSiteProgressService.class);
if (change) {
this.fReloadJob.cancel();
context.schedule(this.fReloadJob, 200);
}
else {
context.schedule(this.fReloadJob, 0);
}
}
@Override
public void addToolListener(final ActiveToolListener action) {
this.fToolListeners.add(action);
}
@Override
public void removeToolListener(final ActiveToolListener action) {
this.fToolListeners.remove(action);
}
public void addFilter(final EntryFilter filter) {
this.fFilter.add(filter);
scheduleRefresh(false);
}
public void removeFilter(final EntryFilter filter) {
this.fFilter.remove(filter);
scheduleRefresh(false);
}
public void replaceFilter(final EntryFilter oldFilter, final EntryFilter newFilter) {
this.fFilter.replace(oldFilter, newFilter);
scheduleRefresh(false);
}
public void search(final String pattern, final boolean forward) {
this.fSearchTextItem.getSearchText().setText(pattern);
search(forward, forward ? 0 : this.fTable.getItemCount()-1);
}
/**
* Returns the tool process, which this view is connected to.
*
* @return a tool process or <code>null</code>, if no process is connected.
*/
@Override
public ToolProcess getTool() {
return this.fProcess;
}
/**
* Returns a shared clipboard resource, which can be used by actions of this view.
*
* @return a clipboard object.
*/
public Clipboard getClipboard() {
if (this.fClipboard == null) {
this.fClipboard = new Clipboard(Display.getCurrent());
}
return this.fClipboard;
}
public Entry[] getSelection() {
final TableItem[] items = this.fTable.getSelection();
final int n = items.length;
final Entry[] selection = new Entry[n];
for (int i = 0; i < n; i++) {
selection[i] = (Entry) items[i].getData();
}
return selection;
}
@Override
public void setFocus() {
// Passing the focus request to the viewer's control.
this.fTable.setFocus();
}
private void search(final boolean forward, final int startIdx) {
if (!UIAccess.isOkToUse(this.fTable)) {
return;
}
final int itemCount = this.fTable.getItemCount();
final String text = this.fSearchTextItem.getText();
if (itemCount == 0 || text.isEmpty()) {
return;
}
int start = 0;
do {
final char c = text.charAt(start);
if (c == ' ' || c == '\t') {
start++;
}
else {
break;
}
} while (start < text.length());
this.fSearchPattern.setPattern(text.substring(start));
int idx;
if (startIdx < 0) {
idx = this.fTable.getSelectionIndex();
}
else {
idx = (forward) ? startIdx-1 : startIdx+1;
}
if (forward) {
idx++;
while (idx < itemCount) {
final Entry e = (Entry) this.fTable.getItem(idx).getData();
final int offset = e.getCommandMarker();
if (this.fSearchPattern.matches(e.getCommand().substring(
offset >= 0 ? offset : -1-offset))) {
this.fTable.setSelection(idx);
return;
}
idx++;
}
}
else {
idx--;
while (idx >= 0) {
final Entry e = (Entry) this.fTable.getItem(idx).getData();
final int offset = e.getCommandMarker();
if (this.fSearchPattern.matches(e.getCommand().substring(
offset >= 0 ? offset : -1-offset))) {
this.fTable.setSelection(idx);
return;
}
idx--;
}
}
Display.getCurrent().beep();
}
@Override
public void dispose() {
if (this.fToolRegistryListener != null) {
NicoUI.getToolRegistry().removeListener(this.fToolRegistryListener);
this.fToolRegistryListener = null;
}
this.fReloadJob.cancel();
final ToolProcess process = this.fProcess;
if (process != null) {
process.getHistory().removeListener(this.fContentProvider);
}
this.fToolListeners.clear();
this.fProcess = null;
this.fCopyAction = null;
this.fSubmitAction = null;
this.fLoadHistoryAction = null;
this.fSaveHistoryAction = null;
if (this.fSearchStartHandler != null) {
this.fSearchStartHandler.dispose();
this.fSearchStartHandler = null;
}
if (this.fSearchPrevHandler != null) {
this.fSearchPrevHandler.dispose();
this.fSearchPrevHandler = null;
}
if (this.fSearchNextHandler != null) {
this.fSearchNextHandler.dispose();
this.fSearchNextHandler = null;
}
super.dispose();
if (this.fClipboard != null) {
this.fClipboard.dispose();
this.fClipboard = null;
}
this.fTable = null;
}
}