diff options
Diffstat (limited to 'terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui')
9 files changed, 1226 insertions, 0 deletions
diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessConnector.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessConnector.java new file mode 100644 index 000000000..9a8c8fba1 --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessConnector.java @@ -0,0 +1,301 @@ +/******************************************************************************* + * 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.process; + +import java.io.File; +import java.io.IOException; +import java.io.StreamTokenizer; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.tcf.te.core.terminals.interfaces.constants.ILineSeparatorConstants; +import org.eclipse.tcf.te.core.terminals.utils.Env; +import org.eclipse.tcf.te.ui.terminals.manager.ConsoleManager; +import org.eclipse.tcf.te.ui.terminals.process.nls.Messages; +import org.eclipse.tcf.te.ui.terminals.streams.AbstractStreamsConnector; +import org.eclipse.tm.internal.terminal.emulator.VT100Emulator; +import org.eclipse.tm.internal.terminal.emulator.VT100TerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage; +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; + +/** + * Process connector implementation. + */ +@SuppressWarnings("restriction") +public class ProcessConnector extends AbstractStreamsConnector { + // Reference to the process settings + private final ProcessSettings settings; + + // Reference to the PTY instance. + private PTY pty; + // Reference to the launched process instance. + private Process process; + // Reference to the process monitor + private ProcessMonitor monitor; + + // The terminal width and height. Initially unknown. + private int width = -1; + private int height = -1; + + /** + * Constructor. + */ + public ProcessConnector() { + this(new ProcessSettings()); + } + + /** + * Constructor. + * + * @param settings The process settings. Must not be <code>null</code> + */ + public ProcessConnector(ProcessSettings settings) { + super(); + + Assert.isNotNull(settings); + this.settings = settings; + } + + /** + * Returns the process object or <code>null</code> if the + * connector is connector. + * + * @return The process object or <code>null</code>. + */ + public Process getProcess() { + return process; + } + + /* (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); + + pty = null; + width = -1; + height = -1; + + try { + boolean isAnsiTerminal = false; + + // Try to determine process and PTY instance from the process settings + process = settings.getProcess(); + pty = settings.getPTY(); + + // No process -> create PTY on supported platforms and execute + // process image. + if (process == null) { + if (PTY.isSupported(PTY.Mode.TERMINAL)) { + try { + pty = new PTY(PTY.Mode.TERMINAL); + + // Initialize the terminal size + VT100Emulator text = ((VT100TerminalControl)control).getTerminalText(); + text.fontChanged(); + } catch (IOException e) { + // PTY not supported + } + } + + // Build up the command + StringBuilder command = new StringBuilder(settings.getImage()); + String arguments = settings.getArguments(); + if (arguments != null && !"".equals(arguments.trim())) { //$NON-NLS-1$ + // Append to the command now + command.append(" "); //$NON-NLS-1$ + command.append(arguments.trim()); + } + + File workingDir =null; + if (settings.getWorkingDir()!=null){ + workingDir = new File(settings.getWorkingDir()); + } + + String[] envp = null; + if (settings.getEnvironment()!=null){ + envp = settings.getEnvironment(); + } + + if (settings.isMergeWithNativeEnvironment()) { + envp = Env.getEnvironment(envp, true); + } + + isAnsiTerminal = getTermVariable(envp).startsWith("ansi"); //$NON-NLS-1$ + + if (pty != null) { + // A PTY is available -> can use the ProcessFactory. + + // Tokenize the command (ProcessFactory takes an array) + StreamTokenizer st = new StreamTokenizer(new StringReader(command.toString())); + st.resetSyntax(); + st.whitespaceChars(0, 32); + st.whitespaceChars(0xa0, 0xa0); + st.wordChars(33, 255); + st.quoteChar('"'); + st.quoteChar('\''); + + List<String> argv = new ArrayList<String>(); + int ttype = st.nextToken(); + while (ttype != StreamTokenizer.TT_EOF) { + argv.add(st.sval); + ttype = st.nextToken(); + } + + // Execute the process + process = ProcessFactory.getFactory().exec(argv.toArray(new String[argv.size()]), envp, workingDir, pty); + } else { + // No PTY -> just execute via the standard Java Runtime implementation. + process = Runtime.getRuntime().exec(command.toString(), envp, workingDir); + } + } + + String lineSeparator = settings.getLineSeparator(); + if (lineSeparator == null && pty == null) { + lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$ + if ("\r".equals(lineSeparator)) { //$NON-NLS-1$ + lineSeparator = ILineSeparatorConstants.LINE_SEPARATOR_CR; + } + else if ("\n".equals(lineSeparator)) { //$NON-NLS-1$ + lineSeparator = ILineSeparatorConstants.LINE_SEPARATOR_LF; + } + else { + lineSeparator = ILineSeparatorConstants.LINE_SEPARATOR_CRLF; + } + } + + // Setup the listeners + setStdoutListeners(settings.getStdOutListeners()); + setStderrListeners(settings.getStdErrListeners()); + + // Enable VT100 line wrapping if we are connected via pty + // And TERM is VT100 compatible + if (pty != null && !isAnsiTerminal) + control.setVT100LineWrapping(true); + + // connect the streams + connectStreams(control, process.getOutputStream(), process.getInputStream(), (pty == null ? process.getErrorStream() : null), settings.isLocalEcho(), lineSeparator); + + // Set the terminal control state to CONNECTED + control.setState(TerminalState.CONNECTED); + + // Create the process monitor + monitor = new ProcessMonitor(this); + monitor.startMonitoring(); + } catch (IOException e) { + // Disconnect right away + disconnect(); + // Lookup the tab item + CTabItem item = ConsoleManager.getInstance().findConsole(control); + if (item != null) item.dispose(); + // Get the error message from the exception + String msg = e.getLocalizedMessage() != null ? e.getLocalizedMessage() : ""; //$NON-NLS-1$ + Assert.isNotNull(msg); + // Strip away "Exec_tty error:" + msg = msg.replace("Exec_tty error:", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + // Repackage into a more user friendly error + msg = NLS.bind(Messages.ProcessConnector_error_creatingProcess, settings.getImage(), msg); + // Open an error dialog + MessageDialog.openError(control.getShell(), Messages.ProcessConnector_error_title, msg); + } + } + + private static String getTermVariable(String[] envp) { + if (envp != null && !Platform.OS_WIN32.equals(Platform.getOS())) + for (String var : envp) + if (var.startsWith("TERM=")) //$NON-NLS-1$ + return var.substring(5); + return "xterm"; //$NON-NLS-1$ + } + + /* (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.internal.terminal.provisional.api.provider.TerminalConnectorImpl#doDisconnect() + */ + @Override + public void doDisconnect() { + // Dispose the process + if (process != null) { process.destroy(); process = null; } + + // 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#makeSettingsPage() + */ + @Override + public ISettingsPage makeSettingsPage() { + return new ProcessSettingsPage(settings); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#getSettingsSummary() + */ + @Override + public String getSettingsSummary() { + return settings.getImage() != null ? settings.getImage() : ""; //$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); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.internal.terminal.provisional.api.provider.TerminalConnectorImpl#setTerminalSize(int, int) + */ + @Override + public void setTerminalSize(int newWidth, int newHeight) { + if (width != newWidth || height != newHeight) { + width = newWidth; + height = newHeight; + if (pty != null) { + pty.setTerminalSize(newWidth, newHeight); + } + } + } + +} diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessLauncherDelegate.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessLauncherDelegate.java new file mode 100644 index 000000000..fc78d60de --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessLauncherDelegate.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * 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.process; + +import java.util.Map; + +import org.eclipse.cdt.utils.pty.PTY; +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; + +/** + * Process launcher delegate implementation. + */ +@SuppressWarnings("restriction") +public class ProcessLauncherDelegate 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.ProcessConnector"; //$NON-NLS-1$ + + // Extract the process properties + String image = (String)properties.get(ITerminalsConnectorConstants.PROP_PROCESS_PATH); + String arguments = (String)properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ARGS); + Process process = (Process)properties.get(ITerminalsConnectorConstants.PROP_PROCESS_OBJ); + PTY pty = (PTY)properties.get(ITerminalsConnectorConstants.PROP_PTY_OBJ); + 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); + String workingDir = (String)properties.get(ITerminalsConnectorConstants.PROP_PROCESS_WORKING_DIR); + + String[] envp = null; + if (properties.containsKey(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT) && + properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT) != null && + properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT) instanceof String[]){ + envp = (String[])properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT); + } + + Assert.isTrue(image != null || process != null); + + // Construct the terminal settings store + ISettingsStore store = new SettingsStore(); + + // Construct the process settings + ProcessSettings processSettings = new ProcessSettings(); + processSettings.setImage(image); + processSettings.setArguments(arguments); + processSettings.setProcess(process); + processSettings.setPTY(pty); + processSettings.setLocalEcho(localEcho); + processSettings.setLineSeparator(lineSeparator); + processSettings.setStdOutListeners(stdoutListeners); + processSettings.setStdErrListeners(stderrListeners); + processSettings.setWorkingDir(workingDir); + processSettings.setEnvironment(envp); + + if (properties.containsKey(ITerminalsConnectorConstants.PROP_PROCESS_MERGE_ENVIRONMENT)) { + value = properties.get(ITerminalsConnectorConstants.PROP_PROCESS_MERGE_ENVIRONMENT); + processSettings.setMergeWithNativeEnvironment(value instanceof Boolean ? ((Boolean)value).booleanValue() : false); + } + + // And save the settings to the store + processSettings.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.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessMonitor.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessMonitor.java new file mode 100644 index 000000000..2e3762aaf --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessMonitor.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * 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.process; + +import org.eclipse.core.runtime.Assert; + + +/** + * Process monitor implementation. + */ +public class ProcessMonitor { + // Reference to the parent process connector + private final ProcessConnector processConnector; + // Reference to the monitored process + private final Process process; + // Reference to the monitor thread + private Thread thread; + // Flag to mark the monitor disposed + private boolean disposed; + + + /** + * Constructor. + * + * @param processConnector The parent process connector. Must not be <code>null</code>. + */ + public ProcessMonitor(ProcessConnector processConnector) { + super(); + + Assert.isNotNull(processConnector); + this.processConnector = processConnector; + + // Query the monitored process for easier access + this.process = processConnector.getProcess(); + } + + /** + * Dispose the process monitor. + */ + public void dispose() { + // Set the disposed status + disposed = true; + // 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() { + monitorProcess(); + } + }; + + // Create the monitor thread + thread = new Thread(runnable, "Terminal Process Monitor Thread"); //$NON-NLS-1$ + + // Configure the monitor thread + thread.setDaemon(true); + + // Start the processing + thread.start(); + } + + /** + * Monitors the associated system process, waiting for it to terminate, + * and notifies the associated process monitor's. + */ + @SuppressWarnings("restriction") + public void monitorProcess() { + // If already disposed -> return immediately + if (disposed) return; + + try { + // Wait for the monitored process to terminate + process.waitFor(); + } catch (InterruptedException ie) { + // clear interrupted state + Thread.interrupted(); + } finally { + // Dispose the parent process connector + processConnector.disconnect(); + } + } +} diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessSettings.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessSettings.java new file mode 100644 index 000000000..4a306ddfb --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessSettings.java @@ -0,0 +1,299 @@ +/******************************************************************************* + * 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.process; + +import org.eclipse.cdt.utils.pty.PTY; +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; + +/** + * Process connector settings implementation. + */ +@SuppressWarnings("restriction") +public class ProcessSettings { + // Reference to the process image + private String image; + // Reference to the process arguments (space separated string) + private String arguments; + // Reference to the process object + private Process process; + // Reference to the pseudo terminal object + private PTY pty; + // Flag to control the local echo (defaults to true if + // the PTY is not supported on the current host platform) + private boolean localEcho = !PTY.isSupported(); + // 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; + // working directory for process + private String workingDir; + // environment + private String[] environment; + // Flag to control if the provided environment is + // automatically merged with the native process environment. + // Defaults to "true". + private boolean mergeWithNativeEnvironment = true; + + /** + * Sets the process image. + * + * @param image The process image or <code>null</code>. + */ + public void setImage(String image) { + this.image = image; + } + + /** + * Returns the process image. + * + * @return The process image or <code>null</code>. + */ + public String getImage() { + return image; + } + + /** + * Sets the process arguments. + * <p> + * The arguments are space separated. The caller is responsible for + * correct quoting. + * + * @param arguments The process arguments or <code>null</code>. + */ + public void setArguments(String arguments) { + this.arguments = arguments; + } + + /** + * Returns the process arguments. + * + * @return The process arguments as space separated list or <code>null</code>. + */ + public String getArguments() { + return arguments; + } + + /** + * Sets the process object. + * + * @param image The process object or <code>null</code>. + */ + public void setProcess(Process process) { + this.process = process; + } + + /** + * Returns the process object. + * + * @return The process object or <code>null</code>. + */ + public Process getProcess() { + return process; + } + + /** + * Sets the pseudo terminal object. + * + * @param pty The pseudo terminal or <code>null</code>. + */ + public void setPTY(PTY pty) { + this.pty = pty; + // If the PTY is set to "null", the local echo will be set to "true" + if (pty == null) setLocalEcho(true); + } + + /** + * Returns the pseudo terminal object. + * + * @return The pseudo terminal or <code>null</code>. + */ + public PTY getPTY() { + return pty; + } + + /** + * 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 process line separator. + * + * @param separator The process line separator <code>null</code>. + */ + public void setLineSeparator(String separator) { + this.lineSeparator = separator; + } + + /** + * Returns the process line separator. + * + * @return The process 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; + } + + /** + * Returns the working directory + * + * @return + */ + public String getWorkingDir() { + return this.workingDir; + } + + /** + * Sets the working directory of the process + * + * @param workingDir the absolute path of the working directory + */ + public void setWorkingDir(String workingDir) { + this.workingDir = workingDir; + } + + /** + * Get the process environment + * + * @return + */ + public String[] getEnvironment() { + return environment; + } + + /** + * Sets the process environment + * + * @param environment - will be added to the "parent" environment of the process + */ + public void setEnvironment(String[] environment) { + this.environment = environment; + } + + /** + * Returns if or if not the provided environment is merged with + * the native process environment. + * + * @return <code>True</code> if the provided environment is merged with the native process environment, <code>false</code> otherwise. + */ + public boolean isMergeWithNativeEnvironment() { + return mergeWithNativeEnvironment; + } + + /** + * Sets if or if not the provided environment is merged with the + * native process environment. + * + * @param value <code>True</code> if the provided environment is merged with the native process environment, <code>false</code> otherwise. + */ + public void setMergeWithNativeEnvironment(boolean value) { + this.mergeWithNativeEnvironment = value; + } + + /** + * Loads the process 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); + image = store.get("Path", null);//$NON-NLS-1$ + arguments = store.get("Arguments", null); //$NON-NLS-1$ + localEcho = Boolean.parseBoolean(store.get("LocalEcho", Boolean.FALSE.toString())); //$NON-NLS-1$ + mergeWithNativeEnvironment = Boolean.parseBoolean(store.get("MergeWithNativeEnvironment", Boolean.FALSE.toString())); //$NON-NLS-1$ + lineSeparator = store.get("LineSeparator", null); //$NON-NLS-1$ + workingDir = store.get("WorkingDir", null); //$NON-NLS-1$ + if (store instanceof SettingsStore) { + process = (Process)((SettingsStore)store).getSettings().get("Process"); //$NON-NLS-1$ + pty = (PTY)((SettingsStore)store).getSettings().get("PTY"); //$NON-NLS-1$ + stdoutListeners = (ITerminalServiceOutputStreamMonitorListener[])((SettingsStore)store).getSettings().get("StdOutListeners"); //$NON-NLS-1$ + stderrListeners = (ITerminalServiceOutputStreamMonitorListener[])((SettingsStore)store).getSettings().get("StdErrListeners"); //$NON-NLS-1$ + environment = (String[])((SettingsStore)store).getSettings().get("Environment"); //$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("Path", image);//$NON-NLS-1$ + store.put("Arguments", arguments); //$NON-NLS-1$ + store.put("LocalEcho", Boolean.toString(localEcho)); //$NON-NLS-1$ + store.put("MergeWithNativeEnvironment", Boolean.toString(mergeWithNativeEnvironment)); //$NON-NLS-1$ + store.put("LineSeparator", lineSeparator); //$NON-NLS-1$ + store.put("WorkingDir", workingDir); //$NON-NLS-1$ + if (store instanceof SettingsStore) { + ((SettingsStore)store).getSettings().put("Process", process); //$NON-NLS-1$ + ((SettingsStore)store).getSettings().put("PTY", pty); //$NON-NLS-1$ + ((SettingsStore)store).getSettings().put("StdOutListeners", stdoutListeners); //$NON-NLS-1$ + ((SettingsStore)store).getSettings().put("StdErrListeners", stderrListeners); //$NON-NLS-1$ + ((SettingsStore)store).getSettings().put("Environment", environment); //$NON-NLS-1$ + } + } +} diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessSettingsPage.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessSettingsPage.java new file mode 100644 index 000000000..150f6c6cb --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/ProcessSettingsPage.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * 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.process; + +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.swt.SWT; +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.Composite; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.tcf.te.ui.terminals.nls.Messages; +import org.eclipse.tm.internal.terminal.provisional.api.AbstractSettingsPage; +import org.eclipse.ui.PlatformUI; +import org.osgi.framework.Bundle; + +/** + * Process connector settings page implementation. + */ +@SuppressWarnings("restriction") +public class ProcessSettingsPage extends AbstractSettingsPage { + private Text processImageSelectorControl; + private Button processImageSelectorControlButton; + private Text processArgumentsControl; + private Button localEchoSelectorControl; + private Text processWorkingDirControl; + + private final ProcessSettings settings; + + /** + * Constructor. + * + * @param settings + */ + public ProcessSettingsPage(ProcessSettings settings) { + super(); + + Assert.isNotNull(settings); + this.settings = settings; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.internal.terminal.provisional.api.ISettingsPage#createControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout()); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + + // The entry fields shall be properly aligned + Composite panel = new Composite(composite, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + layout.marginWidth = 0; layout.marginHeight = 0; + panel.setLayout(layout); + panel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create the process image selector control + Label label = new Label(panel, SWT.HORIZONTAL); + label.setText(Messages.ProcessSettingsPage_processImagePathSelectorControl_label); + + // Text field and browse button are aligned it their own panel + Composite innerPanel = new Composite(panel, SWT.NONE); + layout = new GridLayout(2, false); + layout.marginWidth = 0; layout.marginHeight = 0; + innerPanel.setLayout(layout); + innerPanel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + processImageSelectorControl = new Text(innerPanel, SWT.SINGLE | SWT.BORDER); + processImageSelectorControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + processImageSelectorControlButton = new Button(innerPanel, SWT.PUSH); + processImageSelectorControlButton.setText(Messages.ProcessSettingsPage_processImagePathSelectorControl_button); + processImageSelectorControlButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onBrowseButtonSelected(e); + } + }); + + // Create the process arguments control + label = new Label(panel, SWT.HORIZONTAL); + label.setText(Messages.ProcessSettingsPage_processArgumentsControl_label); + + processArgumentsControl = new Text(panel, SWT.SINGLE | SWT.BORDER); + processArgumentsControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create the process arguments control + label = new Label(panel, SWT.HORIZONTAL); + label.setText(Messages.ProcessSettingsPage_processWorkingDirControl_label); + + processWorkingDirControl = new Text(panel, SWT.SINGLE | SWT.BORDER); + processWorkingDirControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create the local echo check box + localEchoSelectorControl = new Button(composite, SWT.CHECK); + localEchoSelectorControl.setText(Messages.ProcessSettingsPage_localEchoSelectorControl_label); + localEchoSelectorControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + localEchoSelectorControl.setSelection(!PTY.isSupported()); + + // Initialize the control content + loadSettings(); + } + + /** + * Called once the user pressed the browse button. + * + * @param e The selection event or <code>null</code>. + */ + protected void onBrowseButtonSelected(SelectionEvent e) { + // Determine the shell + Shell shell = e != null ? e.widget.getDisplay().getActiveShell() : PlatformUI.getWorkbench().getDisplay().getActiveShell(); + + // create a standard file dialog + FileDialog dialog = new FileDialog(shell, SWT.OPEN); + dialog.setText(Messages.ProcessSettingsPage_dialogTitle); + + // the dialog should open within the directory of the currently selected + // file. If no file has been currently selected, it should open within the + // last browsed directory. + String selectedFile = processImageSelectorControl.getText(); + if (selectedFile != null && selectedFile.trim().length() > 0) { + IPath filePath = new Path(selectedFile); + // If the selected file points to an directory, use the directory as is + IPath filterPath = filePath.toFile().isDirectory() ? filePath : filePath.removeLastSegments(1); + String filterFileName = filePath.toFile().isDirectory() || !filePath.toFile().exists() ? null : filePath.lastSegment(); + + if (!filterPath.isEmpty()) { + dialog.setFilterPath(filterPath.toString()); + } + if (filterFileName != null) { + dialog.setFileName(filterFileName); + } + } else { + Bundle bundle = Platform.getBundle("org.eclipse.core.resources"); //$NON-NLS-1$ + if (bundle != null && (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.ACTIVE)) { + dialog.setFilterPath(org.eclipse.core.resources.ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString()); + } + } + + // Open the dialog + selectedFile = dialog.open(); + if (selectedFile != null) { + processImageSelectorControl.setText(selectedFile); + } + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.internal.terminal.provisional.api.ISettingsPage#saveSettings() + */ + @Override + public void saveSettings() { + settings.setImage(processImageSelectorControl.getText()); + settings.setArguments(processArgumentsControl.getText()); + settings.setLocalEcho(localEchoSelectorControl.getSelection()); + settings.setWorkingDir(processWorkingDirControl.getText()); + settings.setProcess(null); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.internal.terminal.provisional.api.ISettingsPage#loadSettings() + */ + @Override + public void loadSettings() { + processImageSelectorControl.setText(settings.getImage()); + processArgumentsControl.setText(settings.getArguments()); + localEchoSelectorControl.setSelection(settings.isLocalEcho()); + processWorkingDirControl.setText(settings.getWorkingDir()); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.internal.terminal.provisional.api.ISettingsPage#validateSettings() + */ + @Override + public boolean validateSettings() { + // The settings are considered valid if the selected process image can be read. + String selectedFile = processImageSelectorControl.getText(); + return selectedFile != null && !"".equals(selectedFile.trim()) && new Path(selectedFile).toFile().canRead(); //$NON-NLS-1$ + } +} diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/activator/UIPlugin.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/activator/UIPlugin.java new file mode 100644 index 000000000..87d195c74 --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/activator/UIPlugin.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.process.activator; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.graphics.Image; +import org.eclipse.tcf.te.core.terminals.tracing.TraceHandler; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class UIPlugin extends AbstractUIPlugin { + // The shared instance + private static UIPlugin plugin; + + // The trace handler instance + private static volatile TraceHandler traceHandler; + + /** + * 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 plugin. + */ + public static String getUniqueIdentifier() { + if (getDefault() != null && getDefault().getBundle() != null) { + return getDefault().getBundle().getSymbolicName(); + } + return "org.eclipse.tcf.te.ui.terminals.process"; //$NON-NLS-1$ + } + + /** + * 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; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#initializeImageRegistry(org.eclipse.jface.resource.ImageRegistry) + */ + @Override + protected void initializeImageRegistry(ImageRegistry registry) { + super.initializeImageRegistry(registry); + } + + /** + * 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.process/src/org/eclipse/tcf/te/ui/terminals/process/help/IContextHelpIds.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/help/IContextHelpIds.java new file mode 100644 index 000000000..3fb81c57f --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/help/IContextHelpIds.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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.process.help; + +import org.eclipse.tcf.te.ui.terminals.process.activator.UIPlugin; + + +/** + * 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$ + + // ***** Message dialog boxes ***** + + /** + * Process connector: Create process failed + */ + public final static String MESSAGE_CREATE_PROCESS_FAILED = PREFIX + ".status.messageCreateProcessFailed"; //$NON-NLS-1$ +} diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/nls/Messages.java b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/nls/Messages.java new file mode 100644 index 000000000..60656fa69 --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/nls/Messages.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.process.nls; + +import org.eclipse.osgi.util.NLS; + +/** + * Process terminal connector 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.process.nls.Messages"; //$NON-NLS-1$ + + /** + * Static constructor. + */ + static { + // Load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + // **** Declare externalized string id's down here ***** + + public static String ProcessConnector_error_title; + public static String ProcessConnector_error_creatingProcess; +} diff --git a/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/nls/Messages.properties b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/nls/Messages.properties new file mode 100644 index 000000000..a3755e8a0 --- /dev/null +++ b/terminals/plugins/org.eclipse.tcf.te.ui.terminals.process/src/org/eclipse/tcf/te/ui/terminals/process/nls/Messages.properties @@ -0,0 +1,12 @@ +############################################################################### +# Copyright (c) 2012, 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 +############################################################################### + +ProcessConnector_error_title=Error +ProcessConnector_error_creatingProcess=Failed to execute ''{0}''.\n\nPossibly caused by:\n{1} |