blob: 1988c601c6c42795e335aa2b9991b29f5cc46413 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2021 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.console;
import static org.eclipse.statet.ecommons.ui.actions.UIActions.ADDITIONS_GROUP_ID;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler2;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.internal.ui.views.console.ConsoleRemoveAllTerminatedAction;
import org.eclipse.debug.internal.ui.views.console.ConsoleRemoveLaunchAction;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuListener2;
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.dialogs.IDialogSettings;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
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.graphics.Font;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.Sash;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.TextConsoleViewer;
import org.eclipse.ui.console.actions.ClearOutputAction;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.part.IPageBookViewPage;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.ui.texteditor.FindReplaceAction;
import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
import org.eclipse.statet.jcommons.ts.core.ActiveToolListener;
import org.eclipse.statet.jcommons.ts.core.ActiveToolListener.ActiveToolEvent;
import org.eclipse.statet.jcommons.ts.core.ToolProvider;
import org.eclipse.statet.ecommons.preferences.PreferencesUtil;
import org.eclipse.statet.ecommons.preferences.SettingsChangeNotifier.ChangeListener;
import org.eclipse.statet.ecommons.text.ui.TextViewerAction;
import org.eclipse.statet.ecommons.text.ui.TextViewerEditorColorUpdater;
import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler;
import org.eclipse.statet.ecommons.ui.SharedMessages;
import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
import org.eclipse.statet.ecommons.ui.actions.SimpleContributionItem;
import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils;
import org.eclipse.statet.ecommons.ui.util.DNDUtils;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.NestedServices;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.ecommons.ui.workbench.ContextHandlers;
import org.eclipse.statet.internal.nico.ui.LocalTaskTransfer;
import org.eclipse.statet.internal.nico.ui.Messages;
import org.eclipse.statet.internal.nico.ui.NicoUIPlugin;
import org.eclipse.statet.internal.nico.ui.console.OutputViewer;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewerConfigurator;
import org.eclipse.statet.nico.core.runtime.Prompt;
import org.eclipse.statet.nico.core.runtime.Queue;
import org.eclipse.statet.nico.core.runtime.ToolController;
import org.eclipse.statet.nico.core.runtime.ToolProcess;
import org.eclipse.statet.nico.core.runtime.ToolStatus;
import org.eclipse.statet.nico.core.runtime.ToolWorkspace;
import org.eclipse.statet.nico.ui.NicoUI;
import org.eclipse.statet.nico.ui.actions.CancelHandler;
import org.eclipse.statet.nico.ui.util.ExportConsoleOutputWizard;
import org.eclipse.statet.nico.ui.util.NicoWizardDialog;
import org.eclipse.statet.nico.ui.util.OpenTrackingFilesContributionItem;
/**
* A page for a <code>NIConsole</code>.
* <p>
* The page contains beside the usual output viewer
* a separete input field with submit button.
*/
public abstract class NIConsolePage implements IPageBookViewPage,
IAdaptable, IShowInSource, IShowInTargetList,
IPropertyChangeListener, ScrollLockAction.Receiver, ToolProvider, ChangeListener {
private static final String DIALOG_ID= "Console"; //$NON-NLS-1$
private static final String SETTING_INPUTHEIGHT= "InputHeight"; //$NON-NLS-1$
public static final String NICO_CONTROL_MENU_ID= "nico.control"; //$NON-NLS-1$
private class FindReplaceUpdater implements IDocumentListener {
private boolean wasEmpty= true;
@Override
public void documentAboutToBeChanged(final DocumentEvent event) {
}
@Override
public void documentChanged(final DocumentEvent event) {
final boolean isEmpty= (event.fDocument.getLength() == 0);
if (isEmpty != this.wasEmpty) {
NIConsolePage.this.multiActionHandler.updateEnabledState();
this.wasEmpty= isEmpty;
}
}
}
private class PostUpdater implements IDocumentListener, Runnable {
private volatile boolean fIsSheduled= false;
@Override
public void documentAboutToBeChanged(final DocumentEvent event) {
}
@Override
public void documentChanged(final DocumentEvent event) {
if (!this.fIsSheduled) {
this.fIsSheduled= true;
final Display display= UIAccess.getDisplay(getSite().getShell());
display.asyncExec(this);
}
}
@Override
public void run() {
// post change run
this.fIsSheduled= false;
NIConsolePage.this.multiActionHandler.updateEnabledState();
}
}
private class SizeController implements Listener {
private final Sash fSash;
private final GridData fOutputGD;
private final GridData fInputGD;
private int fLastExplicit;
public SizeController(final Sash sash, final GridData outputGD, final GridData inputGD) {
this.fSash= sash;
this.fOutputGD= outputGD;
this.fInputGD= inputGD;
this.fLastExplicit= -1;
}
@Override
public void handleEvent(final Event event) {
if (event.widget == this.fSash) {
if (event.type == SWT.Selection && event.detail != SWT.DRAG) {
final Rectangle bounds= NIConsolePage.this.control.getClientArea();
// System.out.println(bounds.height);
// Rectangle bounds2= fInputGroup.getComposite().getBounds();
// System.out.println(bounds2.y+bounds2.height);
setNewInputHeight(bounds.height - event.y - this.fSash.getSize().y, true);
}
return;
}
if (event.widget == NIConsolePage.this.control) {
if (event.type == SWT.Resize) {
setNewInputHeight(this.fInputGD.heightHint, false);
}
}
}
private void setNewInputHeight(int height, final boolean explicit) {
if (!explicit) {
height= this.fLastExplicit;
}
if (height == -1) {
return;
}
final Rectangle bounds= NIConsolePage.this.control.getClientArea();
final int max= bounds.height - this.fOutputGD.minimumHeight - this.fSash.getSize().y;
if (height > max) {
height= max;
}
if (height < this.fInputGD.minimumHeight) {
height= -1;
}
if (explicit) {
this.fLastExplicit= height;
}
if (this.fInputGD.heightHint == height) {
return;
}
this.fInputGD.heightHint= height;
NIConsolePage.this.control.layout(new Control[] { NIConsolePage.this.inputGroup.getComposite() });
}
private void fontChanged() {
this.fOutputGD.minimumHeight= LayoutUtils.hintHeight(NIConsolePage.this.outputViewer.getTextWidget(), 4);
final ScrollBar bar= NIConsolePage.this.outputViewer.getTextWidget().getHorizontalBar();
if (bar.isVisible()) {
this.fOutputGD.minimumHeight+= bar.getSize().y;
}
this.fInputGD.minimumHeight= NIConsolePage.this.inputGroup.getComposite().computeSize(800, -1).y;
if (this.fInputGD.heightHint != -1
&& this.fInputGD.minimumHeight > this.fInputGD.heightHint) {
this.fInputGD.heightHint= -1;
}
}
}
private class StatusListener implements IDebugEventSetListener {
private boolean isProcessing= false;
private boolean isTerminated= false;
private int updateId= Integer.MIN_VALUE;
private Prompt fNewPrompt= null;
private boolean fCurrentBusy= false;
private boolean fNewBusy= false;
public void init() {
final ToolController controller= getConsole().getProcess().getController();
synchronized (StatusListener.this) {
if (controller != null) {
final ToolStatus status= controller.getStatus();
this.isProcessing= (status == ToolStatus.STARTED_PROCESSING || status == ToolStatus.STARTING);
this.isTerminated= (status == ToolStatus.TERMINATED);
this.fCurrentBusy= this.fNewBusy= (this.isProcessing || this.isTerminated);
}
else {
this.isProcessing= false;
this.isTerminated= true;
this.fCurrentBusy= this.fNewBusy= true;
}
NIConsolePage.this.inputGroup.updatePrompt(null);
NIConsolePage.this.inputGroup.updateBusy(this.fCurrentBusy);
}
}
@Override
public void handleDebugEvents(final DebugEvent[] events) {
final ToolProcess process= getConsole().getProcess();
final ToolWorkspace data= process.getWorkspaceData();
Prompt prompt= null;
boolean match= false;
ITER_EVENTS: for (final DebugEvent event : events) {
if (event.getSource() == process) {
if (event.getKind() == DebugEvent.TERMINATE) {
match= true;
this.isTerminated= true;
onToolTerminated();
}
continue ITER_EVENTS;
}
if (event.getSource() == process.getQueue()) {
if (Queue.isStateChange(event)) {
final Queue.StateDelta delta= (Queue.StateDelta) event.getData();
match= true;
this.isProcessing= (delta.newState == Queue.PROCESSING_STATE);
this.isTerminated= (delta.newState == Queue.TERMINATED_STATE);
}
continue ITER_EVENTS;
}
if (event.getSource() == data) {
if (event.getKind() == DebugEvent.CHANGE
&& event.getDetail() == ToolWorkspace.DETAIL_PROMPT) {
match= true;
prompt= (Prompt) event.getData();
}
continue ITER_EVENTS;
}
}
if (!match) {
return;
}
final int thisId;
final long schedule;
synchronized (StatusListener.this) {
this.fNewBusy= this.isProcessing || this.isTerminated;
schedule= (this.fNewBusy) ? (System.nanoTime() + 50000000L) : System.nanoTime();
if (prompt != null) {
this.fNewPrompt= prompt;
}
if (!NIConsolePage.this.isCreated ||
(this.fNewBusy == this.fCurrentBusy && this.fNewPrompt == null)) {
return;
}
thisId= ++this.updateId;
}
UIAccess.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
final long diff= (schedule - System.nanoTime()) / 1000000L;
if (diff > 5) {
Display.getCurrent().timerExec((int) diff, this);
return;
}
if (!NIConsolePage.this.isCreated) {
return;
}
synchronized (StatusListener.this) {
if (thisId != StatusListener.this.updateId) {
return;
}
if (StatusListener.this.fNewPrompt != null) {
NIConsolePage.this.inputGroup.updatePrompt(StatusListener.this.fNewPrompt);
StatusListener.this.fNewPrompt= null;
}
if (StatusListener.this.fNewBusy != StatusListener.this.fCurrentBusy) {
NIConsolePage.this.inputGroup.updateBusy(StatusListener.this.fNewBusy);
StatusListener.this.fCurrentBusy= StatusListener.this.fNewBusy;
}
}
}
});
}
}
private final NIConsole console;
private final IConsoleView consoleView;
private IPageSite site;
private Composite control;
private Clipboard clipboard;
private OutputViewer outputViewer;
private ConsolePageEditor inputGroup;
private SizeController resizer;
private MenuManager outputMenuManager;
private MenuManager inputMenuManager;
private volatile boolean isCreated= false;
// Actions
private MultiActionHandler multiActionHandler;
private final CopyOnWriteIdentityListSet<ActiveToolListener> toolListeners= new CopyOnWriteIdentityListSet<>();
NestedServices inputServices;
private FindReplaceUpdater findReplaceUpdater;
private FindReplaceAction findReplaceAction;
// Output viewer actions
private TextViewerAction outputCopyAction;
private SubmitPasteAction outputPasteAction;
private TextViewerAction outputSelectAllAction;
private ClearOutputAction outputClearAllAction;
private Action outputScrollLockAction;
// Input viewer actions
private TextViewerAction inputDeleteAction;
private TextViewerAction inputCutAction;
private TextViewerAction inputCopyAction;
private TextViewerAction inputPasteAction;
private TextViewerAction inputSelectAllAction;
private TextViewerAction inputUndoAction;
private TextViewerAction inputRedoAction;
// Process control actions
private StatusListener debugListener;
private ConsoleRemoveLaunchAction removeAction;
private ConsoleRemoveAllTerminatedAction removeAllAction;
private TerminateToolAction terminateAction;
private ContextHandlers pageHandlers;
private ContextHandlers inputHandlers;
/**
* Constructs a console page for the given console in the given view.
*
* @param console the console
* @param view the console view the page is contained in
*/
public NIConsolePage(final NIConsole console, final IConsoleView view) {
this.console= console;
this.consoleView= view;
}
@Override
public void init(final IPageSite site) throws PartInitException {
this.site= site;
this.inputGroup= createInputGroup();
this.debugListener= new StatusListener();
DebugPlugin.getDefault().addDebugEventListener(this.debugListener);
}
protected ConsolePageEditor createInputGroup() {
return new ConsolePageEditor(this, null);
}
protected ConsolePageEditor getInputGroup() {
return this.inputGroup;
}
@Override
public void createControl(final Composite parent) {
PreferencesUtil.getSettingsChangeNotifier().addChangeListener(this);
this.console.addPropertyChangeListener(this);
this.control= new Composite(parent, SWT.NONE) {
@Override
public boolean setFocus() {
NIConsolePage.this.setFocus();
return true;
}
@Override
public void setVisible(final boolean visible) {
super.setVisible(visible);
if (visible) {
NIConsolePage.this.setFocus();
}
}
@Override
public void redraw() {
super.redraw();
// required for hyperlinks / org.eclipse.ui.internal.console.ConsoleManager$RepaintJob
if (UIAccess.isOkToUse(NIConsolePage.this.outputViewer)) {
NIConsolePage.this.outputViewer.getControl().redraw();
}
}
};
final GridLayout layout= new GridLayout(1, false);
layout.marginHeight= 0;
layout.verticalSpacing= 0;
layout.marginWidth= 0;
this.control.setLayout(layout);
this.outputViewer= new OutputViewer(this.control, this.console);
final GridData outputGD= new GridData(SWT.FILL, SWT.FILL, true, true);
this.outputViewer.getControl().setLayoutData(outputGD);
new TextViewerEditorColorUpdater(this.outputViewer, EditorsUI.getPreferenceStore());
this.outputViewer.getTextWidget().addKeyListener(new KeyListener() {
@Override
public void keyPressed(final KeyEvent e) {
if (e.doit
&& (e.character > 0)
&& (e.stateMask == SWT.NONE || e.stateMask == SWT.SHIFT)
&& ((e.keyCode & SWT.KEYCODE_BIT) == 0) ) {
final StyledText textWidget= NIConsolePage.this.inputGroup.getViewer().getTextWidget();
if (!UIAccess.isOkToUse(textWidget)) {
return;
}
final int cType= Character.getType(e.character);
if (cType > 0 && cType < Character.CONTROL && textWidget.getCharCount() == 0) {
textWidget.replaceTextRange(0, 0, Character.toString(e.character));
textWidget.setCaretOffset(textWidget.getCharCount());
}
else {
Display.getCurrent().beep();
}
setFocus();
}
}
@Override
public void keyReleased(final KeyEvent e) {
}
});
final Sash sash= new Sash(this.control, SWT.HORIZONTAL);
// sash.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
sash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
this.inputGroup.createControl(this.control, createInputEditorConfigurator());
final GridData inputGD= new GridData(SWT.FILL, SWT.FILL, true, false);
this.inputGroup.getComposite().setLayoutData(inputGD);
this.outputViewer.getTextWidget().getHorizontalBar().setVisible(false);
this.resizer= new SizeController(sash, outputGD, inputGD);
sash.addListener(SWT.Selection, this.resizer);
this.control.addListener(SWT.Resize, this.resizer);
this.clipboard= new Clipboard(this.control.getDisplay());
initActions();
hookContextMenu();
hookDND();
contributeToActionBars(getSite(), getSite().getActionBars(), this.pageHandlers);
new ConsoleActivationNotifier();
this.isCreated= true;
this.debugListener.init();
final IDialogSettings dialogSettings= DialogUtils.getDialogSettings(NicoUIPlugin.getInstance(), DIALOG_ID);
try {
final int height= dialogSettings.getInt(SETTING_INPUTHEIGHT);
if (height > 0) {
this.resizer.fLastExplicit= height;
}
}
catch (final NumberFormatException e) {
// missing value
}
this.resizer.fontChanged();
Display.getCurrent().asyncExec(new Runnable() {
@Override
public void run() {
if (NIConsolePage.this.inputGroup != null && UIAccess.isOkToUse(NIConsolePage.this.inputGroup.getViewer())) {
NIConsolePage.this.outputViewer.revealEndOfDocument();
if (NIConsolePage.this.outputViewer.getControl().isFocusControl()) {
setFocus();
}
}
}
});
}
/**
* Creates the adapter to configure the input source viewer.
* Will be disposed automatically.
*
* @return the adapter
*/
protected abstract SourceEditorViewerConfigurator createInputEditorConfigurator();
private class ConsoleActivationNotifier implements Listener {
private ConsoleActivationNotifier() {
NIConsolePage.this.control.addListener(SWT.Activate, this);
NIConsolePage.this.control.addListener(SWT.Dispose, this);
if (NIConsolePage.this.control.isVisible()) {
activated();
}
}
@Override
public void handleEvent(final Event event) {
switch (event.type) {
case SWT.Activate:
activated();
break;
case SWT.Dispose:
NIConsolePage.this.control.removeListener(SWT.Activate, this);
NIConsolePage.this.control.removeListener(SWT.Dispose, this);
break;
}
}
}
private void activated() {
NicoUIPlugin.getInstance().getToolRegistry().consoleActivated(this.consoleView, this.console);
// E-Bug 473941
final IEclipseContext service= this.site.getService(IEclipseContext.class);
if (service != null) {
service.activate();
}
}
private void initActions() {
final Control outputControl= this.outputViewer.getControl();
final SourceViewer inputViewer= this.inputGroup.getViewer();
final Control inputControl= inputViewer.getControl();
final IServiceLocator pageServiceLocator= getSite();
this.inputServices= new NestedServices(pageServiceLocator, "ConsoleInput");
this.inputServices.bindToFocus(inputControl);
final IHandlerService pageHandlerService= pageServiceLocator
.getService(IHandlerService.class);
this.pageHandlers= new ContextHandlers(pageHandlerService);
final IHandlerService inputHandlerService= this.inputServices.getLocator()
.getService(IHandlerService.class);
this.inputHandlers= new ContextHandlers(inputHandlerService);
this.multiActionHandler= new MultiActionHandler();
this.removeAction= new ConsoleRemoveLaunchAction(this.console.getProcess().getLaunch());
this.removeAllAction= new ConsoleRemoveAllTerminatedAction();
this.terminateAction= new TerminateToolAction(this.console.getProcess());
{ final IHandler2 handler= new CancelHandler(this, ToolController.CANCEL_CURRENT);
this.pageHandlers.add(NicoUI.CANCEL_CURRENT_COMMAND_ID, handler);
pageHandlerService.activateHandler(NicoUI.CANCEL_CURRENT_COMMAND_ID, handler);
}
{ final IHandler2 handler= new CancelHandler(this, ToolController.CANCEL_ALL);
this.pageHandlers.add(NicoUI.CANCEL_ALL_COMMAND_ID, handler);
pageHandlerService.activateHandler(NicoUI.CANCEL_ALL_COMMAND_ID, handler);
}
{ final IHandler2 handler= new CancelHandler(this, ToolController.CANCEL_CURRENT | ToolController.CANCEL_PAUSE);
this.pageHandlers.add(NicoUI.CANCEL_PAUSE_COMMAND_ID, handler);
pageHandlerService.activateHandler(NicoUI.CANCEL_PAUSE_COMMAND_ID, handler);
}
// Conflict with binding CTRL+Z (in console EOF)
// pageKeys.activateContext("org.eclipse.debug.ui.console"); //$NON-NLS-1$
this.outputCopyAction= TextViewerAction.createCopyAction(this.outputViewer);
this.multiActionHandler.addGlobalAction(outputControl, ActionFactory.COPY.getId(), this.outputCopyAction);
this.outputPasteAction= new SubmitPasteAction(this);
this.outputPasteAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_PASTE);
this.multiActionHandler.addGlobalAction(outputControl, ActionFactory.PASTE.getId(), this.outputPasteAction);
this.outputSelectAllAction= TextViewerAction.createSelectAllAction(this.outputViewer);
this.multiActionHandler.addGlobalAction(outputControl, ActionFactory.SELECT_ALL.getId(), this.outputSelectAllAction);
this.outputClearAllAction= new ClearOutputAction(this.console);
this.outputScrollLockAction= new ScrollLockAction(this, false);
this.inputDeleteAction= TextViewerAction.createDeleteAction(inputViewer);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.DELETE.getId(), this.inputDeleteAction);
this.inputCutAction= TextViewerAction.createCutAction(inputViewer);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.CUT.getId(), this.inputCutAction);
this.inputCopyAction= TextViewerAction.createCopyAction(inputViewer);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.COPY.getId(), this.inputCopyAction);
this.inputPasteAction= TextViewerAction.createPasteAction(inputViewer);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.PASTE.getId(), this.inputPasteAction);
this.inputSelectAllAction= TextViewerAction.createSelectAllAction(inputViewer);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.SELECT_ALL.getId(), this.inputSelectAllAction);
this.inputUndoAction= TextViewerAction.createUndoAction(inputViewer);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.UNDO.getId(), this.inputUndoAction);
this.inputRedoAction= TextViewerAction.createRedoAction(inputViewer);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.REDO.getId(), this.inputRedoAction);
final ResourceBundle bundle= SharedMessages.getCompatibilityBundle();
this.findReplaceAction= new FindReplaceAction(bundle, "FindReplaceAction_", this.consoleView); //$NON-NLS-1$
this.findReplaceAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
this.multiActionHandler.addGlobalAction(outputControl, ActionFactory.FIND.getId(), this.findReplaceAction);
this.multiActionHandler.addGlobalAction(inputControl, ActionFactory.FIND.getId(), this.findReplaceAction);
this.findReplaceUpdater= new FindReplaceUpdater();
this.console.getDocument().addDocumentListener(this.findReplaceUpdater);
inputViewer.getDocument().addDocumentListener(new PostUpdater());
inputViewer.addSelectionChangedListener(this.multiActionHandler);
this.outputViewer.addSelectionChangedListener(this.multiActionHandler);
initActions(getSite(), this.pageHandlers);
this.inputGroup.initActions(this.inputServices.getLocator(), this.inputHandlers);
}
protected void initActions(final IServiceLocator serviceLocator, final HandlerCollection handlers) {
}
private void hookContextMenu() {
String id= NIConsole.NICONSOLE_TYPE + "#OutputContextMenu"; //$NON-NLS-1$
this.outputMenuManager= new MenuManager("ContextMenu", id); //$NON-NLS-1$
this.outputMenuManager.setRemoveAllWhenShown(true);
this.outputMenuManager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(final IMenuManager manager) {
fillOutputContextMenu(manager);
}
});
Control control= this.outputViewer.getControl();
Menu menu= this.outputMenuManager.createContextMenu(control);
control.setMenu(menu);
getSite().registerContextMenu(id, this.outputMenuManager, this.outputViewer);
id= NIConsole.NICONSOLE_TYPE + "#InputContextMenu"; //$NON-NLS-1$
this.inputMenuManager= new MenuManager("ContextMenu", id); //$NON-NLS-1$
this.inputMenuManager.setRemoveAllWhenShown(true);
this.inputMenuManager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(final IMenuManager manager) {
fillInputContextMenu(manager);
}
});
control= this.inputGroup.getViewer().getControl();
menu= this.inputMenuManager.createContextMenu(control);
control.setMenu(menu);
getSite().registerContextMenu(id, this.inputMenuManager, this.inputGroup.getViewer());
}
protected void hookDND() {
DNDUtils.addDropSupport(this.outputViewer.getControl(),
new SubmitDropAdapter(this),
new Transfer[] {
TextTransfer.getInstance(),
LocalTaskTransfer.getTransfer()
} );
}
protected void contributeToActionBars(final IServiceLocator serviceLocator,
final IActionBars actionBars, final HandlerCollection handlers) {
this.multiActionHandler.registerActions(actionBars);
final IToolBarManager toolBarManager= actionBars.getToolBarManager();
toolBarManager.appendToGroup(IConsoleConstants.OUTPUT_GROUP, this.outputClearAllAction);
toolBarManager.appendToGroup(IConsoleConstants.OUTPUT_GROUP, this.outputScrollLockAction);
toolBarManager.appendToGroup(IConsoleConstants.LAUNCH_GROUP, new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator, CancelHandler.MENU_ID,
NicoUI.CANCEL_CURRENT_COMMAND_ID, null,
null, null, null,
Messages.CancelAction_name, null, Messages.CancelAction_tooltip,
HandlerContributionItem.STYLE_PULLDOWN, null, false),
this.pageHandlers.get(NicoUI.CANCEL_CURRENT_COMMAND_ID) ) {
// Workaround for E-Bug #366528
@Override
protected void initDropDownMenu(final MenuManager menuManager) {
menuManager.addMenuListener(new IMenuListener2() {
@Override
public void menuAboutToShow(final IMenuManager manager) {
manager.add(new CommandContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, NicoUI.CANCEL_CURRENT_COMMAND_ID,
CommandContributionItem.STYLE_PUSH )));
manager.add(new CommandContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, NicoUI.CANCEL_PAUSE_COMMAND_ID,
CommandContributionItem.STYLE_PUSH )));
manager.add(new CommandContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, NicoUI.CANCEL_ALL_COMMAND_ID,
CommandContributionItem.STYLE_PUSH )));
}
@Override
public void menuAboutToHide(final IMenuManager manager) {
display.asyncExec(new Runnable() {
@Override
public void run() {
menuManager.dispose();
}
});
}
});
}
});
toolBarManager.appendToGroup(IConsoleConstants.LAUNCH_GROUP, this.terminateAction);
toolBarManager.appendToGroup(IConsoleConstants.LAUNCH_GROUP, this.removeAction);
toolBarManager.appendToGroup(IConsoleConstants.LAUNCH_GROUP, this.removeAllAction);
final IMenuManager menuManager= actionBars.getMenuManager();
menuManager.add(new Separator(NICO_CONTROL_MENU_ID));
menuManager.add(new Separator(ADDITIONS_GROUP_ID));
menuManager.add(new Separator("tracking")); //$NON-NLS-1$
final MenuManager trackingMenu= new MenuManager("Open In Editor") {
@Override
public boolean isVisible() {
return !getTool().getTracks().isEmpty();
}
};
trackingMenu.add(new OpenTrackingFilesContributionItem(getTool()));
menuManager.add(trackingMenu);
menuManager.add(new SimpleContributionItem("Export Console Output...", null) {
@Override
protected void execute() throws ExecutionException {
final ToolProcess tool= getTool();
if (tool == null) {
return;
}
final ExportConsoleOutputWizard wizard= new ExportConsoleOutputWizard(NIConsolePage.this);
final WizardDialog dialog= new NicoWizardDialog(getSite().getShell(), wizard);
dialog.setBlockOnOpen(false);
dialog.open();
}
});
menuManager.add(new Separator("settings")); //$NON-NLS-1$
menuManager.add(new SimpleContributionItem("Preferences...", "P") {
@Override
protected void execute() throws ExecutionException {
final Shell shell= getSite().getShell();
final List<String> pageIds= new ArrayList<>();
NIConsolePage.this.collectContextMenuPreferencePages(pageIds);
if (!pageIds.isEmpty() && (shell == null || !shell.isDisposed())) {
org.eclipse.ui.dialogs.PreferencesUtil.createPreferenceDialogOn(shell,
pageIds.get(0), pageIds.toArray(new String[pageIds.size()]), null)
.open();
}
}
});
menuManager.add(new Separator());
}
protected void fillInputContextMenu(final IMenuManager manager) {
manager.add(this.inputCutAction);
manager.add(this.inputCopyAction);
manager.add(this.inputPasteAction);
manager.add(new GroupMarker(IWorkbenchActionConstants.CUT_EXT));
manager.add(new Separator());
manager.add(this.inputUndoAction);
manager.add(this.inputRedoAction);
manager.add(new GroupMarker(IWorkbenchActionConstants.UNDO_EXT));
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
protected void fillOutputContextMenu(final IMenuManager manager) {
manager.add(this.outputCopyAction);
manager.add(this.outputSelectAllAction);
manager.add(new Separator("more")); //$NON-NLS-1$
manager.add(this.findReplaceAction);
// manager.add(new FollowHyperlinkAction(fViewer));
manager.add(new Separator("submit")); //$NON-NLS-1$
manager.add(this.outputPasteAction);
manager.add(new Separator("view")); //$NON-NLS-1$
manager.add(this.outputClearAllAction);
manager.add(this.outputScrollLockAction);
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
@Override
public void dispose() {
this.console.removePropertyChangeListener(this);
PreferencesUtil.getSettingsChangeNotifier().removeChangeListener(this);
if (this.debugListener != null) {
final DebugPlugin debug= DebugPlugin.getDefault();
if (debug != null) {
debug.removeDebugEventListener(this.debugListener);
}
this.debugListener= null;
}
if (this.isCreated) { // control created
this.isCreated= false;
try {
this.console.getDocument().removeDocumentListener(this.findReplaceUpdater);
this.outputViewer.removeSelectionChangedListener(this.multiActionHandler);
this.inputGroup.getViewer().removeSelectionChangedListener(this.multiActionHandler);
}
catch (final Exception e) {
NicoUIPlugin.logError(NicoUIPlugin.INTERNAL_ERROR, Messages.Console_error_UnexpectedException_message, e);
}
if (this.pageHandlers != null) {
this.pageHandlers.dispose();
this.pageHandlers= null;
}
if (this.inputHandlers != null) {
this.inputHandlers.dispose();
this.inputHandlers= null;
}
this.multiActionHandler.dispose();
this.multiActionHandler= null;
this.inputServices.dispose();
this.inputServices= null;
this.findReplaceAction= null;
this.outputCopyAction= null;
this.outputPasteAction= null;
this.outputSelectAllAction= null;
this.outputClearAllAction= null;
this.inputDeleteAction= null;
this.inputCutAction= null;
this.inputCopyAction= null;
this.inputPasteAction= null;
this.inputSelectAllAction= null;
this.inputUndoAction= null;
this.debugListener= null;
this.removeAction.dispose();
this.removeAction= null;
this.removeAllAction.dispose();
this.removeAllAction= null;
this.terminateAction.dispose();
this.terminateAction= null;
this.outputViewer= null;
}
if (this.inputGroup != null) {
this.inputGroup.dispose();
this.inputGroup= null;
}
}
@Override
public IPageSite getSite() {
return this.site;
}
public IConsoleView getView() {
return this.consoleView;
}
@Override
public Control getControl() {
return this.control;
}
public NIConsole getConsole() {
return this.console;
}
public Clipboard getClipboard() {
return this.clipboard;
}
@Override
public ToolProcess getTool() {
return this.console.getProcess();
}
@Override
public void addToolListener(final ActiveToolListener listener) {
this.toolListeners.add(listener);
}
@Override
public void removeToolListener(final ActiveToolListener listener) {
this.toolListeners.remove(listener);
}
public TextConsoleViewer getOutputViewer() {
return this.outputViewer;
}
public IMenuManager getOutputContextMenuManager() {
return this.outputMenuManager;
}
public IMenuManager getInputContextMenuManager() {
return this.inputMenuManager;
}
/**
* Return the text in the input line.
*
* @return
*/
public String getInput() {
return this.inputGroup.getDocument().get();
}
/**
* Clear the input line (e.g. after successful submit).
*/
public void clearInput() {
this.inputGroup.clear();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAdapter(final Class<T> adapterType) {
if (this.inputGroup != null) {
if (adapterType == Widget.class) {
if (this.outputViewer.getControl().isFocusControl()) {
return (T) this.outputViewer.getTextWidget();
}
return (T) this.inputGroup.getViewer().getTextWidget();
}
if (adapterType == IFindReplaceTarget.class) {
if (this.inputGroup.getViewer().getControl().isFocusControl()) {
return (T) this.inputGroup.getViewer().getFindReplaceTarget();
}
return (T) this.outputViewer.getFindReplaceTarget();
}
}
if (adapterType == ISourceEditor.class) {
return (T) this.inputGroup;
}
if (adapterType == IShowInSource.class) {
return (T) this;
}
if (adapterType == IShowInTargetList.class) {
return (T) this;
}
return this.console.getAdapter(adapterType);
}
@Override
public ShowInContext getShowInContext() {
final IProcess process= this.console.getProcess();
if (process == null) {
return null;
}
final IDebugTarget target= process.getAdapter(IDebugTarget.class);
ISelection selection= null;
if (target == null) {
selection= new TreeSelection(new TreePath(new Object[]{
DebugPlugin.getDefault().getLaunchManager(),
process.getLaunch(),
process}));
} else {
selection= new TreeSelection(new TreePath(new Object[]{
DebugPlugin.getDefault().getLaunchManager(),
target.getLaunch(),
target}));
}
return new ShowInContext(null, selection);
}
@Override
public String[] getShowInTargetIds() {
return new String[] { IDebugUIConstants.ID_DEBUG_VIEW };
}
@Override
public void setActionBars(final IActionBars actionBars) {
// fOutputViewer.setActionBars(actionBars);
}
@Override
public void setFocus() {
this.inputGroup.getViewer().getControl().setFocus();
}
protected void onToolTerminated() {
if (this.isCreated) {
this.terminateAction.update();
final ActiveToolEvent event= new ActiveToolEvent(ActiveToolEvent.TOOL_TERMINATED, getTool());
for (final ActiveToolListener listener : this.toolListeners) {
listener.onToolChanged(event);
}
this.outputPasteAction.setEnabled(false);
final Button button= this.inputGroup.getSubmitButton();
UIAccess.getDisplay(getSite().getShell()).asyncExec(new Runnable() {
@Override
public void run() {
if (UIAccess.isOkToUse(button)) {
button.setEnabled(false);
}
}
});
final IDialogSettings dialogSettings= DialogUtils.getDialogSettings(NicoUIPlugin.getInstance(), DIALOG_ID);
dialogSettings.put(SETTING_INPUTHEIGHT, this.resizer.fLastExplicit);
}
}
@Override
public void setAutoScroll(final boolean enabled) {
this.outputViewer.setAutoScroll(enabled);
this.outputScrollLockAction.setChecked(!enabled);
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
if (UIAccess.isOkToUse(this.control) ) {
final Object source= event.getSource();
final String property= event.getProperty();
if (source.equals(this.console) && property.equals(IConsoleConstants.P_FONT)) {
final Font font= this.console.getFont();
this.outputViewer.setFont(font);
this.inputGroup.setFont(font);
this.resizer.fontChanged();
this.control.layout();
}
else if (property.equals(IConsoleConstants.P_FONT_STYLE)) {
this.control.redraw();
this.outputViewer.getTextWidget().redraw();
}
else if (property.equals(IConsoleConstants.P_STREAM_COLOR)) {
this.outputViewer.getTextWidget().redraw();
}
// else if (source.equals(fConsole) && property.equals(IConsoleConstants.P_TAB_SIZE)) {
// int tabSize= ((Integer) event.getNewValue()).intValue();
// fOutputViewer.setTabWidth(tabSize);
// fInputGroup.getSourceViewer().setTabWidth(tabSize);
// }
else if (source.equals(this.console) && property.equals(IConsoleConstants.P_CONSOLE_WIDTH)) {
this.outputViewer.setConsoleWidth(this.console.getConsoleWidth());
}
}
}
@Override
public void settingsChanged(final Set<String> groupIds) {
final Map<String, Object> options= new HashMap<>();
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
handleSettingsChanged(groupIds, options);
}
});
}
/**
* @see ISettingsChangedHandler#handleSettingsChanged(Set, Map)
*/
protected void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) {
if (this.inputGroup != null && UIAccess.isOkToUse(this.control)) {
this.inputGroup.handleSettingsChanged(groupIds, options);
}
}
protected void collectContextMenuPreferencePages(final List<String> pageIds) {
pageIds.add("org.eclipse.statet.nico.preferencePages.Console"); //$NON-NLS-1$
}
}