diff options
Diffstat (limited to 'terminals/plugins/org.eclipse.tcf.te.ui.terminals/src')
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 "Note:". + */ + 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); + } + } +} |