| /*=============================================================================# |
| # Copyright (c) 2005, 2018 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.ecommons.collections.FastList; |
| import org.eclipse.statet.ecommons.preferences.core.Preference.EnumSetPref; |
| import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener; |
| import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener.ActiveToolEvent; |
| import org.eclipse.statet.ecommons.ts.core.util.ToolProvider; |
| 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.IToolRegistry; |
| import org.eclipse.statet.nico.ui.IToolRegistryListener; |
| import org.eclipse.statet.nico.ui.NicoUI; |
| import org.eclipse.statet.nico.ui.ToolSessionUIData; |
| 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 IToolRegistryListener 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 IToolRegistry toolRegistry = NicoUI.getToolRegistry(); |
| this.fToolRegistryListener = new IToolRegistryListener() { |
| @Override |
| public void toolSessionActivated(final ToolSessionUIData sessionData) { |
| final ToolProcess process = sessionData.getProcess(); |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| connect(process); |
| } |
| }); |
| } |
| @Override |
| public void toolTerminated(final ToolSessionUIData sessionData) { |
| final ToolProcess process = sessionData.getProcess(); |
| UIAccess.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (HistoryView.this.fProcess != null && HistoryView.this.fProcess == process) { |
| connect(null); |
| } |
| } |
| }); |
| } |
| }; |
| toolRegistry.addListener(this.fToolRegistryListener, getViewSite().getPage()); |
| connect(toolRegistry.getActiveToolSession(getViewSite().getPage()).getProcess()); |
| |
| 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 */ |
| public void connect(final ToolProcess process) { |
| 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; |
| } |
| |
| } |