Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'terminals/plugins/org.eclipse.tcf.te.ui.terminals/src')
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/AbstractAction.java189
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/PinTerminalAction.java49
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/SelectEncodingAction.java80
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/TabScrollLockAction.java59
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/ToggleCommandFieldAction.java85
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/activator/UIPlugin.java265
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/ConfigurationPanelControl.java448
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/NoteCompositeHelper.java173
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/help/IContextHelpIds.java34
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanel.java141
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanelContainer.java35
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ILauncherDelegate.java93
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IMementoHandler.java36
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IPreferenceKeys.java25
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ITerminalsView.java52
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IUIConstants.java21
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ImageConsts.java96
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/tracing/ITraceIds.java26
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PreferencesInitializer.java37
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PropertyTester.java59
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/SettingsStore.java69
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/EncodingSelectionDialog.java231
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/LaunchTerminalSettingsDialog.java569
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/AbstractTriggerCommandHandler.java67
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/DisconnectTerminalCommandHandler.java62
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/LaunchTerminalCommandHandler.java157
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/MaximizeViewHandler.java29
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/QuickAccessHandler.java29
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/AbstractLauncherDelegate.java143
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/LauncherDelegateManager.java421
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/AbstractWindowListener.java130
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchPartListener.java112
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchWindowListener.java27
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/manager/ConsoleManager.java641
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.java115
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.properties75
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractConfigurationPanel.java202
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractExtendedConfigurationPanel.java615
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/services/TerminalService.java359
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/AbstractStreamsConnector.java174
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/InputStreamMonitor.java368
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/OutputStreamMonitor.java319
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsConnector.java117
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsLauncherDelegate.java115
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsSettings.java202
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabCommandFieldHandler.java102
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabDisposeListener.java73
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderManager.java794
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderMenuHandler.java385
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderSelectionListener.java55
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderToolbarHandler.java371
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabTerminalListener.java132
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsView.java705
-rw-r--r--terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsViewMementoHandler.java210
54 files changed, 10178 insertions, 0 deletions
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/AbstractAction.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/AbstractAction.java
new file mode 100644
index 000000000..3ab4f8cef
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/AbstractAction.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2013 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.actions;
+
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.expressions.EvaluationContext;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderManager;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderToolbarHandler;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.handlers.IHandlerService;
+
+/**
+ * Abstract terminal action wrapper implementation.
+ */
+@SuppressWarnings("restriction")
+public abstract class AbstractAction extends AbstractTerminalAction {
+ // Reference to the parent toolbar handler
+ private final TabFolderToolbarHandler parent;
+
+ /**
+ * Constructor.
+ *
+ * @param parent
+ * The parent toolbar handler instance. Must not be
+ * <code>null</code>.
+ * @param id
+ * The terminal action id. Must not be <code>null</code>.
+ */
+ public AbstractAction(TabFolderToolbarHandler parent, String id) {
+ super(id);
+
+ Assert.isNotNull(parent);
+ this.parent = parent;
+ }
+
+ /**
+ * Returns the parent toolbar handler.
+ *
+ * @return The parent toolbar handler.
+ */
+ protected final TabFolderToolbarHandler getParent() {
+ return parent;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getParent().getActiveTerminalViewControl();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#run()
+ */
+ @Override
+ public void run() {
+ // Get the active tab item from the tab folder manager
+ TabFolderManager manager = (TabFolderManager)getParent().getAdapter(TabFolderManager.class);
+ if (manager != null) {
+ // If we have the active tab item, we can get the active terminal control
+ CTabItem activeTabItem = manager.getActiveTabItem();
+ if (activeTabItem != null) {
+ // And execute the command
+ executeCommand(activeTabItem.getData("customData")); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * Executes the command for the given data node as current and active menu selection.
+ * <p>
+ * <b>Node:</b> If the provided data node is <code>null</code>, the method will trigger
+ * the command with an empty selection.
+ *
+ * @param data The terminal custom data node or <code>null</code>.
+ */
+ protected void executeCommand(Object data) {
+ // Get the command service from the workbench
+ ICommandService service = (ICommandService)PlatformUI.getWorkbench().getAdapter(ICommandService.class);
+ if (service != null && getCommandId() != null) {
+ // Get the command
+ final Command command = service.getCommand(getCommandId());
+ if (command != null && command.isDefined()) {
+ IHandlerService handlerSvc = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class);
+ Assert.isNotNull(handlerSvc);
+
+ // Construct a selection element
+ IStructuredSelection selection = data != null ? new StructuredSelection(data) : new StructuredSelection();
+ // Construct the application context
+ EvaluationContext context = new EvaluationContext(handlerSvc.getCurrentState(), selection);
+ // Apply the selection to the "activeMenuSelection" and "selection" variable too
+ context.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection);
+ context.addVariable(ISources.ACTIVE_MENU_SELECTION_NAME, selection);
+ // Allow plugin activation
+ context.setAllowPluginActivation(true);
+ // And execute the event
+ try {
+ ParameterizedCommand pCmd = ParameterizedCommand.generateCommand(command, null);
+ Assert.isNotNull(pCmd);
+
+ handlerSvc.executeCommandInContext(pCmd, null, context);
+ } catch (Exception e) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
+ NLS.bind(Messages.AbstractAction_error_commandExecutionFailed, getCommandId(), e.getLocalizedMessage()),
+ e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the command id of the command to execute.
+ *
+ * @return The command id. Must be never <code>null</code>.
+ */
+ protected abstract String getCommandId();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#updateAction(boolean)
+ */
+ @Override
+ public void updateAction(boolean aboutToShow) {
+ // Ignore the flag given from outside. We have to decide ourself
+ // what the enabled state of the action is
+ boolean enabled = getTarget() != null;
+
+ // If a target terminal control is available, we need to find the corresponding
+ // VLM target object which we need to trigger the handler
+ if (enabled) {
+ // The action will be enabled if we can determine the VLM target object
+ enabled = false;
+ // Get the active tab item from the tab folder manager
+ TabFolderManager manager = (TabFolderManager)getParent().getAdapter(TabFolderManager.class);
+ if (manager != null) {
+ // If we have the active tab item, we can get the active terminal control
+ CTabItem activeTabItem = manager.getActiveTabItem();
+ if (activeTabItem != null) {
+ enabled = checkEnableAction(activeTabItem.getData("customData")); //$NON-NLS-1$
+ }
+ }
+ }
+
+ setEnabled(enabled);
+ }
+
+ /**
+ * Checks if the action should be enabled based on the given terminal data object.
+ *
+ * @param data The terminal data node or <code>null</code>.
+ * @return <code>True</code> to enable the action, <code>false</code> otherwise.
+ */
+ protected boolean checkEnableAction(Object data) {
+ return data != null;
+ }
+
+ /**
+ * Returns if the action is a separator. Returning <code>true</code> here
+ * means that an additional separator toolbar element is added right or
+ * above of the action.
+ *
+ * @return <code>True</code> if the action is separating the parent contribution manager, <code>false</code> otherwise.
+ */
+ public boolean isSeparator() {
+ return false;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/PinTerminalAction.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/PinTerminalAction.java
new file mode 100644
index 000000000..f152023c5
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/PinTerminalAction.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ImageConsts;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction;
+
+/**
+ * Pins the currently visible terminal view.
+ */
+@SuppressWarnings("restriction")
+public class PinTerminalAction extends AbstractTerminalAction {
+
+ private ITerminalsView view = null;
+
+ /**
+ * Constructor.
+ */
+ public PinTerminalAction(ITerminalsView view) {
+ super(null, PinTerminalAction.class.getName(), IAction.AS_CHECK_BOX);
+
+ this.view = view;
+ setupAction(Messages.PinTerminalAction_menu, Messages.PinTerminalAction_toolTip,
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_PinTerminal_Hover),
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_PinTerminal_Enabled),
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_PinTerminal_Disabled), true);
+ setChecked(view.isPinned());
+ setEnabled(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ @Override
+ public void run() {
+ view.setPinned(isChecked());
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/SelectEncodingAction.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/SelectEncodingAction.java
new file mode 100644
index 000000000..d1d567211
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/SelectEncodingAction.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.actions;
+
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.window.Window;
+import org.eclipse.tcf.te.ui.terminals.internal.dialogs.EncodingSelectionDialog;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderManager;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+
+/**
+ * Terminal control select encoding action implementation.
+ */
+@SuppressWarnings("restriction")
+public class SelectEncodingAction extends AbstractTerminalAction {
+ // Reference to the parent tab folder manager
+ private final TabFolderManager tabFolderManager;
+
+ /**
+ * Constructor.
+ *
+ * @param tabFolderManager The parent tab folder manager. Must not be <code>null</code>.
+ */
+ public SelectEncodingAction(TabFolderManager tabFolderManager) {
+ super(null, SelectEncodingAction.class.getName(), IAction.AS_PUSH_BUTTON);
+
+ Assert.isNotNull(tabFolderManager);
+ this.tabFolderManager = tabFolderManager;
+
+ setupAction(Messages.SelectEncodingAction_menu,
+ Messages.SelectEncodingAction_tooltip,
+ (ImageDescriptor)null,
+ (ImageDescriptor)null,
+ (ImageDescriptor)null,
+ true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#run()
+ */
+ @Override
+ public void run() {
+ ITerminalViewControl target = getTarget();
+ if (target == null) return;
+
+ EncodingSelectionDialog dialog = new EncodingSelectionDialog(null);
+ dialog.setEncoding(target.getEncoding());
+ if (dialog.open() == Window.OK) {
+ try {
+ target.setEncoding(dialog.getEncoding());
+ tabFolderManager.updateStatusLine();
+ }
+ catch (UnsupportedEncodingException e) { e.printStackTrace(); }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#updateAction(boolean)
+ */
+ @Override
+ public void updateAction(boolean aboutToShow) {
+ setEnabled(aboutToShow
+ && getTarget() != null && getTarget().getState() == TerminalState.CONNECTED);
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/TabScrollLockAction.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/TabScrollLockAction.java
new file mode 100644
index 000000000..571eb8da3
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/TabScrollLockAction.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ImageConsts;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+
+/**
+ * Terminal console tab scroll lock action.
+ */
+@SuppressWarnings("restriction")
+public class TabScrollLockAction extends AbstractTerminalAction {
+
+ /**
+ * Constructor.
+ */
+ public TabScrollLockAction() {
+ super(null, TabScrollLockAction.class.getName(), IAction.AS_RADIO_BUTTON);
+
+ setupAction(Messages.TabScrollLockAction_text,
+ Messages.TabScrollLockAction_tooltip,
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_ScrollLock_Hover),
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_ScrollLock_Enabled),
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_ScrollLock_Disabled),
+ true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#run()
+ */
+ @Override
+ public void run() {
+ ITerminalViewControl target = getTarget();
+ if (target != null) {
+ target.setScrollLock(!target.isScrollLock());
+ setChecked(target.isScrollLock());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#updateAction(boolean)
+ */
+ @Override
+ public void updateAction(boolean aboutToShow) {
+ setEnabled(aboutToShow && getTarget() != null && getTarget().getState() == TerminalState.CONNECTED);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/ToggleCommandFieldAction.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/ToggleCommandFieldAction.java
new file mode 100644
index 000000000..d2eb36b2c
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/actions/ToggleCommandFieldAction.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ImageConsts;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabCommandFieldHandler;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderManager;
+import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+
+/**
+ * Toggle command input field.
+ */
+@SuppressWarnings("restriction")
+public class ToggleCommandFieldAction extends AbstractTerminalAction {
+ private ITerminalsView view = null;
+
+ /**
+ * Constructor.
+ */
+ public ToggleCommandFieldAction(ITerminalsView view) {
+ super(null, ToggleCommandFieldAction.class.getName(), IAction.AS_CHECK_BOX);
+
+ this.view = view;
+ setupAction(Messages.ToggleCommandFieldAction_menu, Messages.ToggleCommandFieldAction_toolTip,
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_ToggleCommandField_Hover),
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_ToggleCommandField_Enabled),
+ UIPlugin.getImageDescriptor(ImageConsts.ACTION_ToggleCommandField_Disabled), true);
+
+ TabCommandFieldHandler handler = getCommandFieldHandler();
+ setChecked(handler != null && handler.hasCommandInputField());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ @Override
+ public void run() {
+ TabCommandFieldHandler handler = getCommandFieldHandler();
+ if (handler != null) {
+ handler.setCommandInputField(!handler.hasCommandInputField());
+ }
+ setChecked(handler != null && handler.hasCommandInputField());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#updateAction(boolean)
+ */
+ @Override
+ public void updateAction(boolean aboutToShow) {
+ setEnabled(aboutToShow && getCommandFieldHandler() != null
+ && getTarget() != null && getTarget().getState() == TerminalState.CONNECTED);
+ }
+
+ /**
+ * Returns the command input field handler for the active tab.
+ *
+ * @return The command input field handler or <code>null</code>.
+ */
+ protected TabCommandFieldHandler getCommandFieldHandler() {
+ TabCommandFieldHandler handler = null;
+ // Get the active tab item from the tab folder manager
+ TabFolderManager manager = (TabFolderManager)view.getAdapter(TabFolderManager.class);
+ if (manager != null) {
+ // If we have the active tab item, we can get the active terminal control
+ CTabItem activeTabItem = manager.getActiveTabItem();
+ if (activeTabItem != null && !activeTabItem.isDisposed()) {
+ handler = manager.getTabCommandFieldHandler(activeTabItem);
+ }
+ }
+ return handler;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/activator/UIPlugin.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/activator/UIPlugin.java
new file mode 100644
index 000000000..f12b8ee35
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/activator/UIPlugin.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361363] [TERMINALS] Implement "Pin&Clone" for the "Terminals" view
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.activator;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tcf.te.core.terminals.preferences.ScopedEclipsePreferences;
+import org.eclipse.tcf.te.core.terminals.tracing.TraceHandler;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ImageConsts;
+import org.eclipse.tcf.te.ui.terminals.listeners.WorkbenchWindowListener;
+import org.eclipse.tcf.te.ui.terminals.view.TerminalsView;
+import org.eclipse.tcf.te.ui.terminals.view.TerminalsViewMementoHandler;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+@SuppressWarnings("restriction")
+public class UIPlugin extends AbstractUIPlugin {
+ // The shared instance
+ private static UIPlugin plugin;
+ // The scoped preferences instance
+ private static volatile ScopedEclipsePreferences scopedPreferences;
+ // The trace handler instance
+ private static volatile TraceHandler traceHandler;
+ // The workbench listener instance
+ private IWorkbenchListener listener;
+ // The global window listener instance
+ private IWindowListener windowListener;
+
+ /**
+ * The constructor
+ */
+ public UIPlugin() {
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static UIPlugin getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Convenience method which returns the unique identifier of this plug-in.
+ */
+ public static String getUniqueIdentifier() {
+ if (getDefault() != null && getDefault().getBundle() != null) {
+ return getDefault().getBundle().getSymbolicName();
+ }
+ return "org.eclipse.tcf.te.ui.terminals"; //$NON-NLS-1$
+ }
+
+ /**
+ * Return the scoped preferences for this plug-in.
+ */
+ public static ScopedEclipsePreferences getScopedPreferences() {
+ if (scopedPreferences == null) {
+ scopedPreferences = new ScopedEclipsePreferences(getUniqueIdentifier());
+ }
+ return scopedPreferences;
+ }
+
+ /**
+ * Returns the bundles trace handler.
+ *
+ * @return The bundles trace handler.
+ */
+ public static TraceHandler getTraceHandler() {
+ if (traceHandler == null) {
+ traceHandler = new TraceHandler(getUniqueIdentifier());
+ }
+ return traceHandler;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+
+ // Create and register the workbench listener instance
+ listener = new IWorkbenchListener() {
+
+ @Override
+ public boolean preShutdown(IWorkbench workbench, boolean forced) {
+ if (workbench != null && workbench.getActiveWorkbenchWindow() != null && workbench.getActiveWorkbenchWindow().getActivePage() != null) {
+ // Find all "Terminals" views
+ IViewReference[] refs = workbench.getActiveWorkbenchWindow().getActivePage().getViewReferences();
+ for (IViewReference ref : refs) {
+ IViewPart part = ref.getView(false);
+ if (part instanceof TerminalsView) {
+ /*
+ * The terminal tabs to save to the views memento on shutdown can
+ * be determined only _before_ the saveState(memento) method of the
+ * view is called. Within saveState, it is already to late and the
+ * terminals might be in CLOSED state already. This depends on the
+ * terminal type and the corresponding connector implementation.
+ *
+ * To be safe, we determine the still opened terminals on shutdown
+ * separately here in the preShutdown.
+ */
+ final List<CTabItem> saveables = new ArrayList<CTabItem>();
+
+ // Get the tab folder
+ CTabFolder tabFolder = (CTabFolder)((TerminalsView)part).getAdapter(CTabFolder.class);
+ if (tabFolder != null && !tabFolder.isDisposed()) {
+ // Get the list of tab items
+ CTabItem[] items = tabFolder.getItems();
+ // Loop the tab items and find the still connected ones
+ for (CTabItem item : items) {
+ // Ignore disposed items
+ if (item.isDisposed()) continue;
+ // Get the terminal view control
+ ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
+ if (terminal == null || terminal.getState() != TerminalState.CONNECTED) {
+ continue;
+ }
+ // Still connected -> Add to the list
+ saveables.add(item);
+ }
+ }
+
+ // Push the determined saveable items to the memento handler
+ TerminalsViewMementoHandler mementoHandler = (TerminalsViewMementoHandler)((TerminalsView)part).getAdapter(TerminalsViewMementoHandler.class);
+ if (mementoHandler != null) mementoHandler.setSaveables(saveables);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void postShutdown(IWorkbench workbench) {
+ }
+ };
+ PlatformUI.getWorkbench().addWorkbenchListener(listener);
+
+ if (windowListener == null && PlatformUI.getWorkbench() != null) {
+ windowListener = new WorkbenchWindowListener();
+ PlatformUI.getWorkbench().addWindowListener(windowListener);
+ activateContexts();
+ }
+ }
+
+ void activateContexts() {
+ if (Display.getCurrent() != null) {
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window != null && windowListener != null) windowListener.windowOpened(window);
+ }
+ else {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable(){
+ @Override
+ public void run() {
+ activateContexts();
+ }});
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ if (windowListener != null && PlatformUI.getWorkbench() != null) {
+ PlatformUI.getWorkbench().removeWindowListener(windowListener);
+ windowListener = null;
+ }
+
+ plugin = null;
+ scopedPreferences = null;
+ traceHandler = null;
+ if (listener != null) { PlatformUI.getWorkbench().removeWorkbenchListener(listener); listener = null; }
+ super.stop(context);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#initializeImageRegistry(org.eclipse.jface.resource.ImageRegistry)
+ */
+ @Override
+ protected void initializeImageRegistry(ImageRegistry registry) {
+ Bundle bundle = Platform.getBundle("org.eclipse.ui.console"); //$NON-NLS-1$
+ if (bundle != null) {
+ URL url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_EVIEW + "console_view.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.VIEW_Terminals, ImageDescriptor.createFromURL(url));
+
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_CLCL + "lock_co.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_ScrollLock_Hover, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_ELCL + "lock_co.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_ScrollLock_Enabled, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_DLCL + "lock_co.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_ScrollLock_Disabled, ImageDescriptor.createFromURL(url));
+
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_CLCL + "pin.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_PinTerminal_Hover, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_ELCL + "pin.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_PinTerminal_Enabled, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_DLCL + "pin.gif"); //$NON-NLS-1$ //$NON-NLS-2$
+ registry.put(ImageConsts.ACTION_PinTerminal_Disabled, ImageDescriptor.createFromURL(url));
+ }
+
+ bundle = getBundle();
+ URL url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_CLCL + "command_input_field.gif"); //$NON-NLS-1$
+ registry.put(ImageConsts.ACTION_ToggleCommandField_Hover, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_ELCL + "command_input_field.gif"); //$NON-NLS-1$
+ registry.put(ImageConsts.ACTION_ToggleCommandField_Enabled, ImageDescriptor.createFromURL(url));
+ url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_DLCL + "command_input_field.gif"); //$NON-NLS-1$
+ registry.put(ImageConsts.ACTION_ToggleCommandField_Disabled, ImageDescriptor.createFromURL(url));
+ }
+
+ /**
+ * Loads the image registered under the specified key from the image
+ * registry and returns the <code>Image</code> object instance.
+ *
+ * @param key The key the image is registered with.
+ * @return The <code>Image</code> object instance or <code>null</code>.
+ */
+ public static Image getImage(String key) {
+ return getDefault().getImageRegistry().get(key);
+ }
+
+ /**
+ * Loads the image registered under the specified key from the image
+ * registry and returns the <code>ImageDescriptor</code> object instance.
+ *
+ * @param key The key the image is registered with.
+ * @return The <code>ImageDescriptor</code> object instance or <code>null</code>.
+ */
+ public static ImageDescriptor getImageDescriptor(String key) {
+ return getDefault().getImageRegistry().getDescriptor(key);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/ConfigurationPanelControl.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/ConfigurationPanelControl.java
new file mode 100644
index 000000000..118c4f12f
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/ConfigurationPanelControl.java
@@ -0,0 +1,448 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.controls;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer;
+import org.eclipse.tcf.te.ui.terminals.panels.AbstractConfigurationPanel;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * Base control to deal with wizard or property page controls
+ * which should share the same UI space.
+ */
+public class ConfigurationPanelControl implements IConfigurationPanelContainer, IMessageProvider {
+ private final Map<String, IConfigurationPanel> configurationPanels = new Hashtable<String, IConfigurationPanel>();
+
+ private String message = null;
+ private int messageType = IMessageProvider.NONE;
+
+ private boolean isGroup;
+
+ private FormToolkit toolkit = null;
+
+ private Composite panel;
+ private StackLayout panelLayout;
+
+ private String activeConfigurationPanelKey = null;
+ private IConfigurationPanel activeConfigurationPanel = null;
+
+ private final AbstractConfigurationPanel EMPTY_PANEL;
+
+ /**
+ * An empty configuration panel implementation.
+ */
+ private static final class EmptySettingsPanel extends AbstractConfigurationPanel {
+
+ /**
+ * Constructor.
+ *
+ * @param container The configuration panel container or <code>null</code>.
+ */
+ public EmptySettingsPanel(IConfigurationPanelContainer container) {
+ super(container);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#setupPanel(org.eclipse.swt.widgets.Composite, org.eclipse.ui.forms.widgets.FormToolkit)
+ */
+ @Override
+ public void setupPanel(Composite parent, FormToolkit toolkit) {
+ Composite panel = new Composite(parent, SWT.NONE);
+ panel.setLayout(new GridLayout());
+ panel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ panel.setBackground(parent.getBackground());
+
+ setControl(panel);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.panels.AbstractConfigurationPanel#isValid()
+ */
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+ }
+
+ /**
+ * Cleanup all resources the control might have been created.
+ */
+ public void dispose() {
+ EMPTY_PANEL.dispose();
+ }
+
+ /**
+ * Constructor.
+ */
+ public ConfigurationPanelControl() {
+ EMPTY_PANEL = new EmptySettingsPanel(this);
+ clear();
+ setPanelIsGroup(false);
+ }
+
+ /**
+ * Sets if or if not the controls panel is a <code>Group</code>.
+ *
+ * @param isGroup <code>True</code> if the controls panel is a group, <code>false</code> otherwise.
+ */
+ public void setPanelIsGroup(boolean isGroup) {
+ this.isGroup = isGroup;
+ }
+
+ /**
+ * Returns if or if not the controls panel is a <code>Group</code>.
+ *
+ * @return <code>True</code> if the controls panel is a group, <code>false</code> otherwise.
+ */
+ public boolean isPanelIsGroup() {
+ return isGroup;
+ }
+
+ /**
+ * Returns the controls panel.
+ *
+ * @return The controls panel or <code>null</code>.
+ */
+ public Composite getPanel() {
+ return panel;
+ }
+
+ /**
+ * Returns the label text to set for the group (if the panel is a group).
+ *
+ * @return The label text to apply or <code>null</code>.
+ */
+ public String getGroupLabel() {
+ return null;
+ }
+
+ /**
+ * Sets the form toolkit to be used for creating the control widgets.
+ *
+ * @param toolkit The form toolkit instance or <code>null</code>.
+ */
+ public final void setFormToolkit(FormToolkit toolkit) {
+ this.toolkit = toolkit;
+ }
+
+ /**
+ * Returns the form toolkit used for creating the control widgets.
+ *
+ * @return The form toolkit instance or <code>null</code>.
+ */
+ public final FormToolkit getFormToolkit() {
+ return toolkit;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer#validate()
+ */
+ @Override
+ public void validate() {
+ }
+
+ /**
+ * To be called from the embedding control to setup the controls UI elements.
+ *
+ * @param parent The parent control. Must not be <code>null</code>!
+ * @param toolkit The form toolkit. Must not be <code>null</code>.
+ */
+ public void setupPanel(Composite parent, String[] configurationPanelKeys, FormToolkit toolkit) {
+ Assert.isNotNull(parent);
+ Assert.isNotNull(toolkit);
+
+ setFormToolkit(toolkit);
+
+ if (isPanelIsGroup()) {
+ panel = new Group(parent, SWT.NONE);
+ if (getGroupLabel() != null) ((Group)panel).setText(getGroupLabel());
+ } else {
+ panel = new Composite(parent, SWT.NONE);
+ }
+ Assert.isNotNull(panel);
+ panel.setFont(parent.getFont());
+ panel.setBackground(parent.getBackground());
+
+ panelLayout = new StackLayout();
+ panel.setLayout(panelLayout);
+
+ setupConfigurationPanels(panel, configurationPanelKeys, toolkit);
+ EMPTY_PANEL.setupPanel(panel, toolkit);
+ }
+
+ /**
+ * Removes all configuration panels.
+ */
+ public void clear() {
+ configurationPanels.clear();
+ }
+
+ /**
+ * Returns a unsorted list of all registered configuration panel id's.
+ *
+ * @return A list of registered configuration panel id's.
+ */
+ public String[] getConfigurationPanelIds() {
+ return configurationPanels.keySet().toArray(new String[configurationPanels.keySet().size()]);
+ }
+
+ /**
+ * Returns the configuration panel instance registered for the given configuration panel key.
+ *
+ * @param key The key to get the configuration panel for. Must not be <code>null</code>!
+ * @return The configuration panel instance or an empty configuration panel if the key is unknown.
+ */
+ public IConfigurationPanel getConfigurationPanel(String key) {
+ IConfigurationPanel panel = key != null ? configurationPanels.get(key) : null;
+ return panel != null ? panel : EMPTY_PANEL;
+ }
+
+ /**
+ * Returns if or if not the given configuration panel is equal to the
+ * empty configuration panel.
+ *
+ * @param panel The configuration panel or <code>null</code>.
+ * @return <code>True</code> if the configuration panel is equal to the empty configuration panel.
+ */
+ public final boolean isEmptyConfigurationPanel(IConfigurationPanel panel) {
+ return EMPTY_PANEL == panel;
+ }
+
+ /**
+ * Adds the given configuration panel under the given configuration panel key to the
+ * list of known panels. If the given configuration panel is <code>null</code>, any
+ * configuration panel stored under the given key is removed from the list of known panels.
+ *
+ * @param key The key to get the configuration panel for. Must not be <code>null</code>!
+ * @param panel The configuration panel instance or <code>null</code>.
+ */
+ public void addConfigurationPanel(String key, IConfigurationPanel panel) {
+ if (key == null) return;
+ if (panel != null) {
+ configurationPanels.put(key, panel);
+ } else {
+ configurationPanels.remove(key);
+ }
+ }
+
+ /**
+ * Setup the configuration panels for being presented to the user. This method is called by the
+ * controls <code>doSetupPanel(...)</code> and initialize all possible configuration panels to show.
+ * The default implementation iterates over the given list of configuration panel keys and calls
+ * <code>setupPanel(...)</code> for each of them.
+ *
+ * @param parent The parent composite to use for the configuration panels. Must not be <code>null</code>!
+ * @param configurationPanelKeys The list of configuration panels to initialize. Might be <code>null</code> or empty!
+ * @param toolkit The form toolkit. Must not be <code>null</code>.
+ */
+ public void setupConfigurationPanels(Composite parent, String[] configurationPanelKeys, FormToolkit toolkit) {
+ Assert.isNotNull(parent);
+ Assert.isNotNull(toolkit);
+
+ if (configurationPanelKeys != null) {
+ for (int i = 0; i < configurationPanelKeys.length; i++) {
+ IConfigurationPanel configPanel = getConfigurationPanel(configurationPanelKeys[i]);
+ Assert.isNotNull(configPanel);
+ configPanel.setupPanel(parent, toolkit);
+ }
+ }
+ }
+
+ /**
+ * Make the wizard configuration panel registered under the given configuration panel key the
+ * most top configuration panel. If no configuration panel is registered under the given key,
+ * nothing will happen.
+ *
+ * @param key The key to get the wizard configuration panel for. Must not be <code>null</code>!
+ */
+ public void showConfigurationPanel(String key) {
+ String activeKey = getActiveConfigurationPanelKey();
+ if (key != null && key.equals(activeKey) && activeConfigurationPanel != null) {
+ return;
+ }
+ IConfigurationPanel configPanel = getActiveConfigurationPanel();
+ Map<String, Object> data = new HashMap<String, Object>();
+ if (configPanel != null) configPanel.extractData(data);
+ configPanel = getConfigurationPanel(key);
+ Assert.isNotNull(configPanel);
+ if (configPanel.getControl() != null) {
+ activeConfigurationPanel = configPanel;
+ activeConfigurationPanelKey = key;
+ panelLayout.topControl = configPanel.getControl();
+ panel.layout();
+ if (!data.isEmpty()) configPanel.updateData(data);
+ configPanel.activate();
+ }
+ else {
+ activeConfigurationPanelKey = key;
+ }
+ }
+
+ /**
+ * Returns the currently active configuration panel.
+ *
+ * @return The active configuration panel or <code>null</code>.
+ */
+ public IConfigurationPanel getActiveConfigurationPanel() {
+ return activeConfigurationPanel;
+ }
+
+ /**
+ * Returns the currently active configuration panel key.
+ *
+ * @return The active configuration panel key or <code>null</code>.
+ */
+ public String getActiveConfigurationPanelKey() {
+ return activeConfigurationPanelKey;
+ }
+
+ /**
+ * Returns the dialog settings to use to save and restore control specific
+ * widget values.
+ *
+ * @param settings The parent dialog settings. Must not be <code>null</code>.
+ * @return The dialog settings to use.
+ */
+ public final IDialogSettings getDialogSettings(IDialogSettings settings) {
+ Assert.isNotNull(settings);
+
+ // Store the settings of the control within it's own section.
+ String sectionName = this.getClass().getSimpleName();
+ Assert.isNotNull(sectionName);
+
+ IDialogSettings section = settings.getSection(sectionName);
+ if (section == null) {
+ section = settings.addNewSection(sectionName);
+ }
+
+ return section;
+ }
+
+ /**
+ * Restore the widget values from the dialog settings store to recreate the control history.
+ * <p>
+ * <b>Note:</b>
+ * The control is saving the widget values into a section equal to the class name {@link Class#getName()}.
+ * After the sections has been created, the method calls <code>doRestoreWidgetValues</code> for restoring
+ * the single properties from the dialog settings. Subclasses may override <code>doRestoreWidgetValues</code>
+ * only to deal with the single properties only or <code>restoreWidgetValues</code> when to override the
+ * creation of the subsections.
+ *
+ * @param settings The dialog settings object instance to restore the widget values from. Must not be <code>null</code>!
+ * @param idPrefix The prefix to use for every dialog settings slot keys. If <code>null</code>, the dialog settings slot keys are not to prefix.
+ */
+ public final void restoreWidgetValues(IDialogSettings settings, String idPrefix) {
+ Assert.isNotNull(settings);
+
+ // now, call the hook for actually reading the single properties from the dialog settings.
+ doRestoreWidgetValues(getDialogSettings(settings), idPrefix);
+ }
+
+ /**
+ * Hook to restore the widget values finally plain from the given dialog settings. This method should
+ * not fragment the given dialog settings any further.
+ *
+ * @param settings The dialog settings to restore the widget values from. Must not be <code>null</code>!
+ * @param idPrefix The prefix to use for every dialog settings slot keys. If <code>null</code>, the dialog settings slot keys are not to prefix.
+ */
+ public void doRestoreWidgetValues(IDialogSettings settings, String idPrefix) {
+ Assert.isNotNull(settings);
+
+ for (String panelKey : configurationPanels.keySet()) {
+ IConfigurationPanel configPanel = getConfigurationPanel(panelKey);
+ if (configPanel != null && !isEmptyConfigurationPanel(configPanel)) {
+ IDialogSettings configPanelSettings = settings.getSection(panelKey);
+ if (configPanelSettings == null) configPanelSettings = settings.addNewSection(panelKey);
+ configPanel.doRestoreWidgetValues(configPanelSettings, idPrefix);
+ }
+ }
+ }
+
+ /**
+ * Saves the widget values to the dialog settings store for remembering the history. The control might
+ * be embedded within multiple pages multiple times handling different properties. Because the single
+ * controls should not mix up the history, we create subsections within the given dialog settings if
+ * they do not already exist. After the sections has been created, the method calls <code>doSaveWidgetValues</code>
+ * for saving the single properties to the dialog settings. Subclasses may override <code>doSaveWidgetValues</code>
+ * only to deal with the single properties only or <code>saveWidgetValues</code> when to override the
+ * creation of the subsections.
+ *
+ * @param settings The dialog settings object instance to save the widget values to. Must not be <code>null</code>!
+ * @param idPrefix The prefix to use for every dialog settings slot keys. If <code>null</code>, the dialog settings slot keys are not to prefix.
+ */
+ public final void saveWidgetValues(IDialogSettings settings, String idPrefix) {
+ Assert.isNotNull(settings);
+
+ // now, call the hook for actually writing the single properties to the dialog settings.
+ doSaveWidgetValues(getDialogSettings(settings), idPrefix);
+ }
+
+ /**
+ * Hook to save the widget values finally plain to the given dialog settings. This method should
+ * not fragment the given dialog settings any further.
+ *
+ * @param settings The dialog settings to save the widget values to. Must not be <code>null</code>!
+ * @param idPrefix The prefix to use for every dialog settings slot keys. If <code>null</code>, the dialog settings slot keys are not to prefix.
+ */
+ public void doSaveWidgetValues(IDialogSettings settings, String idPrefix) {
+ Assert.isNotNull(settings);
+
+ IConfigurationPanel configPanel = getActiveConfigurationPanel();
+ if (configPanel != null && !isEmptyConfigurationPanel(configPanel)) {
+ String key = getActiveConfigurationPanelKey();
+ IDialogSettings configPanelSettings = settings.getSection(key);
+ if (configPanelSettings == null) configPanelSettings = settings.addNewSection(key);
+ configPanel.doSaveWidgetValues(configPanelSettings, idPrefix);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage()
+ */
+ @Override
+ public final String getMessage() {
+ return message;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType()
+ */
+ @Override
+ public final int getMessageType() {
+ return messageType;
+ }
+
+ /**
+ * Set the message and the message type to display.
+ *
+ * @param message The message or <code>null</code>.
+ * @param messageType The message type or <code>IMessageProvider.NONE</code>.
+ */
+ @Override
+ public final void setMessage(String message, int messageType) {
+ this.message = message;
+ this.messageType = messageType;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/NoteCompositeHelper.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/NoteCompositeHelper.java
new file mode 100644
index 000000000..eb998da27
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/controls/NoteCompositeHelper.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.controls;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+
+/**
+ * A helper class to create a composite with a highlighted note
+ * entry and a message text.
+ */
+public class NoteCompositeHelper {
+
+ /**
+ * The common label text to show on a note. Defaults to &quot;Note:&quot;.
+ */
+ public static final String NOTE_LABEL = Messages.NoteCompositeHelper_note_label;
+
+ private static class NoteComposite extends Composite {
+
+ public NoteComposite(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ Control[] childs = getChildren();
+ for (int iChild = 0; iChild < childs.length; iChild++) {
+ Control child = childs[iChild];
+ child.setEnabled(enabled);
+ }
+ }
+ }
+
+ /**
+ * Creates a composite with a highlighted Note entry and a message text.
+ * This is designed to take up the full width of the page.
+ *
+ * @see PreferencePage#createNoteComposite, this is a plain copy of that!
+ * @param font
+ * the font to use
+ * @param composite
+ * the parent composite
+ * @param title
+ * the title of the note
+ * @param message
+ * the message for the note
+ *
+ * @return the composite for the note
+ */
+ public static Composite createNoteComposite(Font font, Composite composite, String title, String message) {
+ return createNoteComposite(font, composite, title, message, SWT.DEFAULT);
+ }
+
+ /**
+ * Creates a composite with a highlighted Note entry and a message text.
+ * This is designed to take up the full width of the page.
+ *
+ * @see PreferencePage#createNoteComposite, this is a plain copy of that!
+ * @param font
+ * the font to use
+ * @param composite
+ * the parent composite
+ * @param title
+ * the title of the note
+ * @param message
+ * the message for the note
+ * @param minCharsPerLine
+ * the minimum number of characters per line. Defaults to '65' if less than '20'.
+ *
+ * @return the composite for the note
+ */
+ public static Composite createNoteComposite(Font font, Composite composite, String title, String message, int minCharsPerLine) {
+ final GC gc = new GC(composite);
+ gc.setFont(font);
+
+ Composite messageComposite = new NoteComposite(composite, SWT.NONE);
+
+ GridLayout messageLayout = new GridLayout();
+ messageLayout.numColumns = 2;
+ messageLayout.marginWidth = 0;
+ messageLayout.marginHeight = 0;
+ messageComposite.setLayout(messageLayout);
+
+ GridData layoutData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ if (composite.getLayout() instanceof GridLayout) {
+ layoutData.horizontalSpan = ((GridLayout) composite.getLayout()).numColumns;
+ }
+ messageComposite.setLayoutData(layoutData);
+ messageComposite.setFont(font);
+
+ final Label noteLabel = new Label(messageComposite, SWT.BOLD);
+ noteLabel.setText(title);
+ noteLabel.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
+ noteLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
+
+ final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ // Note: This is actually wrong but the same as in platforms
+ // PreferencePage
+ if (JFaceResources.BANNER_FONT.equals(event.getProperty())) {
+ noteLabel.setFont(JFaceResources.getFont(JFaceResources.BANNER_FONT));
+ }
+ }
+ };
+ JFaceResources.getFontRegistry().addListener(fontListener);
+ noteLabel.addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent event) {
+ JFaceResources.getFontRegistry().removeListener(fontListener);
+ }
+ });
+
+ Label messageLabel = new Label(messageComposite, SWT.WRAP);
+ messageLabel.setText(message);
+ messageLabel.setFont(font);
+
+ /**
+ * Set the controls style to FILL_HORIZONTAL making it multi-line if
+ * needed
+ */
+ layoutData = new GridData(GridData.FILL_HORIZONTAL);
+ layoutData.widthHint = Dialog.convertWidthInCharsToPixels(gc.getFontMetrics(), minCharsPerLine >= 20 ? minCharsPerLine : 65);
+ messageLabel.setLayoutData(layoutData);
+
+ gc.dispose();
+
+ return messageComposite;
+ }
+
+ /**
+ * change the text of the second label
+ *
+ * @param messageComposite
+ * the NoteComposite that gets returned from createNoteComposite
+ * @param msg
+ * the new text
+ */
+ public static void setMessage(Composite messageComposite, String msg) {
+ if (messageComposite instanceof NoteComposite) {
+ Control[] children = messageComposite.getChildren();
+ if (children.length == 2) {
+ Control c = children[1];
+ if (c instanceof Label) {
+ ((Label) c).setText(msg);
+ messageComposite.pack();
+ }
+ }
+ }
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/help/IContextHelpIds.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/help/IContextHelpIds.java
new file mode 100644
index 000000000..650f83065
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/help/IContextHelpIds.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.help;
+
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+
+
+/**
+ * UI Context help id definitions.
+ */
+public interface IContextHelpIds {
+
+ /**
+ * UI plug-in common context help id prefix.
+ */
+ public final static String PREFIX = UIPlugin.getUniqueIdentifier() + "."; //$NON-NLS-1$
+
+ /**
+ * Launch terminal settings dialog.
+ */
+ public final static String LAUNCH_TERMINAL_SETTINGS_DIALOG = PREFIX + "LaunchTerminalSettingsDialog"; //$NON-NLS-1$
+
+ /**
+ * Terminal control encoding selection dialog.
+ */
+ public final static String ENCODING_SELECTION_DIALOG = PREFIX + "EncodingSelectionDialog"; //$NON-NLS-1$
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanel.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanel.java
new file mode 100644
index 000000000..60eb8d0d3
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanel.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+import java.util.Map;
+
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * Terminal launcher configuration panel.
+ */
+public interface IConfigurationPanel extends IMessageProvider {
+
+ /**
+ * Returns the configuration panel container.
+ *
+ * @return The configuration panel container or <code>null</code>.
+ */
+ public IConfigurationPanelContainer getContainer();
+
+ /**
+ * Creates the terminal launcher configuration panel UI elements within the
+ * given parent composite. Terminal launcher configuration panels should always
+ * create another composite within the given composite, which is the panel top
+ * control. The top control is queried later from the stack layout to show the
+ * different panels if the selected terminal launcher changed.
+ *
+ * @param parent The parent composite to create the UI elements in. Must not be <code>null</code>.
+ * @param toolkit The form toolkit. Must not be <code>null</code>.
+ */
+ public void setupPanel(Composite parent, FormToolkit toolkit);
+
+ /**
+ * Cleanup all resources the wizard configuration panel might have been created.
+ */
+ public void dispose();
+
+ /**
+ * Returns the terminal launcher configuration panels top control, typically a
+ * composite control. This control is requested every time the stack layout is
+ * required to set a new top control because the selected terminal launcher changed.
+ *
+ * @return The top control or <code>null</code> if the configuration panel has been not setup yet.
+ */
+ public Composite getControl();
+
+ /**
+ * Validates the control and sets the message text and type so the parent
+ * page or control is able to display validation result informations.
+ * The default implementation of this method does nothing.
+ *
+ * @return Result of validation.
+ */
+ public boolean isValid();
+
+ /**
+ * Restore the widget values plain from the given dialog settings. This method should
+ * not fragment the given dialog settings any further.
+ *
+ * @param settings The dialog settings to restore the widget values from. Must not be <code>null</code>!
+ * @param idPrefix The prefix to use for every dialog settings slot keys. If <code>null</code>, the dialog settings slot keys are not to prefix.
+ */
+ public void doRestoreWidgetValues(IDialogSettings settings, String idPrefix);
+
+ /**
+ * Save the widget values plain to the given dialog settings. This method should
+ * not fragment the given dialog settings any further.
+ *
+ * @param settings The dialog settings to save the widget values to. Must not be <code>null</code>!
+ * @param idPrefix The prefix to use for every dialog settings slot keys. If <code>null</code>, the dialog settings slot keys are not to prefix.
+ */
+ public void doSaveWidgetValues(IDialogSettings settings, String idPrefix);
+
+ /**
+ * Enables or disables all UI elements belonging to the wizard configuration panel.
+ *
+ * @param enabled <code>True</code> to enable the UI elements, <code>false</code> otherwise.
+ */
+ public void setEnabled(boolean enabled);
+
+ /**
+ * Called when the panel gets the active panel.
+ */
+ public void activate();
+
+ /**
+ * Initialize the widgets based of the data from the given map.
+ * <p>
+ * This method may called multiple times during the lifetime of the panel and the given
+ * map might be even <code>null</code>.
+ *
+ * @param data The map or <code>null</code>.
+ */
+ public void setupData(Map<String, Object> data);
+
+ /**
+ * Extract the data from the widgets and write it back to the given map.
+ * <p>
+ * This method may called multiple times during the lifetime of the panel and the given
+ * map might be even <code>null</code>.
+ *
+ * @param data The map or <code>null</code>.
+ */
+ public void extractData(Map<String, Object> data);
+
+ /**
+ * Update the data from the given properties container which contains the current
+ * working data.
+ * <p>
+ * This method may called multiple times during the lifetime of the panel and the given
+ * map might be even <code>null</code>.
+ *
+ * @param data The map or <code>null</code>.
+ */
+ public void updateData(Map<String, Object> data);
+
+ /**
+ * Set the selection to the terminal launcher configuration panel.
+ *
+ * @param selection The selection or <code>null</code>.
+ */
+ public void setSelection(ISelection selection);
+
+ /**
+ * Returns the selection associated with the terminal launcher configuration panel.
+ *
+ * @return The selection or <code>null</code>.
+ */
+ public ISelection getSelection();
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanelContainer.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanelContainer.java
new file mode 100644
index 000000000..03d3f41ee
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IConfigurationPanelContainer.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+
+
+/**
+ * A container to deal with configuration panels.
+ */
+public interface IConfigurationPanelContainer {
+
+ /**
+ * Validates the container status.
+ * <p>
+ * If necessary, set the corresponding messages and message types to signal when some sub
+ * elements of the container needs user attention.
+ */
+ public void validate();
+
+ /**
+ * Set the message and the message type to display.
+ *
+ * @param message The message or <code>null</code>.
+ * @param messageType The message type or <code>IMessageProvider.NONE</code>.
+ */
+ public void setMessage(String message, int messageType);
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ILauncherDelegate.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ILauncherDelegate.java
new file mode 100644
index 000000000..bf3f717ef
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ILauncherDelegate.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+import java.util.Map;
+
+import org.eclipse.core.expressions.Expression;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
+
+/**
+ * Terminal launcher delegate.
+ */
+@SuppressWarnings("restriction")
+public interface ILauncherDelegate extends IExecutableExtension, IAdaptable {
+
+ /**
+ * Returns the unique id of the launcher delegate. The returned
+ * id must be never <code>null</code> or an empty string.
+ *
+ * @return The unique id.
+ */
+ public String getId();
+
+ /**
+ * Returns the label or UI name of the launcher delegate.
+ *
+ * @return The label or UI name. An empty string if not set.
+ */
+ public String getLabel();
+
+ /**
+ * Returns if or if not the launcher delegate is hidden for the user.
+ *
+ * @return <code>True</code> if the launcher delegate is hidden, <code>false</code> otherwise.
+ */
+ public boolean isHidden();
+
+ /**
+ * Returns the enablement expression.
+ *
+ * @return The enablement expression or <code>null</code>.
+ */
+ public Expression getEnablement();
+
+ /**
+ * Returns if or if not the user needs to set configuration details for this launcher to work.
+ * The settings to configure are provided to the user through the configuration panel returned
+ * by {@link #getPanel(BaseDialogPageControl)}.
+ *
+ * @return <code>True</code> if a user configuration is required, <code>false</code> otherwise.
+ */
+ public boolean needsUserConfiguration();
+
+ /**
+ * Returns the configuration panel instance to present to the user. The instance must be always
+ * the same on subsequent calls until disposed.
+ * <p>
+ * The method may return <code>null</code> if the launcher does not provide any user
+ * configurable settings. In this case, {@link #needsUserConfiguration()} should return
+ * <code>false</code>.
+ *
+ * @param container The configuration panel container or <code>null</code>.
+ * @return The configuration panel instance or <code>null</code>
+ */
+ public IConfigurationPanel getPanel(IConfigurationPanelContainer container);
+
+ /**
+ * Execute the terminal launch.
+ *
+ * @param properties The properties. Must not be <code>null</code>.
+ * @param done The callback or <code>null</code>.
+ */
+ public void execute(Map<String, Object> properties, ITerminalService.Done done);
+
+ /**
+ * Creates the terminal connector for this launcher delegate based on
+ * the given properties.
+ *
+ * @param properties The terminal properties. Must not be <code>null</code>.
+ * @return The terminal connector or <code>null</code>.
+ */
+ public ITerminalConnector createTerminalConnector(Map<String, Object> properties);
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IMementoHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IMementoHandler.java
new file mode 100644
index 000000000..d6ae8e6d9
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IMementoHandler.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+import java.util.Map;
+
+import org.eclipse.ui.IMemento;
+
+/**
+ * Terminal properties memento handler.
+ */
+public interface IMementoHandler {
+
+ /**
+ * Saves the terminal properties in the given memento.
+ *
+ * @param memento The memento. Must not be <code>null</code>.
+ * @param properties The map containing the terminal properties to save. Must not be <code>null</code>.
+ */
+ public void saveState(IMemento memento, Map<String, Object> properties);
+
+ /**
+ * Restore the terminal properties from the given memento.
+ *
+ * @param memento The memento. Must not be <code>null</code>.
+ * @param properties The map receiving the restored terminal properties. Must not be <code>null</code>.
+ */
+ public void restoreState(IMemento memento, Map<String, Object> properties);
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IPreferenceKeys.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IPreferenceKeys.java
new file mode 100644
index 000000000..905b568aa
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IPreferenceKeys.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+/**
+ * Terminals plug-in preference key definitions.
+ */
+public interface IPreferenceKeys {
+ /**
+ * Preference keys family prefix.
+ */
+ public final String PREF_TERMINAL = "terminals"; //$NON-NLS-1$
+
+ /**
+ * Preference key: Remove terminated terminals when a new terminal is created.
+ */
+ public final String PREF_REMOVE_TERMINATED_TERMINALS = PREF_TERMINAL + ".removeTerminatedTerminals"; //$NON-NLS-1$
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ITerminalsView.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ITerminalsView.java
new file mode 100644
index 000000000..fcad40175
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ITerminalsView.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361363] [TERMINALS] Implement "Pin&Clone" for the "Terminals" view
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+import org.eclipse.ui.IViewPart;
+
+/**
+ * Terminals view public interface.
+ */
+public interface ITerminalsView extends IViewPart {
+
+ /**
+ * Switch to the empty page control.
+ */
+ public void switchToEmptyPageControl();
+
+ /**
+ * Switch to the tab folder control.
+ */
+ public void switchToTabFolderControl();
+
+ /**
+ * Returns the context help id associated with the terminal
+ * console view instance.
+ *
+ * @return The context help id or <code>null</code> if none is associated.
+ */
+ public String getContextHelpId();
+
+ /**
+ * Set the state of the view to be pinned, which means a new terminal tab will be created
+ * in a new view instance.
+ *
+ * @param pin <code>True</code> to set the view state to pinned, <code>false</code> otherwise.
+ */
+ public void setPinned(boolean pin);
+
+ /**
+ * Return the pin state of the terminal view
+ *
+ * @return <code>True</code> if the view instance is pinned, <code>false</code> if not.
+ */
+ public boolean isPinned();
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IUIConstants.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IUIConstants.java
new file mode 100644
index 000000000..7c733a0e6
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/IUIConstants.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+/**
+ * Terminals common UI constants.
+ */
+public interface IUIConstants {
+ /**
+ * The view id of the terminals view.
+ */
+ public static final String ID = "org.eclipse.tcf.te.ui.terminals.TerminalsView"; //$NON-NLS-1$
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ImageConsts.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ImageConsts.java
new file mode 100644
index 000000000..ec47afc86
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/ImageConsts.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361363] [TERMINALS] Implement "Pin&Clone" for the "Terminals" view
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces;
+
+/**
+ * Image registry constants.
+ */
+public interface ImageConsts {
+ /**
+ * The root directory where to load the images from, relative to
+ * the bundle directory.
+ */
+ public final static String IMAGE_DIR_ROOT = "icons/"; //$NON-NLS-1$
+
+ /**
+ * The directory where to load colored local toolbar images from,
+ * relative to the image root directory.
+ */
+ public final static String IMAGE_DIR_CLCL = "clcl16/"; //$NON-NLS-1$
+
+ /**
+ * The directory where to load disabled local toolbar images from,
+ * relative to the image root directory.
+ */
+ public final static String IMAGE_DIR_DLCL = "dlcl16/"; //$NON-NLS-1$
+
+ /**
+ * The directory where to load enabled local toolbar images from,
+ * relative to the image root directory.
+ */
+ public final static String IMAGE_DIR_ELCL = "elcl16/"; //$NON-NLS-1$
+
+ /**
+ * The directory where to load view related images from, relative to
+ * the image root directory.
+ */
+ public final static String IMAGE_DIR_EVIEW = "eview16/"; //$NON-NLS-1$
+
+ /**
+ * The key to access the terminals console view image.
+ */
+ public static final String VIEW_Terminals = "TerminalsView"; //$NON-NLS-1$
+
+ /**
+ * The key to access the scroll lock action image (enabled).
+ */
+ public static final String ACTION_ScrollLock_Enabled = "ScrollLockAction_enabled"; //$NON-NLS-1$
+
+ /**
+ * The key to access the scroll lock action image (disabled).
+ */
+ public static final String ACTION_ScrollLock_Disabled = "ScrollLockAction_disabled"; //$NON-NLS-1$
+
+ /**
+ * The key to access the scroll lock action image (hover).
+ */
+ public static final String ACTION_ScrollLock_Hover = "ScrollLockAction_hover"; //$NON-NLS-1$
+
+ /**
+ * The key to access the pin terminal action image (enabled).
+ */
+ public static final String ACTION_PinTerminal_Enabled = "PinTerminalAction_enabled"; //$NON-NLS-1$
+
+ /**
+ * The key to access the pin terminal action image (disabled).
+ */
+ public static final String ACTION_PinTerminal_Disabled = "PinTerminalAction_disabled"; //$NON-NLS-1$
+
+ /**
+ * The key to access the pin terminal action image (hover).
+ */
+ public static final String ACTION_PinTerminal_Hover = "PinTerminalAction_hover"; //$NON-NLS-1$
+
+ /**
+ * The key to access the toggle command field action image (enabled).
+ */
+ public static final String ACTION_ToggleCommandField_Enabled = "ToggleCommandField_enabled"; //$NON-NLS-1$
+
+ /**
+ * The key to access the toggle command field action image (disabled).
+ */
+ public static final String ACTION_ToggleCommandField_Disabled = "ToggleCommandField_disabled"; //$NON-NLS-1$
+
+ /**
+ * The key to access the toggle command field action image (hover).
+ */
+ public static final String ACTION_ToggleCommandField_Hover = "ToggleCommandField_hover"; //$NON-NLS-1$
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/tracing/ITraceIds.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/tracing/ITraceIds.java
new file mode 100644
index 000000000..1ed32c06c
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/interfaces/tracing/ITraceIds.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.interfaces.tracing;
+
+/**
+ * Core plug-in trace slot identifiers.
+ */
+public interface ITraceIds {
+
+ /**
+ * If activated, tracing information about the terminals output stream monitor is printed out.
+ */
+ public static final String TRACE_OUTPUT_STREAM_MONITOR = "trace/outputStreamMonitor"; //$NON-NLS-1$
+
+ /**
+ * If activated, tracing information about the launch terminal command handler is printed out.
+ */
+ public static final String TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER = "trace/launchTerminalCommandHandler"; //$NON-NLS-1$
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PreferencesInitializer.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PreferencesInitializer.java
new file mode 100644
index 000000000..b6b6a6fa4
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PreferencesInitializer.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.tcf.te.core.terminals.preferences.ScopedEclipsePreferences;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IPreferenceKeys;
+
+/**
+ * Terminals default preferences initializer.
+ */
+public class PreferencesInitializer extends AbstractPreferenceInitializer {
+
+ /**
+ * Constructor.
+ */
+ public PreferencesInitializer() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences()
+ */
+ @Override
+ public void initializeDefaultPreferences() {
+ ScopedEclipsePreferences prefs = UIPlugin.getScopedPreferences();
+
+ prefs.putDefaultBoolean(IPreferenceKeys.PREF_REMOVE_TERMINATED_TERMINALS, true);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PropertyTester.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PropertyTester.java
new file mode 100644
index 000000000..cae006f42
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/PropertyTester.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.launcher.LauncherDelegateManager;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+
+
+/**
+ * Terminals property tester implementation.
+ */
+@SuppressWarnings("restriction")
+public class PropertyTester extends org.eclipse.core.expressions.PropertyTester {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.expressions.IPropertyTester#test(java.lang.Object, java.lang.String, java.lang.Object[], java.lang.Object)
+ */
+ @Override
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ if ("hasApplicableLauncherDelegates".equals(property)) { //$NON-NLS-1$
+ ISelection selection = receiver instanceof ISelection ? (ISelection)receiver : new StructuredSelection(receiver);
+ return expectedValue.equals(Boolean.valueOf(LauncherDelegateManager.getInstance().getApplicableLauncherDelegates(selection).length > 0));
+ }
+
+ if ("hasDisconnectButton".equals(property) && receiver instanceof CTabItem) { //$NON-NLS-1$
+ CTabItem tabItem = (CTabItem)receiver;
+ if (!tabItem.isDisposed()) {
+ Boolean hasDisconnectButton = (Boolean) tabItem.getData(ITerminalsConnectorConstants.PROP_HAS_DISCONNECT_BUTTON);
+ return expectedValue.equals(hasDisconnectButton);
+ }
+ return false;
+ }
+
+ if ("canDisconnect".equals(property) && receiver instanceof CTabItem) { //$NON-NLS-1$
+ CTabItem tabItem = (CTabItem)receiver;
+ if (!tabItem.isDisposed() && tabItem.getData() instanceof ITerminalViewControl) {
+ ITerminalViewControl terminal = (ITerminalViewControl)tabItem.getData();
+ TerminalState state = terminal.getState();
+ return expectedValue.equals(Boolean.valueOf(state != TerminalState.CLOSED));
+ }
+ return false;
+ }
+
+ return false;
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/SettingsStore.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/SettingsStore.java
new file mode 100644
index 000000000..31635db6d
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/SettingsStore.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore;
+
+/**
+ * Simple default Terminal settings store implementation keeping the settings
+ * within memory.
+ */
+@SuppressWarnings("restriction")
+public class SettingsStore implements ISettingsStore {
+ private final Map<String, Object> settings = new HashMap<String, Object>();
+
+ /**
+ * Constructor.
+ */
+ public SettingsStore() {
+ }
+
+ /**
+ * Returns the map containing the settings.
+ *
+ * @return The map containing the settings.
+ */
+ public final Map<String, Object> getSettings() {
+ return settings;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.ISettingsStore#get(java.lang.String, java.lang.String)
+ */
+ @Override
+ public final String get(String key, String defaultValue) {
+ Assert.isNotNull(key);
+ String value = settings.get(key) instanceof String ? (String) settings.get(key) : null;
+ return value != null ? value : defaultValue;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.ISettingsStore#get(java.lang.String)
+ */
+ @Override
+ public final String get(String key) {
+ Assert.isNotNull(key);
+ return settings.get(key) instanceof String ? (String) settings.get(key) : null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.ISettingsStore#put(java.lang.String, java.lang.String)
+ */
+ @Override
+ public final void put(String key, String value) {
+ Assert.isNotNull(key);
+ if (value == null) settings.remove(key);
+ else settings.put(key, value);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/EncodingSelectionDialog.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/EncodingSelectionDialog.java
new file mode 100644
index 000000000..628d8902e
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/EncodingSelectionDialog.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal.dialogs;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.te.ui.terminals.help.IContextHelpIds;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tcf.te.ui.terminals.panels.AbstractExtendedConfigurationPanel;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * Encoding selection dialog implementation.
+ */
+public class EncodingSelectionDialog extends TrayDialog {
+ private String contextHelpId = null;
+
+ // The selected encoding or null
+ /* default */ String encoding = null;
+
+ // Reference to the encodings panel
+ private EncodingPanel encodingPanel = null;
+
+ /**
+ * Encodings panel implementation
+ */
+ protected class EncodingPanel extends AbstractExtendedConfigurationPanel {
+
+ /**
+ * Constructor
+ *
+ * @param container The configuration panel container or <code>null</code>.
+ */
+ public EncodingPanel(IConfigurationPanelContainer container) {
+ super(container);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.controls.interfaces.IWizardConfigurationPanel#setupPanel(org.eclipse.swt.widgets.Composite, org.eclipse.ui.forms.widgets.FormToolkit)
+ */
+ @Override
+ public void setupPanel(Composite parent, FormToolkit toolkit) {
+ Composite panel = new Composite(parent, SWT.NONE);
+ panel.setLayout(new GridLayout());
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ panel.setLayoutData(data);
+
+ // Create the encoding selection combo
+ createEncodingUI(panel, false);
+ if (EncodingSelectionDialog.this.encoding != null) {
+ setEncoding(EncodingSelectionDialog.this.encoding);
+ }
+
+ setControl(panel);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.panels.AbstractConfigurationPanel#saveSettingsForHost(boolean)
+ */
+ @Override
+ protected void saveSettingsForHost(boolean add) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.panels.AbstractConfigurationPanel#fillSettingsForHost(java.lang.String)
+ */
+ @Override
+ protected void fillSettingsForHost(String host) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.panels.AbstractConfigurationPanel#getHostFromSettings()
+ */
+ @Override
+ protected String getHostFromSettings() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.panels.AbstractConfigurationPanel#getEncoding()
+ */
+ @Override
+ public String getEncoding() {
+ return super.getEncoding();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.panels.AbstractConfigurationPanel#setEncoding(java.lang.String)
+ */
+ @Override
+ public void setEncoding(String encoding) {
+ super.setEncoding(encoding);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param shell The parent shell or <code>null</code>.
+ */
+ public EncodingSelectionDialog(Shell shell) {
+ super(shell);
+
+ this.contextHelpId = IContextHelpIds.ENCODING_SELECTION_DIALOG;
+ setHelpAvailable(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected final Control createDialogArea(Composite parent) {
+ if (contextHelpId != null) {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, contextHelpId);
+ }
+
+ // Let the super implementation create the dialog area control
+ Control control = super.createDialogArea(parent);
+ // Setup the inner panel as scrollable composite
+ if (control instanceof Composite) {
+ ScrolledComposite sc = new ScrolledComposite((Composite)control, SWT.V_SCROLL);
+
+ GridLayout layout = new GridLayout(1, true);
+ layout.marginHeight = 0; layout.marginWidth = 0;
+ layout.verticalSpacing = 0; layout.horizontalSpacing = 0;
+
+ sc.setLayout(layout);
+ sc.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
+
+ sc.setExpandHorizontal(true);
+ sc.setExpandVertical(true);
+
+ Composite composite = new Composite(sc, SWT.NONE);
+ composite.setLayout(new GridLayout());
+
+ // Setup the dialog area content
+ createDialogAreaContent(composite);
+
+ sc.setContent(composite);
+ sc.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+
+ // Return the scrolled composite as new dialog area control
+ control = sc;
+ }
+
+ return control;
+ }
+
+ /**
+ * Creates the dialog area content.
+ *
+ * @param parent The parent composite. Must not be <code>null</code>.
+ */
+ protected void createDialogAreaContent(Composite parent) {
+ Assert.isNotNull(parent);
+
+ setDialogTitle(Messages.EncodingSelectionDialog_title);
+
+ Composite panel = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginHeight = 0; layout.marginWidth = 0;
+ panel.setLayout(layout);
+ panel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+
+ encodingPanel = new EncodingPanel(null);
+ encodingPanel.setupPanel(panel, null);
+
+ applyDialogFont(panel);
+ }
+
+ /**
+ * Sets the title for this dialog.
+ *
+ * @param title The title.
+ */
+ public void setDialogTitle(String title) {
+ if (getShell() != null && !getShell().isDisposed()) {
+ getShell().setText(title);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.jface.dialogs.CustomTrayDialog#okPressed()
+ */
+ @Override
+ protected void okPressed() {
+ // Save the selected encoding
+ if (encodingPanel != null) encoding = encodingPanel.getEncoding();
+ super.okPressed();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#cancelPressed()
+ */
+ @Override
+ protected void cancelPressed() {
+ // Reset the encoding
+ encoding = null;
+ super.cancelPressed();
+ }
+
+ /**
+ * Set the encoding to default to on creating the dialog.
+ */
+ public final void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Returns the selected encoding or <code>null</code>.
+ */
+ public final String getEncoding() {
+ return encoding;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/LaunchTerminalSettingsDialog.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/LaunchTerminalSettingsDialog.java
new file mode 100644
index 000000000..6ba77106b
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/dialogs/LaunchTerminalSettingsDialog.java
@@ -0,0 +1,569 @@
+/*******************************************************************************
+ * Copyright (c) 2011 - 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361352] [TERMINALS][SSH] Add SSH terminal support
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal.dialogs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.controls.ConfigurationPanelControl;
+import org.eclipse.tcf.te.ui.terminals.help.IContextHelpIds;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate;
+import org.eclipse.tcf.te.ui.terminals.interfaces.tracing.ITraceIds;
+import org.eclipse.tcf.te.ui.terminals.launcher.LauncherDelegateManager;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * Launch terminal settings dialog implementation.
+ */
+public class LaunchTerminalSettingsDialog extends TrayDialog {
+ private String contextHelpId = null;
+
+ // The parent selection
+ private ISelection selection = null;
+
+ // The sub controls
+ /* default */ Combo terminals;
+ /* default */ SettingsPanelControl settings;
+
+ private FormToolkit toolkit = null;
+
+ // Map the label added to the combo box to the corresponding launcher delegate.
+ /* default */ final Map<String, ILauncherDelegate> label2delegate = new HashMap<String, ILauncherDelegate>();
+
+ // The data object containing the currently selected settings
+ private Map<String, Object> data = null;
+
+ // The dialog settings storage
+ private IDialogSettings dialogSettings;
+
+ /**
+ * The control managing the terminal setting panels.
+ */
+ protected class SettingsPanelControl extends ConfigurationPanelControl {
+
+ /**
+ * Constructor.
+ */
+ public SettingsPanelControl() {
+ setPanelIsGroup(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.controls.BaseWizardConfigurationPanelControl#getGroupLabel()
+ */
+ @Override
+ public String getGroupLabel() {
+ return Messages.LaunchTerminalSettingsDialog_group_label;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.controls.BaseWizardConfigurationPanelControl#showConfigurationPanel(java.lang.String)
+ */
+ @Override
+ public void showConfigurationPanel(String key) {
+ // Check if we have to create the panel first
+ IConfigurationPanel configPanel = getConfigurationPanel(key);
+ if (isEmptyConfigurationPanel(configPanel)) {
+ // Get the corresponding delegate
+ ILauncherDelegate delegate = label2delegate.get(key);
+ Assert.isNotNull(delegate);
+ // Create the wizard configuration panel instance
+ configPanel = delegate.getPanel(this);
+ if (configPanel != null) {
+ // Add it to the settings panel control
+ settings.addConfigurationPanel(key, configPanel);
+ // Push the selection to the configuration panel
+ configPanel.setSelection(getSelection());
+ // Create the panel controls
+ configPanel.setupPanel(getPanel(), getFormToolkit());
+ // Restore widget values
+ IDialogSettings dialogSettings = LaunchTerminalSettingsDialog.this.settings.getDialogSettings(LaunchTerminalSettingsDialog.this.getDialogSettings());
+ IDialogSettings configPanelSettings = dialogSettings != null ? dialogSettings.getSection(key) : null;
+ if (configPanelSettings != null) configPanel.doRestoreWidgetValues(configPanelSettings, null);
+ }
+ }
+
+ super.showConfigurationPanel(key);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer#validate()
+ */
+ @Override
+ public void validate() {
+ LaunchTerminalSettingsDialog.this.validate();
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param shell The parent shell or <code>null</code>.
+ */
+ public LaunchTerminalSettingsDialog(Shell shell) {
+ this(shell, 0);
+ }
+
+ private long start = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param shell The parent shell or <code>null</code>.
+ */
+ public LaunchTerminalSettingsDialog(Shell shell, long start) {
+ super(shell);
+ this.start = start;
+
+ initializeDialogSettings();
+
+ this.contextHelpId = IContextHelpIds.LAUNCH_TERMINAL_SETTINGS_DIALOG;
+ setHelpAvailable(true);
+ }
+
+ /**
+ * Sets the parent selection.
+ *
+ * @param selection The parent selection or <code>null</code>.
+ */
+ public void setSelection(ISelection selection) {
+ this.selection = selection;
+ }
+
+ /**
+ * Returns the parent selection.
+ *
+ * @return The parent selection or <code>null</code>.
+ */
+ public ISelection getSelection() {
+ return selection;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#close()
+ */
+ @Override
+ public boolean close() {
+ dispose();
+ return super.close();
+ }
+
+ /**
+ * Dispose the dialog resources.
+ */
+ protected void dispose() {
+ if (settings != null) { settings.dispose(); settings = null; }
+ if (toolkit != null) { toolkit.dispose(); toolkit = null; }
+ dialogSettings = null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#isResizable()
+ */
+ @Override
+ protected boolean isResizable() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createContents(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent) {
+ Control composite = super.createContents(parent);
+
+ // Validate the dialog after having created all the content
+ validate();
+
+ return composite;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected final Control createDialogArea(Composite parent) {
+ if (contextHelpId != null) {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, contextHelpId);
+ }
+
+ // Let the super implementation create the dialog area control
+ Control control = super.createDialogArea(parent);
+ // Setup the inner panel as scrollable composite
+ if (control instanceof Composite) {
+ ScrolledComposite sc = new ScrolledComposite((Composite)control, SWT.V_SCROLL);
+
+ GridLayout layout = new GridLayout(1, true);
+ layout.marginHeight = 0; layout.marginWidth = 0;
+ layout.verticalSpacing = 0; layout.horizontalSpacing = 0;
+
+ sc.setLayout(layout);
+ sc.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
+
+ sc.setExpandHorizontal(true);
+ sc.setExpandVertical(true);
+
+ Composite composite = new Composite(sc, SWT.NONE);
+ composite.setLayout(new GridLayout());
+
+ // Setup the dialog area content
+ createDialogAreaContent(composite);
+
+ sc.setContent(composite);
+ sc.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+
+ // Return the scrolled composite as new dialog area control
+ control = sc;
+ }
+
+ return control;
+ }
+
+ /**
+ * Sets the title for this dialog.
+ *
+ * @param title The title.
+ */
+ public void setDialogTitle(String title) {
+ if (getShell() != null && !getShell().isDisposed()) {
+ getShell().setText(title);
+ }
+ }
+
+ /**
+ * Creates the dialog area content.
+ *
+ * @param parent The parent composite. Must not be <code>null</code>.
+ */
+ protected void createDialogAreaContent(Composite parent) {
+ Assert.isNotNull(parent);
+
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Creating dialog area after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalSettingsDialog.this);
+ }
+
+ setDialogTitle(Messages.LaunchTerminalSettingsDialog_title);
+
+ Composite panel = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginHeight = 0; layout.marginWidth = 0;
+ panel.setLayout(layout);
+ panel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+
+ Label label = new Label(panel, SWT.HORIZONTAL);
+ label.setText(Messages.LaunchTerminalSettingsDialog_combo_label);
+
+ terminals = new Combo(panel, SWT.READ_ONLY);
+ terminals.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ terminals.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ // Get the old panel
+ IConfigurationPanel oldPanel = settings.getActiveConfigurationPanel();
+ // Extract the current settings in an special properties container
+ Map<String, Object> data = new HashMap<String, Object>();
+ if (oldPanel != null) oldPanel.extractData(data);
+ // Clean out settings which are never passed between the panels
+ data.remove(ITerminalsConnectorConstants.PROP_IP_PORT);
+ data.remove(ITerminalsConnectorConstants.PROP_TIMEOUT);
+ data.remove(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID);
+ data.remove(ITerminalsConnectorConstants.PROP_ENCODING);
+ // Switch to the new panel
+ settings.showConfigurationPanel(terminals.getText());
+ // Get the new panel
+ IConfigurationPanel newPanel = settings.getActiveConfigurationPanel();
+ // Re-setup the relevant data
+ if (newPanel != null) newPanel.setupData(data);
+
+ // resize the dialog if needed to show the complete panel
+ getShell().pack();
+ }
+ });
+
+ // fill the combo with content
+ fillCombo(terminals);
+
+ // Create the settings panel control
+ settings = new SettingsPanelControl();
+
+ // Create, initialize and add the first visible panel. All
+ // other panels are created on demand only.
+ String terminalLabel = terminals.getItem(0);
+ if (terminalLabel != null) {
+ // Get the corresponding delegate
+ ILauncherDelegate delegate = label2delegate.get(terminalLabel);
+ Assert.isNotNull(delegate);
+ // Create the wizard configuration panel instance
+ IConfigurationPanel configPanel = delegate.getPanel(settings);
+ if (configPanel != null) {
+ // Add it to the settings panel control
+ settings.addConfigurationPanel(terminalLabel, configPanel);
+ // Push the selection to the configuration panel
+ configPanel.setSelection(getSelection());
+ }
+ }
+
+ // Create the toolkit
+ toolkit = new FormToolkit(panel.getDisplay());
+ // Setup the panel control
+ settings.setupPanel(panel, terminals.getItems(), toolkit);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ layoutData.horizontalSpan = 2;
+ settings.getPanel().setLayoutData(layoutData);
+
+ // Preselect the first terminal launcher
+ terminals.select(0);
+ settings.showConfigurationPanel(terminals.getText());
+
+ terminals.setEnabled(terminals.getItemCount() > 1);
+
+ restoreWidgetValues();
+
+ applyDialogFont(panel);
+
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Created dialog area after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalSettingsDialog.this);
+ }
+ }
+
+ /**
+ * Fill the given combo with content. The content are the terminal
+ * launcher delegate labels.
+ *
+ * @param combo The combo. Must not be <code>null</code>.
+ */
+ protected void fillCombo(Combo combo) {
+ Assert.isNotNull(combo);
+
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Filling combo after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalSettingsDialog.this);
+ }
+
+ List<String> items = new ArrayList<String>();
+
+ if(selection==null || selection.isEmpty()){
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Getting launcher delegates after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalSettingsDialog.this);
+ }
+
+ ILauncherDelegate[] delegates = LauncherDelegateManager.getInstance().getLauncherDelegates(false);
+
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Got launcher delegates after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalSettingsDialog.this);
+ }
+
+ for (ILauncherDelegate delegate : delegates) {
+ if (delegate.isHidden() || isFiltered(selection, delegate)) continue;
+ String label = delegate.getLabel();
+ if (label == null || "".equals(label.trim())) label = delegate.getId(); //$NON-NLS-1$
+ label2delegate.put(label, delegate);
+ items.add(label);
+ }
+ } else {
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Getting applicable launcher delegates after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalSettingsDialog.this);
+ }
+
+ ILauncherDelegate[] delegates = LauncherDelegateManager.getInstance().getApplicableLauncherDelegates(selection);
+
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Got applicable launcher delegates after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalSettingsDialog.this);
+ }
+
+ for (ILauncherDelegate delegate : delegates) {
+ if (delegate.isHidden() || isFiltered(selection, delegate)) continue;
+ String label = delegate.getLabel();
+ if (label == null || "".equals(label.trim())) label = delegate.getId(); //$NON-NLS-1$
+ label2delegate.put(label, delegate);
+ items.add(label);
+ }
+ }
+ Collections.sort(items);
+ combo.setItems(items.toArray(new String[items.size()]));
+ }
+
+ /**
+ * Hook to allow additional filtering of the applicable launcher delegates.
+ * <p>
+ * <b>Note:</b> The default implementation always returns <code>false</code>.
+ *
+ * @param selection The selection or <code>null</code>.
+ * @param delegate The launcher delegate. Must not be <code>null</code>.
+ *
+ * @return <code>True</code> if the launcher delegate is filtered based on the given selection, <code>false</code> otherwise.
+ */
+ protected boolean isFiltered(ISelection selection, ILauncherDelegate delegate) {
+ return false;
+ }
+
+ /**
+ * Validate the dialog.
+ */
+ public void validate() {
+ IConfigurationPanel panel = this.settings.getActiveConfigurationPanel();
+ Button okButton = getButton(IDialogConstants.OK_ID);
+ if (okButton != null) okButton.setEnabled(panel.isValid());
+ }
+
+ /**
+ * Set the given message and message type.
+ *
+ * @param message The message or <code>null</code>.
+ * @param messageType The message type or <code>IMessageProvider.NONE</code>.
+ */
+ public void setMessage(String message, int messageType) {
+ if (settings != null) {
+ settings.setMessage(message, messageType);
+ }
+ }
+
+ /**
+ * Save the dialog's widget values.
+ */
+ protected void saveWidgetValues() {
+ IDialogSettings settings = getDialogSettings();
+ if (settings != null && terminals != null) {
+ settings.put("terminalLabel", terminals.getText()); //$NON-NLS-1$
+ this.settings.saveWidgetValues(settings, null);
+ }
+ }
+
+ /**
+ * Restore the dialog's widget values.
+ */
+ protected void restoreWidgetValues() {
+ IDialogSettings settings = getDialogSettings();
+ if (settings != null) {
+ String terminalLabel = settings.get("terminalLabel"); //$NON-NLS-1$
+ int index = terminalLabel != null ? Arrays.asList(terminals.getItems()).indexOf(terminalLabel) : -1;
+ if (index != -1) {
+ terminals.select(index);
+ this.settings.showConfigurationPanel(terminals.getText());
+ }
+
+ this.settings.restoreWidgetValues(settings, null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+ */
+ @Override
+ protected void okPressed() {
+ IConfigurationPanel panel = this.settings.getActiveConfigurationPanel();
+ Assert.isNotNull(panel);
+
+ if (!panel.isValid()) {
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR | SWT.OK);
+ mb.setText(Messages.LaunchTerminalSettingsDialog_error_title);
+ mb.setMessage(NLS.bind(Messages.LaunchTerminalSettingsDialog_error_invalidSettings, panel.getMessage() != null ? panel.getMessage() : Messages.LaunchTerminalSettingsDialog_error_unknownReason));
+ mb.open();
+ return;
+ }
+ data = new HashMap<String, Object>();
+
+ // Store the id of the selected delegate
+ data.put(ITerminalsConnectorConstants.PROP_DELEGATE_ID, label2delegate.get(terminals.getText()).getId());
+ // Store the selection
+ data.put(ITerminalsConnectorConstants.PROP_SELECTION, selection);
+
+ // Store the delegate specific settings
+ panel.extractData(data);
+
+ // Save the current widget values
+ saveWidgetValues();
+
+ super.okPressed();
+ }
+
+ /**
+ * Returns the configured terminal launcher settings.
+ * <p>
+ * The settings are extracted from the UI widgets once
+ * OK got pressed.
+ *
+ * @return The configured terminal launcher settings or <code>null</code>.
+ */
+ public Map<String, Object> getSettings() {
+ return data;
+ }
+
+ /**
+ * Initialize the dialog settings storage.
+ */
+ protected void initializeDialogSettings() {
+ IDialogSettings settings = UIPlugin.getDefault().getDialogSettings();
+ Assert.isNotNull(settings);
+ IDialogSettings section = settings.getSection(getClass().getSimpleName());
+ if (section == null) {
+ section = settings.addNewSection(getClass().getSimpleName());
+ }
+ setDialogSettings(section);
+ }
+
+ /**
+ * Returns the associated dialog settings storage.
+ *
+ * @return The dialog settings storage.
+ */
+ public IDialogSettings getDialogSettings() {
+ // The dialog settings may not been initialized here. Initialize first in this case
+ // to be sure that we do have always the correct dialog settings.
+ if (dialogSettings == null) {
+ initializeDialogSettings();
+ }
+ return dialogSettings;
+ }
+
+ /**
+ * Sets the associated dialog settings storage.
+ *
+ * @return The dialog settings storage.
+ */
+ public void setDialogSettings(IDialogSettings dialogSettings) {
+ this.dialogSettings = dialogSettings;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/AbstractTriggerCommandHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/AbstractTriggerCommandHandler.java
new file mode 100644
index 000000000..da9dde56e
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/AbstractTriggerCommandHandler.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal.handler;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.expressions.EvaluationContext;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.handlers.IHandlerService;
+
+/**
+ * Abstract command handler triggering a command to be executed.
+ */
+public abstract class AbstractTriggerCommandHandler extends AbstractHandler {
+
+ /**
+ * Trigger a command to be executed.
+ *
+ * @param commandId The command id. Must not be <code>null</code>.
+ * @param selection The selection to pass on to the command or <code>null</code>.
+ */
+ protected void triggerCommand(String commandId, ISelection selection) {
+ Assert.isNotNull(commandId);
+
+ ICommandService service = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class);
+ Command command = service != null ? service.getCommand(commandId) : null;
+ if (command != null && command.isDefined() && command.isEnabled()) {
+ try {
+ ParameterizedCommand pCmd = ParameterizedCommand.generateCommand(command, null);
+ Assert.isNotNull(pCmd);
+ IHandlerService handlerSvc = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class);
+ Assert.isNotNull(handlerSvc);
+ IEvaluationContext ctx = handlerSvc.getCurrentState();
+ if (selection != null) {
+ ctx = new EvaluationContext(ctx, selection);
+ ctx.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection);
+ }
+ handlerSvc.executeCommandInContext(pCmd, null, ctx);
+ } catch (Exception e) {
+ // If the platform is in debug mode, we print the exception to the log view
+ if (Platform.inDebugMode()) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
+ Messages.AbstractTriggerCommandHandler_error_executionFailed, e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ }
+ }
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/DisconnectTerminalCommandHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/DisconnectTerminalCommandHandler.java
new file mode 100644
index 000000000..7858bf9a7
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/DisconnectTerminalCommandHandler.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal.handler;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderManager;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Disconnect terminal connection command handler implementation.
+ */
+@SuppressWarnings("restriction")
+public class DisconnectTerminalCommandHandler extends AbstractHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ CTabItem item = null;
+
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+ if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+ Object element = ((IStructuredSelection)selection).getFirstElement();
+ if (element instanceof CTabItem && ((CTabItem)element).getData() instanceof ITerminalViewControl) {
+ item = (CTabItem)element;
+ }
+ }
+
+ if (item == null && HandlerUtil.getActivePart(event) instanceof ITerminalsView) {
+ ITerminalsView view = (ITerminalsView)HandlerUtil.getActivePart(event);
+ TabFolderManager mgr = (TabFolderManager)view.getAdapter(TabFolderManager.class);
+ if (mgr != null && mgr.getActiveTabItem() != null) {
+ item = mgr.getActiveTabItem();
+ }
+ }
+
+ if (item != null && item.getData() instanceof ITerminalViewControl) {
+ ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
+ if (terminal != null && !terminal.isDisposed()) {
+ terminal.disconnectTerminal();
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/LaunchTerminalCommandHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/LaunchTerminalCommandHandler.java
new file mode 100644
index 000000000..e87f96ce0
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/LaunchTerminalCommandHandler.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal.handler;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.te.core.terminals.TerminalContextPropertiesProviderFactory;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalContextPropertiesProvider;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.IContextPropertiesConstants;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate;
+import org.eclipse.tcf.te.ui.terminals.interfaces.tracing.ITraceIds;
+import org.eclipse.tcf.te.ui.terminals.internal.dialogs.LaunchTerminalSettingsDialog;
+import org.eclipse.tcf.te.ui.terminals.launcher.LauncherDelegateManager;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Launch terminal command handler implementation.
+ */
+public class LaunchTerminalCommandHandler extends AbstractHandler {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ String commandId = event.getCommand().getId();
+ // "org.eclipse.tcf.te.ui.terminals.command.launchToolbar"
+ // "org.eclipse.tcf.te.ui.terminals.command.launch"
+
+ long start = System.currentTimeMillis();
+
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+ String date = format.format(new Date(start));
+
+ UIPlugin.getTraceHandler().trace("Started at " + date + " (" + start + ")", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalCommandHandler.this);
+ }
+
+ // Get the active shell
+ Shell shell = HandlerUtil.getActiveShell(event);
+ // Get the current selection
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+
+ if (commandId.equals("org.eclipse.tcf.te.ui.terminals.command.launchToolbar")) { //$NON-NLS-1$
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("(a) Attempt to open launch terminal settings dialog after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalCommandHandler.this);
+ }
+
+ LaunchTerminalSettingsDialog dialog = new LaunchTerminalSettingsDialog(shell, start);
+
+ if(isValidSelection(selection)){
+ dialog.setSelection(selection);
+ }
+ if (dialog.open() == Window.OK) {
+ // Get the terminal settings from the dialog
+ Map<String, Object> properties = dialog.getSettings();
+ if (properties != null) {
+ String delegateId = (String)properties.get(ITerminalsConnectorConstants.PROP_DELEGATE_ID);
+ Assert.isNotNull(delegateId);
+ ILauncherDelegate delegate = LauncherDelegateManager.getInstance().getLauncherDelegate(delegateId, false);
+ Assert.isNotNull(delegateId);
+ delegate.execute(properties, null);
+ }
+ }
+ }
+ else {
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Getting applicable launcher delegates after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalCommandHandler.this);
+ }
+
+ // Check if the dialog needs to be shown at all
+ ILauncherDelegate[] delegates = LauncherDelegateManager.getInstance().getApplicableLauncherDelegates(selection);
+
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("Got applicable launcher delegates after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalCommandHandler.this);
+ }
+
+ if (delegates.length > 1 || (delegates.length == 1 && delegates[0].needsUserConfiguration())) {
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER)) {
+ UIPlugin.getTraceHandler().trace("(b) Attempt to open launch terminal settings dialog after " + (System.currentTimeMillis() - start) + " ms.", //$NON-NLS-1$ //$NON-NLS-2$
+ ITraceIds.TRACE_LAUNCH_TERMINAL_COMMAND_HANDLER, LaunchTerminalCommandHandler.this);
+ }
+
+ // Create the launch terminal settings dialog
+ LaunchTerminalSettingsDialog dialog = new LaunchTerminalSettingsDialog(shell, start);
+ if(isValidSelection(selection)){
+ dialog.setSelection(selection);
+ }
+ if (dialog.open() == Window.OK) {
+ // Get the terminal settings from the dialog
+ Map<String, Object> properties = dialog.getSettings();
+ if (properties != null) {
+ String delegateId = (String)properties.get(ITerminalsConnectorConstants.PROP_DELEGATE_ID);
+ Assert.isNotNull(delegateId);
+ ILauncherDelegate delegate = LauncherDelegateManager.getInstance().getLauncherDelegate(delegateId, false);
+ Assert.isNotNull(delegateId);
+ delegate.execute(properties, null);
+ }
+ }
+ }
+ else if (delegates.length == 1) {
+ ILauncherDelegate delegate = delegates[0];
+ Map<String, Object> properties = new HashMap<String, Object>();
+
+ // Store the id of the selected delegate
+ properties.put(ITerminalsConnectorConstants.PROP_DELEGATE_ID, delegate.getId());
+ // Store the selection
+ properties.put(ITerminalsConnectorConstants.PROP_SELECTION, selection);
+
+ // Execute
+ delegate.execute(properties, null);
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isValidSelection(ISelection selection) {
+ if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+ Object element = ((IStructuredSelection) selection).getFirstElement();
+ ITerminalContextPropertiesProvider provider = TerminalContextPropertiesProviderFactory.getProvider(element);
+ if (provider != null) {
+ Map<String, String> props = provider.getTargetAddress(element);
+ if (props != null && props.containsKey(IContextPropertiesConstants.PROP_ADDRESS)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/MaximizeViewHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/MaximizeViewHandler.java
new file mode 100644
index 000000000..a95424d37
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/MaximizeViewHandler.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal.handler;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Maximize view handler implementation.
+ */
+public class MaximizeViewHandler extends AbstractTriggerCommandHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ triggerCommand("org.eclipse.ui.window.maximizePart", null); //$NON-NLS-1$
+ return null;
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/QuickAccessHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/QuickAccessHandler.java
new file mode 100644
index 000000000..24401827b
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/internal/handler/QuickAccessHandler.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.internal.handler;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Quick access handler implementation.
+ */
+public class QuickAccessHandler extends AbstractTriggerCommandHandler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ triggerCommand("org.eclipse.ui.window.quickAccess", null); //$NON-NLS-1$
+ return null;
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/AbstractLauncherDelegate.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/AbstractLauncherDelegate.java
new file mode 100644
index 000000000..1b868c438
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/AbstractLauncherDelegate.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.launcher;
+
+import org.eclipse.core.expressions.Expression;
+import org.eclipse.core.expressions.ExpressionConverter;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+
+/**
+ * Abstract launcher delegate implementation.
+ */
+public abstract class AbstractLauncherDelegate extends PlatformObject implements ILauncherDelegate {
+ // The mandatory id of the extension
+ private String id = null;
+ // The label of the extension
+ private String label = null;
+ // The converted expression
+ private Expression expression;
+ // The hidden attribute
+ private boolean hidden;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
+ if (config == null) return;
+
+ // Initialize the id field by reading the <id> extension attribute.
+ // Throws an exception if the id is empty or null.
+ id = config.getAttribute("id"); //$NON-NLS-1$
+ if (id == null || "".equals(id.trim())) { //$NON-NLS-1$
+ throw createMissingMandatoryAttributeException("id", config.getContributor().getName()); //$NON-NLS-1$
+ }
+
+ // Try the "label" attribute first
+ label = config.getAttribute("label"); //$NON-NLS-1$
+ // If "label" is not found or empty, try the "name" attribute as fallback
+ if (label == null || "".equals(label.trim())) { //$NON-NLS-1$
+ label = config.getAttribute("name"); //$NON-NLS-1$
+ }
+
+ // Read the sub elements of the extension
+ IConfigurationElement[] children = config.getChildren();
+ // The "enablement" element is the only expected one
+ if (children != null && children.length > 0) {
+ expression = ExpressionConverter.getDefault().perform(children[0]);
+ }
+
+ // Read "hidden" attribute
+ String value = config.getAttribute("hidden"); //$NON-NLS-1$
+ if (value != null && !"".equals(value.trim())) { //$NON-NLS-1$
+ hidden = Boolean.parseBoolean(value);
+ }
+ }
+
+ /**
+ * Creates a new {@link CoreException} to be thrown if a mandatory extension attribute
+ * is missing.
+ *
+ * @param attributeName The attribute name. Must not be <code>null</code>.
+ * @param extensionId The extension id. Must not be <code>null</code>.
+ *
+ * @return The {@link CoreException} instance.
+ */
+ protected CoreException createMissingMandatoryAttributeException(String attributeName, String extensionId) {
+ Assert.isNotNull(attributeName);
+ Assert.isNotNull(extensionId);
+
+ return new CoreException(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(),
+ 0,
+ NLS.bind(Messages.Extension_error_missingRequiredAttribute, attributeName, extensionId),
+ null));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#getId()
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#getLabel()
+ */
+ @Override
+ public String getLabel() {
+ return label != null ? label.trim() : ""; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#getEnablement()
+ */
+ @Override
+ public Expression getEnablement() {
+ return expression;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#isHidden()
+ */
+ @Override
+ public boolean isHidden() {
+ return hidden;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof AbstractLauncherDelegate) {
+ return id.equals(((AbstractLauncherDelegate)obj).id);
+ }
+ return super.equals(obj);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/LauncherDelegateManager.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/LauncherDelegateManager.java
new file mode 100644
index 000000000..1b99f4389
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/launcher/LauncherDelegateManager.java
@@ -0,0 +1,421 @@
+/*******************************************************************************
+ * Copyright (c) 2011 - 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.launcher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.expressions.EvaluationContext;
+import org.eclipse.core.expressions.EvaluationResult;
+import org.eclipse.core.expressions.Expression;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.IHandlerService;
+
+/**
+ * Terminal launcher delegate manager implementation.
+ */
+public class LauncherDelegateManager {
+ // Flag to mark the extension point manager initialized (extensions loaded).
+ private boolean initialized = false;
+
+ // The map containing all loaded contributions
+ private final Map<String, Proxy> extensionsMap = new HashMap<String, Proxy>();
+
+ // The extension point comparator
+ private ExtensionPointComparator comparator = null;
+
+ /**
+ * Executable extension proxy implementation.
+ */
+ /* default */ static class Proxy {
+ // The extension instance. Created on first access
+ private ILauncherDelegate instance;
+ // The configuration element
+ private final IConfigurationElement element;
+ // The unique id of the extension.
+ private String id;
+
+ /**
+ * Constructor.
+ *
+ * @param element The configuration element. Must not be <code>null</code>.
+ * @throws CoreException In case the configuration element attribute <i>id</i> is <code>null</code> or empty.
+ */
+ public Proxy(IConfigurationElement element) throws CoreException {
+ Assert.isNotNull(element);
+ this.element = element;
+
+ // Extract the extension attributes
+ id = element.getAttribute("id"); //$NON-NLS-1$
+ if (id == null || id.trim().length() == 0) {
+ throw new CoreException(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(),
+ 0,
+ NLS.bind(Messages.Extension_error_missingRequiredAttribute, "id", element.getContributor().getName()), //$NON-NLS-1$
+ null));
+ }
+
+ instance = null;
+ }
+
+ /**
+ * Returns the extensions unique id.
+ *
+ * @return The unique id.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the configuration element for this extension.
+ *
+ * @return The configuration element.
+ */
+ public IConfigurationElement getConfigurationElement() {
+ return element;
+ }
+
+ /**
+ * Returns the extension class instance. The contributing
+ * plug-in will be activated if not yet activated anyway.
+ *
+ * @return The extension class instance or <code>null</code> if the instantiation fails.
+ */
+ public ILauncherDelegate getInstance() {
+ if (instance == null) instance = newInstance();
+ return instance;
+ }
+
+ /**
+ * Returns always a new extension class instance which is different
+ * to what {@link #getInstance()} would return.
+ *
+ * @return A new extension class instance or <code>null</code> if the instantiation fails.
+ */
+ public ILauncherDelegate newInstance() {
+ IConfigurationElement element = getConfigurationElement();
+ Assert.isNotNull(element);
+
+ // The "class" to load can be specified either as attribute or as child element
+ if (element.getAttribute("class") != null || element.getChildren("class").length > 0) { //$NON-NLS-1$ //$NON-NLS-2$
+ try {
+ return (ILauncherDelegate)element.createExecutableExtension("class"); //$NON-NLS-1$
+ } catch (Exception e) {
+ // Possible exceptions: CoreException, ClassCastException.
+ Platform.getLog(UIPlugin.getDefault().getBundle()).log(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(),
+ NLS.bind(Messages.Extension_error_invalidExtensionPoint, element.getDeclaringExtension().getUniqueIdentifier()), e));
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ // Proxies are equal if they have encapsulate an element
+ // with the same unique id
+ if (obj instanceof Proxy) {
+ return getId().equals(((Proxy)obj).getId());
+ }
+ return super.equals(obj);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ // The hash code of a proxy is the one from the id
+ return getId().hashCode();
+ }
+ }
+
+ /**
+ * Extension point comparator implementation.
+ * <p>
+ * The comparator assure that extension are read in a predictable order.
+ * <p>
+ * The order of the extensions is defined as following:<br>
+ * <ul><li>Extensions contributed by our own plug-ins (<code>org.eclipse.tcf.te.*</code>)
+ * in ascending alphabetic order and</li>
+ * <li>Extensions contributed by any other plug-in in ascending alphabetic order.</li>
+ * <li>Extensions contributed by the same plug-in in ascending alphabetic order by the
+ * extensions unique id</li>
+ */
+ /* default */ static class ExtensionPointComparator implements Comparator<IExtension> {
+ private final static String OWN_PLUGINS_PATTERN = "org.eclipse.tcf.te."; //$NON-NLS-1$
+
+ /* (non-Javadoc)
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public int compare(IExtension o1, IExtension o2) {
+ // We ignore any comparisation with null and
+ if (o1 == null || o2 == null) return 0;
+ // Check if it is the exact same element
+ if (o1 == o2) return 0;
+
+ // The extensions are compared by the unique id of the contributing plug-in first
+ String contributor1 = o1.getContributor().getName();
+ String contributor2 = o2.getContributor().getName();
+
+ // Contributions from our own plug-ins comes before 3rdParty plug-ins
+ if (contributor1.startsWith(OWN_PLUGINS_PATTERN) && !contributor2.startsWith(OWN_PLUGINS_PATTERN))
+ return -1;
+ if (!contributor1.startsWith(OWN_PLUGINS_PATTERN) && contributor2.startsWith(OWN_PLUGINS_PATTERN))
+ return 1;
+ if (contributor1.startsWith(OWN_PLUGINS_PATTERN) && contributor2.startsWith(OWN_PLUGINS_PATTERN)) {
+ int value = contributor1.compareTo(contributor2);
+ // Within the same plug-in, the extension are sorted by their unique id (if available)
+ if (value == 0 && o1.getUniqueIdentifier() != null && o2.getUniqueIdentifier() != null)
+ return o1.getUniqueIdentifier().compareTo(o2.getUniqueIdentifier());
+ // Otherwise, just return the comparisation result from the contributors
+ return value;
+ }
+
+ // Contributions from all other plug-ins are sorted alphabetical
+ int value = contributor1.compareTo(contributor2);
+ // Within the same plug-in, the extension are sorted by their unique id (if available)
+ if (value == 0 && o1.getUniqueIdentifier() != null && o2.getUniqueIdentifier() != null)
+ return o1.getUniqueIdentifier().compareTo(o2.getUniqueIdentifier());
+ // Otherwise, just return the comparisation result from the contributors
+ return value;
+ }
+
+ }
+
+ /*
+ * Thread save singleton instance creation.
+ */
+ private static class LazyInstanceHolder {
+ public static LauncherDelegateManager instance = new LauncherDelegateManager();
+ }
+
+ /**
+ * Returns the singleton instance.
+ */
+ public static LauncherDelegateManager getInstance() {
+ return LazyInstanceHolder.instance;
+ }
+
+ /**
+ * Constructor.
+ */
+ LauncherDelegateManager() {
+ super();
+ }
+
+ /**
+ * Returns the list of all contributed terminal launcher delegates.
+ *
+ * @param unique If <code>true</code>, the method returns new instances for each
+ * contributed terminal launcher delegate.
+ *
+ * @return The list of contributed terminal launcher delegates, or an empty array.
+ */
+ public ILauncherDelegate[] getLauncherDelegates(boolean unique) {
+ List<ILauncherDelegate> contributions = new ArrayList<ILauncherDelegate>();
+ for (Proxy launcherDelegate : getExtensions().values()) {
+ ILauncherDelegate instance = unique ? launcherDelegate.newInstance() : launcherDelegate.getInstance();
+ if (instance != null && !contributions.contains(instance)) {
+ contributions.add(instance);
+ }
+ }
+
+ return contributions.toArray(new ILauncherDelegate[contributions.size()]);
+ }
+
+ /**
+ * Returns the terminal launcher delegate identified by its unique id. If no terminal
+ * launcher delegate with the specified id is registered, <code>null</code> is returned.
+ *
+ * @param id The unique id of the terminal launcher delegate or <code>null</code>
+ * @param unique If <code>true</code>, the method returns new instances of the terminal launcher delegate contribution.
+ *
+ * @return The terminal launcher delegate instance or <code>null</code>.
+ */
+ public ILauncherDelegate getLauncherDelegate(String id, boolean unique) {
+ ILauncherDelegate contribution = null;
+ Map<String, Proxy> extensions = getExtensions();
+ if (extensions.containsKey(id)) {
+ Proxy proxy = extensions.get(id);
+ // Get the extension instance
+ contribution = unique ? proxy.newInstance() : proxy.getInstance();
+ }
+
+ return contribution;
+ }
+
+ /**
+ * Returns the applicable terminal launcher delegates for the given selection.
+ *
+ * @param selection The selection or <code>null</code>.
+ * @return The list of applicable terminal launcher delegates or an empty array.
+ */
+ public ILauncherDelegate[] getApplicableLauncherDelegates(ISelection selection) {
+ List<ILauncherDelegate> applicable = new ArrayList<ILauncherDelegate>();
+
+ for (ILauncherDelegate delegate : getLauncherDelegates(false)) {
+ Expression enablement = delegate.getEnablement();
+
+ // The launcher delegate is applicable by default if
+ // no expression is specified.
+ boolean isApplicable = enablement == null;
+
+ if (enablement != null) {
+ if (selection != null) {
+ // Set the default variable to selection.
+ IEvaluationContext currentState = ((IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class)).getCurrentState();
+ EvaluationContext context = new EvaluationContext(currentState, selection);
+ // Set the "selection" variable to the selection.
+ context.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection);
+ // Allow plug-in activation
+ context.setAllowPluginActivation(true);
+ // Evaluate the expression
+ try {
+ isApplicable = enablement.evaluate(context).equals(EvaluationResult.TRUE);
+ } catch (CoreException e) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ } else {
+ // The enablement is false by definition if
+ // there is no selection.
+ isApplicable = false;
+ }
+ }
+
+ // Add the page if applicable
+ if (isApplicable) applicable.add(delegate);
+ }
+
+ return applicable.toArray(new ILauncherDelegate[applicable.size()]);
+ }
+
+ /**
+ * Returns the map of managed extensions. If not loaded before,
+ * this methods trigger the loading of the extensions to the managed
+ * extension point.
+ *
+ * @return The map of extensions.
+ */
+ protected Map<String, Proxy> getExtensions() {
+ // Load and store the extensions thread-safe!
+ synchronized (extensionsMap) {
+ if (!initialized) { loadExtensions(); initialized = true; }
+ }
+ return extensionsMap;
+ }
+
+ /**
+ * Returns the extension point comparator instance. If not available,
+ * {@link #doCreateExtensionPointComparator()} is called to create a new instance.
+ *
+ * @return The extension point comparator or <code>null</code> if the instance creation fails.
+ */
+ protected final ExtensionPointComparator getExtensionPointComparator() {
+ if (comparator == null) {
+ comparator = new ExtensionPointComparator();
+ }
+ return comparator;
+ }
+
+ /**
+ * Returns the extensions of the specified extension point sorted.
+ * <p>
+ * For the order of the extensions, see {@link ExtensionPointComparator}.
+ *
+ * @param point The extension point. Must not be <code>null</code>.
+ * @return The extensions in sorted order or an empty array if the extension point has no extensions.
+ */
+ protected IExtension[] getExtensionsSorted(IExtensionPoint point) {
+ Assert.isNotNull(point);
+
+ List<IExtension> extensions = new ArrayList<IExtension>(Arrays.asList(point.getExtensions()));
+ if (extensions.size() > 0) {
+ Collections.sort(extensions, getExtensionPointComparator());
+ }
+
+ return extensions.toArray(new IExtension[extensions.size()]);
+ }
+
+ /**
+ * Loads the extensions for the managed extension point.
+ */
+ protected void loadExtensions() {
+ // If already initialized, this method will do nothing.
+ if (initialized) return;
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint point = registry.getExtensionPoint("org.eclipse.tcf.te.ui.terminals.launcherDelegates"); //$NON-NLS-1$
+ if (point != null) {
+ IExtension[] extensions = getExtensionsSorted(point);
+ for (IExtension extension : extensions) {
+ IConfigurationElement[] elements = extension.getConfigurationElements();
+ for (IConfigurationElement element : elements) {
+ if ("delegate".equals(element.getName())) { //$NON-NLS-1$
+ try {
+ Proxy candidate = new Proxy(element);
+ if (candidate.getId() != null) {
+ // If no extension with this id had been registered before, register now.
+ if (!extensionsMap.containsKey(candidate.getId())) {
+ extensionsMap.put(candidate.getId(), candidate);
+ }
+ else {
+ throw new CoreException(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(),
+ 0,
+ NLS.bind(Messages.Extension_error_duplicateExtension, candidate.getId(), element.getContributor().getName()),
+ null));
+ }
+ } else {
+ throw new CoreException(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(),
+ 0,
+ NLS.bind(Messages.Extension_error_missingRequiredAttribute, "id", element.getAttribute("label")), //$NON-NLS-1$ //$NON-NLS-2$
+ null));
+ }
+ } catch (CoreException e) {
+ Platform.getLog(UIPlugin.getDefault().getBundle()).log(new Status(IStatus.ERROR,
+ UIPlugin.getUniqueIdentifier(),
+ NLS.bind(Messages.Extension_error_invalidExtensionPoint, element.getDeclaringExtension().getUniqueIdentifier()), e));
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/AbstractWindowListener.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/AbstractWindowListener.java
new file mode 100644
index 000000000..d5752bf4e
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/AbstractWindowListener.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2013, 2014 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.listeners;
+
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+
+/**
+ * Abstract window listener implementation.
+ */
+public abstract class AbstractWindowListener implements IWindowListener {
+ // The part listener instance
+ protected final IPartListener2 partListener;
+ // The perspective listener instance
+ protected final IPerspectiveListener perspectiveListener;
+
+ // Flag to remember if the initialization is done or not
+ private boolean initialized = false;
+
+ /**
+ * Constructor
+ */
+ public AbstractWindowListener() {
+ // Create the part listener instance
+ partListener = createPartListener();
+ // Create the perspective listener instance
+ perspectiveListener = createPerspectiveListener();
+ }
+
+ /**
+ * Creates a new part listener instance.
+ * <p>
+ * <b>Note:</b> The default implementation returns <code>null</code>.
+ *
+ * @return The part listener instance or <code>null</code>.
+ */
+ protected IPartListener2 createPartListener() {
+ return null;
+ }
+
+ /**
+ * Creates a new perspective listener instance.
+ * <p>
+ * <b>Note:</b> The default implementation returns <code>null</code>.
+ *
+ * @return The perspective listener instance or <code>null</code>.
+ */
+ protected IPerspectiveListener createPerspectiveListener() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
+ */
+ @Override
+ public void windowActivated(IWorkbenchWindow window) {
+ if (!initialized && window != null) {
+ windowOpened(window);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
+ */
+ @Override
+ public void windowDeactivated(IWorkbenchWindow window) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
+ */
+ @Override
+ public void windowClosed(IWorkbenchWindow window) {
+ // On close, remove the listeners from the window
+ if (window != null) {
+ if (window.getPartService() != null && partListener != null) {
+ window.getPartService().removePartListener(partListener);
+ }
+
+ if (perspectiveListener != null) window.removePerspectiveListener(perspectiveListener);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
+ */
+ @Override
+ public void windowOpened(IWorkbenchWindow window) {
+ if (window != null) {
+ // On open, register the part listener to the window
+ if (window.getPartService() != null && partListener != null) {
+ // Get the part service
+ IPartService service = window.getPartService();
+ // Unregister the part listener, just in case
+ service.removePartListener(partListener);
+ // Register the part listener
+ service.addPartListener(partListener);
+ // Signal the active part to the part listener after registration
+ IWorkbenchPage page = window.getActivePage();
+ if (page != null) {
+ IWorkbenchPartReference partRef = page.getActivePartReference();
+ if (partRef != null) partListener.partActivated(partRef);
+ }
+ }
+
+ // Register the perspective listener
+ if (perspectiveListener != null) {
+ window.addPerspectiveListener(perspectiveListener);
+ // Signal the active perspective to the perspective listener after registration
+ if (window.getActivePage() != null) {
+ perspectiveListener.perspectiveActivated(window.getActivePage(), window.getActivePage().getPerspective());
+ }
+ }
+
+ initialized = true;
+ }
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchPartListener.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchPartListener.java
new file mode 100644
index 000000000..017b19271
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchPartListener.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.listeners;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.contexts.IContextActivation;
+import org.eclipse.ui.contexts.IContextService;
+
+/**
+ * The part listener implementation. Takes care of
+ * activation and deactivation of key binding contexts.
+ */
+public class WorkbenchPartListener implements IPartListener2 {
+
+ // The context activations per workbench part reference
+ private final Map<IWorkbenchPartReference, IContextActivation> activations = new HashMap<IWorkbenchPartReference, IContextActivation>();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partBroughtToTop(IWorkbenchPartReference partRef) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partOpened(IWorkbenchPartReference partRef) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partClosed(IWorkbenchPartReference partRef) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partVisible(IWorkbenchPartReference partRef) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partHidden(IWorkbenchPartReference partRef) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partActivated(IWorkbenchPartReference partRef) {
+ if ("org.eclipse.tcf.te.ui.terminals.TerminalsView".equals(partRef.getId())) { //$NON-NLS-1$
+ IWorkbenchPart part = partRef.getPart(false);
+ if (part != null && part.getSite() != null) {
+ IContextService service = (IContextService)part.getSite().getService(IContextService.class);
+ if (service != null) {
+ IContextActivation activation = service.activateContext(partRef.getId());
+ if (activation != null) {
+ activations.put(partRef, activation);
+ } else {
+ activations.remove(partRef);
+ }
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partDeactivated(IWorkbenchPartReference partRef) {
+ if ("org.eclipse.tcf.te.ui.terminals.TerminalsView".equals(partRef.getId())) { //$NON-NLS-1$
+ IWorkbenchPart part = partRef.getPart(false);
+ if (part != null && part.getSite() != null) {
+ IContextService service = (IContextService)part.getSite().getService(IContextService.class);
+ if (service != null) {
+ IContextActivation activation = activations.remove(partRef);
+ if (activation != null) {
+ service.deactivateContext(activation);
+ }
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ @Override
+ public void partInputChanged(IWorkbenchPartReference partRef) {
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchWindowListener.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchWindowListener.java
new file mode 100644
index 000000000..21349a954
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/listeners/WorkbenchWindowListener.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.listeners;
+
+import org.eclipse.ui.IPartListener2;
+
+/**
+ * The window listener implementation. Takes care of the
+ * management of the global listeners per workbench window.
+ */
+public class WorkbenchWindowListener extends AbstractWindowListener {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.views.listeners.AbstractWindowListener#createPartListener()
+ */
+ @Override
+ protected IPartListener2 createPartListener() {
+ return new WorkbenchPartListener();
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/manager/ConsoleManager.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/manager/ConsoleManager.java
new file mode 100644
index 000000000..e716c6237
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/manager/ConsoleManager.java
@@ -0,0 +1,641 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361363] [TERMINALS] Implement "Pin&Clone" for the "Terminals" view
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.manager;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IPreferenceKeys;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IUIConstants;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderManager;
+import org.eclipse.tcf.te.ui.terminals.view.TerminalsView;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PerspectiveAdapter;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Terminals console manager.
+ */
+@SuppressWarnings("restriction")
+public class ConsoleManager {
+
+ // Constant to indicate any secondary id is acceptable
+ private final static String ANY_SECONDARY_ID = new String("*"); //$NON-NLS-1$
+
+ // Reference to the perspective listener instance
+ private final IPerspectiveListener perspectiveListener;
+
+ // Internal perspective listener implementation
+ static class ConsoleManagerPerspectiveListener extends PerspectiveAdapter {
+ private final List<IViewReference> references = new ArrayList<IViewReference>();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.PerspectiveAdapter#perspectiveActivated(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor)
+ */
+ @Override
+ public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
+ // If the old references list is empty, just return
+ if (references.isEmpty()) return;
+ // Create a copy of the old view references list
+ List<IViewReference> oldReferences = new ArrayList<IViewReference>(references);
+
+ // Get the current list of view references
+ List<IViewReference> references = new ArrayList<IViewReference>(Arrays.asList(page.getViewReferences()));
+ for (IViewReference reference : oldReferences) {
+ if (references.contains(reference)) continue;
+ // Previous visible terminals console view reference, make visible again
+ try {
+ page.showView(reference.getId(), reference.getSecondaryId(), IWorkbenchPage.VIEW_VISIBLE);
+ } catch (PartInitException e) { /* Failure on part instantiation is ignored */ }
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.PerspectiveAdapter#perspectivePreDeactivate(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor)
+ */
+ @Override
+ public void perspectivePreDeactivate(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
+ references.clear();
+ for (IViewReference reference : page.getViewReferences()) {
+ IViewPart part = reference.getView(false);
+ if (part instanceof TerminalsView && !references.contains(reference)) {
+ references.add(reference);
+ }
+ }
+ }
+ }
+
+ /*
+ * Thread save singleton instance creation.
+ */
+ private static class LazyInstanceHolder {
+ public static ConsoleManager fInstance = new ConsoleManager();
+ }
+
+ /**
+ * Returns the singleton instance for the console manager.
+ */
+ public static ConsoleManager getInstance() {
+ return LazyInstanceHolder.fInstance;
+ }
+
+ /**
+ * Constructor.
+ */
+ ConsoleManager() {
+ super();
+
+ // Attach the perspective listener
+ perspectiveListener = new ConsoleManagerPerspectiveListener();
+ if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) {
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().addPerspectiveListener(perspectiveListener);
+ }
+ }
+
+ /**
+ * Returns the active workbench window page if the workbench is still running.
+ *
+ * @return The active workbench window page or <code>null</code>
+ */
+ private final IWorkbenchPage getActiveWorkbenchPage() {
+ // To lookup the console view, the workbench must be still running
+ if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) {
+ return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the console view if available within the active workbench window page.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ *
+ * @param id The terminals console view id or <code>null</code> to show the default terminals console view.
+ * @param secondaryId The terminals console view secondary id or <code>null</code>.
+ *
+ * @return The console view instance if available or <code>null</code> otherwise.
+ */
+ public ITerminalsView findConsoleView(String id, String secondaryId) {
+ Assert.isNotNull(Display.findDisplay(Thread.currentThread()));
+
+ ITerminalsView view = null;
+
+ // Get the active workbench page
+ IWorkbenchPage page = getActiveWorkbenchPage();
+ if (page != null) {
+ // Look for the view
+ IViewPart part = getTerminalsViewWithSecondaryId(id != null ? id : IUIConstants.ID, secondaryId);
+ // Check the interface
+ if (part instanceof ITerminalsView) {
+ view = (ITerminalsView)part;
+ }
+ }
+
+ return view;
+ }
+
+ /**
+ * Search and return a terminal view with a specific secondary id
+ *
+ * @param id The terminals console view id. Must not be <code>null</code>.
+ * @param secondaryId The terminals console view secondary id or <code>null</code>.
+ *
+ * @return The terminals console view instance or <code>null</code> if not found.
+ */
+ private IViewPart getTerminalsViewWithSecondaryId(String id, String secondaryId) {
+ Assert.isNotNull(id);
+
+ IWorkbenchPage page = getActiveWorkbenchPage();
+
+ IViewReference[] refs = page.getViewReferences();
+ for (int i = 0; i < refs.length; i++) {
+ IViewReference ref = refs[i];
+ if (ref.getId().equals(id)) {
+ IViewPart part = ref.getView(true);
+ if (part instanceof ITerminalsView) {
+ String secId = ((IViewSite) part.getSite()).getSecondaryId();
+ if (secId != null && secId.equals(secondaryId) || secId == null && secondaryId == null) {
+ return part;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Search and return a terminal view that is NOT pinned
+ *
+ * @param id The terminals console view id. Must not be <code>null</code>.
+ * @return The terminals console view instance or <code>null</code> if not found.
+ */
+ private IViewPart getFirstNotPinnedTerminalsView(String id) {
+ Assert.isNotNull(id);
+
+ IWorkbenchPage page = getActiveWorkbenchPage();
+
+ IViewReference[] refs = page.getViewReferences();
+ for (int i = 0; i < refs.length; i++) {
+ IViewReference ref = refs[i];
+ if (ref.getId().equals(id)) {
+ IViewPart part = ref.getView(true);
+ if (part instanceof ITerminalsView) {
+ if (!((ITerminalsView) part).isPinned()) {
+ return part;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Search and return the first available terminal view.
+ *
+ * @param id The terminals console view id. Must not be <code>null</code>.
+ * @param secondaryId The terminals console view secondary id or <code>null</code>.
+ * @param useActive - return only an active terminal view.
+ *
+ * @return The terminals console view instance or <code>null</code> if not found.
+ */
+ private IViewPart getFirstTerminalsView(String id, String secondaryId, boolean useActive) {
+ Assert.isNotNull(id);
+
+ IWorkbenchPage page = getActiveWorkbenchPage();
+
+ IViewReference[] refs = page.getViewReferences();
+ for (int i = 0; i < refs.length; i++) {
+ IViewReference ref = refs[i];
+ if (ref.getId().equals(id)) {
+ if (secondaryId == ANY_SECONDARY_ID
+ || secondaryId == null && ref.getSecondaryId() == null
+ || secondaryId != null && secondaryId.equals(ref.getSecondaryId())) {
+ IViewPart part = ref.getView(true);
+ if (useActive) {
+ if (page.isPartVisible(part)) {
+ return part;
+ }
+ }
+ else {
+ return part;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return a new secondary id to use, based on the number of open terminal views.
+ *
+ * @param id The terminals console view id. Must not be <code>null</code>.
+ * @return The next secondary id, or <code>null</code> if it is the first one
+ */
+ private String getNextTerminalSecondaryId(String id) {
+ Assert.isNotNull(id);
+
+ IWorkbenchPage page = getActiveWorkbenchPage();
+ Map<String, IViewReference> terminalViews = new HashMap<String, IViewReference>();
+
+ int maxNumber = 0;
+ IViewReference[] refs = page.getViewReferences();
+ for (int i = 0; i < refs.length; i++) {
+ IViewReference ref = refs[i];
+ if (ref.getId().equals(id)) {
+ if (ref.getSecondaryId() != null) {
+ terminalViews.put(ref.getSecondaryId(), ref);
+ int scondaryIdInt = Integer.parseInt(ref.getSecondaryId());
+ if (scondaryIdInt > maxNumber) {
+ maxNumber = scondaryIdInt;
+ }
+ }
+ else {
+ // add the one with secondaryId == null with 0 by default
+ terminalViews.put(Integer.toString(0), ref);
+ }
+ }
+ }
+ if (terminalViews.size() == 0) {
+ return null;
+ }
+
+ int i = 0;
+ for (; i < maxNumber; i++) {
+ String secondaryIdStr = Integer.toString(i);
+ if (!terminalViews.keySet().contains(secondaryIdStr)) {
+ // found a free slot
+ if (i == 0)
+ return null;
+ return Integer.toString(i);
+ }
+ }
+ // add a new one
+ return Integer.toString(i + 1);
+ }
+
+ /**
+ * Show the terminals console view specified by the given id.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ *
+ * @param id The terminals console view id or <code>null</code> to show the default terminals console view.
+ */
+ public IViewPart showConsoleView(String id, String secondaryId) {
+ Assert.isNotNull(Display.findDisplay(Thread.currentThread()));
+
+ // Get the active workbench page
+ IWorkbenchPage page = getActiveWorkbenchPage();
+ if (page != null) {
+ try {
+ // show the view
+ IViewPart part = page.showView(id != null ? id : IUIConstants.ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
+ // and force the view to the foreground
+ page.bringToTop(part);
+ return part;
+ }
+ catch (PartInitException e) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Bring the terminals console view, specified by the given id, to the top of the view stack.
+ *
+ * @param id The terminals console view id or <code>null</code> to show the default terminals console view.
+ * @param secondaryId The terminals console view secondary id or <code>null</code>.
+ * @param activate If <code>true</code> activate the console view.
+ */
+ private IViewPart bringToTop(String id, String secondaryId, boolean activate) {
+ // Get the active workbench page
+ IWorkbenchPage page = getActiveWorkbenchPage();
+ if (page != null) {
+ // Look for any terminal view
+ IViewPart anyTerminal = getFirstTerminalsView(id != null ? id : IUIConstants.ID, secondaryId, false);
+ // there is at least one terminal available
+ if (anyTerminal != null) {
+ // is there an active terminal view
+ IViewPart activePart = getFirstTerminalsView(id != null ? id : IUIConstants.ID, secondaryId, true);
+ // no terminal view active
+ if (activePart == null) {
+ // use the first not pinned
+ IViewPart notPinnedPart = getFirstNotPinnedTerminalsView(id != null ? id : IUIConstants.ID);
+ if (notPinnedPart != null) {
+ if (activate) {
+ page.activate(notPinnedPart);
+ }
+ else {
+ page.bringToTop(notPinnedPart);
+ }
+ return notPinnedPart;
+ }
+ // else we need to create a new one
+ IViewPart newPart = showConsoleView(id != null ? id : IUIConstants.ID, getNextTerminalSecondaryId(id != null ? id : IUIConstants.ID));
+ return newPart;
+ }
+ // we found a active terminal page
+ // if it is pinned search for a non pinned (not active)
+ if (((ITerminalsView) activePart).isPinned() && secondaryId == ANY_SECONDARY_ID) {
+ // we found one so use it
+ IViewPart notPinnedPart = getFirstNotPinnedTerminalsView(id != null ? id : IUIConstants.ID);
+ if (notPinnedPart != null) {
+ if (activate) {
+ page.activate(notPinnedPart);
+ }
+ else {
+ page.bringToTop(notPinnedPart);
+ }
+ return notPinnedPart;
+ }
+ // else we need to create a new one
+ IViewPart newPart = showConsoleView(id != null ? id : IUIConstants.ID, getNextTerminalSecondaryId(id != null ? id : IUIConstants.ID));
+ return newPart;
+ }
+ // else return the active one
+ return activePart;
+ }
+ // create first new terminal
+ if (activate) {
+ IViewPart newPart = showConsoleView(id != null ? id : IUIConstants.ID, getNextTerminalSecondaryId(id != null ? id : IUIConstants.ID));
+ return newPart;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Opens the console with the given title and connector.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ *
+ * @param id The terminals console view id or <code>null</code> to show the default terminals console view.
+ * @param title The console title. Must not be <code>null</code>.
+ * @param encoding The terminal encoding or <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ * @param flags The flags controlling how the console is opened or <code>null</code> to use defaults.
+ */
+ public CTabItem openConsole(String id, String title, String encoding, ITerminalConnector connector, Object data, Map<String, Boolean> flags) {
+ return openConsole(id, ANY_SECONDARY_ID, title, encoding, connector, data, flags);
+ }
+
+ /**
+ * Opens the console with the given title and connector.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ *
+ * @param id The terminals console view id or <code>null</code> to show the default terminals console view.
+ * @param secondaryId The terminals console view secondary id or <code>null</code>.
+ * @param title The console title. Must not be <code>null</code>.
+ * @param encoding The terminal encoding or <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ * @param flags The flags controlling how the console is opened or <code>null</code> to use defaults.
+ */
+ public CTabItem openConsole(String id, String secondaryId, String title, String encoding, ITerminalConnector connector, Object data, Map<String, Boolean> flags) {
+ Assert.isNotNull(title);
+ Assert.isNotNull(connector);
+ Assert.isNotNull(Display.findDisplay(Thread.currentThread()));
+
+ // Get the flags handled by the openConsole method itself
+ boolean activate = flags != null && flags.containsKey("activate") ? flags.get("activate").booleanValue() : false; //$NON-NLS-1$ //$NON-NLS-2$
+ boolean forceNew = flags != null && flags.containsKey(ITerminalsConnectorConstants.PROP_FORCE_NEW) ? flags.get(ITerminalsConnectorConstants.PROP_FORCE_NEW).booleanValue() : false;
+
+ // Make the consoles view visible
+ IViewPart part = bringToTop(id, secondaryId, activate);
+ if (!(part instanceof ITerminalsView)) return null;
+ // Cast to the correct type
+ ITerminalsView view = (ITerminalsView)part;
+
+ // Get the tab folder manager associated with the view
+ TabFolderManager manager = (TabFolderManager)view.getAdapter(TabFolderManager.class);
+ if (manager == null) return null;
+
+ // Lookup an existing console first
+ String secId = ((IViewSite)part.getSite()).getSecondaryId();
+ CTabItem item = findConsole(id, secId, title, connector, data);
+
+ // Switch to the tab folder page _before_ calling TabFolderManager#createItem(...).
+ // The createItem(...) method invokes the corresponding connect and this may take
+ // a while if connecting to a remote host. To allow a "Connecting..." decoration,
+ // the tab folder page needs to be visible.
+ view.switchToTabFolderControl();
+
+ // If no existing console exist or forced -> Create the tab item
+ if (item == null || forceNew) {
+ // If configured, check all existing tab items if they are associated
+ // with terminated consoles
+ if (UIPlugin.getScopedPreferences().getBoolean(IPreferenceKeys.PREF_REMOVE_TERMINATED_TERMINALS)) {
+ // Remote all terminated tab items. This will invoke the
+ // tab's dispose listener.
+ manager.removeTerminatedItems();
+ // Switch back to the tab folder control as removeTerminatedItems()
+ // may have triggered the switch to the empty space control.
+ view.switchToTabFolderControl();
+ }
+
+ // Create a new tab item
+ item = manager.createTabItem(title, encoding, connector, data, flags);
+ }
+ // If still null, something went wrong
+ if (item == null) return null;
+
+ // Make the item the active console
+ manager.bringToTop(item);
+
+ // Make sure the terminals view has the focus after opening a new terminal
+ view.setFocus();
+
+ // Return the tab item of the opened console
+ return item;
+ }
+
+ /**
+ * Lookup a console with the given title and the given terminal connector.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ * <b>Note:</b> The method will handle unified console titles itself.
+ *
+ * @param id The terminals console view id or <code>null</code> to show the default terminals console view.
+ * @param secondaryId The terminals console view secondary id or <code>null</code>.
+ * @param title The console title. Must not be <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ *
+ * @return The corresponding console tab item or <code>null</code>.
+ */
+ public CTabItem findConsole(String id, String secondaryId, String title, ITerminalConnector connector, Object data) {
+ Assert.isNotNull(title);
+ Assert.isNotNull(connector);
+ Assert.isNotNull(Display.findDisplay(Thread.currentThread()));
+
+ // Get the console view
+ ITerminalsView view = findConsoleView(id, secondaryId);
+ if (view == null) return null;
+
+ // Get the tab folder manager associated with the view
+ TabFolderManager manager = (TabFolderManager)view.getAdapter(TabFolderManager.class);
+ if (manager == null) return null;
+
+ return manager.findTabItem(title, connector, data);
+ }
+
+ /**
+ * Lookup a console which is assigned with the given terminal control.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ *
+ * @param control The terminal control. Must not be <code>null</code>.
+ * @return The corresponding console tab item or <code>null</code>.
+ */
+ public CTabItem findConsole(ITerminalControl control) {
+ Assert.isNotNull(control);
+
+ CTabItem item = null;
+
+ IWorkbenchPage page = getActiveWorkbenchPage();
+ if (page != null) {
+ IViewReference[] refs = page.getViewReferences();
+ for (int i = 0; i < refs.length; i++) {
+ IViewReference ref = refs[i];
+ IViewPart part = ref != null ? ref.getView(false) : null;
+ if (part instanceof ITerminalsView) {
+ CTabFolder tabFolder = (CTabFolder) part.getAdapter(CTabFolder.class);
+ if (tabFolder == null) continue;
+ CTabItem[] candidates = tabFolder.getItems();
+ for (CTabItem candidate : candidates) {
+ Object data = candidate.getData();
+ if (data instanceof ITerminalControl && control.equals(data)) {
+ item = candidate;
+ break;
+ }
+ }
+ }
+ if (item != null) break;
+ }
+ }
+
+ return item;
+ }
+
+ /**
+ * Search all console views for the one that contains a specific connector.
+ * <p>
+ * <b>Note:</b> The method will handle unified console titles itself.
+ *
+ * @param id The terminals console view id or <code>null</code> to show the default terminals console view.
+ * @param title The console title. Must not be <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ *
+ * @return The corresponding console tab item or <code>null</code>.
+ */
+ private CTabItem findConsoleForTerminalConnector(String id, String title, ITerminalConnector connector, Object data) {
+ Assert.isNotNull(title);
+ Assert.isNotNull(connector);
+
+ IWorkbenchPage page = getActiveWorkbenchPage();
+ if (page != null) {
+ IViewReference[] refs = page.getViewReferences();
+ for (int i = 0; i < refs.length; i++) {
+ IViewReference ref = refs[i];
+ if (ref.getId().equals(id)) {
+ IViewPart part = ref.getView(true);
+ if (part instanceof ITerminalsView) {
+ // Get the tab folder manager associated with the view
+ TabFolderManager manager = (TabFolderManager) part.getAdapter(TabFolderManager.class);
+ if (manager == null) {
+ continue;
+ }
+ CTabItem item = manager.findTabItem(title, connector, data);
+ if (item != null) {
+ return item;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Close the console with the given title and the given terminal connector.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ * <b>Note:</b> The method will handle unified console titles itself.
+ *
+ * @param title The console title. Must not be <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ */
+ public void closeConsole(String id, String title, ITerminalConnector connector, Object data) {
+ Assert.isNotNull(title);
+ Assert.isNotNull(connector);
+ Assert.isNotNull(Display.findDisplay(Thread.currentThread()));
+
+ // Lookup the console with this connector
+ CTabItem console = findConsoleForTerminalConnector(id, title, connector, data);
+ // If found, dispose the console
+ if (console != null) {
+ console.dispose();
+ }
+ }
+
+ /**
+ * Terminate (disconnect) the console with the given title and the given terminal connector.
+ * <p>
+ * <b>Note:</b> The method must be called within the UI thread.
+ * <b>Note:</b> The method will handle unified console titles itself.
+ *
+ * @param title The console title. Must not be <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ */
+ public void terminateConsole(String id, String title, ITerminalConnector connector, Object data) {
+ Assert.isNotNull(title);
+ Assert.isNotNull(connector);
+ Assert.isNotNull(Display.findDisplay(Thread.currentThread()));
+
+ // Lookup the console
+ CTabItem console = findConsoleForTerminalConnector(id, title, connector, data);
+ // If found, disconnect the console
+ if (console != null && !console.isDisposed()) {
+ ITerminalViewControl terminal = (ITerminalViewControl)console.getData();
+ if (terminal != null && !terminal.isDisposed()) {
+ terminal.disconnectTerminal();
+ }
+ }
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.java
new file mode 100644
index 000000000..0d47192eb
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2011 - 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361363] [TERMINALS] Implement "Pin&Clone" for the "Terminals" view
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.nls;
+
+import java.lang.reflect.Field;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Terminals plug-in externalized strings management.
+ */
+public class Messages extends NLS {
+
+ // The plug-in resource bundle name
+ private static final String BUNDLE_NAME = "org.eclipse.tcf.te.ui.terminals.nls.Messages"; //$NON-NLS-1$
+
+ /**
+ * Static constructor.
+ */
+ static {
+ // Load message values from bundle file
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ /**
+ * Returns the corresponding string for the given externalized strings
+ * key or <code>null</code> if the key does not exist.
+ *
+ * @param key The externalized strings key or <code>null</code>.
+ * @return The corresponding string or <code>null</code>.
+ */
+ public static String getString(String key) {
+ if (key != null) {
+ try {
+ Field field = Messages.class.getDeclaredField(key);
+ return (String)field.get(null);
+ } catch (Exception e) { /* ignored on purpose */ }
+ }
+
+ return null;
+ }
+
+ // **** Declare externalized string id's down here *****
+
+ public static String Extension_error_missingRequiredAttribute;
+ public static String Extension_error_duplicateExtension;
+ public static String Extension_error_invalidExtensionPoint;
+
+ public static String AbstractTriggerCommandHandler_error_executionFailed;
+
+ public static String AbstractAction_error_commandExecutionFailed;
+
+ public static String AbstractConfigurationPanel_delete;
+ public static String AbstractConfigurationPanel_deleteButtonTooltip;
+ public static String AbstractConfigurationPanel_hosts;
+ public static String AbstractConfigurationPanel_encoding;
+ public static String AbstractConfigurationPanel_encoding_custom;
+ public static String AbstractConfigurationPanel_encoding_custom_title;
+ public static String AbstractConfigurationPanel_encoding_custom_message;
+ public static String AbstractConfigurationPanel_encoding_custom_error;
+
+ public static String TabTerminalListener_consoleClosed;
+ public static String TabTerminalListener_consoleConnecting;
+
+ public static String PinTerminalAction_menu;
+ public static String PinTerminalAction_toolTip;
+
+ public static String ToggleCommandFieldAction_menu;
+ public static String ToggleCommandFieldAction_toolTip;
+
+ public static String SelectEncodingAction_menu;
+ public static String SelectEncodingAction_tooltip;
+
+ public static String ProcessSettingsPage_dialogTitle;
+ public static String ProcessSettingsPage_processImagePathSelectorControl_label;
+ public static String ProcessSettingsPage_processImagePathSelectorControl_button;
+ public static String ProcessSettingsPage_processArgumentsControl_label;
+ public static String ProcessSettingsPage_processWorkingDirControl_label;
+ public static String ProcessSettingsPage_localEchoSelectorControl_label;
+
+ public static String OutputStreamMonitor_error_readingFromStream;
+
+ public static String InputStreamMonitor_error_writingToStream;
+
+ public static String TerminalService_error_cannotCreateConnector;
+ public static String TerminalService_defaultTitle;
+
+ public static String LaunchTerminalSettingsDialog_title;
+ public static String LaunchTerminalSettingsDialog_combo_label;
+ public static String LaunchTerminalSettingsDialog_group_label;
+
+ public static String TabScrollLockAction_text;
+ public static String TabScrollLockAction_tooltip;
+
+ public static String LaunchTerminalSettingsDialog_error_title;
+ public static String LaunchTerminalSettingsDialog_error_invalidSettings;
+ public static String LaunchTerminalSettingsDialog_error_unknownReason;
+
+ public static String EncodingSelectionDialog_title;
+
+ public static String TabFolderManager_encoding;
+ public static String TabFolderManager_state_connected;
+ public static String TabFolderManager_state_connecting;
+ public static String TabFolderManager_state_closed;
+
+ public static String NoteCompositeHelper_note_label;
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.properties b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.properties
new file mode 100644
index 000000000..f7a77a435
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/nls/Messages.properties
@@ -0,0 +1,75 @@
+###############################################################################
+# Copyright (c) 2012 - 2015 Wind River Systems, Inc. and others. All rights reserved.
+# This program and the accompanying materials are made available under the terms
+# of the Eclipse Public License v1.0 which accompanies this distribution, and is
+# available at http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Wind River Systems - initial API and implementation
+###############################################################################
+
+Extension_error_missingRequiredAttribute=Required attribute "{0}" missing for extension "{1}"!
+Extension_error_duplicateExtension=Duplicate extension with id ''{0}''. Ignoring duplicated contribution from contributor ''{1}''!
+Extension_error_invalidExtensionPoint=Failed to instantiate the executable extension from extension point ''{0}''.
+
+AbstractTriggerCommandHandler_error_executionFailed=Failed to execute command {0}.
+
+AbstractAction_error_commandExecutionFailed="Failed to execute command (id = {0}). Possibly caused by: {1}
+
+AbstractConfigurationPanel_delete=Delete
+AbstractConfigurationPanel_deleteButtonTooltip=Delete terminal settings for host
+AbstractConfigurationPanel_hosts=Hosts:
+AbstractConfigurationPanel_encoding=Encoding:
+AbstractConfigurationPanel_encoding_custom=Other...
+AbstractConfigurationPanel_encoding_custom_title=Other...
+AbstractConfigurationPanel_encoding_custom_message=Please enter the name of the encoding to use for the terminal.
+AbstractConfigurationPanel_encoding_custom_error=Unsupported encoding. Please enter the name of a supported encoding.
+
+TabTerminalListener_consoleClosed=<{1}> {0}
+TabTerminalListener_consoleConnecting={0} : {1}...
+
+PinTerminalAction_menu=Pin
+PinTerminalAction_toolTip=Pin the Terminal View
+
+ToggleCommandFieldAction_menu=Toggle Command Input Field
+ToggleCommandFieldAction_toolTip=Toggle Command Input Field
+
+SelectEncodingAction_menu=Switch Encoding...
+SelectEncodingAction_tooltip=Switch the Encoding of the active Terminal
+
+ProcessSettingsPage_dialogTitle=Select Process Image
+ProcessSettingsPage_processImagePathSelectorControl_label=Image Path:
+ProcessSettingsPage_processImagePathSelectorControl_button=Browse
+ProcessSettingsPage_processArgumentsControl_label=Arguments:
+ProcessSettingsPage_processWorkingDirControl_label=Working Dir:
+ProcessSettingsPage_localEchoSelectorControl_label=Local Echo
+
+OutputStreamMonitor_error_readingFromStream=Exception when reading from stream. Possibly caused by: {0}
+
+InputStreamMonitor_error_writingToStream=Exception when writing to stream. Possibly caused by: {0}
+
+TerminalService_error_cannotCreateConnector=Cannot create a valid terminal connector instance from the provided terminal properties.
+TerminalService_defaultTitle=Console
+
+LaunchTerminalSettingsDialog_title=Launch Terminal
+LaunchTerminalSettingsDialog_combo_label=Choose terminal:
+LaunchTerminalSettingsDialog_group_label=Settings
+
+TabScrollLockAction_text=Scroll &Lock
+TabScrollLockAction_tooltip=Scroll Lock
+
+LaunchTerminalSettingsDialog_error_title=Terminal Settings
+LaunchTerminalSettingsDialog_error_invalidSettings=The specified settings are invalid\n\n\
+{0}\n\n\
+Please review and specify valid settings. Or cancel the settings dialog to abort.
+LaunchTerminalSettingsDialog_error_unknownReason=Cannot determine specifically which setting is invalid.
+
+EncodingSelectionDialog_title=Encoding
+
+TabFolderManager_encoding=Encoding: {0}
+TabFolderManager_state_connected=Connected
+TabFolderManager_state_connecting=Connecting
+TabFolderManager_state_closed=Closed
+
+NoteCompositeHelper_note_label=Note:
+
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractConfigurationPanel.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractConfigurationPanel.java
new file mode 100644
index 000000000..8ee9b2506
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractConfigurationPanel.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.panels;
+
+import java.util.Map;
+
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer;
+
+/**
+ * Abstract terminal launcher configuration panel implementation.
+ */
+public abstract class AbstractConfigurationPanel implements IConfigurationPanel {
+ private final IConfigurationPanelContainer container;
+
+ private Composite topControl = null;
+
+ // The selection
+ private ISelection selection;
+
+ private String message = null;
+ private int messageType = IMessageProvider.NONE;
+
+ private boolean enabled = true;
+
+ /**
+ * Constructor.
+ *
+ * @param container The configuration panel container or <code>null</code>.
+ */
+ public AbstractConfigurationPanel(IConfigurationPanelContainer container) {
+ super();
+ this.container = container;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#getContainer()
+ */
+ @Override
+ public IConfigurationPanelContainer getContainer() {
+ return container;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage()
+ */
+ @Override
+ public final String getMessage() {
+ return message;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType()
+ */
+ @Override
+ public final int getMessageType() {
+ return messageType;
+ }
+
+ /**
+ * Set the message and the message type to display.
+ *
+ * @param message The message or <code>null</code>.
+ * @param messageType The message type or <code>IMessageProvider.NONE</code>.
+ */
+ protected final void setMessage(String message, int messageType) {
+ this.message = message;
+ this.messageType = messageType;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#dispose()
+ */
+ @Override
+ public void dispose() {
+ }
+
+ /**
+ * Sets the top control.
+ *
+ * @param topControl The top control or <code>null</code>.
+ */
+ protected void setControl(Composite topControl) {
+ this.topControl = topControl;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#getControl()
+ */
+ @Override
+ public Composite getControl() {
+ return topControl;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#setSelection(org.eclipse.jface.viewers.ISelection)
+ */
+ @Override
+ public void setSelection(ISelection selection) {
+ this.selection = selection;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#getSelection()
+ */
+ @Override
+ public ISelection getSelection() {
+ return selection;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#doRestoreWidgetValues(org.eclipse.jface.dialogs.IDialogSettings, java.lang.String)
+ */
+ @Override
+ public void doRestoreWidgetValues(IDialogSettings settings, String idPrefix) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#doSaveWidgetValues(org.eclipse.jface.dialogs.IDialogSettings, java.lang.String)
+ */
+ @Override
+ public void doSaveWidgetValues(IDialogSettings settings, String idPrefix) {
+ }
+
+ /**
+ * Returns the correctly prefixed dialog settings slot id. In case the given id
+ * suffix is <code>null</code> or empty, <code>id</code> is returned as is.
+ *
+ * @param settingsSlotId The dialog settings slot id to prefix.
+ * @param prefix The prefix.
+ * @return The correctly prefixed dialog settings slot id.
+ */
+ public final String prefixDialogSettingsSlotId(String settingsSlotId, String prefix) {
+ if (settingsSlotId != null && prefix != null && prefix.trim().length() > 0) {
+ settingsSlotId = prefix + "." + settingsSlotId; //$NON-NLS-1$
+ }
+ return settingsSlotId;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#setEnabled(boolean)
+ */
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * @return Returns the enabled state.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#isValid()
+ */
+ @Override
+ public boolean isValid() {
+ setMessage(null, NONE);
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#activate()
+ */
+ @Override
+ public void activate() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#extractData(java.util.Map)
+ */
+ @Override
+ public void extractData(Map<String, Object> data) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#setupData(java.util.Map)
+ */
+ @Override
+ public void setupData(Map<String, Object> data) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel#updateData(java.util.Map)
+ */
+ @Override
+ public void updateData(Map<String, Object> data) {
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractExtendedConfigurationPanel.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractExtendedConfigurationPanel.java
new file mode 100644
index 000000000..63cd09311
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/panels/AbstractExtendedConfigurationPanel.java
@@ -0,0 +1,615 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.panels;
+
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.tcf.te.core.terminals.TerminalContextPropertiesProviderFactory;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalContextPropertiesProvider;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.IContextPropertiesConstants;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.WorkbenchEncoding;
+import org.osgi.framework.Bundle;
+
+/**
+ * Abstract terminal configuration panel implementation.
+ */
+public abstract class AbstractExtendedConfigurationPanel extends AbstractConfigurationPanel {
+ private static final String LAST_HOST_TAG = "lastHost";//$NON-NLS-1$
+ private static final String HOSTS_TAG = "hosts";//$NON-NLS-1$
+ private static final String ENCODINGS_TAG = "encodings"; //$NON-NLS-1$
+
+ // The sub-controls
+ /* default */ Combo hostCombo;
+ private Button deleteHostButton;
+ /* default */ Combo encodingCombo;
+
+ // The last selected encoding
+ /* default */ String lastSelectedEncoding;
+ // The last entered custom encodings
+ /* default */ final List<String> encodingHistory = new ArrayList<String>();
+
+ // A map containing the settings per host
+ protected final Map<String, Map<String, String>> hostSettingsMap = new HashMap<String, Map<String, String>>();
+
+ /**
+ * Constructor.
+ *
+ * @param container The configuration panel container or <code>null</code>.
+ */
+ public AbstractExtendedConfigurationPanel(IConfigurationPanelContainer container) {
+ super(container);
+ }
+
+ /**
+ * Returns the host name or IP from the current selection.
+ *
+ * @return The host name or IP, or <code>null</code>.
+ */
+ public String getSelectionHost() {
+ ISelection selection = getSelection();
+ if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+ Object element = ((IStructuredSelection) selection).getFirstElement();
+ ITerminalContextPropertiesProvider provider = TerminalContextPropertiesProviderFactory.getProvider(element);
+ if (provider != null) {
+ Map<String, String> props = provider.getTargetAddress(element);
+ if (props != null && props.containsKey(IContextPropertiesConstants.PROP_ADDRESS)) {
+ return props.get(IContextPropertiesConstants.PROP_ADDRESS);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the default encoding based on the current selection.
+ *
+ * @return The default encoding or <code>null</code>.
+ */
+ public String getSelectionEncoding() {
+ ISelection selection = getSelection();
+ if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+ Object element = ((IStructuredSelection) selection).getFirstElement();
+ ITerminalContextPropertiesProvider provider = TerminalContextPropertiesProviderFactory.getProvider(element);
+ if (provider != null) {
+ Object encoding = provider.getProperty(element, IContextPropertiesConstants.PROP_DEFAULT_ENCODING);
+ if (encoding instanceof String) return ((String) encoding).trim();
+ }
+ }
+
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.controls.panels.AbstractWizardConfigurationPanel#doRestoreWidgetValues(org.eclipse.jface.dialogs.IDialogSettings, java.lang.String)
+ */
+ @Override
+ public void doRestoreWidgetValues(IDialogSettings settings, String idPrefix) {
+ Assert.isNotNull(settings);
+
+ String[] hosts = settings.getArray(HOSTS_TAG);
+ if (hosts != null) {
+ for (int i = 0; i < hosts.length; i++) {
+ String hostEntry = hosts[i];
+ String[] hostString = hostEntry.split("\\|");//$NON-NLS-1$
+ String hostName = hostString[0];
+ if (hostString.length == 2) {
+ HashMap<String, String> attr = deSerialize(hostString[1]);
+ hostSettingsMap.put(hostName, attr);
+ }
+ else {
+ hostSettingsMap.put(hostName, new HashMap<String, String>());
+ }
+ }
+ }
+
+ if (!isWithoutSelection()) {
+ String host = getSelectionHost();
+ if (host != null) {
+ fillSettingsForHost(host);
+ }
+ }
+ else {
+ if (hostCombo != null) {
+ fillHostCombo();
+ String lastHost = settings.get(LAST_HOST_TAG);
+ if (lastHost != null) {
+ int index = hostCombo.indexOf(lastHost);
+ if (index != -1) {
+ hostCombo.select(index);
+ }
+ else {
+ hostCombo.select(0);
+ }
+ }
+ else {
+ hostCombo.select(0);
+ }
+ fillSettingsForHost(hostCombo.getText());
+ }
+ }
+
+ encodingHistory.clear();
+ String[] encodings = settings.getArray(ENCODINGS_TAG);
+ if (encodings != null && encodings.length > 0) {
+ encodingHistory.addAll(Arrays.asList(encodings));
+ for (String encoding : encodingHistory) {
+ encodingCombo.add(encoding, encodingCombo.getItemCount() - 1);
+ }
+ }
+ }
+
+ /**
+ * Restore the encodings widget values.
+ *
+ * @param settings The dialog settings. Must not be <code>null</code>.
+ * @param idPrefix The prefix or <code>null</code>.
+ */
+ protected void doRestoreEncodingsWidgetValues(IDialogSettings settings, String idPrefix) {
+ Assert.isNotNull(settings);
+
+ String encoding = settings.get(prefixDialogSettingsSlotId(ITerminalsConnectorConstants.PROP_ENCODING, idPrefix));
+ if (encoding != null && encoding.trim().length() > 0) {
+ setEncoding(encoding);
+ }
+ }
+
+ /**
+ * Decode the host settings from the given string.
+ *
+ * @param hostString The encoded host settings. Must not be <code>null</code>.
+ * @return The decoded host settings.
+ */
+ private HashMap<String, String> deSerialize(String hostString) {
+ Assert.isNotNull(hostString);
+ HashMap<String, String> attr = new HashMap<String, String>();
+
+ if (hostString.length() != 0) {
+ String[] hostAttrs = hostString.split("\\:");//$NON-NLS-1$
+ for (int j = 0; j < hostAttrs.length-1; j = j + 2) {
+ String key = hostAttrs[j];
+ String value = hostAttrs[j + 1];
+ attr.put(key, value);
+ }
+ }
+ return attr;
+ }
+
+ /**
+ * Encode the host settings to a string.
+ *
+ * @param hostEntry The host settings. Must not be <code>null</code>.
+ * @param hostString The host string to encode to. Must not be <code>null</code>.
+ */
+ private void serialize(Map<String, String> hostEntry, StringBuilder hostString) {
+ Assert.isNotNull(hostEntry);
+ Assert.isNotNull(hostString);
+
+ if (hostEntry.keySet().size() != 0) {
+ Iterator<Entry<String, String>> nextHostAttr = hostEntry.entrySet().iterator();
+ while (nextHostAttr.hasNext()) {
+ Entry<String, String> entry = nextHostAttr.next();
+ String attrKey = entry.getKey();
+ String attrValue = entry.getValue();
+ hostString.append(attrKey + ":" + attrValue + ":");//$NON-NLS-1$ //$NON-NLS-2$
+ }
+ hostString.deleteCharAt(hostString.length() - 1);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.controls.panels.AbstractWizardConfigurationPanel#doSaveWidgetValues(org.eclipse.jface.dialogs.IDialogSettings, java.lang.String)
+ */
+ @Override
+ public void doSaveWidgetValues(IDialogSettings settings, String idPrefix) {
+ Iterator<String> nextHost = hostSettingsMap.keySet().iterator();
+ String[] hosts = new String[hostSettingsMap.keySet().size()];
+ int i = 0;
+ while (nextHost.hasNext()) {
+ StringBuilder hostString = new StringBuilder();
+ String host = nextHost.next();
+ hostString.append(host + "|");//$NON-NLS-1$
+ Map<String, String> hostEntry = hostSettingsMap.get(host);
+ serialize(hostEntry, hostString);
+ hosts[i] = hostString.toString();
+ i = i + 1;
+ }
+ settings.put(HOSTS_TAG, hosts);
+ if (isWithoutSelection()) {
+ if (hostCombo != null) {
+ String host = getHostFromSettings();
+ if (host != null) settings.put(LAST_HOST_TAG, host);
+ }
+ }
+
+ if (!encodingHistory.isEmpty()) {
+ settings.put(ENCODINGS_TAG, encodingHistory.toArray(new String[encodingHistory.size()]));
+ }
+ }
+
+ /**
+ * Save the encodings widget values.
+ *
+ * @param settings The dialog settings. Must not be <code>null</code>.
+ * @param idPrefix The prefix or <code>null</code>.
+ */
+ protected void doSaveEncodingsWidgetValues(IDialogSettings settings, String idPrefix) {
+ Assert.isNotNull(settings);
+
+ String encoding = getEncoding();
+ if (encoding != null) {
+ settings.put(prefixDialogSettingsSlotId(ITerminalsConnectorConstants.PROP_ENCODING, idPrefix), encoding);
+ }
+ }
+
+ protected abstract void saveSettingsForHost(boolean add);
+
+ protected abstract void fillSettingsForHost(String host);
+
+ protected abstract String getHostFromSettings();
+
+ protected void removeSecurePassword(String host) {
+ // noop by default
+ }
+
+ /**
+ * Returns the selected host from the hosts combo widget.
+ *
+ * @return The selected host or <code>null</code>.
+ */
+ protected final String getHostFromCombo() {
+ return hostCombo != null && !hostCombo.isDisposed() ? hostCombo.getText() : null;
+ }
+
+ protected void removeSettingsForHost(String host) {
+ if (hostSettingsMap.containsKey(host)) {
+ hostSettingsMap.remove(host);
+ }
+ }
+
+ /**
+ * Returns the list of host names of the persisted hosts.
+ *
+ * @return The list of host names.
+ */
+ private List<String> getHostList() {
+ List<String> hostList = new ArrayList<String>();
+ hostList.addAll(hostSettingsMap.keySet());
+ return hostList;
+ }
+
+ /**
+ * Fill the host combo with the stored per host setting names.
+ */
+ protected void fillHostCombo() {
+ if (hostCombo != null) {
+ hostCombo.removeAll();
+ List<String> hostList = getHostList();
+ Collections.sort(hostList);
+ Iterator<String> nextHost = hostList.iterator();
+ while (nextHost.hasNext()) {
+ String host = nextHost.next();
+ hostCombo.add(host);
+ }
+ if (hostList.size() <= 1) {
+ hostCombo.setEnabled(false);
+ }
+ else {
+ hostCombo.setEnabled(true);
+
+ }
+ if (deleteHostButton != null) {
+ if (hostList.size() == 0) {
+ deleteHostButton.setEnabled(false);
+ }
+ else {
+ deleteHostButton.setEnabled(true);
+ }
+ }
+ }
+ }
+
+ public boolean isWithoutSelection() {
+ ISelection selection = getSelection();
+ if (selection == null) {
+ return true;
+ }
+ if (selection instanceof IStructuredSelection && selection.isEmpty()) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isWithHostList() {
+ return true;
+ }
+
+ /**
+ * Create the host selection combo.
+ *
+ * @param parent The parent composite. Must not be <code>null</code>.
+ * @param separator If <code>true</code>, a separator will be added after the controls.
+ */
+ protected void createHostsUI(Composite parent, boolean separator) {
+ Assert.isNotNull(parent);
+
+ if (isWithoutSelection() && isWithHostList()) {
+ Composite comboComposite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(3, false);
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ comboComposite.setLayout(layout);
+ comboComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ Label label = new Label(comboComposite, SWT.HORIZONTAL);
+ label.setText(Messages.AbstractConfigurationPanel_hosts);
+
+ hostCombo = new Combo(comboComposite, SWT.READ_ONLY);
+ hostCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ hostCombo.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String host = hostCombo.getText();
+ fillSettingsForHost(host);
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ });
+
+ deleteHostButton = new Button(comboComposite, SWT.NONE);
+ // deleteHostButton.setText(Messages.AbstractConfigurationPanel_delete);
+
+ ISharedImages workbenchImages = UIPlugin.getDefault().getWorkbench().getSharedImages();
+ deleteHostButton.setImage(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE).createImage());
+
+ deleteHostButton.setToolTipText(Messages.AbstractConfigurationPanel_deleteButtonTooltip);
+ deleteHostButton.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ String host = getHostFromCombo();
+ if (host != null && host.length() != 0) {
+ removeSettingsForHost(host);
+ removeSecurePassword(host);
+ fillHostCombo();
+ hostCombo.select(0);
+ host = getHostFromCombo();
+ if (host != null && host.length() != 0) {
+ fillSettingsForHost(host);
+ }
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ });
+
+ if (separator) {
+ Label sep = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
+ sep.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ }
+ }
+ }
+
+ /**
+ * Create the encoding selection combo.
+ *
+ * @param parent The parent composite. Must not be <code>null</code>.
+ * @param separator If <code>true</code>, a separator will be added before the controls.
+ */
+ protected void createEncodingUI(final Composite parent, boolean separator) {
+ Assert.isNotNull(parent);
+
+ if (separator) {
+ Label sep = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
+ sep.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ }
+
+ Composite panel = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginHeight = 0; layout.marginWidth = 0;
+ panel.setLayout(layout);
+ panel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ Label label = new Label(panel, SWT.HORIZONTAL);
+ label.setText(Messages.AbstractConfigurationPanel_encoding);
+
+ encodingCombo = new Combo(panel, SWT.READ_ONLY);
+ encodingCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ encodingCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (Messages.AbstractConfigurationPanel_encoding_custom.equals(encodingCombo.getText())) {
+ InputDialog dialog = new InputDialog(parent.getShell(),
+ Messages.AbstractConfigurationPanel_encoding_custom_title,
+ Messages.AbstractConfigurationPanel_encoding_custom_message,
+ null,
+ new IInputValidator() {
+ @Override
+ public String isValid(String newText) {
+ boolean valid = false;
+ try {
+ if (newText != null && !"".equals(newText)) { //$NON-NLS-1$
+ valid = Charset.isSupported(newText);
+ }
+ } catch (IllegalCharsetNameException e) { /* ignored on purpose */ }
+
+ if (!valid) {
+ return newText != null && !"".equals(newText) ? Messages.AbstractConfigurationPanel_encoding_custom_error : ""; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return null;
+ }
+ });
+ if (dialog.open() == Window.OK) {
+ String encoding = dialog.getValue();
+ encodingCombo.add(encoding, encodingCombo.getItemCount() - 1);
+ encodingCombo.select(encodingCombo.indexOf(encoding));
+ lastSelectedEncoding = encodingCombo.getText();
+
+ // Remember the last 5 custom encodings entered
+ if (!encodingHistory.contains(encoding)) {
+ if (encodingHistory.size() == 5) encodingHistory.remove(4);
+ encodingHistory.add(encoding);
+ }
+
+ } else {
+ encodingCombo.select(encodingCombo.indexOf(lastSelectedEncoding));
+ }
+ }
+ }
+ });
+
+ fillEncodingCombo();
+
+ // Apply any default encoding derived from the current selection
+ String defaultEncoding = getSelectionEncoding();
+ if (defaultEncoding != null && !"".equals(defaultEncoding)) { //$NON-NLS-1$
+ setEncoding(defaultEncoding);
+ }
+ }
+
+ /**
+ * Fill the encoding combo.
+ */
+ protected void fillEncodingCombo() {
+ if (encodingCombo != null) {
+ List<String> encodings = new ArrayList<String>();
+
+ // Some hard-coded encodings
+ encodings.add("Default (ISO-8859-1)"); //$NON-NLS-1$
+ encodings.add("UTF-8"); //$NON-NLS-1$
+
+ // The currently selected IDE encoding from the preferences
+ String ideEncoding = getResourceEncoding();
+ if (ideEncoding != null && !encodings.contains(ideEncoding)) encodings.add(ideEncoding);
+
+ // The default Eclipse Workbench encoding (configured in the preferences)
+ String eclipseEncoding = WorkbenchEncoding.getWorkbenchDefaultEncoding();
+ if (eclipseEncoding != null && !encodings.contains(eclipseEncoding)) encodings.add(eclipseEncoding);
+
+ // The default host (Java VM) encoding
+ //
+ // Note: We do not use Charset.defaultCharset().displayName() here as it returns the bit
+ // unusual name "windows-1252" on Windows. As there is no access to the "historical"
+ // name "Cp1252" stored in MS1252.class, stick to the older way of retrieving an encoding.
+ String hostEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding();
+ if (!encodings.contains(hostEncoding)) encodings.add(hostEncoding);
+
+ // The "Other..." encoding
+ encodings.add(Messages.AbstractConfigurationPanel_encoding_custom);
+
+ encodingCombo.setItems(encodings.toArray(new String[encodings.size()]));
+ encodingCombo.select(0);
+
+ lastSelectedEncoding = encodingCombo.getText();
+ }
+ }
+
+ /**
+ * Get the current value of the encoding preference. If the value is not set
+ * return <code>null</code>.
+ * <p>
+ * <b>Note:</b> Copied from <code>org.eclipse.ui.ide.IDEEncoding</code>.
+ *
+ * @return String
+ */
+ @SuppressWarnings("deprecation")
+ private String getResourceEncoding() {
+ String preference = null;
+ Bundle bundle = Platform.getBundle("org.eclipse.core.resources"); //$NON-NLS-1$
+ if (bundle != null && (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.ACTIVE)) {
+ preference = org.eclipse.core.resources.ResourcesPlugin.getPlugin().getPluginPreferences().getString(org.eclipse.core.resources.ResourcesPlugin.PREF_ENCODING);
+ }
+
+ return preference != null && preference.length() > 0 ? preference : null;
+ }
+
+ /**
+ * Select the encoding.
+ *
+ * @param encoding The encoding. Must not be <code>null</code>.
+ */
+ protected void setEncoding(String encoding) {
+ Assert.isNotNull(encoding);
+
+ if (encodingCombo != null && !encodingCombo.isDisposed()) {
+ int index = encodingCombo.indexOf("ISO-8859-1".equals(encoding) ? "Default (ISO-8859-1)" : encoding); //$NON-NLS-1$ //$NON-NLS-2$
+ if (index != -1) encodingCombo.select(index);
+ else {
+ encodingCombo.add(encoding, encodingCombo.getItemCount() - 1);
+ encodingCombo.select(encodingCombo.indexOf(encoding));
+ }
+
+ lastSelectedEncoding = encodingCombo.getText();
+ }
+ }
+
+ /**
+ * Returns the selected encoding.
+ *
+ * @return The selected encoding or <code>null</code>.
+ */
+ protected String getEncoding() {
+ String encoding = encodingCombo != null && !encodingCombo.isDisposed() ? encodingCombo.getText() : null;
+ return encoding != null && encoding.startsWith("Default") ? null : encoding; //$NON-NLS-1$
+ }
+
+ /**
+ * Returns if or if not the selected encoding is supported.
+ *
+ * @return <code>True</code> if the selected encoding is supported.
+ */
+ protected boolean isEncodingValid() {
+ try {
+ String encoding = getEncoding();
+ return Charset.isSupported(encoding != null ? encoding : "ISO-8859-1"); //$NON-NLS-1$
+ } catch (IllegalCharsetNameException e) {
+ return false;
+ }
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/services/TerminalService.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/services/TerminalService.java
new file mode 100644
index 000000000..08398fcf4
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/services/TerminalService.java
@@ -0,0 +1,359 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.services;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tcf.te.core.terminals.activator.CoreBundleActivator;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalTabListener;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IUIConstants;
+import org.eclipse.tcf.te.ui.terminals.launcher.LauncherDelegateManager;
+import org.eclipse.tcf.te.ui.terminals.manager.ConsoleManager;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Terminal service implementation.
+ */
+@SuppressWarnings("restriction")
+public class TerminalService implements ITerminalService {
+ /**
+ * The registered terminal tab dispose listeners.
+ */
+ private final ListenerList terminalTabListeners = new ListenerList();
+
+ // Flag to remember if the terminal view has been restored or not.
+ private boolean fRestoringView;
+
+ // Terminal tab events
+
+ /**
+ * A terminal tab got disposed.
+ */
+ public static final int TAB_DISPOSED = 1;
+
+ /**
+ * Common terminal service runnable implementation.
+ */
+ protected static abstract class TerminalServiceRunnable {
+
+ /**
+ * Invoked to execute the terminal service runnable.
+ *
+ * @param id The terminals view id or <code>null</code>.
+ * @param secondaryId The terminals view secondary id or <code>null</code>.
+ * @param title The terminal tab title. Must not be <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ * @param done The callback to invoke if the operation finished or <code>null</code>.
+ */
+ public abstract void run(String id, String secondaryId, String title, ITerminalConnector connector, Object data, Done done);
+
+ /**
+ * Returns if or if not to execute the runnable asynchronously.
+ * <p>
+ * The method returns per default <code>true</code>. Overwrite to
+ * modify the behavior.
+ *
+ * @return <code>True</code> to execute the runnable asynchronously, <code>false</code> otherwise.
+ */
+ public boolean isExecuteAsync() { return true; }
+ }
+
+ /**
+ * Constructor
+ */
+ public TerminalService() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService#addTerminalTabListener(org.eclipse.tcf.te.core.terminals.interfaces.ITerminalTabListener)
+ */
+ @Override
+ public final void addTerminalTabListener(ITerminalTabListener listener) {
+ Assert.isNotNull(listener);
+ terminalTabListeners.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService#removeTerminalTabListener(org.eclipse.tcf.te.core.terminals.interfaces.ITerminalTabListener)
+ */
+ @Override
+ public final void removeTerminalTabListener(ITerminalTabListener listener) {
+ Assert.isNotNull(listener);
+ terminalTabListeners.remove(listener);
+ }
+
+ /**
+ * Convenience method for notifying the registered terminal tab listeners.
+ *
+ * @param event The terminal tab event.
+ * @param source The disposed tab item. Must not be <code>null</code>.
+ * @param data The custom data object associated with the disposed tab item or <code>null</code>.
+ */
+ public final void fireTerminalTabEvent(final int event, final Object source, final Object data) {
+ Assert.isNotNull(source);
+
+ // If no listener is registered, we are done here
+ if (terminalTabListeners.isEmpty()) return;
+
+ // Get the list or currently registered listeners
+ Object[] l = terminalTabListeners.getListeners();
+ // Loop the registered terminal tab listeners and invoke the proper method
+ for (int i = 0; i < l.length; i++) {
+ final ITerminalTabListener listener = (ITerminalTabListener) l[i];
+ ISafeRunnable job = new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable exception) {
+ // already logged in Platform#run()
+ }
+
+ @Override
+ public void run() throws Exception {
+ switch (event) {
+ case TAB_DISPOSED:
+ listener.terminalTabDisposed(source, data);
+ break;
+ default:
+ }
+ }
+ };
+ SafeRunner.run(job);
+ }
+ }
+
+ /**
+ * Executes the given runnable operation and invokes the given callback, if any,
+ * after the operation finished.
+ *
+ * @param properties The terminal properties. Must not be <code>null</code>.
+ * @param runnable The terminal service runnable. Must not be <code>null</code>.
+ * @param done The callback to invoke if the operation has been finished or <code>null</code>.
+ */
+ protected final void executeServiceOperation(final Map<String, Object> properties, final TerminalServiceRunnable runnable, final Done done) {
+ Assert.isNotNull(properties);
+ Assert.isNotNull(runnable);
+
+ // Extract the properties
+ String id = (String)properties.get(ITerminalsConnectorConstants.PROP_ID);
+ String secondaryId = (String)properties.get(ITerminalsConnectorConstants.PROP_SECONDARY_ID);
+ String title = (String)properties.get(ITerminalsConnectorConstants.PROP_TITLE);
+ Object data = properties.get(ITerminalsConnectorConstants.PROP_DATA);
+
+ // Normalize the terminals console view id
+ id = normalizeId(id, data);
+ // Normalize the terminal console tab title
+ title = normalizeTitle(title, data);
+
+ // Create the terminal connector instance
+ final ITerminalConnector connector = createTerminalConnector(properties);
+ if (connector == null) {
+ // Properties contain invalid connector arguments
+ if (done != null) {
+ Exception e = new IllegalArgumentException(Messages.TerminalService_error_cannotCreateConnector);
+ done.done(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), e.getLocalizedMessage(), e));
+ }
+ return;
+ }
+
+ // Finalize the used variables
+ final String finId = id;
+ final String finSecondaryId = secondaryId;
+ final String finTitle = title;
+ final Object finData = data;
+
+ // Execute the operation
+ if (!runnable.isExecuteAsync()) {
+ runnable.run(finId, finSecondaryId, finTitle, connector, finData, done);
+ }
+ else {
+ try {
+ Display display = PlatformUI.getWorkbench().getDisplay();
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ runnable.run(finId, finSecondaryId, finTitle, connector, finData, done);
+ }
+ });
+ }
+ catch (Exception e) {
+ // if display is disposed, silently ignore.
+ }
+ }
+ }
+
+ /**
+ * Normalize the terminals view id.
+ *
+ * @param id The terminals view id or <code>null</code>.
+ * @param data The custom data object or <code>null</code>.
+ *
+ * @return The normalized terminals console view id.
+ */
+ protected String normalizeId(String id, Object data) {
+ return id != null ? id : IUIConstants.ID;
+ }
+
+ /**
+ * Normalize the terminal tab title.
+ *
+ * @param title The terminal tab title or <code>null</code>.
+ * @param data The custom data object or <code>null</code>.
+ *
+ * @return The normalized terminal tab title.
+ */
+ protected String normalizeTitle(String title, Object data) {
+ // If the title is explicitly specified, return as is
+ if (title != null) return title;
+
+ // Return the default console title in all other cases
+ return Messages.TerminalService_defaultTitle;
+ }
+
+ /**
+ * Creates the terminal connector configured within the given properties.
+ *
+ * @param properties The terminal console properties. Must not be <code>null</code>.
+ * @return The terminal connector or <code>null</code>.
+ */
+ protected ITerminalConnector createTerminalConnector(Map<String, Object> properties) {
+ Assert.isNotNull(properties);
+
+ // The terminal connector result object
+ ITerminalConnector connector = null;
+
+ // Get the launcher delegate id from the properties
+ String delegateId = (String)properties.get(ITerminalsConnectorConstants.PROP_DELEGATE_ID);
+ if (delegateId != null) {
+ // Get the launcher delegate
+ ILauncherDelegate delegate = LauncherDelegateManager.getInstance().getLauncherDelegate(delegateId, false);
+ if (delegate != null) {
+ // Create the terminal connector
+ connector = delegate.createTerminalConnector(properties);
+ }
+ }
+
+ return connector;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService#openConsole(java.util.Map, org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService.Done)
+ */
+ @Override
+ public void openConsole(final Map<String, Object> properties, final Done done) {
+ Assert.isNotNull(properties);
+ final boolean restoringView = fRestoringView;
+
+ executeServiceOperation(properties, new TerminalServiceRunnable() {
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void run(final String id, final String secondaryId, final String title,
+ final ITerminalConnector connector, final Object data, final Done done) {
+ if (restoringView) {
+ doRun(id, secondaryId, title, connector, data, done);
+ } else {
+ // First, restore the view. This opens consoles from the memento
+ fRestoringView = true;
+ ConsoleManager.getInstance().showConsoleView(id, secondaryId);
+ fRestoringView = false;
+
+ // After that schedule opening the requested console
+ try {
+ Display display = PlatformUI.getWorkbench().getDisplay();
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ doRun(id, secondaryId, title, connector, data, done);
+ }
+ });
+ }
+ catch (Exception e) {
+ // if display is disposed, silently ignore.
+ }
+ }
+ }
+
+ public void doRun(String id, String secondaryId, String title, ITerminalConnector connector, Object data, Done done) {
+ // Determine the terminal encoding
+ String encoding = (String)properties.get(ITerminalsConnectorConstants.PROP_ENCODING);
+ // Create the flags to pass on to openConsole
+ Map<String, Boolean> flags = new HashMap<String, Boolean>();
+ flags.put("activate", Boolean.TRUE); //$NON-NLS-1$
+ if (properties.get(ITerminalsConnectorConstants.PROP_FORCE_NEW) instanceof Boolean) {
+ flags.put(ITerminalsConnectorConstants.PROP_FORCE_NEW, (Boolean)properties.get(ITerminalsConnectorConstants.PROP_FORCE_NEW));
+ }
+ if (properties.get(ITerminalsConnectorConstants.PROP_HAS_DISCONNECT_BUTTON) instanceof Boolean) {
+ flags.put(ITerminalsConnectorConstants.PROP_HAS_DISCONNECT_BUTTON, (Boolean)properties.get(ITerminalsConnectorConstants.PROP_HAS_DISCONNECT_BUTTON));
+ }
+ // Open the new console
+ CTabItem item;
+ if (secondaryId != null)
+ item = ConsoleManager.getInstance().openConsole(id, secondaryId, title, encoding, connector, data, flags);
+ else
+ item = ConsoleManager.getInstance().openConsole(id, title, encoding, connector, data, flags);
+ // Associate the original terminal properties with the tab item.
+ // This makes it easier to persist the connection data within the memento handler
+ if (item != null && !item.isDisposed()) item.setData("properties", properties); //$NON-NLS-1$
+
+ // Invoke the callback
+ if (done != null) done.done(Status.OK_STATUS);
+ }
+ }, done);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService#closeConsole(java.util.Map, org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService.Done)
+ */
+ @Override
+ public void closeConsole(final Map<String, Object> properties, final Done done) {
+ Assert.isNotNull(properties);
+
+ executeServiceOperation(properties, new TerminalServiceRunnable() {
+ @Override
+ public void run(String id, String secondaryId, String title, ITerminalConnector connector, Object data, Done done) {
+ // Close the console
+ ConsoleManager.getInstance().closeConsole(id, title, connector, data);
+ // Invoke the callback
+ if (done != null) done.done(Status.OK_STATUS);
+ }
+ }, done);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService#terminateConsole(java.util.Map, org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService.Done)
+ */
+ @Override
+ public void terminateConsole(Map<String, Object> properties, Done done) {
+ Assert.isNotNull(properties);
+
+ executeServiceOperation(properties, new TerminalServiceRunnable() {
+ @Override
+ public void run(String id, String secondaryId, String title, ITerminalConnector connector, Object data, Done done) {
+ // Close the console
+ ConsoleManager.getInstance().terminateConsole(id, title, connector, data);
+ // Invoke the callback
+ if (done != null) done.done(Status.OK_STATUS);
+ }
+ }, done);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/AbstractStreamsConnector.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/AbstractStreamsConnector.java
new file mode 100644
index 000000000..51496bcbf
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/AbstractStreamsConnector.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.streams;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalServiceOutputStreamMonitorListener;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
+import org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl;
+import org.eclipse.ui.services.IDisposable;
+
+/**
+ * Streams connector implementation.
+ */
+@SuppressWarnings("restriction")
+public abstract class AbstractStreamsConnector extends TerminalConnectorImpl {
+ // Reference to the stdin monitor
+ private InputStreamMonitor stdInMonitor;
+ // Reference to the stdout monitor
+ private OutputStreamMonitor stdOutMonitor;
+ // Reference to the stderr monitor
+ private OutputStreamMonitor stdErrMonitor;
+
+ // Reference to the list of stdout output listeners
+ private ITerminalServiceOutputStreamMonitorListener[] stdoutListeners = null;
+ // Reference to the list of stderr output listeners
+ private ITerminalServiceOutputStreamMonitorListener[] stderrListeners = null;
+
+ /**
+ * Set the list of stdout listeners.
+ *
+ * @param listeners The list of stdout listeners or <code>null</code>.
+ */
+ protected final void setStdoutListeners(ITerminalServiceOutputStreamMonitorListener[] listeners) {
+ this.stdoutListeners = listeners;
+ }
+
+ /**
+ * Set the list of stderr listeners.
+ *
+ * @param listeners The list of stderr listeners or <code>null</code>.
+ */
+ protected final void setStderrListeners(ITerminalServiceOutputStreamMonitorListener[] listeners) {
+ this.stderrListeners = listeners;
+ }
+
+ /**
+ * Connect the given streams. The streams connector will wrap each stream
+ * with a corresponding terminal stream monitor.
+ *
+ * @param terminalControl The terminal control. Must not be <code>null</code>.
+ * @param stdin The stdin stream or <code>null</code>.
+ * @param stdout The stdout stream or <code>null</code>.
+ * @param stderr The stderr stream or <code>null</code>.
+ * @param localEcho Local echo on or off.
+ * @param lineSeparator The line separator used by the stream.
+ */
+ protected void connectStreams(ITerminalControl terminalControl, OutputStream stdin, InputStream stdout, InputStream stderr, boolean localEcho, String lineSeparator) {
+ Assert.isNotNull(terminalControl);
+
+ // Create the input stream monitor
+ if (stdin != null) {
+ stdInMonitor = createStdInMonitor(terminalControl, stdin, localEcho, lineSeparator);
+ // Register the connector if it implements IDisposable and stdout/stderr are not monitored
+ if (stdout == null && stderr == null && this instanceof IDisposable) stdInMonitor.addDisposable((IDisposable)this);
+ // Start the monitoring
+ stdInMonitor.startMonitoring();
+ }
+
+ // Create the output stream monitor
+ if (stdout != null) {
+ stdOutMonitor = createStdOutMonitor(terminalControl, stdout, lineSeparator);
+ // Register the connector if it implements IDisposable
+ if (this instanceof IDisposable) stdOutMonitor.addDisposable((IDisposable)this);
+ // Register the listeners
+ if (stdoutListeners != null) {
+ for (ITerminalServiceOutputStreamMonitorListener l : stdoutListeners) {
+ stdOutMonitor.addListener(l);
+ }
+ }
+ // Start the monitoring
+ stdOutMonitor.startMonitoring();
+ }
+
+ // Create the error stream monitor
+ if (stderr != null) {
+ stdErrMonitor = createStdErrMonitor(terminalControl, stderr, lineSeparator);
+ // Register the connector if it implements IDisposable and stdout is not monitored
+ if (stdout == null && this instanceof IDisposable) stdErrMonitor.addDisposable((IDisposable)this);
+ // Register the listeners
+ if (stderrListeners != null) {
+ for (ITerminalServiceOutputStreamMonitorListener l : stderrListeners) {
+ stdErrMonitor.addListener(l);
+ }
+ }
+ // Start the monitoring
+ stdErrMonitor.startMonitoring();
+ }
+ }
+
+ /**
+ * Creates an stdin monitor for the given terminal control and stdin stream.
+ * Subclasses may override to create a specialized stream monitor.
+ *
+ * @param terminalControl The terminal control. Must not be <code>null</code>.
+ * @param stdin The stdin stream or <code>null</code>.
+ * @param localEcho Local echo on or off.
+ * @param lineSeparator The line separator used by the stream.
+ *
+ * @return input stream monitor
+ */
+ protected InputStreamMonitor createStdInMonitor(ITerminalControl terminalControl, OutputStream stdin, boolean localEcho, String lineSeparator) {
+ return new InputStreamMonitor(terminalControl, stdin, localEcho, lineSeparator);
+ }
+
+ /**
+ * Creates an stdout monitor for the given terminal control and stdout stream.
+ * Subclasses may override to create a specialized stream monitor.
+ *
+ * @param terminalControl The terminal control. Must not be <code>null</code>.
+ * @param stdout The stdout stream or <code>null</code>.
+ * @param lineSeparator The line separator used by the stream.
+ *
+ * @return output stream monitor
+ */
+ protected OutputStreamMonitor createStdOutMonitor(ITerminalControl terminalControl, InputStream stdout, String lineSeparator) {
+ return new OutputStreamMonitor(terminalControl, stdout, lineSeparator);
+ }
+
+ /**
+ * Creates an stderr monitor for the given terminal control and stderr stream.
+ * Subclasses may override to create a specialized stream monitor.
+ *
+ * @param terminalControl The terminal control. Must not be <code>null</code>.
+ * @param stderr The stderr stream or <code>null</code>.
+ * @param lineSeparator The line separator used by the stream.
+ *
+ * @return output stream monitor
+ */
+ protected OutputStreamMonitor createStdErrMonitor(ITerminalControl terminalControl, InputStream stderr, String lineSeparator) {
+ return new OutputStreamMonitor(terminalControl, stderr, lineSeparator);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#doDisconnect()
+ */
+ @Override
+ protected void doDisconnect() {
+ // Dispose the streams
+ if (stdInMonitor != null) { stdInMonitor.dispose(); stdInMonitor = null; }
+ if (stdOutMonitor != null) { stdOutMonitor.dispose(); stdOutMonitor = null; }
+ if (stdErrMonitor != null) { stdErrMonitor.dispose(); stdErrMonitor = null; }
+
+ super.doDisconnect();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#getTerminalToRemoteStream()
+ */
+ @Override
+ public OutputStream getTerminalToRemoteStream() {
+ return stdInMonitor;
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/InputStreamMonitor.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/InputStreamMonitor.java
new file mode 100644
index 000000000..928e0179c
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/InputStreamMonitor.java
@@ -0,0 +1,368 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.streams;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ILineSeparatorConstants;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
+import org.eclipse.ui.services.IDisposable;
+
+/**
+ * Input stream monitor implementation.
+ * <p>
+ * <b>Note:</b> The input is coming <i>from</i> the terminal. Therefore, the input
+ * stream monitor is attached to the stdin stream of the monitored (remote) process.
+ */
+@SuppressWarnings("restriction")
+public class InputStreamMonitor extends OutputStream implements IDisposable {
+ // Reference to the parent terminal control
+ private final ITerminalControl terminalControl;
+
+ // Reference to the monitored (output) stream
+ private final OutputStream stream;
+
+ // Reference to the thread writing the stream
+ private volatile Thread thread;
+
+ // Flag to mark the monitor disposed. When disposed,
+ // no further data is written from the monitored stream.
+ private volatile boolean disposed;
+
+ // A list of object to dispose if this monitor is disposed
+ private final List<IDisposable> disposables = new ArrayList<IDisposable>();
+
+ // Queue to buffer the data to write to the output stream
+ private final Queue<byte[]> queue = new LinkedList<byte[]>();
+
+ // ***** Line separator replacement logic *****
+ // ***** Adapted from org.eclipse.tcf.internal.terminal.local.LocalTerminalOutputStream *****
+
+ private final static int TERMINAL_SENDS_CR = 0;
+ private final static int TERMINAL_SENDS_CRLF = 1;
+ private final static int PROGRAM_EXPECTS_LF = 0;
+ private final static int PROGRAM_EXPECTS_CRLF = 1;
+ private final static int PROGRAM_EXPECTS_CR = 2;
+ private final static int NO_CHANGE = 0;
+ private final static int CHANGE_CR_TO_LF = 1;
+ private final static int INSERT_LF_AFTER_CR = 2;
+ private final static int REMOVE_CR = 3;
+ private final static int REMOVE_LF = 4;
+
+ // CRLF conversion table:
+ //
+ // Expected line separator --> | LF | CRLF | CR |
+ // ------------------------------------+-----------------+--------------------+----------------+
+ // Local echo off - control sends CR | change CR to LF | insert LF after CR | no change |
+ // ------------------------------------+-----------------+--------------------+----------------+
+ // Local echo on - control sends CRLF | remove CR | no change | remove LF |
+ //
+ private final static int[][] CRLF_REPLACEMENT = {
+
+ {CHANGE_CR_TO_LF, INSERT_LF_AFTER_CR, NO_CHANGE},
+ {REMOVE_CR, NO_CHANGE, REMOVE_LF}
+ };
+
+ private int replacement;
+
+ /**
+ * Constructor.
+ *
+ * @param terminalControl The parent terminal control. Must not be <code>null</code>.
+ * @param stream The stream. Must not be <code>null</code>.
+ * @param localEcho Local echo on or off.
+ * @param lineSeparator The line separator used by the stream.
+ */
+ public InputStreamMonitor(ITerminalControl terminalControl, OutputStream stream, boolean localEcho, String lineSeparator) {
+ super();
+
+ Assert.isNotNull(terminalControl);
+ this.terminalControl = terminalControl;
+ Assert.isNotNull(stream);
+ this.stream = stream;
+
+ // Determine the line separator replacement setting
+ int terminalSends = localEcho ? TERMINAL_SENDS_CRLF : TERMINAL_SENDS_CR;
+ if (lineSeparator == null) {
+ replacement = NO_CHANGE;
+ } else {
+ int programExpects;
+ if (lineSeparator.equals(ILineSeparatorConstants.LINE_SEPARATOR_LF)) {
+ programExpects = PROGRAM_EXPECTS_LF;
+ }
+ else if (lineSeparator.equals(ILineSeparatorConstants.LINE_SEPARATOR_CR)) {
+ programExpects = PROGRAM_EXPECTS_CR;
+ }
+ else {
+ programExpects = PROGRAM_EXPECTS_CRLF;
+ }
+ replacement = CRLF_REPLACEMENT[terminalSends][programExpects];
+ }
+
+ }
+
+ /**
+ * Returns the associated terminal control.
+ *
+ * @return The associated terminal control.
+ */
+ protected final ITerminalControl getTerminalControl() {
+ return terminalControl;
+ }
+
+ /**
+ * Adds the given disposable object to the list. The method will do nothing
+ * if either the disposable object is already part of the list or the monitor
+ * is disposed.
+ *
+ * @param disposable The disposable object. Must not be <code>null</code>.
+ */
+ public final void addDisposable(IDisposable disposable) {
+ Assert.isNotNull(disposable);
+ if (!disposed && !disposables.contains(disposable)) disposables.add(disposable);
+ }
+
+ /**
+ * Removes the disposable object from the list.
+ *
+ * @param disposable The disposable object. Must not be <code>null</code>.
+ */
+ public final void removeDisposable(IDisposable disposable) {
+ Assert.isNotNull(disposable);
+ disposables.remove(disposable);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.interfaces.IDisposable#dispose()
+ */
+ @Override
+ public void dispose() {
+ // If already disposed --> return immediately
+ if (disposed) return;
+
+ // Mark the monitor disposed
+ disposed = true;
+
+ // Close the stream (ignore exceptions on close)
+ try { stream.close(); } catch (IOException e) { /* ignored on purpose */ }
+ // And interrupt the thread
+ close();
+
+ // Dispose all registered disposable objects
+ for (IDisposable disposable : disposables) disposable.dispose();
+ // Clear the list
+ disposables.clear();
+ }
+
+ /**
+ * Close the terminal input stream monitor.
+ */
+ @Override
+ public void close() {
+ // Not initialized -> return immediately
+ if (thread == null) return;
+
+ // Copy the reference
+ final Thread oldThread = thread;
+ // Unlink the monitor from the thread
+ thread = null;
+ // And interrupt the writer thread
+ oldThread.interrupt();
+ }
+
+ /**
+ * Starts the terminal output stream monitor.
+ */
+ public void startMonitoring() {
+ // If already initialized -> return immediately
+ if (thread != null) return;
+
+ // Create a new runnable which is constantly reading from the stream
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ writeStream();
+ }
+ };
+
+ // Create the writer thread
+ thread = new Thread(runnable, "Terminal Input Stream Monitor Thread"); //$NON-NLS-1$
+
+ // Configure the writer thread
+ thread.setDaemon(true);
+
+ // Start the processing
+ thread.start();
+ }
+
+
+ /**
+ * Reads from the queue and writes the read content to the stream.
+ */
+ protected void writeStream() {
+ // Read from the queue and write to the stream until disposed
+ outer: while (thread != null && !disposed) {
+ byte[] data;
+ // If the queue is empty, wait until notified
+ synchronized(queue) {
+ while (queue.isEmpty()) {
+ if (disposed) break outer;
+ try {
+ queue.wait();
+ } catch (InterruptedException e) {
+ break outer;
+ }
+ }
+ // Retrieves the queue head (is null if queue is empty (should never happen))
+ data = queue.poll();
+ }
+ if (data != null) {
+ try {
+ // Break up writes into max 1000 byte junks to avoid console input buffer overflows on Windows
+ int written = 0;
+ byte[] buf = new byte[1000];
+ while (written < data.length) {
+ int len = Math.min(buf.length, data.length - written);
+ System.arraycopy(data, written, buf, 0, len);
+ // Write the data to the stream
+ stream.write(buf, 0, len);
+ written += len;
+ // Flush the stream immediately
+ stream.flush();
+ // Wait a little between writes to allow input being processed
+ if (written < data.length)
+ Thread.sleep(100);
+ }
+ } catch (IOException e) {
+ // IOException received. If this is happening when already disposed -> ignore
+ if (!disposed) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
+ NLS.bind(Messages.InputStreamMonitor_error_writingToStream, e.getLocalizedMessage()), e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ }
+ catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ // Dispose the stream
+ dispose();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#write(int)
+ */
+ @Override
+ public void write(int b) throws IOException {
+ synchronized(queue) {
+ queue.add(new byte[] { (byte)b });
+ queue.notifyAll();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#write(byte[], int, int)
+ */
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ // Write the whole block to the queue to avoid synchronization
+ // to happen for every byte. To do so, we have to avoid calling
+ // the super method. Therefore we have to do the same checking
+ // here as the base class does.
+
+ // Null check. See the implementation in OutputStream.
+ if (b == null) throw new NullPointerException();
+
+ // Boundary check. See the implementation in OutputStream.
+ if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ }
+ else if (len == 0) {
+ return;
+ }
+
+ // Make sure that the written block is not interlaced with other input.
+ synchronized(queue) {
+ // Preprocess the block to be written
+ byte[] processedBytes = onWriteContentToStream(b, off, len);
+ // If the returned array is not the original one, adjust offset and length
+ if (processedBytes != b) {
+ off = 0; len = processedBytes.length; b = processedBytes;
+ }
+
+ // Get the content from the byte buffer specified by offset and length
+ byte[] bytes = new byte[len];
+ int j = 0;
+ for (int i = 0 ; i < len ; i++) {
+ bytes[j++] = b[off + i];
+ }
+
+ queue.add(bytes);
+ queue.notifyAll();
+ }
+ }
+
+ /**
+ * Allow for processing of data from byte stream from the terminal before
+ * it is written to the output stream. If the returned byte array is different
+ * than the one that was passed in with the bytes argument, then the
+ * length value will be adapted.
+ *
+ * @param bytes The byte stream. Must not be <code>null</code>.
+ * @param off The offset.
+ * @param len the length.
+ *
+ * @return The processed byte stream.
+ *
+ */
+ protected byte[] onWriteContentToStream(byte[] bytes, int off, int len) {
+ Assert.isNotNull(bytes);
+
+ if (replacement != NO_CHANGE && len > 0) {
+ String origText = new String(bytes, off, len);
+ String text = null;
+ //
+ // TODO: check whether this is correct! new String(byte[], int, int) always uses the default
+ // encoding!
+
+ if (replacement == CHANGE_CR_TO_LF) {
+ text = origText.replace('\r', '\n');
+ }
+ else if (replacement == INSERT_LF_AFTER_CR) {
+ text = origText.replaceAll("\r\n|\r", "\r\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else if (replacement == REMOVE_CR) {
+ text = origText.replaceAll(ILineSeparatorConstants.LINE_SEPARATOR_CR, ""); //$NON-NLS-1$
+ }
+ else if (replacement == REMOVE_LF) {
+ text = origText.replaceAll(ILineSeparatorConstants.LINE_SEPARATOR_LF, ""); //$NON-NLS-1$
+ }
+
+ if (text != null && !origText.equals(text)) {
+ bytes = text.getBytes();
+ }
+ }
+
+ return bytes;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/OutputStreamMonitor.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/OutputStreamMonitor.java
new file mode 100644
index 000000000..5714bf9fd
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/OutputStreamMonitor.java
@@ -0,0 +1,319 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.streams;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalServiceOutputStreamMonitorListener;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ILineSeparatorConstants;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.tracing.ITraceIds;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
+import org.eclipse.ui.services.IDisposable;
+
+/**
+ * Output stream monitor implementation.
+ * <p>
+ * <b>Note:</b> The output is going <i>to</i> the terminal. Therefore, the output
+ * stream monitor is attached to the stdout and/or stderr stream of the monitored
+ * (remote) process.
+ */
+@SuppressWarnings("restriction")
+public class OutputStreamMonitor implements IDisposable {
+ // The default buffer size to use
+ private static final int BUFFER_SIZE = 8192;
+
+ // Reference to the parent terminal control
+ private final ITerminalControl terminalControl;
+
+ // Reference to the monitored (input) stream
+ private final InputStream stream;
+
+ // The line separator used by the monitored (input) stream
+ private final String lineSeparator;
+
+ // Reference to the thread reading the stream
+ private Thread thread;
+
+ // Flag to mark the monitor disposed. When disposed,
+ // no further data is read from the monitored stream.
+ private boolean disposed;
+
+ // A list of object to dispose if this monitor is disposed
+ private final List<IDisposable> disposables = new ArrayList<IDisposable>();
+
+ // The list of registered listener
+ private final ListenerList listeners;
+
+ /**
+ * Constructor.
+ *
+ * @param terminalControl The parent terminal control. Must not be <code>null</code>.
+ * @param stream The stream. Must not be <code>null</code>.
+ * @param lineSeparator The line separator used by the stream.
+ */
+ public OutputStreamMonitor(ITerminalControl terminalControl, InputStream stream, String lineSeparator) {
+ super();
+
+ Assert.isNotNull(terminalControl);
+ this.terminalControl = terminalControl;
+ Assert.isNotNull(stream);
+ this.stream = new BufferedInputStream(stream, BUFFER_SIZE);
+
+ this.lineSeparator = lineSeparator;
+
+ this.listeners = new ListenerList();
+ }
+
+ /**
+ * Register a streams data receiver listener.
+ *
+ * @param listener The listener. Must not be <code>null</code>.
+ */
+ public final void addListener(ITerminalServiceOutputStreamMonitorListener listener) {
+ Assert.isNotNull(listener);
+ listeners.add(listener);
+ }
+
+ /**
+ * Unregister a streams data receiver listener.
+ *
+ * @param listener The listener. Must not be <code>null</code>.
+ */
+ public final void removeListener(ITerminalServiceOutputStreamMonitorListener listener) {
+ Assert.isNotNull(listener);
+ listeners.remove(listener);
+ }
+
+ /**
+ * Adds the given disposable object to the list. The method will do nothing
+ * if either the disposable object is already part of the list or the monitor
+ * is disposed.
+ *
+ * @param disposable The disposable object. Must not be <code>null</code>.
+ */
+ public final void addDisposable(IDisposable disposable) {
+ Assert.isNotNull(disposable);
+ if (!disposed && !disposables.contains(disposable)) disposables.add(disposable);
+ }
+
+ /**
+ * Removes the disposable object from the list.
+ *
+ * @param disposable The disposable object. Must not be <code>null</code>.
+ */
+ public final void removeDisposable(IDisposable disposable) {
+ Assert.isNotNull(disposable);
+ disposables.remove(disposable);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.interfaces.IDisposable#dispose()
+ */
+ @Override
+ public void dispose() {
+ // If already disposed --> return immediately
+ if (disposed) return;
+
+ // Mark the monitor disposed
+ disposed = true;
+
+ // Close the stream (ignore exceptions on close)
+ try { stream.close(); } catch (IOException e) { /* ignored on purpose */ }
+
+ // Dispose all registered disposable objects
+ for (IDisposable disposable : disposables) disposable.dispose();
+ // Clear the list
+ disposables.clear();
+ }
+
+ /**
+ * Starts the terminal output stream monitor.
+ */
+ protected void startMonitoring() {
+ // If already initialized -> return immediately
+ if (thread != null) return;
+
+ // Create a new runnable which is constantly reading from the stream
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ readStream();
+ }
+ };
+
+ // Create the reader thread
+ thread = new Thread(runnable, "Terminal Output Stream Monitor Thread"); //$NON-NLS-1$
+
+ // Configure the reader thread
+ thread.setDaemon(true);
+ thread.setPriority(Thread.MIN_PRIORITY);
+
+ // Start the processing
+ thread.start();
+ }
+
+ /**
+ * Returns the terminal control that this stream monitor is associated with.
+ */
+ protected ITerminalControl getTerminalControl() {
+ return terminalControl;
+ }
+
+ /**
+ * Reads from the output stream and write the read content
+ * to the terminal control output stream.
+ */
+ void readStream() {
+ // Creates the read buffer
+ byte[] readBuffer = new byte[BUFFER_SIZE];
+
+ // We need to maintain UI responsiveness but still stream the content
+ // to the terminal control fast. Put the thread to a short sleep each second.
+ long sleepMarker = System.currentTimeMillis();
+
+ // Read from the stream until EOS is reached or the
+ // monitor is marked disposed.
+ int read = 0;
+ while (read >= 0 && !disposed) {
+ try {
+ // Read from the stream
+ read = stream.read(readBuffer);
+ // If some data has been read, append to the terminal
+ // control output stream
+ if (read > 0) {
+ // Allow for post processing the read content before appending
+ byte[] processedReadBuffer = onContentReadFromStream(readBuffer, read);
+ if (processedReadBuffer != readBuffer) {
+ read = processedReadBuffer.length;
+ }
+ terminalControl.getRemoteToTerminalOutputStream().write(processedReadBuffer, 0, read);
+ }
+ } catch (IOException e) {
+ // IOException received. If this is happening when already disposed -> ignore
+ if (!disposed) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
+ NLS.bind(Messages.OutputStreamMonitor_error_readingFromStream, e.getLocalizedMessage()), e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ break;
+ } catch (NullPointerException e) {
+ // killing the stream monitor while reading can cause an NPE
+ // when reading from the stream
+ if (!disposed && thread != null) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
+ NLS.bind(Messages.OutputStreamMonitor_error_readingFromStream, e.getLocalizedMessage()), e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ break;
+ }
+
+ // See above -> Thread will go to sleep each second
+ if (System.currentTimeMillis() - sleepMarker > 1000) {
+ sleepMarker = System.currentTimeMillis();
+ try { Thread.sleep(1); } catch (InterruptedException e) { /* ignored on purpose */ }
+ }
+ }
+
+ // Dispose ourself
+ dispose();
+ }
+
+ /**
+ * Allow for processing of data from byte stream after it is read from
+ * client but before it is appended to the terminal. If the returned byte
+ * array is different than the one that was passed in with the byteBuffer
+ * argument, then the bytesRead value will be ignored and the full
+ * returned array will be written out.
+ *
+ * @param byteBuffer The byte stream. Must not be <code>null</code>.
+ * @param bytesRead The number of bytes that were read into the read buffer.
+ * @return The processed byte stream.
+ *
+ */
+ protected byte[] onContentReadFromStream(byte[] byteBuffer, int bytesRead) {
+ Assert.isNotNull(byteBuffer);
+
+ // If tracing is enabled, print out the decimal byte values read
+ if (UIPlugin.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_OUTPUT_STREAM_MONITOR)) {
+ StringBuilder debug = new StringBuilder("byteBuffer [decimal, " + bytesRead + " bytes] : "); //$NON-NLS-1$ //$NON-NLS-2$
+ for (int i = 0; i < bytesRead; i++) {
+ debug.append(Byte.valueOf(byteBuffer[i]).intValue());
+ debug.append(' ');
+ }
+ System.out.println(debug.toString());
+ }
+
+ // Remember if the text got changed.
+ boolean changed = false;
+
+ // How can me make sure that we don't mess with the encoding here?
+ String text = new String(byteBuffer, 0, bytesRead);
+
+ // Shift-In (14) and Shift-Out(15) confuses the terminal widget
+ if (text.indexOf(14) != -1 || text.indexOf(15) != -1) {
+ text = text.replaceAll("\\x0e", "").replaceAll("\\x0f", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ changed = true;
+ }
+
+ // Check on the line separator setting
+ if (lineSeparator != null
+ && !ILineSeparatorConstants.LINE_SEPARATOR_CRLF.equals(lineSeparator)) {
+ String separator = ILineSeparatorConstants.LINE_SEPARATOR_LF.equals(lineSeparator) ? "\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$
+ String separator2 = ILineSeparatorConstants.LINE_SEPARATOR_LF.equals(lineSeparator) ? "\r" : "\n"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ if (text.indexOf(separator) != -1) {
+ String[] fragments = text.split(separator);
+ StringBuilder b = new StringBuilder();
+ for (int i = 0; i < fragments.length; i++) {
+ String fragment = fragments[i];
+ String nextFragment = i + 1 < fragments.length ? fragments[i + 1] : null;
+ b.append(fragment);
+ if (fragment.endsWith(separator2) || (nextFragment != null && nextFragment.startsWith(separator2))) {
+ // Both separators are found, just add the original separator
+ b.append(separator);
+ } else {
+ b.append("\n\r"); //$NON-NLS-1$
+ }
+ }
+ if (!text.equals(b.toString())) {
+ text = b.toString();
+ changed = true;
+ }
+ }
+ }
+
+ // If changed, get the new bytes array
+ if (changed) {
+ byteBuffer = text.getBytes();
+ bytesRead = byteBuffer.length;
+ }
+
+ // If listeners are registered, invoke the listeners now.
+ if (listeners.size() > 0) {
+ for (Object candidate : listeners.getListeners()) {
+ if (!(candidate instanceof ITerminalServiceOutputStreamMonitorListener)) continue;
+ ((ITerminalServiceOutputStreamMonitorListener)candidate).onContentReadFromStream(byteBuffer, bytesRead);
+ }
+ }
+
+ return byteBuffer;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsConnector.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsConnector.java
new file mode 100644
index 000000000..b9780be51
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsConnector.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.streams;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+import org.eclipse.ui.services.IDisposable;
+
+/**
+ * Streams connector implementation.
+ */
+@SuppressWarnings("restriction")
+public class StreamsConnector extends AbstractStreamsConnector implements IDisposable {
+ // Reference to the streams settings
+ private final StreamsSettings settings;
+
+ /**
+ * Constructor.
+ */
+ public StreamsConnector() {
+ this(new StreamsSettings());
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param settings The streams settings. Must not be <code>null</code>
+ */
+ public StreamsConnector(StreamsSettings settings) {
+ super();
+
+ Assert.isNotNull(settings);
+ this.settings = settings;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#connect(org.eclipse.tcf.internal.terminal.provisional.api.ITerminalControl)
+ */
+ @Override
+ public void connect(ITerminalControl control) {
+ Assert.isNotNull(control);
+ super.connect(control);
+
+ // Setup the listeners
+ setStdoutListeners(settings.getStdOutListeners());
+ setStderrListeners(settings.getStdErrListeners());
+
+ // connect the streams
+ connectStreams(control, settings.getStdinStream(), settings.getStdoutStream(), settings.getStderrStream(), settings.isLocalEcho(), settings.getLineSeparator());
+
+ // Set the terminal control state to CONNECTED
+ control.setState(TerminalState.CONNECTED);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#isLocalEcho()
+ */
+ @Override
+ public boolean isLocalEcho() {
+ return settings.isLocalEcho();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.interfaces.IDisposable#dispose()
+ */
+ @Override
+ public void dispose() {
+ disconnect();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#doDisconnect()
+ */
+ @Override
+ public void doDisconnect() {
+ // Dispose the streams
+ super.doDisconnect();
+
+ // Set the terminal control state to CLOSED.
+ fControl.setState(TerminalState.CLOSED);
+ }
+
+ // ***** Process Connector settings handling *****
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#getSettingsSummary()
+ */
+ @Override
+ public String getSettingsSummary() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#load(org.eclipse.tcf.internal.terminal.provisional.api.ISettingsStore)
+ */
+ @Override
+ public void load(ISettingsStore store) {
+ settings.load(store);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#save(org.eclipse.tcf.internal.terminal.provisional.api.ISettingsStore)
+ */
+ @Override
+ public void save(ISettingsStore store) {
+ settings.save(store);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsLauncherDelegate.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsLauncherDelegate.java
new file mode 100644
index 000000000..6d492e7ba
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsLauncherDelegate.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.streams;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tcf.te.core.terminals.TerminalServiceFactory;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService.Done;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalServiceOutputStreamMonitorListener;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanel;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer;
+import org.eclipse.tcf.te.ui.terminals.internal.SettingsStore;
+import org.eclipse.tcf.te.ui.terminals.launcher.AbstractLauncherDelegate;
+import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalConnectorExtension;
+
+/**
+ * Streams launcher delegate implementation.
+ */
+@SuppressWarnings("restriction")
+public class StreamsLauncherDelegate extends AbstractLauncherDelegate {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#needsUserConfiguration()
+ */
+ @Override
+ public boolean needsUserConfiguration() {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#getPanel(org.eclipse.tcf.te.ui.terminals.interfaces.IConfigurationPanelContainer)
+ */
+ @Override
+ public IConfigurationPanel getPanel(IConfigurationPanelContainer container) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#execute(java.util.Map, org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService.Done)
+ */
+ @Override
+ public void execute(Map<String, Object> properties, Done done) {
+ Assert.isNotNull(properties);
+
+ // Get the terminal service
+ ITerminalService terminal = TerminalServiceFactory.getService();
+ // If not available, we cannot fulfill this request
+ if (terminal != null) {
+ terminal.openConsole(properties, done);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate#createTerminalConnector(java.util.Map)
+ */
+ @Override
+ public ITerminalConnector createTerminalConnector(Map<String, Object> properties) {
+ Assert.isNotNull(properties);
+
+ // Check for the terminal connector id
+ String connectorId = (String)properties.get(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID);
+ if (connectorId == null) connectorId = "org.eclipse.tcf.te.ui.terminals.StreamsConnector"; //$NON-NLS-1$
+
+ // Extract the streams properties
+ OutputStream stdin = (OutputStream)properties.get(ITerminalsConnectorConstants.PROP_STREAMS_STDIN);
+ InputStream stdout = (InputStream)properties.get(ITerminalsConnectorConstants.PROP_STREAMS_STDOUT);
+ InputStream stderr = (InputStream)properties.get(ITerminalsConnectorConstants.PROP_STREAMS_STDERR);
+ Object value = properties.get(ITerminalsConnectorConstants.PROP_LOCAL_ECHO);
+ boolean localEcho = value instanceof Boolean ? ((Boolean)value).booleanValue() : false;
+ String lineSeparator = (String)properties.get(ITerminalsConnectorConstants.PROP_LINE_SEPARATOR);
+ ITerminalServiceOutputStreamMonitorListener[] stdoutListeners = (ITerminalServiceOutputStreamMonitorListener[])properties.get(ITerminalsConnectorConstants.PROP_STDOUT_LISTENERS);
+ ITerminalServiceOutputStreamMonitorListener[] stderrListeners = (ITerminalServiceOutputStreamMonitorListener[])properties.get(ITerminalsConnectorConstants.PROP_STDERR_LISTENERS);
+
+ // Construct the terminal settings store
+ ISettingsStore store = new SettingsStore();
+
+ // Construct the streams settings
+ StreamsSettings streamsSettings = new StreamsSettings();
+ streamsSettings.setStdinStream(stdin);
+ streamsSettings.setStdoutStream(stdout);
+ streamsSettings.setStderrStream(stderr);
+ streamsSettings.setLocalEcho(localEcho);
+ streamsSettings.setLineSeparator(lineSeparator);
+ streamsSettings.setStdOutListeners(stdoutListeners);
+ streamsSettings.setStdErrListeners(stderrListeners);
+ // And save the settings to the store
+ streamsSettings.save(store);
+
+ // Construct the terminal connector instance
+ ITerminalConnector connector = TerminalConnectorExtension.makeTerminalConnector(connectorId);
+ if (connector != null) {
+ // Apply default settings
+ connector.makeSettingsPage();
+ // And load the real settings
+ connector.load(store);
+ }
+
+ return connector;
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsSettings.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsSettings.java
new file mode 100644
index 000000000..dcaaac457
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/streams/StreamsSettings.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.streams;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalServiceOutputStreamMonitorListener;
+import org.eclipse.tcf.te.ui.terminals.internal.SettingsStore;
+import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore;
+
+/**
+ * Streams connector settings implementation.
+ */
+@SuppressWarnings("restriction")
+public class StreamsSettings {
+ // Reference to the stdin stream
+ private OutputStream stdin;
+ // Reference to the stdout stream
+ private InputStream stdout;
+ // Reference to the stderr stream
+ private InputStream stderr;
+ // Flag to control the local echo
+ private boolean localEcho = true;
+ // The line separator setting
+ private String lineSeparator = null;
+ // The list of stdout output listeners
+ private ITerminalServiceOutputStreamMonitorListener[] stdoutListeners = null;
+ // The list of stderr output listeners
+ private ITerminalServiceOutputStreamMonitorListener[] stderrListeners = null;
+
+ /**
+ * Sets the stdin stream instance.
+ *
+ * @param stdin The stream instance or <code>null</code>.
+ */
+ public void setStdinStream(OutputStream stdin) {
+ this.stdin = stdin;
+ }
+
+ /**
+ * Returns the stdin stream instance.
+ *
+ * @return The stream instance or <code>null</code>.
+ */
+ public OutputStream getStdinStream() {
+ return stdin;
+ }
+
+ /**
+ * Sets the stdout stream instance.
+ *
+ * @param stdout The stream instance or <code>null</code>.
+ */
+ public void setStdoutStream(InputStream stdout) {
+ this.stdout = stdout;
+ }
+
+ /**
+ * Returns the stdout stream instance.
+ *
+ * @return The stream instance or <code>null</code>.
+ */
+ public InputStream getStdoutStream() {
+ return stdout;
+ }
+
+ /**
+ * Sets the stderr stream instance.
+ *
+ * @param stderr The stream instance or <code>null</code>.
+ */
+ public void setStderrStream(InputStream stderr) {
+ this.stderr = stderr;
+ }
+
+ /**
+ * Returns the stderr stream instance.
+ *
+ * @return The stream instance or <code>null</code>.
+ */
+ public InputStream getStderrStream() {
+ return stderr;
+ }
+
+ /**
+ * Sets if the process requires a local echo from the terminal widget.
+ *
+ * @param value Specify <code>true</code> to enable the local echo, <code>false</code> otherwise.
+ */
+ public void setLocalEcho(boolean value) {
+ this.localEcho = value;
+ }
+
+ /**
+ * Returns <code>true</code> if the process requires a local echo
+ * from the terminal widget.
+ *
+ * @return <code>True</code> if local echo is enabled, <code>false</code> otherwise.
+ */
+ public boolean isLocalEcho() {
+ return localEcho;
+ }
+
+ /**
+ * Sets the stream line separator.
+ *
+ * @param separator The stream line separator <code>null</code>.
+ */
+ public void setLineSeparator(String separator) {
+ this.lineSeparator = separator;
+ }
+
+ /**
+ * Returns the stream line separator.
+ *
+ * @return The stream line separator or <code>null</code>.
+ */
+ public String getLineSeparator() {
+ return lineSeparator;
+ }
+
+ /**
+ * Sets the list of stdout listeners.
+ *
+ * @param listeners The list of stdout listeners or <code>null</code>.
+ */
+ public void setStdOutListeners(ITerminalServiceOutputStreamMonitorListener[] listeners) {
+ this.stdoutListeners = listeners;
+ }
+
+ /**
+ * Returns the list of stdout listeners.
+ *
+ * @return The list of stdout listeners or <code>null</code>.
+ */
+ public ITerminalServiceOutputStreamMonitorListener[] getStdOutListeners() {
+ return stdoutListeners;
+ }
+
+ /**
+ * Sets the list of stderr listeners.
+ *
+ * @param listeners The list of stderr listeners or <code>null</code>.
+ */
+ public void setStdErrListeners(ITerminalServiceOutputStreamMonitorListener[] listeners) {
+ this.stderrListeners = listeners;
+ }
+
+ /**
+ * Returns the list of stderr listeners.
+ *
+ * @return The list of stderr listeners or <code>null</code>.
+ */
+ public ITerminalServiceOutputStreamMonitorListener[] getStdErrListeners() {
+ return stderrListeners;
+ }
+
+ /**
+ * Loads the streams settings from the given settings store.
+ *
+ * @param store The settings store. Must not be <code>null</code>.
+ */
+ public void load(ISettingsStore store) {
+ Assert.isNotNull(store);
+ localEcho = Boolean.parseBoolean(store.get("LocalEcho", Boolean.FALSE.toString())); //$NON-NLS-1$
+ lineSeparator = store.get("LineSeparator", null); //$NON-NLS-1$
+ if (store instanceof SettingsStore) {
+ stdin = (OutputStream)((SettingsStore)store).getSettings().get("stdin"); //$NON-NLS-1$
+ stdout = (InputStream)((SettingsStore)store).getSettings().get("stdout"); //$NON-NLS-1$
+ stderr = (InputStream)((SettingsStore)store).getSettings().get("stderr"); //$NON-NLS-1$
+ stdoutListeners = (ITerminalServiceOutputStreamMonitorListener[])((SettingsStore)store).getSettings().get("StdOutListeners"); //$NON-NLS-1$
+ stderrListeners = (ITerminalServiceOutputStreamMonitorListener[])((SettingsStore)store).getSettings().get("StdErrListeners"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Saves the process settings to the given settings store.
+ *
+ * @param store The settings store. Must not be <code>null</code>.
+ */
+ public void save(ISettingsStore store) {
+ Assert.isNotNull(store);
+ store.put("LocalEcho", Boolean.toString(localEcho)); //$NON-NLS-1$
+ store.put("LineSeparator", lineSeparator); //$NON-NLS-1$
+ if (store instanceof SettingsStore) {
+ ((SettingsStore)store).getSettings().put("stdin", stdin); //$NON-NLS-1$
+ ((SettingsStore)store).getSettings().put("stdout", stdout); //$NON-NLS-1$
+ ((SettingsStore)store).getSettings().put("stderr", stderr); //$NON-NLS-1$
+ ((SettingsStore)store).getSettings().put("StdOutListeners", stdoutListeners); //$NON-NLS-1$
+ ((SettingsStore)store).getSettings().put("StdErrListeners", stderrListeners); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabCommandFieldHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabCommandFieldHandler.java
new file mode 100644
index 000000000..c6f5e814d
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabCommandFieldHandler.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.tabs;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.tm.internal.terminal.control.CommandInputFieldWithHistory;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.ui.services.IDisposable;
+
+/**
+ * Tab command input field handler implementation.
+ */
+@SuppressWarnings("restriction")
+public class TabCommandFieldHandler implements IDisposable, IAdaptable {
+ // Reference to the parent tab folder manager
+ private final TabFolderManager tabFolderManager;
+ // Reference to the associated tab
+ private final CTabItem item;
+
+ // Reference to the command input field
+ private CommandInputFieldWithHistory field;
+ // The command field history
+ private String history;
+
+ /**
+ * Constructor.
+ *
+ * @param tabFolderManager The parent tab folder manager. Must not be <code>null</code>
+ * @param item The associated tab item. Must not be <code>null</code>.
+ */
+ public TabCommandFieldHandler(TabFolderManager tabFolderManager, CTabItem item) {
+ Assert.isNotNull(tabFolderManager);
+ this.tabFolderManager = tabFolderManager;
+ Assert.isNotNull(item);
+ this.item = item;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.services.IDisposable#dispose()
+ */
+ @Override
+ public void dispose() {
+ field = null;
+ history = null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (TabFolderManager.class.equals(adapter)) {
+ return tabFolderManager;
+ }
+ if (CTabItem.class.equals(adapter)) {
+ return item;
+ }
+ return null;
+ }
+
+ /**
+ * Returns if or if not the associated tab item has the command input field enabled.
+ *
+ * @return <code>True</code> if the command input field is enabled, <code>false</code> otherwise.
+ */
+ public boolean hasCommandInputField() {
+ return field != null;
+ }
+
+ /**
+ * Set the command input field on or off.
+ *
+ * @param on <code>True</code> for on, <code>false</code> for off.
+ */
+ public void setCommandInputField(boolean on) {
+ // save the old history
+ if (field != null) {
+ history = field.getHistory();
+ field = null;
+ }
+
+ if (on) {
+ field = new CommandInputFieldWithHistory(100);
+ field.setHistory(history);
+ }
+
+ // Apply to the terminal control
+ Assert.isTrue(!item.isDisposed());
+ ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
+ if (terminal != null) terminal.setCommandInputField(field);
+ }
+
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabDisposeListener.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabDisposeListener.java
new file mode 100644
index 000000000..382b734fe
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabDisposeListener.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2011 - 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.tabs;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.tcf.te.core.terminals.TerminalServiceFactory;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService;
+import org.eclipse.tcf.te.ui.terminals.services.TerminalService;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+
+/**
+ * Terminals tab default dispose listener implementation.
+ */
+@SuppressWarnings("restriction")
+public class TabDisposeListener implements DisposeListener {
+ private final TabFolderManager parentTabFolderManager;
+
+ /**
+ * Constructor.
+ *
+ * @param parentTabFolderManager The parent tab folder manager. Must not be <code>null</code>
+ */
+ public TabDisposeListener(TabFolderManager parentTabFolderManager) {
+ Assert.isNotNull(parentTabFolderManager);
+ this.parentTabFolderManager = parentTabFolderManager;
+ }
+
+ /**
+ * Returns the parent terminal console tab folder manager instance.
+ *
+ * @return The parent terminal console tab folder manager instance.
+ */
+ protected final TabFolderManager getParentTabFolderManager() {
+ return parentTabFolderManager;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+ */
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ // If a tab item gets disposed, we have to dispose the terminal as well
+ if (e.getSource() instanceof CTabItem) {
+ // Get the terminal control (if any) from the tab item
+ Object candidate = ((CTabItem)e.getSource()).getData();
+ if (candidate instanceof ITerminalViewControl) ((ITerminalViewControl)candidate).disposeTerminal();
+ // Dispose the command input field handler
+ parentTabFolderManager.disposeTabCommandFieldHandler((CTabItem)e.getSource());
+
+ // If all items got removed, we have to switch back to the empty page control
+ if (parentTabFolderManager.getTabFolder() != null && parentTabFolderManager.getTabFolder().getItemCount() == 0) {
+ parentTabFolderManager.getParentView().switchToEmptyPageControl();
+ }
+ // Fire selection changed event
+ parentTabFolderManager.fireSelectionChanged();
+ // Fire the terminal console disposed event
+ ITerminalService service = TerminalServiceFactory.getService();
+ if (service instanceof TerminalService) {
+ ((TerminalService)service).fireTerminalTabEvent(TerminalService.TAB_DISPOSED, e.getSource(), ((CTabItem)e.getSource()).getData("customData")); //$NON-NLS-1$
+ }
+ }
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderManager.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderManager.java
new file mode 100644
index 000000000..34dda2459
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderManager.java
@@ -0,0 +1,794 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.tabs;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ImageConsts;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tm.internal.terminal.control.ITerminalListener;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.control.TerminalViewControlFactory;
+import org.eclipse.tm.internal.terminal.emulator.VT100TerminalControl;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
+import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Terminals tab folder manager.
+ */
+@SuppressWarnings({ "restriction" })
+public class TabFolderManager extends PlatformObject implements ISelectionProvider {
+ // Reference to the parent terminal consoles view
+ private final ITerminalsView parentView;
+ // Reference to the selection listener instance
+ private final SelectionListener selectionListener;
+
+ /**
+ * List of selection changed listeners.
+ */
+ private final List<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>();
+
+ /**
+ * Map of tab command input field handler per tab item
+ */
+ private final Map<CTabItem, TabCommandFieldHandler> commandFieldHandler = new HashMap<CTabItem, TabCommandFieldHandler>();
+
+ /**
+ * The terminal control selection listener implementation.
+ */
+ private class TerminalControlSelectionListener implements DisposeListener, MouseListener {
+ private final ITerminalViewControl terminal;
+ private boolean selectMode;
+
+ /**
+ * Constructor.
+ *
+ * @param terminal The terminal control. Must not be <code>null</code>.
+ */
+ public TerminalControlSelectionListener(ITerminalViewControl terminal) {
+ Assert.isNotNull(terminal);
+ this.terminal = terminal;
+
+ // Register ourself as the required listener
+ terminal.getControl().addDisposeListener(this);
+ terminal.getControl().addMouseListener(this);
+ }
+
+ /**
+ * Returns the associated terminal view control.
+ *
+ * @return The terminal view control.
+ */
+ protected final ITerminalViewControl getTerminal() {
+ return terminal;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+ */
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ // Widget got disposed, check if it is ours
+ // If a tab item gets disposed, we have to dispose the terminal as well
+ if (e.getSource().equals(terminal.getControl())) {
+ // Remove as listener
+ getTerminal().getControl().removeDisposeListener(this);
+ getTerminal().getControl().removeMouseListener(this);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseDown(MouseEvent e) {
+ // Left button down -> select mode starts
+ if (e.button == 1) selectMode = true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseUp(MouseEvent e) {
+ if (e.button == 1 && selectMode) {
+ selectMode = false;
+ // Fire a selection changed event with the terminal controls selection
+ try {
+ Display display = PlatformUI.getWorkbench().getDisplay();
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ fireSelectionChanged(new StructuredSelection(getTerminal().getSelection()));
+ }
+ });
+ }
+ catch (Exception ex) {
+ // if display is disposed, silently ignore.
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param parentView The parent terminals console view. Must not be <code>null</code>.
+ */
+ public TabFolderManager(ITerminalsView parentView) {
+ super();
+ Assert.isNotNull(parentView);
+ this.parentView = parentView;
+
+ // Attach a selection listener to the tab folder
+ selectionListener = doCreateTabFolderSelectionListener(this);
+ if (getTabFolder() != null) getTabFolder().addSelectionListener(selectionListener);
+ }
+
+ /**
+ * Creates the terminal console tab folder selection listener instance.
+ *
+ * @param parent The parent terminal console tab folder manager. Must not be <code>null</code>.
+ * @return The selection listener instance.
+ */
+ protected TabFolderSelectionListener doCreateTabFolderSelectionListener(TabFolderManager parent) {
+ Assert.isNotNull(parent);
+ return new TabFolderSelectionListener(parent);
+ }
+
+ /**
+ * Returns the parent terminal consoles view.
+ *
+ * @return The terminal consoles view instance.
+ */
+ protected final ITerminalsView getParentView() {
+ return parentView;
+ }
+
+ /**
+ * Returns the tab folder associated with the parent view.
+ *
+ * @return The tab folder or <code>null</code>.
+ */
+ protected final CTabFolder getTabFolder() {
+ return (CTabFolder)getParentView().getAdapter(CTabFolder.class);
+ }
+
+ /**
+ * Returns the selection changed listeners currently registered.
+ *
+ * @return The registered selection changed listeners or an empty array.
+ */
+ protected final ISelectionChangedListener[] getSelectionChangedListeners() {
+ return selectionChangedListeners.toArray(new ISelectionChangedListener[selectionChangedListeners.size()]);
+ }
+
+ /**
+ * Dispose the tab folder manager instance.
+ */
+ public void dispose() {
+ // Dispose the selection listener
+ if (getTabFolder() != null && !getTabFolder().isDisposed()) getTabFolder().removeSelectionListener(selectionListener);
+ // Dispose the tab command field handler
+ for (TabCommandFieldHandler handler : commandFieldHandler.values()) {
+ handler.dispose();
+ }
+ commandFieldHandler.clear();
+ }
+
+ /**
+ * Creates a new tab item with the given title and connector.
+ *
+ * @param title The tab title. Must not be <code>null</code>.
+ * @param encoding The terminal encoding or <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ * @param flags The flags controlling how the console is opened or <code>null</code> to use defaults.
+ *
+ * @return The created tab item or <code>null</code> if failed.
+ */
+ @SuppressWarnings({ "unused", "unchecked" })
+ public CTabItem createTabItem(String title, String encoding, ITerminalConnector connector, Object data, Map<String, Boolean> flags) {
+ Assert.isNotNull(title);
+ Assert.isNotNull(connector);
+
+ // The result tab item
+ CTabItem item = null;
+
+ // Get the tab folder from the parent viewer
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder != null) {
+ // Generate a unique title string for the new tab item (must be called before creating the item itself)
+ title = makeUniqueTitle(title, tabFolder);
+ // Create the tab item
+ item = new CTabItem(tabFolder, SWT.CLOSE);
+ // Set the tab item title
+ item.setText(title);
+ // Set the tab icon
+ Image image = getTabItemImage(connector, data);
+ if (image != null) item.setImage(image);
+
+ // Setup the tab item listeners
+ setupTerminalTabListeners(item);
+
+ // Create the composite to create the terminal control within
+ Composite composite = new Composite(tabFolder, SWT.NONE);
+ composite.setLayout(new FillLayout());
+ // Associate the composite with the tab item
+ item.setControl(composite);
+
+ // Refresh the layout
+ tabFolder.getParent().layout(true);
+
+ // Create the terminal control
+ ITerminalViewControl terminal = TerminalViewControlFactory.makeControl(doCreateTerminalTabTerminalListener(this, item), composite, new ITerminalConnector[] { connector }, true);
+ if (terminal instanceof ITerminalControl && data instanceof Map<?,?>) {
+ Object value = ((Map<String, Object>)data).get(ITerminalsConnectorConstants.PROP_DATA_NO_RECONNECT);
+ boolean noReconnect = value instanceof Boolean ? ((Boolean)value).booleanValue() : false;
+ ((ITerminalControl)terminal).setConnectOnEnterIfClosed(!noReconnect);
+ }
+
+ // Add the "selection" listener to the terminal control
+ new TerminalControlSelectionListener(terminal);
+ // Configure the terminal encoding
+ try { terminal.setEncoding(encoding); } catch (UnsupportedEncodingException e) { /* ignored on purpose */ }
+ // Associated the terminal with the tab item
+ item.setData(terminal);
+ // Associated the custom data node with the tab item (if any)
+ if (data != null) item.setData("customData", data); //$NON-NLS-1$
+ // Set the property that marks the item has a disconnect button or not if selected
+ item.setData(ITerminalsConnectorConstants.PROP_HAS_DISCONNECT_BUTTON, flags != null && flags.containsKey(ITerminalsConnectorConstants.PROP_HAS_DISCONNECT_BUTTON) ? flags.get(ITerminalsConnectorConstants.PROP_HAS_DISCONNECT_BUTTON) : Boolean.FALSE);
+
+ // Overwrite the text canvas help id
+ String contextHelpId = getParentView().getContextHelpId();
+ if (contextHelpId != null) {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(terminal.getControl(), contextHelpId);
+ }
+
+ // Set the context menu
+ TabFolderMenuHandler menuHandler = (TabFolderMenuHandler)getParentView().getAdapter(TabFolderMenuHandler.class);
+ if (menuHandler != null) {
+ Menu menu = (Menu)menuHandler.getAdapter(Menu.class);
+ if (menu != null) {
+ // One weird occurrence of IllegalArgumentException: Widget has wrong parent.
+ // Inspecting the code, this seem extremely unlikely. The terminal is created
+ // from a composite parent, the composite parent from the tab folder and the menu
+ // from the tab folder. Means, at the end all should have the same menu shell, shouldn't they?
+ try {
+ terminal.getControl().setMenu(menu);
+ } catch (IllegalArgumentException e) {
+ // Log exception only if debug mode is set to 1.
+ if (UIPlugin.getTraceHandler().isSlotEnabled(1, null)) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ // Select the created item within the tab folder
+ tabFolder.setSelection(item);
+
+ // Set the connector
+ terminal.setConnector(connector);
+
+ // And connect the terminal
+ terminal.connectTerminal();
+
+ // Fire selection changed event
+ fireSelectionChanged();
+ }
+
+ // Return the create tab item finally.
+ return item;
+ }
+
+ /**
+ * Used for DnD of terminal tab items between terminal views
+ * <p>
+ * Create a new tab item in the "dropped" terminal view using the
+ * information stored in the given item.
+ *
+ * @param oldItem The old dragged tab item. Must not be <code>null</code>.
+ * @return The new dropped tab item.
+ */
+ @SuppressWarnings("unchecked")
+ public CTabItem cloneTabItemAfterDrop(CTabItem oldItem) {
+ Assert.isNotNull(oldItem);
+
+ ITerminalViewControl terminal = (ITerminalViewControl)oldItem.getData();
+ ITerminalConnector connector = terminal.getTerminalConnector();
+ Object data = oldItem.getData("customData"); //$NON-NLS-1$
+ Map<String, Object> properties = (Map<String, Object>)oldItem.getData("properties"); //$NON-NLS-1$
+ String title = oldItem.getText();
+
+ // The result tab item
+ CTabItem item = null;
+
+ // Get the tab folder from the parent viewer
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder != null) {
+ // Generate a unique title string for the new tab item (must be called before creating the item itself)
+ title = makeUniqueTitle(title, tabFolder);
+ // Create the tab item
+ item = new CTabItem(tabFolder, SWT.CLOSE);
+ // Set the tab item title
+ item.setText(title);
+ // Set the tab icon
+ Image image = getTabItemImage(connector, data);
+ if (image != null) item.setImage(image);
+
+ // Setup the tab item listeners
+ setupTerminalTabListeners(item);
+
+ // Create the composite to create the terminal control within
+ Composite composite = new Composite(tabFolder, SWT.NONE);
+ composite.setLayout(new FillLayout());
+ // Associate the composite with the tab item
+ item.setControl(composite);
+
+ // Refresh the layout
+ tabFolder.getParent().layout(true);
+
+ // change the "parent".
+ //
+ // Note: We have to cast to VT100TerminalControl here until setupTerminal is
+ // re-exposed to clients via the ITerminalControl.
+ Assert.isTrue(terminal instanceof VT100TerminalControl);
+ ((VT100TerminalControl)terminal).setupTerminal(composite);
+
+ item.setData(terminal);
+
+ // Associate the custom data node with the tab item (if any)
+ if (data != null) item.setData("customData", data); //$NON-NLS-1$
+ // Associate the properties with the tab item (if any)
+ if (properties != null) item.setData("properties", properties); //$NON-NLS-1$
+
+ // Overwrite the text canvas help id
+ String contextHelpId = getParentView().getContextHelpId();
+ if (contextHelpId != null) {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(terminal.getControl(), contextHelpId);
+ }
+
+ // Set the context menu
+ TabFolderMenuHandler menuHandler = (TabFolderMenuHandler)getParentView().getAdapter(TabFolderMenuHandler.class);
+ if (menuHandler != null) {
+ Menu menu = (Menu)menuHandler.getAdapter(Menu.class);
+ if (menu != null) {
+ // One weird occurrence of IllegalArgumentException: Widget has wrong parent.
+ // Inspecting the code, this seem extremely unlikely. The terminal is created
+ // from a composite parent, the composite parent from the tab folder and the menu
+ // from the tab folder. Means, at the end all should have the same menu shell, shouldn't they?
+ try {
+ terminal.getControl().setMenu(menu);
+ } catch (IllegalArgumentException e) {
+ // Log exception only if debug mode is set to 1.
+ if (UIPlugin.getTraceHandler().isSlotEnabled(1, null)) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ // Select the created item within the tab folder
+ tabFolder.setSelection(item);
+
+ // Set the connector
+ terminal.setConnector(connector);
+
+ // needed to get the focus and cursor
+ Assert.isTrue(terminal instanceof ITerminalControl);
+ ((ITerminalControl)terminal).setState(TerminalState.CONNECTED);
+
+ // Fire selection changed event
+ fireSelectionChanged();
+ }
+
+ // Return the create tab item finally.
+ return item;
+ }
+
+
+ /**
+ * Generate a unique title string based on the given proposal.
+ *
+ * @param proposal The proposal. Must not be <code>null</code>.
+ * @return The unique title string.
+ */
+ protected String makeUniqueTitle(String proposal, CTabFolder tabFolder) {
+ Assert.isNotNull(proposal);
+ Assert.isNotNull(tabFolder);
+
+ String title = proposal;
+ int index = 0;
+
+ // Loop all existing tab items and check the titles. We have to remember
+ // all found titles as modifying the proposal might in turn conflict again
+ // with the title of a tab already checked.
+ List<String> titles = new ArrayList<String>();
+ for (CTabItem item : tabFolder.getItems()) {
+ // Get the tab item title
+ titles.add(item.getText());
+ }
+ // Make the proposal unique be appending (<n>) against all known titles.
+ while (titles.contains(title)) title = proposal + " (" + ++index + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ return title;
+ }
+
+ /**
+ * Setup the terminal console tab item listeners.
+ *
+ * @param item The tab item. Must not be <code>null</code>.
+ */
+ protected void setupTerminalTabListeners(final CTabItem item) {
+ Assert.isNotNull(item);
+
+ // Create and associate the disposal listener
+ DisposeListener disposeListener = doCreateTerminalTabDisposeListener(this);
+
+ // store the listener to make access easier e.g. needed in DnD
+ item.setData("disposeListener", disposeListener); //$NON-NLS-1$
+ item.addDisposeListener(disposeListener);
+ }
+
+ /**
+ * Creates a new terminal console tab terminal listener instance.
+ *
+ * @param tabFolderManager The tab folder manager. Must not be <code>null</code>.
+ * @param item The tab item. Must not be <code>null</code>.
+ *
+ * @return The terminal listener instance.
+ */
+ protected ITerminalListener doCreateTerminalTabTerminalListener(TabFolderManager tabFolderManager, CTabItem item) {
+ Assert.isNotNull(item);
+ return new TabTerminalListener(tabFolderManager, item);
+ }
+
+ /**
+ * Creates a new terminal console tab dispose listener instance.
+ *
+ * @param parent The parent terminal console tab folder manager. Must not be <code>null</code>.
+ * @return The dispose listener instance.
+ */
+ protected DisposeListener doCreateTerminalTabDisposeListener(TabFolderManager parent) {
+ Assert.isNotNull(parent);
+ return new TabDisposeListener(parent);
+ }
+
+ /**
+ * Returns the tab item image.
+ *
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ *
+ * @return The tab item image or <code>null</code>.
+ */
+ protected Image getTabItemImage(ITerminalConnector connector, Object data) {
+ Assert.isNotNull(connector);
+ return UIPlugin.getImage(ImageConsts.VIEW_Terminals);
+ }
+
+ /**
+ * Lookup a tab item with the given title and the given terminal connector.
+ * <p>
+ * <b>Note:</b> The method will handle unified tab item titles itself.
+ *
+ * @param title The tab item title. Must not be <code>null</code>.
+ * @param connector The terminal connector. Must not be <code>null</code>.
+ * @param data The custom terminal data node or <code>null</code>.
+ *
+ * @return The corresponding tab item or <code>null</code>.
+ */
+ public CTabItem findTabItem(String title, ITerminalConnector connector, Object data) {
+ Assert.isNotNull(title);
+ Assert.isNotNull(connector);
+
+ // Get the tab folder
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder == null) return null;
+
+ // Loop all existing tab items and try to find a matching title
+ for (CTabItem item : tabFolder.getItems()) {
+ // Disposed items cannot be matched
+ if (item.isDisposed()) continue;
+ // Get the title from the current tab item
+ String itemTitle = item.getText();
+ // The terminal console state might be signaled to the user via the
+ // terminal console tab title. Filter out any prefix "<.*>\s*".
+ itemTitle = itemTitle.replaceFirst("^<.*>\\s*", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ if (itemTitle.startsWith(title)) {
+ // The title string matches -> double check with the terminal connector
+ ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
+ ITerminalConnector connector2 = terminal.getTerminalConnector();
+ // If the connector id and name matches -> check on the settings
+ if (connector.getId().equals(connector2.getId()) && connector.getName().equals(connector2.getName())) {
+ if (!connector.isInitialized()) {
+ // an uninitialized connector does not yield a sensible summary
+ return item;
+ }
+ String summary = connector.getSettingsSummary();
+ String summary2 = connector2.getSettingsSummary();
+ // If we have matching settings -> we've found the matching item
+ if (summary.equals(summary2)) return item;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Make the given tab item the active tab and bring the tab to the top.
+ *
+ * @param item The tab item. Must not be <code>null</code>.
+ */
+ public void bringToTop(CTabItem item) {
+ Assert.isNotNull(item);
+
+ // Get the tab folder
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder == null) return;
+
+ // Set the given tab item as selection to the tab folder
+ tabFolder.setSelection(item);
+ // Fire selection changed event
+ fireSelectionChanged();
+ }
+
+ /**
+ * Returns the currently active tab.
+ *
+ * @return The active tab item or <code>null</code> if none.
+ */
+ public CTabItem getActiveTabItem() {
+ // Get the tab folder
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder == null) return null;
+
+ return tabFolder.getSelection();
+ }
+
+ /**
+ * Remove all terminated tab items.
+ */
+ public void removeTerminatedItems() {
+ // Get the tab folder
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder == null) return;
+
+ // Loop the items and check for terminated status
+ for (CTabItem item: tabFolder.getItems()) {
+ // Disposed items cannot be matched
+ if (item.isDisposed()) continue;
+ // Check if the item is terminated
+ if (isTerminatedTabItem(item)) {
+ // item is terminated -> dispose
+ item.dispose();
+ }
+ }
+ }
+
+ /**
+ * Checks if the given tab item represents a terminated console. Subclasses may
+ * overwrite this method to extend the definition of terminated.
+ *
+ * @param item The tab item or <code>null</code>.
+ * @return <code>True</code> if the tab item represents a terminated console, <code>false</code> otherwise.
+ */
+ protected boolean isTerminatedTabItem(CTabItem item) {
+ // Null items or disposed items cannot be matched
+ if (item == null || item.isDisposed()) return false;
+
+ // First, match the item title. If it contains "<terminated>", the item can be removed
+ String itemTitle = item.getText();
+ if (itemTitle != null && itemTitle.contains("<terminated>")) { //$NON-NLS-1$
+ return true;
+ }
+ // Second, check if the associated terminal control is closed
+ // The title string matches -> double check with the terminal connector
+ ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
+ if (terminal != null && terminal.getState() == TerminalState.CLOSED) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the command input field handler for the given tab item.
+ *
+ * @param item The tab item or <code>null</code>.
+ * @return The command input field handler or <code>null</code>.
+ */
+ public final TabCommandFieldHandler getTabCommandFieldHandler(CTabItem item) {
+ // Null items or disposed items cannot be matched
+ if (item == null || item.isDisposed()) return null;
+
+ TabCommandFieldHandler handler = commandFieldHandler.get(item);
+ if (handler == null) {
+ handler = new TabCommandFieldHandler(this, item);
+ commandFieldHandler.put(item, handler);
+ }
+ return handler;
+ }
+
+ /**
+ * Dispose the command input field handler for the given tab item.
+ *
+ * @param item The tab item or <code>null</code>.
+ */
+ protected void disposeTabCommandFieldHandler(CTabItem item) {
+ // Null items or disposed items cannot be matched
+ if (item == null || item.isDisposed()) return;
+
+ TabCommandFieldHandler handler = commandFieldHandler.remove(item);
+ if (handler != null) handler.dispose();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ if (listener != null && !selectionChangedListeners.contains(listener)) selectionChangedListeners.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ if (listener != null) selectionChangedListeners.remove(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
+ */
+ @Override
+ public ISelection getSelection() {
+ CTabItem activeTabItem = getActiveTabItem();
+ return activeTabItem != null ? new StructuredSelection(activeTabItem) : new StructuredSelection();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
+ */
+ @Override
+ public void setSelection(ISelection selection) {
+ if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+ // The first selection element which is a CTabItem will become the active item
+ Iterator<?> iterator = ((IStructuredSelection)selection).iterator();
+ while (iterator.hasNext()) {
+ Object candidate = iterator.next();
+ if (candidate instanceof CTabItem) { bringToTop((CTabItem)candidate); return; }
+ }
+ }
+ // fire a changed event in any case
+ fireSelectionChanged(selection);
+ }
+
+ /**
+ * Fire the selection changed event to the registered listeners.
+ */
+ protected void fireSelectionChanged() {
+ updateStatusLine();
+ fireSelectionChanged(getSelection());
+ }
+
+ /**
+ * Fire the selection changed event to the registered listeners.
+ */
+ protected final void fireSelectionChanged(ISelection selection) {
+ // Create the selection changed event
+ SelectionChangedEvent event = new SelectionChangedEvent(TabFolderManager.this, selection);
+
+ // First, invoke the registered listeners and let them do their job
+ for (ISelectionChangedListener listener : selectionChangedListeners) {
+ listener.selectionChanged(event);
+ }
+ }
+
+ /**
+ * Update the parent view status line.
+ */
+ public final void updateStatusLine() {
+ String message = null;
+ IStatusLineManager manager = parentView.getViewSite().getActionBars().getStatusLineManager();
+
+ CTabItem item = getActiveTabItem();
+ if (item != null && !item.isDisposed()) {
+ ITerminalViewControl terminal = (ITerminalViewControl)item.getData();
+ if (terminal != null && !terminal.isDisposed()) {
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(state2msg(item, terminal.getState()));
+ buffer.append(" - "); //$NON-NLS-1$
+
+ String encoding = terminal.getEncoding();
+ if (encoding == null || "ISO-8859-1".equals(encoding)) { //$NON-NLS-1$
+ encoding = "Default (ISO-8859-1)"; //$NON-NLS-1$
+ }
+ buffer.append(NLS.bind(Messages.TabFolderManager_encoding, encoding));
+
+ message = buffer.toString();
+ }
+ }
+
+ manager.setMessage(message);
+ }
+
+ /**
+ * Returns the string representation of the given terminal state.
+ *
+ * @param item The tab folder item. Must not be <code>null</code>.
+ * @param state The terminal state. Must not be <code>null</code>.
+ *
+ * @return The string representation.
+ */
+ @SuppressWarnings("unchecked")
+ protected String state2msg(CTabItem item, TerminalState state) {
+ Assert.isNotNull(item);
+ Assert.isNotNull(state);
+
+ // Determine the terminal properties of the tab folder
+ Map<String, Object> properties = (Map<String, Object>)item.getData("properties"); //$NON-NLS-1$
+
+ // Get he current terminal state as string
+ String stateStr = state.toString();
+ // Lookup a matching text representation of the state
+ String key = "TabFolderManager_state_" + stateStr.replaceAll("\\.", " ").trim().toLowerCase(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ String stateMsg = null;
+ if (properties != null) stateMsg = properties.get(key) instanceof String ? (String) properties.get(key) : null;
+ if (stateMsg == null) stateMsg = Messages.getString(key);
+ if (stateMsg == null) stateMsg = stateStr;
+
+ return stateMsg;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderMenuHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderMenuHandler.java
new file mode 100644
index 000000000..3a3e745c0
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderMenuHandler.java
@@ -0,0 +1,385 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.tabs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IMenuListener2;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.actions.SelectEncodingAction;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction;
+import org.eclipse.tm.internal.terminal.control.actions.TerminalActionClearAll;
+import org.eclipse.tm.internal.terminal.control.actions.TerminalActionCopy;
+import org.eclipse.tm.internal.terminal.control.actions.TerminalActionPaste;
+import org.eclipse.tm.internal.terminal.control.actions.TerminalActionSelectAll;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+import org.eclipse.ui.IWorkbenchActionConstants;
+
+/**
+ * Terminals tab folder menu handler.
+ */
+@SuppressWarnings("restriction")
+public class TabFolderMenuHandler extends PlatformObject {
+ // Reference to the parent terminals console view
+ private final ITerminalsView parentView;
+ // Reference to the tab folder context menu manager
+ private MenuManager contextMenuManager;
+ // Reference to the tab folder context menu
+ private Menu contextMenu;
+ // The list of actions available within the context menu
+ private final List<AbstractTerminalAction> contextMenuActions = new ArrayList<AbstractTerminalAction>();
+
+ // The list of invalid context menu contributions "startsWith" expressions
+ /* default */ static final String[] INVALID_CONTRIBUTIONS_STARTS_WITH = {
+ "org.eclipse.cdt", "org.eclipse.ui.edit" //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ /**
+ * Default menu listener implementation.
+ */
+ protected class MenuListener implements IMenuListener2 {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.action.IMenuListener2#menuAboutToHide(org.eclipse.jface.action.IMenuManager)
+ */
+ @Override
+ public void menuAboutToHide(IMenuManager manager) {
+ // CQ:WIND00192293 and CQ:WIND194204 - don't update actions on menuAboutToHide
+ // See also http://bugs.eclipse.org/296212
+ // updateMenuItems(false);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager)
+ */
+ @Override
+ public void menuAboutToShow(IMenuManager manager) {
+ removeInvalidContributions(manager);
+ updateMenuItems(true);
+ }
+
+ /**
+ * Bug 392249: Remove contributions that appear in the context in Eclipse 4.x which are
+ * not visible in Eclipse 3.8.x. Re-evaluate from time to time!
+ *
+ * @param manager The menu manager or <code>null</code>
+ */
+ private void removeInvalidContributions(IMenuManager manager) {
+ if (manager == null) return;
+
+ IContributionItem[] items = manager.getItems();
+ for (IContributionItem item : items) {
+ String id = item.getId();
+ if (id != null) {
+ for (String prefix : INVALID_CONTRIBUTIONS_STARTS_WITH) {
+ if (id.startsWith(prefix)) {
+ manager.remove(item);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param parentView The parent terminals console view. Must not be <code>null</code>.
+ */
+ public TabFolderMenuHandler(ITerminalsView parentView) {
+ super();
+ Assert.isNotNull(parentView);
+ this.parentView = parentView;
+ }
+
+ /**
+ * Returns the parent terminals console view.
+ *
+ * @return The parent terminals console view instance.
+ */
+ protected final ITerminalsView getParentView() {
+ return parentView;
+ }
+
+ /**
+ * Returns the tab folder associated with the parent view.
+ *
+ * @return The tab folder or <code>null</code>.
+ */
+ protected final CTabFolder getTabFolder() {
+ return (CTabFolder)getParentView().getAdapter(CTabFolder.class);
+ }
+
+ /**
+ * Dispose the tab folder menu handler instance.
+ */
+ public void dispose() {
+ // Dispose the context menu
+ if (contextMenu != null) { contextMenu.dispose(); contextMenu = null; }
+ // Dispose the context menu manager
+ if (contextMenuManager != null) { contextMenuManager.dispose(); contextMenuManager = null; }
+ // Clear all actions
+ contextMenuActions.clear();
+ }
+
+ /**
+ * Setup the context menu for the tab folder. The method will return
+ * immediately if the menu handler had been initialized before.
+ *
+ * @param tabFolder The tab folder control. Must not be <code>null</code>.
+ */
+ public void initialize() {
+ // Return immediately if the menu manager and menu got initialized already
+ if (contextMenuManager != null && contextMenu != null) {
+ return;
+ }
+
+ // Get the tab folder
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder == null) {
+ return;
+ }
+
+ // Create the menu manager if not done before
+ contextMenuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+
+ // Bug 392249: Register our menu listener after registering the context menu
+ // for contributions. That way we can use our menu listener to get
+ // rid of unwanted/misguided contributions. At least until this is
+ // fixed in the Eclipse 4.x platform.
+
+ // Create the context menu
+ contextMenu = contextMenuManager.createContextMenu(tabFolder);
+
+ // Create the context menu action instances
+ doCreateContextMenuActions();
+
+ // Fill the context menu
+ doFillContextMenu(contextMenuManager);
+
+ // Register to the view site to open the menu for contributions
+ getParentView().getSite().registerContextMenu(contextMenuManager, getParentView().getSite().getSelectionProvider());
+
+ // Create and associated the menu listener
+ contextMenuManager.addMenuListener(new MenuListener());
+ }
+
+ /**
+ * Adds the given action to the context menu actions list.
+ *
+ * @param action The action instance. Must not be <code>null</code>.
+ */
+ protected final void add(AbstractTerminalAction action) {
+ Assert.isNotNull(action);
+ contextMenuActions.add(action);
+ }
+
+ /**
+ * Create the context menu actions.
+ */
+ protected void doCreateContextMenuActions() {
+ // Create and add the copy action
+ add(new TerminalActionCopy() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+
+ // Create and add the paste action
+ add(new TerminalActionPaste() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+ // Determine if pasting to the active tab require backslash translation
+ boolean needsTranslation = false;
+
+ TabFolderManager manager = (TabFolderManager)getParentView().getAdapter(TabFolderManager.class);
+ if (manager != null) {
+ // If we have the active tab item, we can get the active terminal control
+ CTabItem activeTabItem = manager.getActiveTabItem();
+ if (activeTabItem != null) {
+ Map<String, Object> properties = (Map<String, Object>)activeTabItem.getData("properties"); //$NON-NLS-1$
+ if (properties != null && properties.containsKey(ITerminalsConnectorConstants.PROP_TRANSLATE_BACKSLASHES_ON_PASTE)) {
+ Object value = properties.get(ITerminalsConnectorConstants.PROP_TRANSLATE_BACKSLASHES_ON_PASTE);
+ needsTranslation = value instanceof Boolean ? ((Boolean)value).booleanValue() : false;
+ }
+ }
+ }
+
+ if (needsTranslation) {
+ ITerminalViewControl target = getTarget();
+ if (target != null && target.getClipboard() != null && !target.getClipboard().isDisposed()) {
+ String text = (String) target.getClipboard().getContents(TextTransfer.getInstance());
+ if (text != null) {
+ text = text.replace('\\', '/');
+
+ Object[] data = new Object[] { text };
+ Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
+ target.getClipboard().setContents(data, types, DND.CLIPBOARD);
+ }
+ }
+ }
+
+ super.run();
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+
+ // Create and add the clear all action
+ add(new TerminalActionClearAll() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.internal.terminal.control.actions.TerminalActionPaste#updateAction(boolean)
+ */
+ @Override
+ public void updateAction(boolean aboutToShow) {
+ super.updateAction(aboutToShow);
+ if (getTarget() != null && getTarget().getState() != TerminalState.CONNECTED) {
+ setEnabled(false);
+ }
+ }
+ });
+
+ // Create and add the select all action
+ add(new TerminalActionSelectAll() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+
+ // Create and add the select encoding action
+ add (new SelectEncodingAction((TabFolderManager)getParentView().getAdapter(TabFolderManager.class)) {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+ }
+
+ /**
+ * Returns the currently active terminal control.
+ *
+ * @return The currently active terminal control or <code>null</code>.
+ */
+ protected ITerminalViewControl getActiveTerminalViewControl() {
+ ITerminalViewControl terminal = null;
+
+ // Get the active tab item from the tab folder manager
+ TabFolderManager manager = (TabFolderManager)getParentView().getAdapter(TabFolderManager.class);
+ if (manager != null) {
+ // If we have the active tab item, we can get the active terminal control
+ CTabItem activeTabItem = manager.getActiveTabItem();
+ if (activeTabItem != null) {
+ terminal = (ITerminalViewControl)activeTabItem.getData();
+ }
+ }
+
+ return terminal;
+ }
+
+ /**
+ * Fill in the context menu content within the given manager.
+ *
+ * @param manager The menu manager. Must not be <code>null</code>.
+ */
+ protected void doFillContextMenu(MenuManager manager) {
+ Assert.isNotNull(manager);
+
+ // Loop all actions and add them to the menu manager
+ for (AbstractTerminalAction action : contextMenuActions) {
+ manager.add(action);
+ // Add a separator after the paste action
+ if (action instanceof TerminalActionPaste) {
+ manager.add(new Separator());
+ }
+ // Add a separator after the select all action
+ if (action instanceof TerminalActionSelectAll) {
+ manager.add(new Separator());
+ }
+ }
+
+ // Menu contributions will end up here
+ manager.add(new Separator());
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ /**
+ * Update the context menu items on showing or hiding the context menu.
+ *
+ * @param aboutToShow <code>True</code> if the menu is about to show, <code>false</code> otherwise.
+ */
+ protected void updateMenuItems(boolean aboutToShow) {
+ // Loop all actions and update the status
+ for (AbstractTerminalAction action : contextMenuActions) {
+ action.updateAction(aboutToShow);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (MenuManager.class.isAssignableFrom(adapter)) {
+ return contextMenuManager;
+ } else if (Menu.class.isAssignableFrom(adapter)) {
+ return contextMenu;
+ }
+
+ // Try the parent view
+ Object adapted = getParentView().getAdapter(adapter);
+ if (adapted != null) {
+ return adapted;
+ }
+
+ return super.getAdapter(adapter);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderSelectionListener.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderSelectionListener.java
new file mode 100644
index 000000000..37e4110cb
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderSelectionListener.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.tabs;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+
+/**
+ * Terminals tab folder default selection listener implementation.
+ */
+public class TabFolderSelectionListener implements SelectionListener {
+ private final TabFolderManager parentTabFolderManager;
+
+ /**
+ * Constructor.
+ *
+ * @param parentTabFolderManager The parent tab folder manager. Must not be <code>null</code>
+ */
+ public TabFolderSelectionListener(TabFolderManager parentTabFolderManager) {
+ Assert.isNotNull(parentTabFolderManager);
+ this.parentTabFolderManager = parentTabFolderManager;
+ }
+
+ /**
+ * Returns the parent terminal console tab folder manager instance.
+ *
+ * @return The parent terminal console tab folder manager instance.
+ */
+ protected final TabFolderManager getParentTabFolderManager() {
+ return parentTabFolderManager;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ parentTabFolderManager.fireSelectionChanged();
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderToolbarHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderToolbarHandler.java
new file mode 100644
index 000000000..07d1a7868
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabFolderToolbarHandler.java
@@ -0,0 +1,371 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2014 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361363] [TERMINALS] Implement "Pin&Clone" for the "Terminals" view
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.tabs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.tcf.te.ui.terminals.actions.AbstractAction;
+import org.eclipse.tcf.te.ui.terminals.actions.PinTerminalAction;
+import org.eclipse.tcf.te.ui.terminals.actions.TabScrollLockAction;
+import org.eclipse.tcf.te.ui.terminals.actions.ToggleCommandFieldAction;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction;
+import org.eclipse.tm.internal.terminal.control.actions.TerminalActionClearAll;
+import org.eclipse.tm.internal.terminal.control.actions.TerminalActionCopy;
+import org.eclipse.tm.internal.terminal.control.actions.TerminalActionPaste;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IWorkbenchActionConstants;
+
+
+/**
+ * Terminals tab folder toolbar handler.
+ */
+@SuppressWarnings("restriction")
+public class TabFolderToolbarHandler extends PlatformObject {
+ // Reference to the parent terminals console view
+ private final ITerminalsView parentView;
+ // Reference to the toolbar manager
+ private IToolBarManager toolbarManager;
+ // Reference to the selection listener
+ private ToolbarSelectionChangedListener selectionChangedListener;
+ // The list of actions available within the toolbar
+ private final List<AbstractTerminalAction> toolbarActions = new ArrayList<AbstractTerminalAction>();
+
+ /**
+ * Default selection listener implementation.
+ */
+ protected class ToolbarSelectionChangedListener implements ISelectionChangedListener {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ boolean enable = event != null;
+
+ // The VlmConsoleTabFolderManager is listening to the selection changes of the
+ // TabFolder and fires selection changed events.
+ if (enable && event.getSource() instanceof TabFolderManager) {
+ enable = event.getSelection() instanceof StructuredSelection
+ && !event.getSelection().isEmpty()
+ && (((StructuredSelection)event.getSelection()).getFirstElement() instanceof CTabItem
+ || ((StructuredSelection)event.getSelection()).getFirstElement() instanceof String);
+ }
+
+ updateToolbarItems(enable);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param parentView The parent terminals console view. Must not be <code>null</code>.
+ */
+ public TabFolderToolbarHandler(ITerminalsView parentView) {
+ super();
+ Assert.isNotNull(parentView);
+ this.parentView = parentView;
+ }
+
+ /**
+ * Returns the parent terminals console view.
+ *
+ * @return The terminals console view instance.
+ */
+ protected final ITerminalsView getParentView() {
+ return parentView;
+ }
+
+ /**
+ * Returns the tab folder associated with the parent view.
+ *
+ * @return The tab folder or <code>null</code>.
+ */
+ protected final CTabFolder getTabFolder() {
+ return (CTabFolder)getParentView().getAdapter(CTabFolder.class);
+ }
+
+ /**
+ * Returns the currently active terminal control.
+ *
+ * @return The currently active terminal control or <code>null</code>.
+ */
+ public ITerminalViewControl getActiveTerminalViewControl() {
+ ITerminalViewControl terminal = null;
+
+ // Get the active tab item from the tab folder manager
+ TabFolderManager manager = (TabFolderManager)getParentView().getAdapter(TabFolderManager.class);
+ if (manager != null) {
+ // If we have the active tab item, we can get the active terminal control
+ CTabItem activeTabItem = manager.getActiveTabItem();
+ if (activeTabItem != null && !activeTabItem.isDisposed()) {
+ terminal = (ITerminalViewControl)activeTabItem.getData();
+ }
+ }
+
+ return terminal;
+ }
+
+ /**
+ * Dispose the tab folder menu handler instance.
+ */
+ public void dispose() {
+ // Dispose the selection changed listener
+ if (selectionChangedListener != null) {
+ getParentView().getViewSite().getSelectionProvider().removeSelectionChangedListener(selectionChangedListener);
+ selectionChangedListener = null;
+ }
+
+ // Clear all actions
+ toolbarActions.clear();
+ }
+
+ /**
+ * Setup the context menu for the tab folder. The method will return
+ * immediately if the toolbar handler had been initialized before.
+ *
+ * @param tabFolder The tab folder control. Must not be <code>null</code>.
+ */
+ public void initialize() {
+ // Return immediately if the toolbar manager got initialized already
+ if (toolbarManager != null) {
+ return;
+ }
+
+ // Register ourself as selection listener to the tab folder
+ selectionChangedListener = doCreateSelectionChangedListener();
+ Assert.isNotNull(selectionChangedListener);
+ getParentView().getViewSite().getSelectionProvider().addSelectionChangedListener(selectionChangedListener);
+
+ // Get the parent view action bars
+ IActionBars bars = getParentView().getViewSite().getActionBars();
+
+ // From the action bars, get the toolbar manager
+ toolbarManager = bars.getToolBarManager();
+
+ // Create the toolbar action instances
+ doCreateToolbarActions();
+
+ // Fill the toolbar
+ doFillToolbar(toolbarManager);
+
+ // Update actions
+ updateToolbarItems(false);
+ }
+
+ /**
+ * Creates a new selection changed listener instance.
+ *
+ * @return The new selection changed listener instance.
+ */
+ protected ToolbarSelectionChangedListener doCreateSelectionChangedListener() {
+ return new ToolbarSelectionChangedListener();
+ }
+
+ /**
+ * Adds the given action to the toolbar actions list.
+ *
+ * @param action The action instance. Must not be <code>null</code>.
+ */
+ protected final void add(AbstractTerminalAction action) {
+ Assert.isNotNull(action);
+ toolbarActions.add(action);
+ }
+
+ /**
+ * Removes the given action from the toolbar actions list.
+ *
+ * @param action The action instance. Must not be <code>null</code>.
+ */
+ protected final void remove(AbstractTerminalAction action) {
+ Assert.isNotNull(action);
+ toolbarActions.remove(action);
+ }
+
+ /**
+ * Create the toolbar actions.
+ */
+ protected void doCreateToolbarActions() {
+ // Create and add the paste action
+ add(new TerminalActionPaste() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+
+ // Create and add the copy action
+ add(new TerminalActionCopy() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+
+ // Create and add the scroll lock action
+ add (new TabScrollLockAction() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+
+ // Create and add the clear all action
+ add(new TerminalActionClearAll() {
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.internal.terminal.control.actions.TerminalActionPaste#updateAction(boolean)
+ */
+ @Override
+ public void updateAction(boolean aboutToShow) {
+ super.updateAction(aboutToShow);
+ if (getTarget() != null && getTarget().getState() != TerminalState.CONNECTED) {
+ setEnabled(false);
+ }
+ }
+ });
+
+ // Create and add the toggle command input field action
+ add (new ToggleCommandFieldAction(getParentView()) {
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+
+ // Create and add the pin view action
+ add (new PinTerminalAction(getParentView()) {
+ /* (non-Javadoc)
+ * @see org.eclipse.tm.internal.terminal.control.actions.AbstractTerminalAction#getTarget()
+ */
+ @Override
+ protected ITerminalViewControl getTarget() {
+ return getActiveTerminalViewControl();
+ }
+ });
+ }
+
+ /**
+ * Fill in the context menu content within the given manager.
+ *
+ * @param manager The menu manager. Must not be <code>null</code>.
+ */
+ protected void doFillToolbar(IToolBarManager manager) {
+ Assert.isNotNull(manager);
+
+ // Note: For the toolbar, the actions are added from left to right!
+ // So we start with the additions marker here which is the most
+ // left contribution item.
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ manager.add(new Separator("anchor")); //$NON-NLS-1$
+
+ // we want that at the end
+ PinTerminalAction pinAction=null;
+
+ // Loop all actions and add them to the menu manager
+ for (AbstractTerminalAction action : toolbarActions) {
+ // Add a separator before the clear all action or if the action is a separator
+ if (action instanceof TabScrollLockAction
+ || (action instanceof AbstractAction && ((AbstractAction)action).isSeparator())) {
+ manager.insertAfter("anchor", new Separator()); //$NON-NLS-1$
+ }
+ // skip pin action for now
+ if(action instanceof PinTerminalAction){
+ pinAction=(PinTerminalAction)action;
+ continue;
+ }
+ // Add the action itself
+ manager.insertAfter("anchor", action); //$NON-NLS-1$
+ }
+ // now add pin at the end
+ if(pinAction!=null){
+ manager.add(pinAction);
+ }
+ }
+
+ /**
+ * Update the toolbar items.
+ *
+ * @param enabled <code>True</code> if the items shall be enabled, <code>false</code> otherwise.
+ */
+ protected void updateToolbarItems(boolean enabled) {
+ // Determine the currently active terminal control
+ ITerminalViewControl control = getActiveTerminalViewControl();
+ // Loop all actions and update the status
+ for (AbstractTerminalAction action : toolbarActions) {
+ // If the terminal control is not available, the updateAction
+ // method of certain actions enable the action (bugzilla #260372).
+ // Workaround by forcing the action to get disabled with setEnabled.
+ if (control == null && !(action instanceof PinTerminalAction)) {
+ action.setEnabled(false);
+ }
+ else {
+ action.updateAction(enabled);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (IToolBarManager.class.isAssignableFrom(adapter)) {
+ return toolbarManager;
+ }
+
+ // Try the toolbar actions
+ for (AbstractTerminalAction action : toolbarActions) {
+ if (adapter.isAssignableFrom(action.getClass())) {
+ return action;
+ }
+ }
+
+ // Try the parent view
+ Object adapted = getParentView().getAdapter(adapter);
+ if (adapted != null) {
+ return adapted;
+ }
+
+ return super.getAdapter(adapter);
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabTerminalListener.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabTerminalListener.java
new file mode 100644
index 000000000..efd1b2f87
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/tabs/TabTerminalListener.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.tabs;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tm.internal.terminal.control.ITerminalListener;
+import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
+
+/**
+ * Terminals tab default terminal listener implementation.
+ */
+@SuppressWarnings("restriction")
+public class TabTerminalListener implements ITerminalListener {
+ /* default */ final TabFolderManager tabFolderManager;
+ private final CTabItem tabItem;
+ private final String tabItemTitle;
+
+ /**
+ * Constructor.
+ *
+ * @param tabFolderManager The parent tab folder manager. Must not be <code>null</code>.
+ * @param tabItem The parent tab item. Must not be <code>null</code>.
+ */
+ public TabTerminalListener(TabFolderManager tabFolderManager, CTabItem tabItem) {
+ super();
+ Assert.isNotNull(tabFolderManager);
+ this.tabFolderManager = tabFolderManager;
+ Assert.isNotNull(tabItem);
+ this.tabItem = tabItem;
+
+ // Remember the original tab item title
+ tabItemTitle = tabItem.getText();
+ }
+
+ /**
+ * Returns the associated parent tab item.
+ *
+ * @return The parent tab item.
+ */
+ protected final CTabItem getTabItem() {
+ return tabItem;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.ITerminalListener#setState(org.eclipse.tcf.internal.terminal.provisional.api.TerminalState)
+ */
+ @Override
+ public void setState(final TerminalState state) {
+ // The tab item must have been not yet disposed
+ final CTabItem item = getTabItem();
+ if (item == null || item.isDisposed()) return;
+
+ // Run asynchronously in the display thread
+ item.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // Update the tab item title
+ String newTitle = getTerminalConsoleTabTitle(state);
+ if (newTitle != null) item.setText(newTitle);
+
+ // Turn off the command field (if necessary)
+ TabCommandFieldHandler handler = tabFolderManager.getTabCommandFieldHandler(item);
+ if (TerminalState.CLOSED.equals(state) && handler != null && handler.hasCommandInputField()) {
+ handler.setCommandInputField(false);
+ // Trigger a selection changed event to update the action enablements
+ // and the status line
+ ISelectionProvider provider = tabFolderManager.getParentView().getViewSite().getSelectionProvider();
+ Assert.isNotNull(provider);
+ provider.setSelection(provider.getSelection());
+ } else {
+ // Update the status line
+ tabFolderManager.updateStatusLine();
+ }
+ }
+ });
+ }
+
+ /**
+ * Returns the title to set to the terminal console tab for the given state.
+ * <p>
+ * <b>Note:</b> This method is called from {@link #setState(TerminalState)} and
+ * is expected to by called within the UI thread.
+ *
+ * @param state The terminal state. Must not be <code>null</code>.
+ * @return The terminal console tab title to set or <code>null</code> to leave the title unchanged.
+ */
+ protected String getTerminalConsoleTabTitle(TerminalState state) {
+ Assert.isNotNull(state);
+ Assert.isNotNull(Display.findDisplay(Thread.currentThread()));
+
+ // The tab item must have been not yet disposed
+ CTabItem item = getTabItem();
+ if (item == null || item.isDisposed()) return null;
+
+ // Get the current tab title
+ String oldTitle = item.getText();
+
+ // Construct the new title
+ String newTitle = null;
+
+ if (TerminalState.CLOSED.equals(state)) {
+ newTitle = NLS.bind(Messages.TabTerminalListener_consoleClosed, tabItemTitle, tabFolderManager.state2msg(item, state));
+ }
+ else if (TerminalState.CONNECTING.equals(state)) {
+ newTitle = NLS.bind(Messages.TabTerminalListener_consoleConnecting, tabItemTitle, tabFolderManager.state2msg(item, state));
+ }
+ else if (TerminalState.CONNECTED.equals(state)) {
+ newTitle = tabItemTitle;
+ }
+
+ return newTitle != null && !newTitle.equals(oldTitle) ? newTitle : null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.internal.terminal.control.ITerminalListener#setTerminalTitle(java.lang.String)
+ */
+ @Override
+ public void setTerminalTitle(String title) {
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsView.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsView.java
new file mode 100644
index 000000000..4120959fd
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsView.java
@@ -0,0 +1,705 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ * Max Weninger (Wind River) - [361363] [TERMINALS] Implement "Pin&Clone" for the "Terminals" view
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.view;
+
+import java.util.Iterator;
+import java.util.UUID;
+
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.core.expressions.EvaluationContext;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.dnd.ByteArrayTransfer;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.tcf.te.ui.terminals.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView;
+import org.eclipse.tcf.te.ui.terminals.nls.Messages;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderManager;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderMenuHandler;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderToolbarHandler;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchPreferenceConstants;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.part.IShowInTarget;
+import org.eclipse.ui.part.PageBook;
+import org.eclipse.ui.part.ShowInContext;
+import org.eclipse.ui.part.ViewPart;
+import org.osgi.framework.Bundle;
+
+/**
+ * Terminals view.
+ */
+public class TerminalsView extends ViewPart implements ITerminalsView, IShowInTarget {
+
+ // Reference to the main page book control
+ private PageBook pageBookControl;
+ // Reference to the tab folder maintaining the consoles
+ /* default */ CTabFolder tabFolderControl;
+ // Reference to the tab folder manager
+ /* default */ TabFolderManager tabFolderManager;
+ // Reference to the tab folder menu handler
+ private TabFolderMenuHandler tabFolderMenuHandler;
+ // Reference to the tab folder toolbar handler
+ private TabFolderToolbarHandler tabFolderToolbarHandler;
+ // Reference to the empty page control (to be show if no console is open)
+ private Control emptyPageControl;
+ // Whether this terminal is pinned.
+ private boolean pinned = false;
+ // The view's memento handler
+ private final TerminalsViewMementoHandler mementoHandler = new TerminalsViewMementoHandler();
+
+ /**
+ * "dummy" transfer just to store the information needed for the DnD
+ *
+ */
+ private static class TerminalTransfer extends ByteArrayTransfer {
+ // The static terminal transfer type name. Unique per terminals view instance.
+ private static final String TYPE_NAME = "terminal-transfer-format:" + UUID.randomUUID().toString(); //$NON-NLS-1$
+ // Register the type name and remember the associated unique type id.
+ private static final int TYPEID = registerType(TYPE_NAME);
+
+ private CTabItem draggedFolderItem;
+ private TabFolderManager draggedTabFolderManager;
+
+ /*
+ * Thread save singleton instance creation.
+ */
+ private static class LazyInstanceHolder {
+ public static TerminalTransfer instance = new TerminalTransfer();
+ }
+
+ /**
+ * Constructor.
+ */
+ TerminalTransfer() {
+ }
+
+ /**
+ * Returns the singleton terminal transfer instance.
+ * @return
+ */
+ public static TerminalTransfer getInstance() {
+ return LazyInstanceHolder.instance;
+ }
+
+ /**
+ * Sets the dragged folder item.
+ *
+ * @param tabFolderItem The dragged folder item or <code>null</code>.
+ */
+ public void setDraggedFolderItem(CTabItem tabFolderItem) {
+ draggedFolderItem = tabFolderItem;
+ }
+
+ /**
+ * Returns the dragged folder item.
+ *
+ * @return The dragged folder item or <code>null</code>.
+ */
+ public CTabItem getDraggedFolderItem() {
+ return draggedFolderItem;
+ }
+
+ /**
+ * Sets the tab folder manager the associated folder item is dragged from.
+ *
+ * @param tabFolderManager The tab folder manager or <code>null</code>.
+ */
+ public void setTabFolderManager(TabFolderManager tabFolderManager) {
+ draggedTabFolderManager = tabFolderManager;
+ }
+
+ /**
+ * Returns the tab folder manager the associated folder item is dragged from.
+ *
+ * @return The tab folder manager or <code>null</code>.
+ */
+ public TabFolderManager getTabFolderManager() {
+ return draggedTabFolderManager;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.dnd.Transfer#getTypeIds()
+ */
+ @Override
+ protected int[] getTypeIds() {
+ return new int[] { TYPEID };
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.dnd.Transfer#getTypeNames()
+ */
+ @Override
+ protected String[] getTypeNames() {
+ return new String[] { TYPE_NAME };
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.dnd.ByteArrayTransfer#javaToNative(java.lang.Object, org.eclipse.swt.dnd.TransferData)
+ */
+ @Override
+ public void javaToNative(Object data, TransferData transferData) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.dnd.ByteArrayTransfer#nativeToJava(org.eclipse.swt.dnd.TransferData)
+ */
+ @Override
+ public Object nativeToJava(TransferData transferData) {
+ return null;
+ }
+ }
+
+ /**
+ * Constructor.
+ */
+ public TerminalsView() {
+ super();
+ }
+
+ /**
+ * Initialize the drag support.
+ */
+ private void addDragSupport() {
+ // The event listener is registered as filter. It will receive events from all widgets.
+ PlatformUI.getWorkbench().getDisplay().addFilter(SWT.DragDetect, new Listener() {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ @Override
+ public void handleEvent(Event event) {
+ // Only handle events where a CTabFolder is the source
+ if (!(event.widget instanceof CTabFolder)) return;
+ // TabFolderManager must be created
+ if (tabFolderManager == null) return;
+
+ // only for own tab folders
+ if (event.widget != tabFolderControl) return;
+
+ // Skip drag if DnD is still ongoing (bug 443787)
+ if (tabFolderControl.getData(DND.DRAG_SOURCE_KEY) != null) return;
+
+ final CTabFolder draggedFolder = (CTabFolder) event.widget;
+
+ int operations = DND.DROP_MOVE | DND.DROP_DEFAULT;
+ final DragSource dragSource = new DragSource(draggedFolder, operations);
+
+ // Initialize the terminal transfer type data
+ TerminalTransfer.getInstance().setDraggedFolderItem(tabFolderManager.getActiveTabItem());
+ TerminalTransfer.getInstance().setTabFolderManager(tabFolderManager);
+
+ Transfer[] transferTypes = new Transfer[] { TerminalTransfer.getInstance() };
+ dragSource.setTransfer(transferTypes);
+
+ // Add a drag source listener to cleanup after the drag operation finished
+ dragSource.addDragListener(new DragSourceListener() {
+ @Override
+ public void dragStart(DragSourceEvent event) {
+ }
+
+ @Override
+ public void dragSetData(DragSourceEvent event) {
+ }
+
+ @Override
+ public void dragFinished(DragSourceEvent event) {
+ // dispose this drag-source-listener by disposing its drag-source
+ dragSource.dispose();
+
+ // Inhibit the action of CTabFolder's default DragDetect-listeners,
+ // fire a mouse-click event on the widget that was dragged.
+ draggedFolder.notifyListeners(SWT.MouseUp, null);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Initialize the drop support on the terminals page book control.
+ */
+ private void addDropSupport() {
+ int operations = DND.DROP_MOVE | DND.DROP_DEFAULT;
+ final DropTarget target = new DropTarget(pageBookControl, operations);
+
+ Transfer[] transferTypes = new Transfer[] { TerminalTransfer.getInstance() };
+ target.setTransfer(transferTypes);
+
+ target.addDropListener(new DropTargetListener() {
+ @Override
+ public void dragEnter(DropTargetEvent event) {
+ // only if the drop target is different then the drag source
+ if (TerminalTransfer.getInstance().getTabFolderManager() == tabFolderManager) {
+ event.detail = DND.DROP_NONE;
+ }
+ else {
+ event.detail = DND.DROP_MOVE;
+ }
+ }
+
+ @Override
+ public void dragOver(DropTargetEvent event) {
+ }
+
+ @Override
+ public void dragOperationChanged(DropTargetEvent event) {
+ }
+
+ @Override
+ public void dragLeave(DropTargetEvent event) {
+ }
+
+ @Override
+ public void dropAccept(DropTargetEvent event) {
+ }
+
+ @Override
+ public void drop(DropTargetEvent event) {
+ if (TerminalTransfer.getInstance().getDraggedFolderItem() != null && tabFolderManager != null) {
+ CTabItem draggedItem = TerminalTransfer.getInstance().getDraggedFolderItem();
+
+ CTabItem item = tabFolderManager.cloneTabItemAfterDrop(draggedItem);
+ tabFolderManager.bringToTop(item);
+ switchToTabFolderControl();
+
+ // need to remove the dispose listener first
+ DisposeListener disposeListener = (DisposeListener) draggedItem.getData("disposeListener"); //$NON-NLS-1$
+ draggedItem.removeDisposeListener(disposeListener);
+ draggedItem.dispose();
+
+ // make sure the "new" terminals view has the focus after dragging a terminal
+ setFocus();
+ }
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#dispose()
+ */
+ @Override
+ public void dispose() {
+ // Dispose the tab folder manager
+ if (tabFolderManager != null) {
+ tabFolderManager.dispose();
+ tabFolderManager = null;
+ }
+ // Dispose the tab folder menu handler
+ if (tabFolderMenuHandler != null) {
+ tabFolderMenuHandler.dispose();
+ tabFolderMenuHandler = null;
+ }
+ // Dispose the tab folder toolbar handler
+ if (tabFolderToolbarHandler != null) {
+ tabFolderToolbarHandler.dispose();
+ tabFolderToolbarHandler = null;
+ }
+
+ super.dispose();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
+ */
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException {
+ super.init(site, memento);
+ restoreState(memento);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createPartControl(Composite parent) {
+ // Create the page book control
+ pageBookControl = doCreatePageBookControl(parent);
+ Assert.isNotNull(pageBookControl);
+ // Configure the page book control
+ doConfigurePageBookControl(pageBookControl);
+
+ // Create the empty page control
+ emptyPageControl = doCreateEmptyPageControl(pageBookControl);
+ Assert.isNotNull(emptyPageControl);
+ // Configure the empty page control
+ doConfigureEmptyPageControl(emptyPageControl);
+
+ // Create the tab folder control (empty)
+ tabFolderControl = doCreateTabFolderControl(pageBookControl);
+ Assert.isNotNull(tabFolderControl);
+ // Configure the tab folder control
+ doConfigureTabFolderControl(tabFolderControl);
+
+ // Create the tab folder manager
+ tabFolderManager = doCreateTabFolderManager(this);
+ Assert.isNotNull(tabFolderManager);
+ // Set the tab folder manager as the selection provider
+ getSite().setSelectionProvider(tabFolderManager);
+
+ // Setup the tab folder menu handler
+ tabFolderMenuHandler = doCreateTabFolderMenuHandler(this);
+ Assert.isNotNull(tabFolderMenuHandler);
+ doConfigureTabFolderMenuHandler(tabFolderMenuHandler);
+
+ // Setup the tab folder toolbar handler
+ tabFolderToolbarHandler = doCreateTabFolderToolbarHandler(this);
+ Assert.isNotNull(tabFolderToolbarHandler);
+ doConfigureTabFolderToolbarHandler(tabFolderToolbarHandler);
+
+ // Show the empty page control by default
+ switchToEmptyPageControl();
+
+ String secondaryId = ((IViewSite) getSite()).getSecondaryId();
+ if (secondaryId != null) {
+ String defaultTitle = getPartName();
+ // set title
+ setPartName(defaultTitle + " " + secondaryId); //$NON-NLS-1$
+ }
+
+ // Initialize DnD support
+ addDragSupport();
+ addDropSupport();
+ }
+
+ /**
+ * Creates the {@link PageBook} instance.
+ *
+ * @param parent The parent composite. Must not be <code>null</code>.
+ * @return The page book instance. Must never be <code>null</code>.
+ */
+ protected PageBook doCreatePageBookControl(Composite parent) {
+ return new PageBook(parent, SWT.NONE);
+ }
+
+ /**
+ * Configure the given page book control.
+ *
+ * @param pagebook The page book control. Must not be <code>null</code>.
+ */
+ protected void doConfigurePageBookControl(PageBook pagebook) {
+ Assert.isNotNull(pagebook);
+
+ if (getContextHelpId() != null) PlatformUI.getWorkbench().getHelpSystem().setHelp(pagebook, getContextHelpId());
+ }
+
+ /**
+ * Returns the context help id associated with the terminals console view instance.
+ * <p>
+ * <b>Note:</b> The default implementation returns the view id as context help id.
+ *
+ * @return The context help id or <code>null</code> if none is associated.
+ */
+ @Override
+ public String getContextHelpId() {
+ return getViewSite().getId();
+ }
+
+ /**
+ * Creates the empty page control instance.
+ *
+ * @param parent The parent composite. Must not be <code>null</code>.
+ * @return The empty page control instance. Must never be <code>null</code>.
+ */
+ protected Control doCreateEmptyPageControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ return composite;
+ }
+
+ /**
+ * Configures the empty page control.
+ *
+ * @param control The empty page control. Must not be <code>null</code>.
+ */
+ protected void doConfigureEmptyPageControl(Control control) {
+ Assert.isNotNull(control);
+ }
+
+ /**
+ * Creates the tab folder control instance.
+ *
+ * @param parent The parent composite. Must not be <code>null</code>.
+ * @return The tab folder control instance. Must never be <code>null</code>.
+ */
+ protected CTabFolder doCreateTabFolderControl(Composite parent) {
+ return new CTabFolder(parent, SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM | SWT.FLAT | SWT.BORDER);
+ }
+
+ /**
+ * Configures the tab folder control.
+ *
+ * @param tabFolder The tab folder control. Must not be <code>null</code>.
+ */
+ protected void doConfigureTabFolderControl(CTabFolder tabFolder) {
+ Assert.isNotNull(tabFolder);
+
+ // Set the layout data
+ tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // Set the tab gradient coloring from the global preferences
+ if (useGradientTabBackgroundColor()) {
+ tabFolder.setSelectionBackground(new Color[] {
+ JFaceResources.getColorRegistry().get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START"), //$NON-NLS-1$
+ JFaceResources.getColorRegistry().get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_END") //$NON-NLS-1$
+ }, new int[] { 100 }, true);
+ }
+ // Apply the tab folder selection foreground color
+ tabFolder.setSelectionForeground(JFaceResources.getColorRegistry().get("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR")); //$NON-NLS-1$
+
+ // Set the tab style from the global preferences
+ tabFolder.setSimple(PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS));
+ }
+
+ /**
+ * If <code>True</code> is returned, the inner tabs are colored with gradient coloring set in
+ * the Eclipse workbench color settings.
+ *
+ * @return <code>True</code> to use gradient tab colors, <code>false</code> otherwise.
+ */
+ protected boolean useGradientTabBackgroundColor() {
+ return false;
+ }
+
+ /**
+ * Creates the tab folder manager.
+ *
+ * @param parentView The parent view instance. Must not be <code>null</code>.
+ * @return The tab folder manager. Must never be <code>null</code>.
+ */
+ protected TabFolderManager doCreateTabFolderManager(ITerminalsView parentView) {
+ Assert.isNotNull(parentView);
+ return new TabFolderManager(parentView);
+ }
+
+ /**
+ * Creates the tab folder menu handler.
+ *
+ * @param parentView The parent view instance. Must not be <code>null</code>.
+ * @return The tab folder menu handler. Must never be <code>null</code>.
+ */
+ protected TabFolderMenuHandler doCreateTabFolderMenuHandler(ITerminalsView parentView) {
+ Assert.isNotNull(parentView);
+ return new TabFolderMenuHandler(parentView);
+ }
+
+ /**
+ * Configure the tab folder menu handler
+ *
+ * @param menuHandler The tab folder menu handler. Must not be <code>null</code>.
+ */
+ protected void doConfigureTabFolderMenuHandler(TabFolderMenuHandler menuHandler) {
+ Assert.isNotNull(menuHandler);
+ menuHandler.initialize();
+ }
+
+ /**
+ * Creates the tab folder toolbar handler.
+ *
+ * @param parentView The parent view instance. Must not be <code>null</code>.
+ * @return The tab folder toolbar handler. Must never be <code>null</code>.
+ */
+ protected TabFolderToolbarHandler doCreateTabFolderToolbarHandler(ITerminalsView parentView) {
+ Assert.isNotNull(parentView);
+ return new TabFolderToolbarHandler(parentView);
+ }
+
+ /**
+ * Configure the tab folder toolbar handler
+ *
+ * @param toolbarHandler The tab folder toolbar handler. Must not be <code>null</code>.
+ */
+ protected void doConfigureTabFolderToolbarHandler(TabFolderToolbarHandler toolbarHandler) {
+ Assert.isNotNull(toolbarHandler);
+ toolbarHandler.initialize();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
+ */
+ @Override
+ public void setFocus() {
+ if (pageBookControl != null) pageBookControl.setFocus();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView#switchToEmptyPageControl()
+ */
+ @Override
+ public void switchToEmptyPageControl() {
+ if (pageBookControl != null && !pageBookControl.isDisposed()
+ && emptyPageControl != null && !emptyPageControl.isDisposed()) {
+ pageBookControl.showPage(emptyPageControl);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView#switchToTabFolderControl()
+ */
+ @Override
+ public void switchToTabFolderControl() {
+ if (pageBookControl != null && !pageBookControl.isDisposed()
+ && tabFolderControl != null && !tabFolderControl.isDisposed()) {
+ pageBookControl.showPage(tabFolderControl);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (CTabFolder.class.isAssignableFrom(adapter)) {
+ return tabFolderControl;
+ }
+ if (TabFolderManager.class.isAssignableFrom(adapter)) {
+ return tabFolderManager;
+ }
+ if (TabFolderMenuHandler.class.isAssignableFrom(adapter)) {
+ return tabFolderMenuHandler;
+ }
+ if (TabFolderToolbarHandler.class.isAssignableFrom(adapter)) {
+ return tabFolderToolbarHandler;
+ }
+ if (TerminalsViewMementoHandler.class.isAssignableFrom(adapter)) {
+ return mementoHandler;
+ }
+
+ return super.getAdapter(adapter);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView#setPinned(boolean)
+ */
+ @Override
+ public void setPinned(boolean pin) {
+ this.pinned = pin;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.terminals.interfaces.ITerminalsView#isPinned()
+ */
+ @Override
+ public boolean isPinned() {
+ return pinned;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
+ */
+ @Override
+ public void saveState(IMemento memento) {
+ super.saveState(memento);
+ if (memento == null) return;
+ mementoHandler.saveState(this, memento);
+ }
+
+ /**
+ * Restore the view state from the given memento.
+ *
+ * @param memento The memento or <code>null</code>.
+ */
+ public void restoreState(IMemento memento) {
+ if (memento == null) return;
+ mementoHandler.restoreState(this, memento);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.IShowInTarget#show(org.eclipse.ui.part.ShowInContext)
+ */
+ @Override
+ public boolean show(ShowInContext context) {
+ if (context != null) {
+ // Get the selection from the context
+ ISelection selection = context.getSelection();
+ // The selection must contain elements that can be adapted to IResource
+ if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+ boolean isValid = true;
+
+ Iterator<?> iterator = ((IStructuredSelection)selection).iterator();
+ while (iterator.hasNext() && isValid) {
+ Object element = iterator.next();
+ Object adapted = null;
+
+ Bundle bundle = Platform.getBundle("org.eclipse.core.resources"); //$NON-NLS-1$
+ if (bundle != null && (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.ACTIVE)) {
+ if (element instanceof org.eclipse.core.resources.IResource) continue;
+
+ adapted = element instanceof IAdaptable ? ((IAdaptable)element).getAdapter(org.eclipse.core.resources.IResource.class) : null;
+ if (adapted == null) adapted = Platform.getAdapterManager().getAdapter(element, org.eclipse.core.resources.IResource.class);
+ if (adapted == null) adapted = Platform.getAdapterManager().loadAdapter(element, org.eclipse.core.resources.IResource.class.getName());
+ }
+
+ isValid = adapted != null;
+ }
+
+ // If the selection is valid, fire the command to open the local terminal
+ if (isValid) {
+ ICommandService service = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class);
+ Command command = service != null ? service.getCommand("org.eclipse.tcf.te.ui.terminals.local.command.launch") : null; //$NON-NLS-1$
+ if (command != null && command.isDefined() && command.isEnabled()) {
+ try {
+ ParameterizedCommand pCmd = ParameterizedCommand.generateCommand(command, null);
+ Assert.isNotNull(pCmd);
+ IHandlerService handlerSvc = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class);
+ Assert.isNotNull(handlerSvc);
+ IEvaluationContext ctx = handlerSvc.getCurrentState();
+ ctx = new EvaluationContext(ctx, selection);
+ ctx.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection);
+ handlerSvc.executeCommandInContext(pCmd, null, ctx);
+ } catch (Exception e) {
+ // If the platform is in debug mode, we print the exception to the log view
+ if (Platform.inDebugMode()) {
+ IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
+ Messages.AbstractTriggerCommandHandler_error_executionFailed, e);
+ UIPlugin.getDefault().getLog().log(status);
+ }
+ }
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsViewMementoHandler.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsViewMementoHandler.java
new file mode 100644
index 000000000..4fcd76596
--- /dev/null
+++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals/src/org/eclipse/tcf/te/ui/terminals/view/TerminalsViewMementoHandler.java
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2015 Wind River Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.terminals.view;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.tcf.te.core.terminals.interfaces.ITerminalService;
+import org.eclipse.tcf.te.core.terminals.interfaces.constants.ITerminalsConnectorConstants;
+import org.eclipse.tcf.te.ui.terminals.actions.PinTerminalAction;
+import org.eclipse.tcf.te.ui.terminals.interfaces.ILauncherDelegate;
+import org.eclipse.tcf.te.ui.terminals.interfaces.IMementoHandler;
+import org.eclipse.tcf.te.ui.terminals.launcher.LauncherDelegateManager;
+import org.eclipse.tcf.te.ui.terminals.tabs.TabFolderToolbarHandler;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Take care of the persisted state handling of the "Terminals" view.
+ */
+public class TerminalsViewMementoHandler {
+ // The list of items to save. See the workbench listener implementation
+ // in o.e.tcf.te.ui.terminals.activator.UIPlugin.
+ private final List<CTabItem> saveables = new ArrayList<CTabItem>();
+
+ /**
+ * Sets the list of saveable items.
+ *
+ * @param saveables The list of saveable items. Must not be <code>null</code>.
+ */
+ public void setSaveables(List<CTabItem> saveables) {
+ Assert.isNotNull(saveables);
+ this.saveables.clear();
+ this.saveables.addAll(saveables);
+ }
+
+ /**
+ * Saves the view state in the given memento.
+ *
+ * @param view The terminals view. Must not be <code>null</code>.
+ * @param memento The memento. Must not be <code>null</code>.
+ */
+ @SuppressWarnings("unchecked")
+ public void saveState(TerminalsView view, IMemento memento) {
+ Assert.isNotNull(view);
+ Assert.isNotNull(memento);
+
+ // Create a child element within the memento holding the
+ // connection info of the open, non-terminated tab items
+ memento = memento.createChild("terminalConnections"); //$NON-NLS-1$
+ Assert.isNotNull(memento);
+
+ // Write the view id and secondary id
+ memento.putString("id", view.getViewSite().getId()); //$NON-NLS-1$
+ memento.putString("secondaryId", view.getViewSite().getSecondaryId()); //$NON-NLS-1$
+
+ // Save the pinned state
+ memento.putBoolean("pinned", view.isPinned()); //$NON-NLS-1$
+
+ // Loop the saveable items and store the connection data of each
+ // item to the memento
+ for (CTabItem item : saveables) {
+ // Ignore disposed items
+ if (item.isDisposed()) continue;
+
+ // Get the original terminal properties associated with the tab item
+ Map<String, Object> properties = (Map<String, Object>)item.getData("properties"); //$NON-NLS-1$
+ if (properties == null) continue;
+
+ // Get the terminal launcher delegate
+ String delegateId = (String)properties.get(ITerminalsConnectorConstants.PROP_DELEGATE_ID);
+ ILauncherDelegate delegate = delegateId != null ? LauncherDelegateManager.getInstance().getLauncherDelegate(delegateId, false) : null;
+ IMementoHandler mementoHandler = delegate != null ? (IMementoHandler)delegate.getAdapter(IMementoHandler.class) : null;
+ if (mementoHandler != null) {
+ // Create terminal connection child memento
+ IMemento connectionMemento = memento.createChild("connection"); //$NON-NLS-1$
+ Assert.isNotNull(connectionMemento);
+ // Store the common attributes
+ connectionMemento.putString(ITerminalsConnectorConstants.PROP_DELEGATE_ID, delegateId);
+
+ String terminalConnectorId = (String)properties.get(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID);
+ if (terminalConnectorId != null) {
+ connectionMemento.putString(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID, terminalConnectorId);
+ }
+
+ if (properties.get(ITerminalsConnectorConstants.PROP_FORCE_NEW) instanceof Boolean) {
+ connectionMemento.putBoolean(ITerminalsConnectorConstants.PROP_FORCE_NEW, ((Boolean)properties.get(ITerminalsConnectorConstants.PROP_FORCE_NEW)).booleanValue());
+ }
+
+ // Pass on to the memento handler
+ mementoHandler.saveState(connectionMemento, properties);
+ }
+ }
+ }
+
+ /**
+ * Restore the view state from the given memento.
+ *
+ * @param view The terminals view. Must not be <code>null</code>.
+ * @param memento The memento. Must not be <code>null</code>.
+ */
+ protected void restoreState(final TerminalsView view, IMemento memento) {
+ Assert.isNotNull(view);
+ Assert.isNotNull(memento);
+
+ // Get the "terminalConnections" memento
+ memento = memento.getChild("terminalConnections"); //$NON-NLS-1$
+ if (memento != null) {
+ // Read view id and secondary id
+ String id = memento.getString("id"); //$NON-NLS-1$
+ String secondaryId = memento.getString("secondaryId"); //$NON-NLS-1$
+ if ("null".equals(secondaryId)) secondaryId = null; //$NON-NLS-1$
+
+ final IMemento finMemento = memento;
+ // Restore the pinned state of the after all connections completed
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (finMemento.getBoolean("pinned") != null) { //$NON-NLS-1$
+ asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ view.setPinned(finMemento.getBoolean("pinned").booleanValue()); //$NON-NLS-1$
+
+ TabFolderToolbarHandler toolbarHandler = (TabFolderToolbarHandler)view.getAdapter(TabFolderToolbarHandler.class);
+ if (toolbarHandler != null) {
+ PinTerminalAction action = (PinTerminalAction)toolbarHandler.getAdapter(PinTerminalAction.class);
+ if (action != null) action.setChecked(view.isPinned());
+ }
+ }
+ });
+ }
+ }
+ };
+
+ final AtomicBoolean allProcessed = new AtomicBoolean(false);
+ final List<ITerminalService.Done> callbacks = new ArrayList<ITerminalService.Done>();
+
+ // Get all the "connection" memento's.
+ IMemento[] connections = memento.getChildren("connection"); //$NON-NLS-1$
+ for (IMemento connection : connections) {
+ // Create the properties container that holds the terminal properties
+ Map<String, Object> properties = new HashMap<String, Object>();
+
+ // Set the view id attributes
+ properties.put(ITerminalsConnectorConstants.PROP_ID, id);
+ properties.put(ITerminalsConnectorConstants.PROP_SECONDARY_ID, secondaryId);
+
+ // Restore the common attributes
+ properties.put(ITerminalsConnectorConstants.PROP_DELEGATE_ID, connection.getString(ITerminalsConnectorConstants.PROP_DELEGATE_ID));
+ properties.put(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID, connection.getString(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID));
+ if (connection.getBoolean(ITerminalsConnectorConstants.PROP_FORCE_NEW) != null) {
+ properties.put(ITerminalsConnectorConstants.PROP_FORCE_NEW, connection.getBoolean(ITerminalsConnectorConstants.PROP_FORCE_NEW));
+ }
+
+ // Get the terminal launcher delegate
+ String delegateId = (String)properties.get(ITerminalsConnectorConstants.PROP_DELEGATE_ID);
+ ILauncherDelegate delegate = delegateId != null ? LauncherDelegateManager.getInstance().getLauncherDelegate(delegateId, false) : null;
+ IMementoHandler mementoHandler = delegate != null ? (IMementoHandler)delegate.getAdapter(IMementoHandler.class) : null;
+ if (mementoHandler != null) {
+ // Pass on to the memento handler
+ mementoHandler.restoreState(connection, properties);
+ }
+
+ // Restore the terminal connection
+ if (delegate != null && !properties.isEmpty()) {
+ ITerminalService.Done done = new ITerminalService.Done() {
+ @Override
+ public void done(IStatus status) {
+ callbacks.remove(this);
+ if (allProcessed.get() && callbacks.isEmpty()) {
+ asyncExec(runnable);
+ }
+ }
+ };
+ callbacks.add(done);
+ delegate.execute(properties, done);
+ }
+ }
+
+ allProcessed.set(true);
+ if (callbacks.isEmpty()) asyncExec(runnable);
+ }
+ }
+
+ /**
+ * Executes the given runnable asynchronously in the display thread.
+ *
+ * @param runnable The runnable. Must not be <code>null</code>.
+ */
+ /* default */ void asyncExec(Runnable runnable) {
+ Assert.isNotNull(runnable);
+ if (PlatformUI.getWorkbench() != null && PlatformUI.getWorkbench().getDisplay() != null && !PlatformUI.getWorkbench().getDisplay().isDisposed()) {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(runnable);
+ }
+ }
+}

Back to the top