diff --git a/org.eclipse.tm.terminal.local-feature/.project b/org.eclipse.tm.terminal.local-feature/.project
new file mode 100644
index 00000000000..c8b6195f159
--- /dev/null
+++ b/org.eclipse.tm.terminal.local-feature/.project
@@ -0,0 +1,17 @@
+
+
Eclipse Public License - v 1.0
+ +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.
+ +1. DEFINITIONS
+ +"Contribution" means:
+ +a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+i) changes to the Program, and
+ii) additions to the Program;
+where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.
+ +"Contributor" means any person or entity that distributes +the Program.
+ +"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.
+ +"Program" means the Contributions distributed in accordance +with this Agreement.
+ +"Recipient" means anyone who receives the Program under +this Agreement, including all Contributors.
+ +2. GRANT OF RIGHTS
+ +a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.
+ +b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.
+ +c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.
+ +d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.
+ +3. REQUIREMENTS
+ +A Contributor may choose to distribute the Program in object code +form under its own license agreement, provided that:
+ +a) it complies with the terms and conditions of this +Agreement; and
+ +b) its license agreement:
+ +i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;
+ +ii) effectively excludes on behalf of all Contributors +all liability for damages, including direct, indirect, special, +incidental and consequential damages, such as lost profits;
+ +iii) states that any provisions which differ from this +Agreement are offered by that Contributor alone and not by any other +party; and
+ +iv) states that source code for the Program is available +from such Contributor, and informs licensees how to obtain it in a +reasonable manner on or through a medium customarily used for software +exchange.
+ +When the Program is made available in source code form:
+ +a) it must be made available under this Agreement; and
+ +b) a copy of this Agreement must be included with each +copy of the Program.
+ +Contributors may not remove or alter any copyright notices contained +within the Program.
+ +Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.
+ +4. COMMERCIAL DISTRIBUTION
+ +Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use of +the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create +potential liability for other Contributors. Therefore, if a Contributor +includes the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and +indemnify every other Contributor ("Indemnified Contributor") +against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the +acts or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In +order to qualify, an Indemnified Contributor must: a) promptly notify +the Commercial Contributor in writing of such claim, and b) allow the +Commercial Contributor to control, and cooperate with the Commercial +Contributor in, the defense and any related settlement negotiations. The +Indemnified Contributor may participate in any such claim at its own +expense.
+ +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.
+ +5. NO WARRANTY
+ +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.
+ +6. DISCLAIMER OF LIABILITY
+ +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT +NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ +7. GENERAL
+ +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.
+ +If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.
+ +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.
+ +Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.
+ +This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No party +to this Agreement will bring a legal action under this Agreement more +than one year after the cause of action arose. Each party waives its +rights to a jury trial in any resulting litigation.
+ + \ No newline at end of file diff --git a/org.eclipse.tm.terminal.local-feature/feature.properties b/org.eclipse.tm.terminal.local-feature/feature.properties new file mode 100644 index 00000000000..e289100d181 --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/feature.properties @@ -0,0 +1,117 @@ +#################################################################################################### +# Copyright (c) 2008 Mirko Raner 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: +# Mirko Raner - initial implementation for Eclipse Bug 196337 +#################################################################################################### + +featureName=Target Management Terminal Local Connector +description=A local connector implementation for the Terminal. +copyright=\ +Copyright (c) 2008 Mirko Raner and others.\n\ +All rights reserved. This program and the accompanying materials\n\ +are made available under the terms of the Eclipse Public License v1.0\n\ +which accompanies this distribution, and is available at\n\ +http://www.eclipse.org/legal/epl-v10.html +licenseURL=license.html +license=\ +ECLIPSE FOUNDATION SOFTWARE USER AGREEMENT\n\ +March 17, 2005\n\ +\n\ +Usage Of Content\n\ +\n\ +THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\ +OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\ +USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\ +AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\ +NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU\n\ +AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\ +AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\ +OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\ +TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\ +OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\ +BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\ +\n\ +Applicable Licenses\n\ +\n\ +Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\ +is provided to you under the terms and conditions of the Eclipse Public\n\ +License Version 1.0 ("EPL"). A copy of the EPL is provided with this\n\ +Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\ +For purposes of the EPL, "Program" will mean the Content.\n\ +\n\ +Content includes, but is not limited to, source code, object code,\n\ +documentation and other files maintained in the Eclipse.org CVS\n\ +repository ("Repository") in CVS modules ("Modules") and made available\n\ +as downloadable archives ("Downloads").\n\ +\n\ + - Content may be structured and packaged into modules to facilitate delivering,\n\ + extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\ + plug-in fragments ("Fragments"), and features ("Features").\n\ + - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java(TM) ARchive)\n\ + in a directory named "plugins".\n\ + - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\ + Each Feature may be packaged as a sub-directory in a directory named "features".\n\ + Within a Feature, files named "feature.xml" may contain a list of the names and version\n\ + numbers of the Plug-ins and/or Fragments associated with that Feature.\n\ + - Features may also include other Features ("Included Features"). Within a Feature, files\n\ + named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\ +\n\ +Features may also include other Features ("Included Features"). Files named\n\ +"feature.xml" may contain a list of the names and version numbers of\n\ +Included Features.\n\ +\n\ +The terms and conditions governing Plug-ins and Fragments should be\n\ +contained in files named "about.html" ("Abouts"). The terms and\n\ +conditions governing Features and Included Features should be contained\n\ +in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\ +Licenses may be located in any directory of a Download or Module\n\ +including, but not limited to the following locations:\n\ +\n\ + - The top-level (root) directory\n\ + - Plug-in and Fragment directories\n\ + - Inside Plug-ins and Fragments packaged as JARs\n\ + - Sub-directories of the directory named "src" of certain Plug-ins\n\ + - Feature directories\n\ +\n\ +Note: if a Feature made available by the Eclipse Foundation is installed using the\n\ +Eclipse Update Manager, you must agree to a license ("Feature Update\n\ +License") during the installation process. If the Feature contains\n\ +Included Features, the Feature Update License should either provide you\n\ +with the terms and conditions governing the Included Features or inform\n\ +you where you can locate them. Feature Update Licenses may be found in\n\ +the "license" property of files named "feature.properties". Such Abouts,\n\ +Feature Licenses and Feature Update Licenses contain the terms and\n\ +conditions (or references to such terms and conditions) that govern your\n\ +use of the associated Content in that directory.\n\ +\n\ +THE ABOUTS, FEATURE LICENSES AND FEATURE UPDATE LICENSES MAY REFER\n\ +TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\ +SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\ +\n\ + - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\ + - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\ + - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\ + - IBM Public License 1.0 (available at http://oss.software.ibm.com/developerworks/opensource/license10.html)\n\ + - Metro Link Public License 1.00 (available at http://www.opengroup.org/openmotif/supporters/metrolink/license.html)\n\ + - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\ +\n\ +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\ +TO USE OF THE CONTENT. If no About, Feature License or Feature Update License\n\ +is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\ +govern that particular Content.\n\ +\n\ +Cryptography\n\ +\n\ +Content may contain encryption software. The country in which you are\n\ +currently may have restrictions on the import, possession, and use,\n\ +and/or re-export to another country, of encryption software. BEFORE\n\ +using any encryption software, please check the country's laws,\n\ +regulations and policies concerning the import, possession, or use,\n\ +and re-export of encryption software, to see if this is permitted.\n\ +\n\ +Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.\n diff --git a/org.eclipse.tm.terminal.local-feature/feature.xml b/org.eclipse.tm.terminal.local-feature/feature.xml new file mode 100644 index 00000000000..3f4a937aa82 --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/feature.xml @@ -0,0 +1,47 @@ + + +March 17, 2005
+ +THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS + (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND + CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE + OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR + NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND + CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.
+ +Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0 + ("EPL"). A copy of the EPL is provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html. + For purposes of the EPL, "Program" will mean the Content.
+ +Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse.org CVS repository ("Repository") in CVS + modules ("Modules") and made available as downloadable archives ("Downloads").
+ +The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and +Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module +including, but not limited to the following locations:
+ +Note: if a Feature made available by the Eclipse Foundation is installed using the Eclipse Update Manager, you must agree to a license ("Feature Update License") during the +installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or +inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature. +Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in +that directory.
+ +THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE +OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):
+ +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please +contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.
+ +Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to + another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import, + possession, or use, and re-export of encryption software, to see if this is permitted.
+ +Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. + + diff --git a/org.eclipse.tm.terminal.local/.classpath b/org.eclipse.tm.terminal.local/.classpath new file mode 100644 index 00000000000..64c5e31b7a2 --- /dev/null +++ b/org.eclipse.tm.terminal.local/.classpath @@ -0,0 +1,7 @@ + ++ August 4, 2008 +
+
+ If you did not receive this Content directly from Mirko Raner, the Content is
+ being redistributed by another party ("Redistributor") and different terms and
+ conditions may apply to your use of any object code in the Content. Check the Redistributor's
+ license that was provided with the Content. If no such license exists, contact the Redistributor.
+ Unless otherwise indicated below, the terms and conditions of the EPL still apply to any source
+ code in the Content.
+
+
diff --git a/org.eclipse.tm.terminal.local/about.ini b/org.eclipse.tm.terminal.local/about.ini
new file mode 100644
index 00000000000..2d185b182ff
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/about.ini
@@ -0,0 +1,13 @@
+####################################################################################################
+# Copyright (c) 2008 Mirko Raner 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:
+# Mirko Raner - initial implementation for Eclipse Bug 196337
+####################################################################################################
+
+featureImage=icons/terminal-local.png
+aboutText=%feature.information
diff --git a/org.eclipse.tm.terminal.local/about.mappings b/org.eclipse.tm.terminal.local/about.mappings
new file mode 100644
index 00000000000..14f8af2448b
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/about.mappings
@@ -0,0 +1,12 @@
+####################################################################################################
+# Copyright (c) 2008 Mirko Raner 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:
+# Mirko Raner - initial implementation for Eclipse Bug 196337
+####################################################################################################
+
+0=@build@
diff --git a/org.eclipse.tm.terminal.local/about.properties b/org.eclipse.tm.terminal.local/about.properties
new file mode 100644
index 00000000000..5e2a9b09682
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/about.properties
@@ -0,0 +1,17 @@
+####################################################################################################
+# Copyright (c) 2008 Mirko Raner 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:
+# Mirko Raner - initial implementation for Eclipse Bug 196337
+####################################################################################################
+
+feature.information=Target Management Terminal Local Connector\n\
+\n\
+Version: {featureVersion}\n\
+\n\
+(c) Copyright Mirko Raner and others, 2008. All rights reserved.\n\
+Visit http://www.eclipse.org/dsdp/tm
diff --git a/org.eclipse.tm.terminal.local/build.properties b/org.eclipse.tm.terminal.local/build.properties
new file mode 100644
index 00000000000..15b26046ff5
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/build.properties
@@ -0,0 +1,11 @@
+source.. = src/
+output.. = bin/
+bin.includes = plugin.xml,\
+ plugin.properties,\
+ about.html,\
+ about.ini,\
+ about.mappings,\
+ about.properties,\
+ icons/,\
+ META-INF/,\
+ .
diff --git a/org.eclipse.tm.terminal.local/icons/terminal-launch.gif b/org.eclipse.tm.terminal.local/icons/terminal-launch.gif
new file mode 100644
index 00000000000..adfb9c2c80f
Binary files /dev/null and b/org.eclipse.tm.terminal.local/icons/terminal-launch.gif differ
diff --git a/org.eclipse.tm.terminal.local/icons/terminal-local.png b/org.eclipse.tm.terminal.local/icons/terminal-local.png
new file mode 100644
index 00000000000..f04a52044ac
Binary files /dev/null and b/org.eclipse.tm.terminal.local/icons/terminal-local.png differ
diff --git a/org.eclipse.tm.terminal.local/plugin.properties b/org.eclipse.tm.terminal.local/plugin.properties
new file mode 100644
index 00000000000..477118979e2
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/plugin.properties
@@ -0,0 +1,16 @@
+####################################################################################################
+# Copyright (c) 2008 Mirko Raner 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:
+# Mirko Raner - initial implementation for Eclipse Bug 196337
+####################################################################################################
+
+pluginName=Local Terminal Connector
+providerName=Mirko Raner
+terminalLaunch=Terminal
+terminalLaunchDescription=Run a program in a terminal
+localConnection=Local program
diff --git a/org.eclipse.tm.terminal.local/plugin.xml b/org.eclipse.tm.terminal.local/plugin.xml
new file mode 100644
index 00000000000..4689ae6f4ba
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/plugin.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ * Unfortunately, the Process Console Manager determines the need for a Console view by checking the
+ * {@link IStreamsProxy} of the process for
+ * However, for a launch to be properly terminated it needs to have at least one process that can
+ * be terminated. Launches that do not have any processes associated with them are not considered
+ * terminated and actually terminating them is not possible. To work around this secondary issue,
+ * the process is added back to its launch just before the launch is terminated. This activity is
+ * performed by {@link LocalTerminalProcessRegistry#addProcessBackToFinishedLaunch(ILaunch)}. To
+ * prevent a console allocation during this last step, the {@link #resetStreamsProxy()} method will
+ * be invoked, which will cause subsequent calls to {@link IProcess#getStreamsProxy()} to return
+ *
+ *
+ * This solution for preventing standard consoles from being opened does certainly not deserve the
+ * cleanliness award for straightforward coding, but at least it doesn't rely on internal API at
+ * this point. Ideally, the whole issue should be resolved with some sort of console renderer
+ * extension point as proposed in bug 242373 (https://bugs.eclipse.org/bugs/show_bug.cgi?id=242373).
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.3 $
+ */
+public final class LocalTerminalProcess extends RuntimeProcess {
+
+ /**
+ * The process type ID of processes produced by this factory.
+ */
+ public final static String PROCESS_TYPE = "org.eclipse.tm.terminal.localProcess"; //$NON-NLS-1$
+
+ private boolean resetStreamsProxy;
+
+ /**
+ * Creates a new {@link LocalTerminalProcess}.
+ *
+ * @param launch the current {@link ILaunch}
+ * @param process the underlying low-level {@link Process}
+ * @param name the process name
+ * @param attributes additional attributes of the process
+ */
+ protected LocalTerminalProcess(ILaunch launch, Process process, String name, Map attributes) {
+
+ super(launch, process, name, setProcessType(attributes));
+ LocalTerminalProcessRegistry.registerWithLaunch(launch, this);
+ launch.removeProcess(this);
+ }
+
+ /**
+ * Sends a bash
shell or
+ * vi
editor).
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.4 $
+ */
+public class LocalTerminalConnector extends TerminalConnectorImpl
+implements IDebugEventSetListener {
+
+ // Shorthand for attribute names:
+ //
+ private final static String ATTR_CAPTURE_IN_CONSOLE = IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE;
+ private final static String ATTR_CAPTURE_OUTPUT = DebugPlugin.ATTR_CAPTURE_OUTPUT;
+ private final static String ATTR_PROCESS_FACTORY_ID = DebugPlugin.ATTR_PROCESS_FACTORY_ID;
+
+ private LocalTerminalOutputStream terminalToLocalProcessStream;
+ private LocalTerminalOutputListener outputListener;
+ private LocalTerminalOutputListener errorListener;
+ private ILocalTerminalSettings settings;
+ private IStreamMonitor outputMonitor;
+ private IStreamMonitor errorMonitor;
+ private IProcess process;
+ private ILaunch launch;
+
+ /**
+ * Creates a new {@link LocalTerminalConnector}. This constructor is invoked by the framework.
+ */
+ public LocalTerminalConnector() {
+
+ settings = new LocalTerminalSettings();
+ }
+
+ /**
+ * Loads the connector's settings from the specified store.
+ *
+ * @param store the {@link ISettingsStore}
+ *
+ * @see TerminalConnectorImpl#load(ISettingsStore)
+ *
+ * TODO: the load(ISettingsStore) method should probably be made abstract in
+ * TerminalConnectorImpl, otherwise it is not immediately clear that clients need to
+ * override this method if custom settings are necessary (which they usually are).
+ * Maybe the whole settings store mechanism should be redesigned. The current scheme
+ * requires clients to implement load/save methods in their connector implementation
+ * classes (necessity to override is not immediately obvious) and in the settings store
+ * implementations (not enforced at all; merely expected by convention). Structurally,
+ * all client implementations look more or less the same, and probably could be handled
+ * by the framework in a uniform way. Maybe a configuration mechanism using attributes
+ * (like, for example, ILaunchConfiguration) might be beneficial here.
+ */
+ public void load(ISettingsStore store) {
+
+ settings.load(store);
+ }
+
+ /**
+ * Stores the connector's settings into the specified store.
+ * See {@link #load(ISettingsStore)} for additional notes.
+ *
+ * @param store the {@link ISettingsStore}
+ *
+ * @see TerminalConnectorImpl#save(ISettingsStore)
+ */
+ public void save(ISettingsStore store) {
+
+ settings.save(store);
+ }
+
+ /**
+ * Creates the {@link ISettingsPage} for the settings of this connector.
+ *
+ * @return a new page that can be used in a dialog to setup this connection, or
+ * null
if the connection cannot be customized or configured
+ *
+ * @see TerminalConnectorImpl#makeSettingsPage()
+ */
+ public ISettingsPage makeSettingsPage() {
+
+ return new LocalTerminalSettingsPage(settings);
+ }
+
+ /**
+ * Returns a string that represents the settings of the connection.
+ *
+ * @return the name of the launch configuration that is running in the terminal
+ *
+ * @see TerminalConnectorImpl#getSettingsSummary()
+ * @see ILocalTerminalSettings#getLaunchConfigurationName()
+ */
+ public String getSettingsSummary() {
+
+ return settings.getLaunchConfigurationName();
+ }
+
+ /**
+ * Checks if local echo is required.
+ *
+ * @return true
if the connection settings specify that local echo is enable,
+ * false
otherwise
+ *
+ * @see TerminalConnectorImpl#isLocalEcho()
+ * @see LocalTerminalLaunchUtilities#ATTR_LOCAL_ECHO
+ */
+ public boolean isLocalEcho() {
+
+ return LocalTerminalUtilities.getLocalEcho(settings);
+ }
+
+ /**
+ * Returns an {@link OutputStream} that writes to the local program's standard input. For the
+ * stream in the other direction (remote to terminal) see
+ * {@link ITerminalControl#getRemoteToTerminalOutputStream()}.
+ *
+ * @return the terminal-to-remote-stream (bytes written to this stream will be sent to the
+ * local program)
+ */
+ public OutputStream getTerminalToRemoteStream() {
+
+ return terminalToLocalProcessStream;
+ }
+
+ /**
+ * Connects a locally launched program to the {@link ITerminalControl}.
+ *
+ * @param control the {@link ITerminalControl} through which the user can interact with the
+ * program
+ */
+ public void connect(ITerminalControl control) {
+
+ super.connect(control);
+ control.setState(TerminalState.CONNECTING);
+ ILaunchConfigurationWorkingCopy workingCopy = null;
+ ILaunchConfiguration configuration = null;
+ try {
+
+ String configurationName = settings.getLaunchConfigurationName();
+ configuration = LocalTerminalUtilities.findLaunchConfiguration(configurationName);
+
+ // Always set the the process factory ID and enable console output (there is no need
+ // to restore these attributes afterwards; disabling console output does not make
+ // sense for terminal launches and will be overridden when the configuration is
+ // actually launched):
+ //
+ workingCopy = configuration.getWorkingCopy();
+ workingCopy.setAttribute(ATTR_CAPTURE_OUTPUT, true);
+ workingCopy.setAttribute(ATTR_CAPTURE_IN_CONSOLE, true);
+ workingCopy.setAttribute(ATTR_PROCESS_FACTORY_ID, LocalTerminalProcessFactory.ID);
+ configuration = workingCopy.doSave();
+ launch = configuration.launch(ILaunchManager.RUN_MODE, null);
+
+ // To prevent a console from being allocated, the launch will actually not contain a
+ // reference to the runtime process. The process has to be obtained from the
+ // LocalTerminalProcessRegistry instead:
+ //
+ process = LocalTerminalProcessRegistry.getFromLaunch(launch);
+ IStreamsProxy streamsProxy = process.getStreamsProxy();
+
+ // Hook up standard input:
+ //
+ terminalToLocalProcessStream = new LocalTerminalOutputStream(process, settings);
+
+ // Hook up standard output:
+ //
+ outputMonitor = streamsProxy.getOutputStreamMonitor();
+ outputListener = new LocalTerminalOutputListener(control, settings);
+ outputMonitor.addListener(outputListener);
+ outputListener.streamAppended(outputMonitor.getContents(), outputMonitor);
+
+ // Hook up standard error:
+ //
+ errorMonitor = streamsProxy.getErrorStreamMonitor();
+ errorListener = new LocalTerminalOutputListener(control, settings);
+ errorMonitor.addListener(errorListener);
+ errorListener.streamAppended(errorMonitor.getContents(), errorMonitor);
+ //
+ // TODO: add proper synchronization for incoming data from stdout and stderr:
+ // currently, the data gets sometimes processed in the wrong order, for example,
+ // the next prompt (which shells like bash print to stderr) sometimes appears
+ // before the command's proper output that was sent to stdout. For example,
+ // you get:
+ //
+ // $ echo hello
+ // $ hello
+ //
+ // instead of the correct output of:
+ //
+ // $ echo hello
+ // hello
+ // $
+
+ // Listen for process termination and update the terminal state:
+ //
+ DebugPlugin.getDefault().addDebugEventListener(this);
+ control.setState(TerminalState.CONNECTED);
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ }
+ }
+
+ /**
+ * Disconnects the connector if it is currently connected or does nothing otherwise. This method
+ * will try to terminate the underlying launched process and will remove all registered
+ * listeners.
+ */
+ public void doDisconnect() {
+
+ try {
+
+ removeAllListeners();
+
+ // To prevent a console from being allocated, Terminal launches don't have an IProcess
+ // associated with them while they are running. However, to properly terminate a launch
+ // the launch has to contain at least one process that can be terminated (launches
+ // without processes effectively cannot be terminated):
+ //
+ LocalTerminalProcessRegistry.addProcessBackToFinishedLaunch(launch);
+
+ // Now, terminate the process if it hasn't been terminated already:
+ //
+ if (launch.canTerminate()) {
+
+ launch.terminate();
+ //
+ // NOTE: canTerminate() merely indicates that the launch has not been terminated
+ // previously already
+ }
+ }
+ catch (DebugException couldNotTerminate) {
+
+ Logger.logException(couldNotTerminate);
+ }
+ }
+
+ /**
+ * Listens for self-induced termination of the launched process. For example, this method will
+ * be notified if a launched shell is terminated by pressing Control-D or by calling
+ * exit
, or if a vi
editor is terminated by means of a
+ * :q!
command.
+ *
+ * @param event the debug events
+ *
+ * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[])
+ */
+ public void handleDebugEvents(DebugEvent[] event) {
+
+ int numberOfEvents = event.length;
+ for (int index = 0; index < numberOfEvents; index++) {
+
+ if (event[index].getSource().equals(process)
+ && (event[index].getKind() == DebugEvent.TERMINATE)) {
+
+ fControl.setState(TerminalState.CLOSED);
+ removeAllListeners();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Removes any listeners that the {@link LocalTerminalConnector} might have registered in its
+ * {@link #connect(ITerminalControl)} method. This method is necessary for clean-up when a
+ * connection is closed. It prevents that orphaned or meaningless listeners keep accumulating
+ * on certain objects.
+ */
+ protected void removeAllListeners() {
+
+ if (outputMonitor != null && outputListener != null) {
+
+ outputMonitor.removeListener(outputListener);
+ }
+ if (errorMonitor != null && errorListener != null) {
+
+ errorMonitor.removeListener(errorListener);
+ }
+ DebugPlugin.getDefault().removeDebugEventListener(this);
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchLabelProvider.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchLabelProvider.java
new file mode 100644
index 00000000000..ebac822d946
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchLabelProvider.java
@@ -0,0 +1,72 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.jface.viewers.BaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities;
+
+/**
+ * The class {@link LocalTerminalLaunchLabelProvider} is an {@link ILabelProvider} for lists (or
+ * tables) of {@link ILaunchConfiguration}s. It returns a configuration's name as the text label,
+ * and the configuration type's regular icon as the image label.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.2 $
+ */
+public class LocalTerminalLaunchLabelProvider extends BaseLabelProvider implements ILabelProvider {
+
+ /**
+ * Creates a new {@link LocalTerminalLaunchLabelProvider}.
+ */
+ public LocalTerminalLaunchLabelProvider() {
+
+ super();
+ }
+
+ /**
+ * Returns the image for the label of the given element.
+ *
+ * @param element the element for which the image was requested
+ * @return the image, or null
if no image could be found
+ *
+ * @see ILabelProvider#getImage(Object)
+ */
+ public Image getImage(Object element) {
+
+ if (element instanceof ILaunchConfiguration) {
+
+ return LocalTerminalLaunchUtilities.getImage((ILaunchConfiguration)element);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the text for the label of the given element.
+ *
+ * @param element the element for which to provide the label text
+ * @return the text string used to label the element, or null
if there is no text
+ * label for the given object
+ *
+ * @see ILabelProvider#getText(Object)
+ */
+ public String getText(Object element) {
+
+ if (element instanceof ILaunchConfiguration) {
+
+ return ((ILaunchConfiguration)element).getName();
+ }
+ return String.valueOf(element);
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchListProvider.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchListProvider.java
new file mode 100644
index 00000000000..63738ec529e
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchListProvider.java
@@ -0,0 +1,90 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+
+/**
+ * The class {@link LocalTerminalLaunchListProvider} is a {@link IStructuredContentProvider} that
+ * provides a list of all {@link ILaunchConfiguration}s of the type "External Tools"/"Program".
+ * Those launch configurations can be used to start a new session in the Terminal View.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.1 $
+ */
+public class LocalTerminalLaunchListProvider implements IStructuredContentProvider {
+
+ /**
+ * Creates a new {@link LocalTerminalLaunchListProvider}.
+ */
+ public LocalTerminalLaunchListProvider() {
+
+ super();
+ }
+
+ /**
+ * Returns the matching {@link ILaunchConfiguration}s for the given input element. This content
+ * provider does not really use the concept of "input" because the input can only be obtained in
+ * one way (from the {@link ILaunchManager}.
+ *
+ * @param input the input element (not checked or used by this method)
+ * @return the matching {@link ILaunchConfiguration}s
+ *
+ * @see IStructuredContentProvider#getElements(Object)
+ */
+ public Object[] getElements(Object input) {
+
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType type = LocalTerminalUtilities.PROGRAM_LAUNCH_TYPE;
+ ILaunchConfiguration[] configurations = null;
+ try {
+
+ configurations = launchManager.getLaunchConfigurations(type);
+ }
+ catch (CoreException couldNotObtainLaunchConfigurations) {
+
+ Logger.logException(couldNotObtainLaunchConfigurations);
+ }
+ return configurations;
+ }
+
+ /**
+ * Disposes of this {@link LocalTerminalLaunchListProvider}. Currently, there is no additional
+ * clean-up necessary, and this method is empty.
+ *
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ public void dispose() {
+
+ // Does nothing...
+ }
+
+ /**
+ * Notifies the {@link LocalTerminalLaunchListProvider} that its input has changed. This method
+ * is currently empty because {@link LocalTerminalLaunchListProvider} is not aware of the
+ * concept of "input"
+ *
+ * @see #getElements(Object)
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, Object, Object)
+ */
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+
+ // Does nothing...
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.java
new file mode 100644
index 00000000000..38c7e9d5a47
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.java
@@ -0,0 +1,103 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * The class {@link LocalTerminalMessages} provides localization keys to internationalized display
+ * messages used by the Local Terminal Connector.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.3 $
+ */
+public class LocalTerminalMessages extends NLS {
+
+ static {
+
+ NLS.initializeMessages(LocalTerminalMessages.class.getName(), LocalTerminalMessages.class);
+ }
+
+ private LocalTerminalMessages() {
+
+ super();
+ }
+
+ /** The title for the launch configuration selection: "Launch configuration:". */
+ public static String launchConfiguration;
+
+ /** The label for the "New..." button. */
+ public static String labelNew;
+
+ /** The label for the "Edit..." button. */
+ public static String labelEdit;
+
+ /** The label for the "Enable terminal echo" check box. */
+ public static String enableLocalEcho;
+
+ /** The label for the "Send SIGINT when Ctrl-C is pressed" check box. */
+ public static String sendInterruptOnCtrlC;
+
+ /** The line separator option "LF". */
+ public static String lineSeparatorLF;
+
+ /** The line separator option "CR". */
+ public static String lineSeparatorCR;
+
+ /** The line separator option "CRLF". */
+ public static String lineSeparatorCRLF;
+
+ /** The default line separator option. */
+ public static String lineSeparatorDefault;
+
+ /** The base name for new launch configurations. */
+ public static String newTerminalLaunchName;
+
+ /** The error message to be issued if a launch configuration could not be found. */
+ public static String noSuchLaunchConfiguration;
+
+ /** The message displayed while launching a configuration. */
+ public static String launchingConfiguration;
+
+ /** The error message displayed when process creation failed. */
+ public static String couldNotCreateIProcess;
+
+ /** The error message for a missing executable path. */
+ public static String locationNotSpecified;
+
+ /** The error message for a specified but invalid executable path. */
+ public static String invalidLocation;
+
+ /** The error message for an invalid working directory location. */
+ public static String invalidWorkingDirectory;
+
+ /** The title string of the warning displayed when terminal launches are still running. */
+ public static String warningTitleTerminalsStillRunning;
+
+ /** The warning message displayed when terminal launches are still running. */
+ public static String warningMessageTerminalsStillRunning;
+
+ /** The label for the button that quits the workbench anyway. */
+ public static String quitWorkbenchAnyway;
+
+ /** The label for the button that vetoes a shutdown of the workbench. */
+ public static String doNoQuitWorkbench;
+
+ /** The label for a terminal process that was terminated during workbench shut-down. */
+ public static String terminatedProcess;
+
+ /** The name of the launch configuration tab for terminal settings. */
+ public static String terminalTabName;
+
+ /** The group label for the terminal settings on the launch configuration page. */
+ public static String terminalSettings;
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.properties b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.properties
new file mode 100644
index 00000000000..e54522aca35
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.properties
@@ -0,0 +1,39 @@
+####################################################################################################
+# Copyright (c) 2008 Mirko Raner.
+# 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:
+# Mirko Raner - initial implementation for Eclipse Bug 196337
+####################################################################################################
+
+launchConfiguration=Launch configuration\:
+enableLocalEcho=Enable terminal echo
+sendInterruptOnCtrlC=Send SIGINT when Ctrl-C is pressed
+lineSeparatorDefault=Program uses default line separator - or\:
+lineSeparatorCRLF=CRLF
+lineSeparatorLF=LF
+lineSeparatorCR=CR
+labelNew=New...
+labelEdit=Edit...
+terminalTabName=Terminal
+terminalSettings=Terminal settings\:
+newTerminalLaunchName=New Terminal Launch
+noSuchLaunchConfiguration=A launch configuration called ''{0}'' does not exist
+launchingConfiguration=Launching {0}...
+couldNotCreateIProcess=The IProcess could not be created
+locationNotSpecified=Executable location was not specified in configuration ''{0}''
+invalidLocation=Executable does not exist for the external tool named ''{0}''
+invalidWorkingDirectory=The path {0} is not a directory and cannot be used as working directory \
+ for ''{1}''
+warningTitleTerminalsStillRunning=Warning: Terminals with active processes are still running
+warningMessageTerminalsStillRunning=The workbench is about to be shut down even though one or more \
+ terminals with active processes are still running. You may abort the shut-down of the workbench \
+ or you may quit the workbench nonetheless, in which case processes might be terminated in an \
+ unexpected fashion. You may also terminate processes individually. The workbench shut-down will \
+ continue automatically once no more terminal processes are running.
+quitWorkbenchAnyway=Quit Workbench Anyway
+doNoQuitWorkbench=Do Not Quit Workbench
+terminatedProcess={0} true
if a launch configuration is selected.
+ *
+ * @return true
if a launch configuration has been selected, false
+ * otherwise
+ */
+ public boolean validateSettings() {
+
+ return viewer != null && !viewer.getSelection().isEmpty();
+ }
+
+ /**
+ * Enables or disables the Edit... button depending on whether a launch configuration is
+ * currently selected in the viewer.
+ *
+ * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
+ */
+ public void selectionChanged(SelectionChangedEvent event) {
+
+ buttonEdit.setEnabled(!event.getSelection().isEmpty());
+ }
+
+ /**
+ * Handles default button clicks for the Edit... and New.. buttons. This method will simply
+ * pass on the call to {@link #widgetSelected(SelectionEvent)}.
+ *
+ * @param event the {@link SelectionEvent}
+ *
+ * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent event) {
+
+ widgetSelected(event);
+ }
+
+ /**
+ * Handles default button clicks for the Edit... and New.. buttons.
+ *
+ * @param event the {@link SelectionEvent}
+ *
+ * @see SelectionListener#widgetSelected(SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent event) {
+
+ ILaunchConfiguration configuration = null;
+ Widget widget = event.widget;
+ if (widget == null) {
+
+ return;
+ }
+ if (widget.equals(buttonNew)) {
+
+ ILaunchConfigurationWorkingCopy newlyCreatedConfiguration;
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ String baseName = LocalTerminalMessages.newTerminalLaunchName;
+ String uniqueName = launchManager.generateUniqueLaunchConfigurationNameFrom(baseName);
+ ILaunchConfigurationType type = LocalTerminalUtilities.PROGRAM_LAUNCH_TYPE;
+ try {
+
+ newlyCreatedConfiguration = type.newInstance(null, uniqueName);
+ configuration = newlyCreatedConfiguration.doSave();
+ }
+ catch (CoreException couldNotCreateNewLaunchConfiguration) {
+
+ Logger.logException(couldNotCreateNewLaunchConfiguration);
+ }
+ }
+ if (widget.equals(buttonEdit) || configuration != null) {
+
+ ILaunchGroup group;
+ Shell shell = DebugUIPlugin.getShell();
+ IStructuredSelection selection = (IStructuredSelection)viewer.getSelection();
+ if (configuration == null) {
+
+ configuration = (ILaunchConfiguration)selection.getFirstElement();
+ }
+ group = DebugUITools.getLaunchGroup(configuration, ILaunchManager.RUN_MODE);
+ String groupID = group.getIdentifier();
+ DebugUITools.openLaunchConfigurationDialog(shell, configuration, groupID, null);
+ //
+ // TODO: handle return value (maybe start terminal right away if "Run" was selected)
+ // - a return value of Window.CANCEL indicates that "Close" was selected
+ // - a return value of Window.OK indicates that "Run" was selected
+ // TODO: prevent "Run" button from launching in the regular console
+ // (maybe tweak process factory settings before opening the configuration in the
+ // dialog?)
+
+ viewer.refresh();
+ viewer.setSelection(new StructuredSelection(configuration), true);
+ //
+ // TODO: handle renamed configurations; setSelection(...) will not work if the user
+ // renamed the configuration in the dialog (apparently, because renaming actually
+ // creates a different ILaunchConfiguration object, rather than just renaming the
+ // existing one)
+ }
+ }
+
+ //------------------------------------ PRIVATE SECTION ---------------------------------------//
+
+ private Button pushButton(Composite parent, String label, boolean grabVertical) {
+
+ GridData layoutData;
+ Button button = new Button(parent, SWT.PUSH);
+ button.setText(label);
+ layoutData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING|GridData.HORIZONTAL_ALIGN_FILL);
+ layoutData.grabExcessVerticalSpace = grabVertical;
+ button.setLayoutData(layoutData);
+ button.addSelectionListener(this);
+ return button;
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalUtilities.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalUtilities.java
new file mode 100644
index 00000000000..54bc719d405
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalUtilities.java
@@ -0,0 +1,158 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchDelegate;
+import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+
+/**
+ * The class {@link LocalTerminalUtilities} is a collection of commonly used constants and utility
+ * methods.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.3 $
+ */
+public class LocalTerminalUtilities {
+
+ private static String NULL = null;
+ private static String LOCAL_TERMINAL = LocalTerminalLaunchDelegate.LAUNCH_CONFIGURATION_TYPE_ID;
+
+ /** The name of the line separator system property (i.e., "line.separator
"). */
+ public final static String LINE_SEPARATOR_PROPERTY = "line.separator"; //$NON-NLS-1$
+
+ /** The line separator CRLF (i.e., "\r\n
"). */
+ public final static String CRLF = "\r\n"; //$NON-NLS-1$
+
+ /** The line separator CR (i.e., "\r
"). */
+ public final static String CR = "\r"; //$NON-NLS-1$
+
+ /** The line separator LF (i.e., "\n
"). */
+ public final static String LF = "\n"; //$NON-NLS-1$
+
+ private LocalTerminalUtilities() {
+
+ super();
+ }
+
+ /** The {@link DebugPlugin}'s {@link ILaunchManager} instance. */
+ public final static ILaunchManager LAUNCH_MANAGER = DebugPlugin.getDefault().getLaunchManager();
+
+ /**
+ * The {@link ILaunchConfigurationType} for "Program" launches (in the "External Tools"
+ * category).
+ */
+ public final static ILaunchConfigurationType PROGRAM_LAUNCH_TYPE =
+ LAUNCH_MANAGER.getLaunchConfigurationType(LOCAL_TERMINAL);
+
+ /**
+ * Finds a launch configuration by its name.
+ *
+ * @param name the name of the launch configuration
+ * @return the corresponding {@link ILaunchConfiguration} object or null
if the
+ * configuration could not be found
+ * @throws CoreException if there was a general problem accessing launch configurations
+ */
+ public static ILaunchConfiguration findLaunchConfiguration(String name) throws CoreException {
+
+ ILaunchConfiguration[] configuration;
+ ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
+ configuration = manager.getLaunchConfigurations(LocalTerminalUtilities.PROGRAM_LAUNCH_TYPE);
+ int numberOfConfigurations = configuration.length;
+ for (int index = 0; index < numberOfConfigurations; index++) {
+
+ if (configuration[index].getName().equals(name)) {
+
+ return configuration[index];
+ }
+ }
+ String error = NLS.bind(LocalTerminalMessages.noSuchLaunchConfiguration, name);
+ throw new CoreException(new Status(IStatus.ERROR, LocalTerminalActivator.PLUGIN_ID, error));
+ }
+
+ /**
+ * Gets the local echo setting that is stored in the launch configuration for the given
+ * {@link ILocalTerminalSettings}.
+ *
+ * @param settings the {@link ILocalTerminalSettings}
+ * @return true
for local echo enabled, false
otherwise
+ */
+ public static boolean getLocalEcho(ILocalTerminalSettings settings) {
+
+ return getBooleanSetting(settings, LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO);
+ }
+
+ /**
+ * Gets the Ctrl-C/SIGINT setting that is stored in the launch configuration for the given
+ * {@link ILocalTerminalSettings}.
+ *
+ * @param settings the {@link ILocalTerminalSettings}
+ * @return true
if sending SIGINT for Ctrl-C is enabled,
+ * false
otherwise
+ */
+ public static boolean getCtrlC(ILocalTerminalSettings settings) {
+
+ return getBooleanSetting(settings, LocalTerminalLaunchUtilities.ATTR_CTRL_C);
+ }
+
+ /**
+ * Gets the line separator setting that is stored in the launch configuration for the given
+ * {@link ILocalTerminalSettings}.
+ *
+ * @param settings the {@link ILocalTerminalSettings}
+ * @return {@link ILocalTerminalSettings#LINE_SEPARATOR_LF},
+ * {@link ILocalTerminalSettings#LINE_SEPARATOR_CRLF},
+ * {@link ILocalTerminalSettings#LINE_SEPARATOR_CR}, or null
for the platform's
+ * default line separator
+ */
+ public static String getLineSeparator(ILocalTerminalSettings settings) {
+
+ String configurationName = settings.getLaunchConfigurationName();
+ try {
+
+ String ls;
+ ILaunchConfiguration configuration = findLaunchConfiguration(configurationName);
+ ls = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, NULL);
+ return ls;
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ return null;
+ }
+ }
+
+ //------------------------------------- PRIVATE SECTION --------------------------------------//
+
+ private static boolean getBooleanSetting(ILocalTerminalSettings settings, String attribute) {
+
+ String configurationName = settings.getLaunchConfigurationName();
+ try {
+
+ ILaunchConfiguration configuration = findLaunchConfiguration(configurationName);
+ return configuration.getAttribute(attribute, false);
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchDelegate.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchDelegate.java
new file mode 100644
index 00000000000..a6974da1268
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchDelegate.java
@@ -0,0 +1,265 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner 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:
+ * Mirko Raner - [196337] Adapted from org.eclipse.ui.externaltools/ProgramLaunchDelegate
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.launch;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.cdt.utils.spawner.ProcessFactory;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalActivator;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * The class {@link LocalTerminalLaunchDelegate} provides a launch configuration delegate for local
+ * terminal launches. It is based on the ProgramLaunchDelegate
class in the
+ * org.eclipse.ui.externaltools
plug-in. In contrast to the original class,
+ * {@link LocalTerminalLaunchDelegate} creates its low-level {@link Process} object using the CDT
+ * {@link ProcessFactory}, which allows the process to run with a pseudo-terminal ({@link PTY}).
+ *
+ * @author Mirko Raner and others
+ * @version $Revision: 1.2 $
+ */
+public class LocalTerminalLaunchDelegate extends LaunchConfigurationDelegate {
+
+ private final static String EMPTY = ""; //$NON-NLS-1$
+ private final static String PLUGIN_ID = LocalTerminalActivator.PLUGIN_ID;
+
+ /**
+ * The launch configuration type ID for terminal launches.
+ */
+ public final static String LAUNCH_CONFIGURATION_TYPE_ID = PLUGIN_ID + ".launch"; //$NON-NLS-1$
+
+ private static LocalTerminalStillRunningListener workbenchCloseListener;
+
+ /**
+ * Creates a new {@link LocalTerminalLaunchDelegate}.
+ */
+ public LocalTerminalLaunchDelegate() {
+
+ super();
+ }
+
+ /**
+ * Launches a new Local Terminal configuration in the specified mode. The launch object has
+ * already been registered with the launch manager.
+ *
+ * @param configuration the {@link ILaunchConfiguration} to launch
+ * @param mode the mode in which to launch; currently, Local Terminal launches only support the
+ * mode {@link org.eclipse.debug.core.ILaunchManager#RUN_MODE}
+ * @param progressMonitor an {@link IProgressMonitor}, or null
for no progress
+ * monitoring
+ * @param launch the {@link ILaunch} object
+ * @exception CoreException if launching fails
+ */
+ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch,
+ IProgressMonitor progressMonitor) throws CoreException {
+
+ // Extract all relevant information from the ILaunchConfiguration; the original
+ // ProgramLaunchDelegate class checks for cancellation again and again after each step,
+ // which is a somewhat suspect pattern; however, for now, LocalTerminalLaunchDelegate
+ // handles cancellation in the same way:
+ //
+ if (progressMonitor.isCanceled()) {
+
+ return;
+ }
+ IPath location = LocalTerminalLaunchUtilities.getLocation(configuration);
+ if (progressMonitor.isCanceled()) {
+
+ return;
+ }
+ IPath workingDirectory = LocalTerminalLaunchUtilities.getWorkingDirectory(configuration);
+ if (progressMonitor.isCanceled()) {
+
+ return;
+ }
+ String[] arguments = LocalTerminalLaunchUtilities.getArguments(configuration);
+ if (progressMonitor.isCanceled()) {
+
+ return;
+ }
+ String[] commandLine = new String[arguments != null? arguments.length+1:1];
+ commandLine[0] = location.toOSString();
+ if (arguments != null) {
+
+ System.arraycopy(arguments, 0, commandLine, 1, arguments.length);
+ }
+ File workingDirectoryAsFile = null;
+ if (workingDirectory != null) {
+
+ workingDirectoryAsFile = workingDirectory.toFile();
+ }
+ if (progressMonitor.isCanceled()) {
+
+ return;
+ }
+ String[] environment = LocalTerminalUtilities.LAUNCH_MANAGER.getEnvironment(configuration);
+ if (progressMonitor.isCanceled()) {
+
+ return;
+ }
+ //
+ // TODO: check if there is a better way of handling cancellation of terminal launches!
+
+ // Install an IWindowListener that checks for left-over terminal processes when the
+ // workbench is closed:
+ //
+ if (workbenchCloseListener == null) {
+
+ workbenchCloseListener = new LocalTerminalStillRunningListener();
+ PlatformUI.getWorkbench().addWorkbenchListener(workbenchCloseListener);
+ }
+
+ // Create the low-level Process object:
+ //
+ Process spawner;
+ try {
+
+ ProcessFactory factory = ProcessFactory.getFactory();
+ if (PTY.isSupported()) {
+
+ spawner = factory.exec(commandLine, environment, workingDirectoryAsFile, new PTY());
+ }
+ else {
+
+ spawner = factory.exec(commandLine, environment, workingDirectoryAsFile);
+ }
+ }
+ catch (IOException exception) {
+
+ Status error;
+ String message = exception.getMessage();
+ error = new Status(IStatus.ERROR, LocalTerminalActivator.PLUGIN_ID, message, exception);
+ throw new CoreException(error);
+ }
+
+ // Use program name as "process type" attribute:
+ //
+ Map processAttributes = new HashMap();
+ String programName = location.lastSegment();
+ String extension = location.getFileExtension();
+ if (extension != null) {
+
+ programName = programName.substring(0, programName.length()-extension.length()-1);
+ }
+ processAttributes.put(IProcess.ATTR_PROCESS_TYPE, programName.toLowerCase());
+
+ // Create the IProcess:
+ //
+ IProcess process = null;
+ if (spawner != null) {
+
+ String[] configurationName = {configuration.getName()};
+ String task = NLS.bind(LocalTerminalMessages.launchingConfiguration, configurationName);
+ progressMonitor.beginTask(task, IProgressMonitor.UNKNOWN);
+ process = DebugPlugin.newProcess(launch, spawner, commandLine[0], processAttributes);
+ }
+ if (spawner == null || process == null) {
+
+ if (spawner != null) {
+
+ spawner.destroy();
+ }
+ String pluginID = LocalTerminalActivator.PLUGIN_ID;
+ String errorMessage = LocalTerminalMessages.couldNotCreateIProcess;
+ Status error = new Status(IStatus.ERROR, pluginID, IStatus.ERROR, errorMessage, null);
+ throw new CoreException(error);
+ }
+ process.setAttribute(IProcess.ATTR_CMDLINE, generateCommandLine(commandLine));
+
+ // Wait for process termination if necessary (though probably highly unusual for terminal
+ // launches); again, the busy waiting pattern was copied from ProgramLaunchDelegate and is
+ // somewhat suspect:
+ //
+ if (!CommonTab.isLaunchInBackground(configuration)) {
+
+ while (!process.isTerminated()) {
+
+ try {
+
+ if (progressMonitor.isCanceled()) {
+
+ process.terminate();
+ break;
+ }
+ Thread.sleep(50);
+ }
+ catch (InterruptedException interrupt) {
+
+ Logger.logException(interrupt);
+ }
+ }
+ }
+ //
+ // TODO: find a better replacement for the busy waiting loop
+ }
+
+ //------------------------------------- PRIVATE SECTION --------------------------------------//
+
+ private String generateCommandLine(String[] commandLine) {
+
+ if (commandLine.length < 1) {
+
+ return EMPTY;
+ }
+ StringBuffer buffer = new StringBuffer();
+ for (int element = 0; element < commandLine.length; element++) {
+
+ if (element > 0) {
+
+ buffer.append(' ');
+ }
+ StringBuffer argument = new StringBuffer();
+ char[] characters = commandLine[element].toCharArray();
+ boolean argumentContainsSpace = false;
+ for (int index = 0; index < characters.length; index++) {
+
+ char character = characters[index];
+ if (character == '"') {
+
+ argument.append('\\');
+ }
+ else if (character == ' ') {
+
+ argumentContainsSpace = true;
+ }
+ argument.append(character);
+ }
+ if (argumentContainsSpace) {
+
+ buffer.append('"').append(argument).append('"');
+ }
+ else {
+
+ buffer.append(argument);
+ }
+ }
+ return buffer.toString();
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchUtilities.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchUtilities.java
new file mode 100644
index 00000000000..8eed6da5555
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchUtilities.java
@@ -0,0 +1,201 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner 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:
+ * Mirko Raner - [196337] Adapted from org.eclipse.ui.externaltools/ExternalToolsUtil
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.launch;
+
+import java.io.File;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.variables.IStringVariableManager;
+import org.eclipse.core.variables.VariablesPlugin;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalActivator;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+
+/**
+ * The class {@link LocalTerminalLaunchUtilities} provides some utility methods that are used by the
+ * {@link LocalTerminalLaunchDelegate}. The class is based on the ExternalToolsUtil
+ * class in the org.eclipse.ui.externaltools
plug-in. This code had to be duplicated
+ * because the original class is not part of the public API of its plug-in.
+ *
+ * @author Mirko Raner and others
+ * @version $Revision: 1.2 $
+ */
+public class LocalTerminalLaunchUtilities {
+
+ /** The launch configuration attribute for the local echo setting. */
+ public final static String ATTR_LOCAL_ECHO = LocalTerminalActivator.PLUGIN_ID +
+ ".echo"; //$NON-NLS-1$
+
+ /** The launch configuration attribute for the Ctrl-C/SIGINT setting. */
+ public final static String ATTR_CTRL_C = LocalTerminalActivator.PLUGIN_ID +
+ ".sigint"; //$NON-NLS-1$
+
+ /** The launch configuration attribute for the line terminator setting. */
+ public final static String ATTR_LINE_SEPARATOR = LocalTerminalActivator.PLUGIN_ID +
+ ".lineseparator"; //$NON-NLS-1$
+
+ private final static String[] EMPTY = {};
+ private final static String STRING = null;
+
+ // These constants were copied from IExternalToolConstants to avoid references to internal API:
+ //
+ private final static String XT = "org.eclipse.ui.externaltools"; //$NON-NLS-1$;
+ private final static String ATTR_LOCATION = XT+".ATTR_LOCATION"; //$NON-NLS-1$
+ private final static String ATTR_TOOL_ARGUMENTS = XT+".ATTR_TOOL_ARGUMENTS"; //$NON-NLS-1$
+ private final static String ATTR_WORKING_DIRECTORY = XT+".ATTR_WORKING_DIRECTORY"; //$NON-NLS-1$
+
+ private LocalTerminalLaunchUtilities() {
+
+ super();
+ }
+
+ /**
+ * Gets the image that should be used for representing the given launch configuration.
+ *
+ * @param configuration the {@link ILaunchConfiguration}
+ * @return an SWT {@link Image} or null
if no suitable image was found
+ */
+ public static Image getImage(ILaunchConfiguration configuration) {
+
+ String identifier;
+ try {
+
+ identifier = configuration.getType().getIdentifier();
+ }
+ catch (CoreException couldNotDetermineConfigurationType) {
+
+ identifier = null;
+ Logger.logException(couldNotDetermineConfigurationType);
+ }
+ if (identifier != null) {
+
+ return DebugUITools.getImage(identifier);
+ }
+ return null;
+ }
+
+ /**
+ * Expands and returns the location attribute of the given launch configuration. The location is
+ * verified to point to an existing file in the local file system.
+ *
+ * @param configuration the {@link ILaunchConfiguration}
+ * @return an absolute path to a file in the local file system
+ * @throws CoreException if unable to retrieve the associated launch configuration attribute, or
+ * if unable to resolve any variables, or if the resolved location does not point to an existing
+ * file in the local file system
+ */
+ public static IPath getLocation(ILaunchConfiguration configuration) throws CoreException {
+
+ Object[] configurationName = {configuration.getName()};
+ String location = configuration.getAttribute(ATTR_LOCATION, STRING);
+ if (location == null) {
+
+ abort(NLS.bind(LocalTerminalMessages.locationNotSpecified, configurationName), null, 0);
+ }
+ String expandedLocation = getStringVariableManager().performStringSubstitution(location);
+ if (expandedLocation == null || expandedLocation.length() == 0) {
+
+ abort(NLS.bind(LocalTerminalMessages.invalidLocation, configurationName), null, 0);
+ }
+ File file = new File(expandedLocation);
+ if (!file.isFile()) {
+
+ abort(NLS.bind(LocalTerminalMessages.invalidLocation, configurationName), null, 0);
+ }
+ return new Path(expandedLocation);
+ }
+
+ /**
+ * Expands and returns the working directory attribute of the given launch configuration.
+ * Returns null
if a working directory is not specified. If specified, the working
+ * directory is guaranteed to point to an existing directory in the local file system.
+ *
+ * @param configuration the {@link ILaunchConfiguration}
+ * @return an absolute path to a directory in the local file system, or null
if
+ * no working directory was specified
+ * @throws CoreException if unable to retrieve the associated launch configuration attribute,
+ * or if unable to resolve any variables, or if the resolved location does not point to an
+ * existing directory in the local file system
+ */
+ public static IPath getWorkingDirectory(ILaunchConfiguration configuration)
+ throws CoreException {
+
+ String location = configuration.getAttribute(ATTR_WORKING_DIRECTORY, STRING);
+ if (location != null) {
+
+ String expandedLocation;
+ expandedLocation = getStringVariableManager().performStringSubstitution(location);
+ if (expandedLocation.length() > 0) {
+
+ File path = new File(expandedLocation);
+ if (!path.isDirectory()) {
+
+ Object[] detail = {expandedLocation, configuration.getName()};
+ abort(NLS.bind(LocalTerminalMessages.invalidWorkingDirectory, detail), null, 0);
+ }
+ }
+ return new Path(expandedLocation);
+ }
+ return null;
+ }
+
+ /**
+ * Expands and returns the arguments attribute of the given launch configuration. Returns
+ * null
if arguments were not specified.
+ *
+ * @param configuration the {@link ILaunchConfiguration}
+ * @return an array of resolved arguments, or null
if no arguments were specified
+ * @throws CoreException if unable to retrieve the associated launch configuration attribute,
+ * or if unable to resolve any variables
+ */
+ public static String[] getArguments(ILaunchConfiguration configuration) throws CoreException {
+
+ String arguments = configuration.getAttribute(ATTR_TOOL_ARGUMENTS, STRING);
+ if (arguments != null) {
+
+ String expanded = getStringVariableManager().performStringSubstitution(arguments);
+ return parseStringIntoList(expanded);
+ }
+ return null;
+ }
+
+ //------------------------------------- PRIVATE SECTION --------------------------------------//
+
+ private static IStringVariableManager getStringVariableManager() {
+
+ return VariablesPlugin.getDefault().getStringVariableManager();
+ }
+
+ private static String[] parseStringIntoList(String arguments) {
+
+ if (arguments == null || arguments.length() == 0) {
+
+ return EMPTY;
+ }
+ return DebugPlugin.parseArguments(arguments);
+ }
+
+ private static void abort(String text, Throwable exception, int code) throws CoreException {
+
+ Status status;
+ status = new Status(IStatus.ERROR, LocalTerminalActivator.PLUGIN_ID, code, text, exception);
+ throw new CoreException(status);
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalStillRunningListener.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalStillRunningListener.java
new file mode 100644
index 00000000000..9014b5fb389
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalStillRunningListener.java
@@ -0,0 +1,119 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner 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:
+ * Mirko Raner - [196337] Adapted from org.eclipse.ui.externaltools/ProgramLaunchDelegate
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.launch;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities;
+import org.eclipse.tm.internal.terminal.local.launch.ui.LocalTerminalStillRunningDialog;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.IWorkbenchWindow;
+
+/**
+ * The class {@link LocalTerminalStillRunningListener} is an {@link IWorkbenchListener} that warns
+ * the user about any terminal launches that are still running when the workbench closes. The user
+ * might want to take specific action to deal with such left-over processes. Typically, this
+ * listener will trigger only on very rare cases because the
+ * {@link org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector} implementation will
+ * terminate left-over launches when the workbench window is closed. However, it is possible that
+ * a terminal launch does not get automatically terminated, for example, if it was started through
+ * an External Tools launch rather than through the terminal.
+ *
+ * The class {@link LocalTerminalStillRunningListener} is inspired by the
+ * ProgramLaunchWindowListener
class inside ProgramLaunchDelegate
in the
+ * org.eclipse.ui.externaltools
plug-in, though it works through a slightly different
+ * mechanism.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.1 $
+ */
+public class LocalTerminalStillRunningListener implements IWorkbenchListener {
+
+ /**
+ * Creates a new {@link LocalTerminalStillRunningListener}.
+ */
+ public LocalTerminalStillRunningListener() {
+
+ super();
+ }
+
+ /**
+ * Gets notified when the workbench is closed and informs the user about any left-over
+ * terminal launches.
+ *
+ * @param workbench the {@link IWorkbench}
+ * @param forced true
if a forced shutdown occurred, false
otherwise
+ * @return true
to allow the workbench to proceed with shutdown, false
+ * to prevent a shutdown (only for non-forced shutdown)
+ */
+ public boolean preShutdown(IWorkbench workbench, boolean forced) {
+
+ if (forced) {
+
+ return true;
+ }
+ ILaunchConfigurationType launchType;
+ String launchTypeID = LocalTerminalLaunchDelegate.LAUNCH_CONFIGURATION_TYPE_ID;
+ launchType = LocalTerminalUtilities.LAUNCH_MANAGER.getLaunchConfigurationType(launchTypeID);
+ if (launchType == null) {
+
+ return true;
+ }
+ List notTerminated = new ArrayList();
+ ILaunch launches[] = LocalTerminalUtilities.LAUNCH_MANAGER.getLaunches();
+ ILaunchConfigurationType configurationType;
+ ILaunchConfiguration configuration;
+ for (int launch = 0; launch < launches.length; launch++) {
+
+ try {
+
+ configuration = launches[launch].getLaunchConfiguration();
+ if (configuration == null) {
+
+ continue;
+ }
+ configurationType= configuration.getType();
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ continue;
+ }
+ if (configurationType.equals(launchType) && !launches[launch].isTerminated()) {
+
+ notTerminated.add(launches[launch]);
+ }
+ }
+ if (!notTerminated.isEmpty()) {
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ ILaunch[] launch = (ILaunch[])notTerminated.toArray(new ILaunch[notTerminated.size()]);
+ return LocalTerminalStillRunningDialog.openDialog(window.getShell(), launch);
+ }
+ return true;
+ }
+
+ /**
+ * Not implemented.
+ * @see IWorkbenchListener#postShutdown(IWorkbench)
+ */
+ public void postShutdown(IWorkbench workbench) {
+
+ // Not implemented
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalLaunchTabGroup.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalLaunchTabGroup.java
new file mode 100644
index 00000000000..4779739d606
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalLaunchTabGroup.java
@@ -0,0 +1,113 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.launch.ui;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.EnvironmentTab;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.debug.ui.ILaunchConfigurationTabGroup;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+
+/**
+* The class {@link LocalTerminalLaunchTabGroup} defines the tabs for the launch configuration
+* dialog that is used for terminal-based launches. The tab groups consists of the main tab for
+* a standard program launch (lifted from the org.eclipse.ui.externaltools
plug-in), the
+* custom {@link LocalTerminalSettingsTab}, and the {@link EnvironmentTab} and {@link CommonTab},
+* which can be publicly accessed from the org.eclipse.debug.ui
plug-in.
+*
+* @author Mirko Raner
+* @version $Revision: 1.1 $
+**/
+public class LocalTerminalLaunchTabGroup extends AbstractLaunchConfigurationTabGroup
+implements IDebugUIConstants {
+
+ private final static String ID = "id"; //$NON-NLS-1$
+ private final static String CLASS = "class"; //$NON-NLS-1$
+ private final static String LC_TAB_GROUPS = EXTENSION_POINT_LAUNCH_CONFIGURATION_TAB_GROUPS;
+ private final static String PROGRAM_TAB_GROUP =
+ "org.eclipse.ui.externaltools.launchConfigurationTabGroup.program"; //$NON-NLS-1$
+
+ /**
+ * Creates a new {@link LocalTerminalLaunchTabGroup}.
+ **/
+ public LocalTerminalLaunchTabGroup() {
+
+ super();
+ }
+
+ /**
+ * Creates the tabs contained in the local terminal launch configuration dialog for the specified
+ * launch mode. The tabs control's are not yet created. This is the first method called in the
+ * life-cycle of a tab group.
+ *
+ * @param dialog the launch configuration dialog this tab group is contained in
+ * @param mode the mode the launch configuration dialog was opened in
+ * @see AbstractLaunchConfigurationTabGroup#createTabs(ILaunchConfigurationDialog, String)
+ **/
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+
+ ILaunchConfigurationTab main = getMainTab(dialog, mode);
+ ILaunchConfigurationTab terminal = new LocalTerminalSettingsTab();
+ ILaunchConfigurationTab environment = new EnvironmentTab();
+ ILaunchConfigurationTab common = new CommonTab();
+ ILaunchConfigurationTab[] tabs = {main, terminal, environment, common};
+ setTabs(tabs);
+ }
+
+ //-------------------------------------- PRIVATE SECTION -------------------------------------//
+
+ private ILaunchConfigurationTab getMainTab(ILaunchConfigurationDialog dialog, String mode) {
+
+ // Find the main tab for the external program launch in the registry (a direct search is
+ // only possible for extensions that actually declare a unique ID, which most extensions
+ // don't; the search for the "id" attribute of a configuration element has to be done
+ // manually):
+ //
+ IConfigurationElement[] element;
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ element = registry.getConfigurationElementsFor(IDebugUIConstants.PLUGIN_ID, LC_TAB_GROUPS);
+ int numberOfElements = element.length;
+ for (int index = 0; index < numberOfElements; index++) {
+
+ if (element[index].getAttribute(ID).equals(PROGRAM_TAB_GROUP)) {
+
+ try {
+
+ ILaunchConfigurationTabGroup tabGroup;
+ Object executable = element[index].createExecutableExtension(CLASS);
+ tabGroup = (ILaunchConfigurationTabGroup)executable;
+ tabGroup.createTabs(dialog, mode);
+
+ // It's not possible to make assumptions about the class name of the program
+ // main tab (without over-stepping API boundaries), but it's usually the very
+ // first tab in the group (which is an assumption that actually also over-steps
+ // API boundaries, but it's the best possible solution, short of copying the
+ // whole source code):
+ //
+ return tabGroup.getTabs()[0];
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalSettingsTab.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalSettingsTab.java
new file mode 100644
index 00000000000..295cc7b8427
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalSettingsTab.java
@@ -0,0 +1,265 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.launch.ui;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.tm.internal.terminal.local.ILocalTerminalSettings;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages;
+import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchDelegate;
+import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+
+/**
+ * The class {@link LocalTerminalSettingsTab} provides the UI for custom settings that are specific
+ * to terminal-based launches. Currently, the tab allows the user to control the local echo settings
+ * and the line separator string.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.2 $
+ **/
+public class LocalTerminalSettingsTab extends AbstractLaunchConfigurationTab
+implements SelectionListener {
+
+ private final static String NULL = null;
+
+ private Button buttonEcho;
+ private Button buttonCtrlC;
+ private Button separatorDefault;
+ private Button separatorLF;
+ private Button separatorCRLF;
+ private Button separatorCR;
+
+ /**
+ * Creates a new {@link LocalTerminalSettingsTab}.
+ **/
+ public LocalTerminalSettingsTab() {
+
+ super();
+ }
+
+ /**
+ * Creates the top-level control for this launch configuration tab under the given parent
+ * composite. This method is called once on tab creation.
+ *
+ * @param parent the parent {@link Composite}
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(Composite)
+ **/
+ public void createControl(Composite parent) {
+
+ Composite container = new Composite(parent, SWT.NONE);
+ setControl(container);
+ container.setLayout(new GridLayout());
+ Group composite = new Group(container, SWT.NONE);
+ composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ composite.setText(LocalTerminalMessages.terminalSettings);
+ composite.setLayout(new GridLayout());
+
+ // Create echo check box:
+ //
+ buttonEcho = button(composite, LocalTerminalMessages.enableLocalEcho, SWT.CHECK);
+ buttonEcho.setLayoutData(new GridData());
+
+ // Create Ctrl-C/SIGINT check box:
+ //
+ buttonCtrlC = button(composite, LocalTerminalMessages.sendInterruptOnCtrlC, SWT.CHECK);
+ buttonCtrlC.setLayoutData(new GridData());
+
+ // Create radio buttons for line separator settings:
+ //
+ Composite separator = new Composite(composite, SWT.NONE);
+ RowLayout rowLayout = new RowLayout();
+ rowLayout.wrap = false;
+ separator.setLayout(rowLayout);
+ separatorDefault = button(separator, LocalTerminalMessages.lineSeparatorDefault, SWT.RADIO);
+ separatorLF = button(separator, LocalTerminalMessages.lineSeparatorLF, SWT.RADIO);
+ separatorCRLF = button(separator, LocalTerminalMessages.lineSeparatorCRLF, SWT.RADIO);
+ separatorCR = button(separator, LocalTerminalMessages.lineSeparatorCR, SWT.RADIO);
+ separator.setLayoutData(new GridData());
+ }
+
+ /**
+ * Returns the name of this tab.
+ *
+ * @return the name of this tab
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName()
+ **/
+ public String getName() {
+
+ return LocalTerminalMessages.terminalTabName;
+ }
+
+ /**
+ * Returns the image for this tab, or null
if none
+ *
+ * @return the image for this tab, or null
if none
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage()
+ **/
+ public Image getImage() {
+
+ return DebugUITools.getImage(LocalTerminalLaunchDelegate.LAUNCH_CONFIGURATION_TYPE_ID);
+ }
+
+ /**
+ * Initializes this tab's controls with values from the given launch configuration. This method
+ * is called when a configuration is selected to view or edit, after the tab's control has been
+ * created.
+ *
+ * @param configuration the launch configuration
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(ILaunchConfiguration)
+ **/
+ public void initializeFrom(ILaunchConfiguration configuration) {
+
+ boolean echo;
+ try {
+
+ echo = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO, false);
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ echo = false;
+ }
+ boolean ctrlC;
+ try {
+
+ ctrlC = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_CTRL_C, false);
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ ctrlC = false;
+ }
+ String ls;
+ try {
+
+ ls = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, NULL);
+ }
+ catch (CoreException exception) {
+
+ Logger.logException(exception);
+ ls = null;
+ }
+ buttonEcho.setSelection(echo);
+ buttonCtrlC.setSelection(ctrlC);
+ if (ILocalTerminalSettings.LINE_SEPARATOR_LF.equals(ls)) {
+
+ separatorLF.setSelection(true);
+ }
+ else if (ILocalTerminalSettings.LINE_SEPARATOR_LF.equals(ls)) {
+
+ separatorLF.setSelection(true);
+ }
+ else if (ILocalTerminalSettings.LINE_SEPARATOR_CRLF.equals(ls)) {
+
+ separatorCRLF.setSelection(true);
+ }
+ else if (ILocalTerminalSettings.LINE_SEPARATOR_CR.equals(ls)) {
+
+ separatorCR.setSelection(true);
+ }
+ else {
+
+ separatorDefault.setSelection(true);
+ }
+ }
+
+ /**
+ * Copies values from this tab into the given launch configuration.
+ *
+ * @param configuration the launch configuration
+ * @see AbstractLaunchConfigurationTab#performApply(ILaunchConfigurationWorkingCopy)
+ **/
+ public void performApply(ILaunchConfigurationWorkingCopy configuration) {
+
+ boolean echo = buttonEcho.getSelection();
+ configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO, echo);
+ boolean ctrlC = buttonCtrlC.getSelection();
+ configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_CTRL_C, ctrlC);
+ String lineSeparator = null;
+ if (separatorCRLF.getSelection()) {
+
+ lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_CRLF;
+ }
+ else if (separatorCR.getSelection()) {
+
+ lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_CR;
+ }
+ else if (separatorLF.getSelection()) {
+
+ lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_LF;
+ }
+ configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, lineSeparator);
+ }
+
+ /**
+ * Initializes the given launch configuration with default values for this tab. This method is
+ * called when a new launch configuration is created such that the configuration can be
+ * initialized with meaningful values. This method may be called before the tab's control is
+ * created.
+ *
+ * @param configuration the launch configuration
+ * @see AbstractLaunchConfigurationTab#setDefaults(ILaunchConfigurationWorkingCopy)
+ **/
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
+
+ configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO, false);
+ configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_CTRL_C, false);
+ configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, NULL);
+ }
+
+ /**
+ * Handles selection of any of the buttons in the tab.
+ *
+ * @param event the {@link SelectionEvent}
+ * @see SelectionListener#widgetSelected(SelectionEvent)
+ **/
+ public void widgetSelected(SelectionEvent event) {
+
+ setDirty(true);
+ getLaunchConfigurationDialog().updateButtons();
+ }
+
+ /**
+ * Handles default selection of any of the buttons in the tab.
+ *
+ * @param event the {@link SelectionEvent}
+ * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
+ **/
+ public void widgetDefaultSelected(SelectionEvent event) {
+
+ widgetSelected(event);
+ }
+
+ //-------------------------------------- PRIVATE SECTION -------------------------------------//
+
+ private Button button(Composite parent, String label, int buttonType) {
+
+ Button button = new Button(parent, buttonType);
+ button.addSelectionListener(this);
+ button.setText(label);
+ return button;
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalStillRunningDialog.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalStillRunningDialog.java
new file mode 100644
index 00000000000..f2b554e2474
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalStillRunningDialog.java
@@ -0,0 +1,298 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.launch.ui;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchesListener2;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+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.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities;
+import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities;
+import org.eclipse.tm.internal.terminal.local.process.LocalTerminalProcessRegistry;
+import org.eclipse.tm.internal.terminal.provisional.api.Logger;
+
+/**
+ * The class {@link LocalTerminalStillRunningDialog} is a dialog that is shown when the workbench is
+ * about to exit and one or more terminal launches are still running. It gives the user a choice
+ * between aborting the workbench shut-down, proceeding, or terminating the outstanding launches
+ * individually. When no more launches are running the dialog will automatically disappear and
+ * workbench shut-down will proceed.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.1 $
+ */
+public class LocalTerminalStillRunningDialog extends MessageDialog
+implements Runnable, SelectionListener, ILaunchesListener2 {
+
+ private final static String TITLE = LocalTerminalMessages.warningTitleTerminalsStillRunning;
+ private final static String MESSAGE = LocalTerminalMessages.warningMessageTerminalsStillRunning;
+ private final static String QUIT_ANYWAY = LocalTerminalMessages.quitWorkbenchAnyway;
+ private final static String DO_NOT_QUIT = LocalTerminalMessages.doNoQuitWorkbench;
+ private final static String[] BUTTONS = {QUIT_ANYWAY, DO_NOT_QUIT};
+ private final static RGB WHITE = new RGB(255, 255, 255);
+ private final static int SCROLLABLE_HEIGHT = 100;
+
+ // Image key copied from IInternalDebugUIConstants:
+ //
+ private final static String IMG_LCL_TERMINATE = "IMG_LCL_TERMINATE"; //$NON-NLS-1$
+
+ private ILaunch[] unterminated;
+ private Composite content;
+
+ private LocalTerminalStillRunningDialog(Shell parentShell, ILaunch[] launches) {
+
+ super(parentShell, TITLE, null, MESSAGE, WARNING, BUTTONS, 0);
+ setShellStyle(SWT.BORDER|SWT.TITLE|SWT.APPLICATION_MODAL|SWT.RESIZE|SWT.MAX);
+ unterminated = launches;
+ }
+
+ /**
+ * Opens a dialog that lists all terminal launches that have not yet terminated.
+ *
+ * @param shell the parent {@link Shell} for the dialog
+ * @param launches the launches that have not yet terminated
+ * @return true
to allow the workbench to proceed with shutdown, false
+ * to prevent a shutdown (only for non-forced shutdown)
+ */
+ public static boolean openDialog(Shell shell, ILaunch[] launches) {
+
+ LocalTerminalStillRunningDialog dialog;
+ dialog = new LocalTerminalStillRunningDialog(shell, launches);
+ dialog.setBlockOnOpen(true);
+ try {
+
+ LocalTerminalUtilities.LAUNCH_MANAGER.addLaunchListener(dialog);
+ return dialog.open() == 0;
+ }
+ finally {
+
+ LocalTerminalUtilities.LAUNCH_MANAGER.removeLaunchListener(dialog);
+ }
+ }
+
+ /**
+ * Creates the dialog buttons and sets the focus on the default button. This is done because
+ * otherwise the focus might land on one of the stop buttons of the unterminated launches, which
+ * looks somewhat funny.
+ *
+ * @param parent the parent {@link Composite}
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+
+ super.createButtonsForButtonBar(parent);
+ getButton(1).forceFocus();
+ }
+
+ /**
+ * Creates the custom area of the dialog that shows the list of terminal launches that have not
+ * yet been terminated.
+ *
+ * @param parent the parent {@link Composite} into which the custom area is inserted
+ * @return the {@link ScrolledComposite} for the custom area
+ *
+ * @see MessageDialog#createCustomArea(Composite)
+ */
+ protected Control createCustomArea(Composite parent) {
+
+ ScrolledComposite scrollable = new ScrolledComposite(parent, SWT.BORDER|SWT.V_SCROLL);
+ GridData gridData = new GridData(GridData.FILL_BOTH);
+ gridData.heightHint = SCROLLABLE_HEIGHT;
+ scrollable.setLayoutData(gridData);
+ scrollable.setExpandHorizontal(true);
+ scrollable.setExpandVertical(true);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = gridLayout.marginHeight = gridLayout.verticalSpacing = 0;
+ content = new Composite(scrollable, SWT.NONE);
+ content.setLayout(gridLayout);
+ content.setBackground(new Color(parent.getDisplay(), WHITE));
+ scrollable.setContent(content);
+ for (int index = 0; index < unterminated.length; index++) {
+
+ Composite item = createItem(content, unterminated[index]);
+ item.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ }
+ content.pack();
+ scrollable.setMinHeight(content.getBounds().height);
+ return scrollable;
+ }
+
+ /**
+ * Handles the {@link SelectionEvent}s that are sent when the user clicks the stop button of a
+ * launch. The stop button will immediately be disabled to indicate that the stop request is
+ * being processed. The actual launch termination will be confirmed in an asynchronous fashion
+ * by the {@link #launchesTerminated(ILaunch[])} method.
+ *
+ * @param event the {@link SelectionEvent}
+ *
+ * @see #launchesTerminated(ILaunch[])
+ */
+ public void widgetSelected(SelectionEvent event) {
+
+ ToolItem item = (ToolItem)event.widget;
+ ILaunch launch = (ILaunch)item.getParent().getParent().getData();
+ item.setEnabled(false);
+ try {
+
+ LocalTerminalProcessRegistry.addProcessBackToFinishedLaunch(launch);
+ launch.terminate();
+ }
+ catch (DebugException exception) {
+
+ Logger.logException(exception);
+ }
+ }
+
+ /**
+ * Handles default selection events by passing them to {@link #widgetSelected(SelectionEvent)}.
+ *
+ * @param event the {@link SelectionEvent}
+ *
+ * @see #widgetSelected(SelectionEvent)
+ * @see SelectionListener#widgetSelected(SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent event) {
+
+ widgetSelected(event);
+ }
+
+ /**
+ * Removes terminated launches from the list displayed by the dialog and closes the dialog once
+ * all outstanding launches have been terminated.
+ *
+ * @see #launchesTerminated(ILaunch[])
+ */
+ public void run() {
+
+ boolean allLaunchesTerminated = true;
+ Control[] child = content.getChildren();
+ int numberOfChildren = child.length;
+ for (int number = 0; number < numberOfChildren; number++) {
+
+ ILaunch launch = (ILaunch)child[number].getData();
+ if (launch != null && launch.isTerminated()) {
+
+ child[number].setData(null);
+ String exitValue;
+ try {
+
+ exitValue = String.valueOf(launch.getProcesses()[0].getExitValue());
+ }
+ catch (DebugException couldNotGetExitValue) {
+
+ exitValue = '(' + couldNotGetExitValue.getMessage() + ')';
+ }
+ Label label = (Label)((Composite)child[number]).getChildren()[1];
+ String process = label.getText();
+ process = NLS.bind(LocalTerminalMessages.terminatedProcess, process, exitValue);
+ label.setText(process);
+
+ // In case the launch terminated by itself (and not because the user pressed the
+ // stop button) disable the stop button so that no attempt can be made to stop the
+ // process twice:
+ //
+ ((Composite)child[number]).getChildren()[2].setEnabled(false);
+ }
+ if (child[number].getData() != null) {
+
+ allLaunchesTerminated = false;
+ }
+ }
+ if (allLaunchesTerminated) {
+
+ setReturnCode(0);
+ close();
+ }
+ }
+
+ /**
+ * Removes a recently terminated launch from the list displayed by the dialog. The actual work
+ * needs to be done in the UI thread and is performed by the {@link #run()} method.
+ *
+ * @param terminated a list of terminated launches
+ *
+ * @see #run()
+ */
+ public void launchesTerminated(ILaunch[] terminated) {
+
+ Display.getDefault().syncExec(this);
+ }
+
+ /**
+ * Not implemented.
+ * @see ILaunchesListener2#launchesAdded(ILaunch[])
+ */
+ public void launchesAdded(ILaunch[] launches) {
+
+ // Not implemented...
+ }
+
+ /**
+ * Not implemented.
+ * @see ILaunchesListener2#launchesChanged(ILaunch[])
+ */
+ public void launchesChanged(ILaunch[] launches) {
+
+ // Not implemented...
+ }
+
+ /**
+ * Not implemented.
+ * @see ILaunchesListener2#launchesRemoved(ILaunch[])
+ */
+ public void launchesRemoved(ILaunch[] launches) {
+
+ // Not implemented...
+ }
+
+ //-------------------------------------- PRIVATE SECTION -------------------------------------//
+
+ private Composite createItem(Composite parent, ILaunch launch) {
+
+ Composite item = new Composite(parent, SWT.NULL);
+ GridLayout gridLayout = new GridLayout(3, false);
+ item.setData(launch);
+ item.setLayout(gridLayout);
+ Image processImage = LocalTerminalLaunchUtilities.getImage(launch.getLaunchConfiguration());
+ Label icon = new Label(item, SWT.NULL);
+ icon.setImage(processImage);
+ Label label = new Label(item, SWT.NULL);
+ label.setText(launch.getLaunchConfiguration().getName());
+ ToolItem stopButton = new ToolItem(new ToolBar(item, SWT.FLAT), SWT.PUSH);
+ stopButton.addSelectionListener(this);
+ Image deleteImage = DebugUITools.getImage(IMG_LCL_TERMINATE);
+ stopButton.setImage(deleteImage);
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.verticalAlignment = GridData.CENTER;
+ gridData.grabExcessHorizontalSpace = true;
+ label.setLayoutData(gridData);
+ return item;
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcess.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcess.java
new file mode 100644
index 00000000000..76e89302180
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcess.java
@@ -0,0 +1,156 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.process;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.cdt.utils.spawner.Spawner;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStreamsProxy;
+import org.eclipse.debug.core.model.RuntimeProcess;
+
+/**
+ * The class {@link LocalTerminalProcess} is a customized {@link RuntimeProcess} for use by the
+ * {@link org.eclipse.tm.internal.terminal.local.LocalTerminalConnector}. It serves the purpose of
+ * preventing the {@link org.eclipse.debug.internal.ui.DebugUIPlugin DebugUIPlugin}'s
+ * {@link org.eclipse.debug.internal.ui.views.console.ProcessConsoleManager ProcessConsoleManager}
+ * from allocating a Console view in addition to the Terminal view that serves as the program's main
+ * I/O device. null
. If the process has a non-null
+ * {@link IStreamsProxy} a console will be automatically allocated. This is problematic because
+ * the Local Terminal Connector requires an {@link IStreamsProxy} but obviously not an additional
+ * console view. It would have been better if the Process Console Manager would check the
+ * corresponding attributes in the launch configuration rather than checking the
+ * {@link IStreamsProxy} of the process. The work-around for now is to remove the underlying
+ * process from the launch. No console will be allocated for a launch that doesn't have a process
+ * associated with it. Consequently, a currently running terminal launch will appear in the Debug
+ * view's list of active launches but the view will not show any child elements (and the element
+ * cannot be expanded, either). The {@link LocalTerminalProcessRegistry} keeps track of which
+ * {@link LocalTerminalProcess} is associated with a particular launch. Client code that needs to
+ * find the process of a launch can obtain it through that registry. null
. After the launch is terminated it will appear in the Debug view with the
+ * terminated process as its child element. The exit value of the terminal process can also be seen
+ * in that view. SIGINT
signal to the underlying system {@link Process}. This is roughly
+ * equivalent to the user pressing Ctrl-C.
+ *
+ * @return true
if the interrupt signal was sent successfully; false
+ * if the signal was not sent successfully or if no signal was sent because the underlying
+ * process is not a CDT {@link Spawner}
+ */
+ public boolean interrupt() {
+
+ Process process = getSystemProcess();
+ if (process instanceof Spawner) {
+
+ return ((Spawner)process).interrupt() == 0;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the {@link IStreamsProxy} of the process.
+ *
+ * @return the original result of {@link RuntimeProcess#getStreamsProxy()}, or null
+ * after {@link #resetStreamsProxy()} has been called.
+ */
+ public IStreamsProxy getStreamsProxy() {
+
+ if (resetStreamsProxy) {
+
+ return null;
+ }
+ return super.getStreamsProxy();
+ }
+
+ /**
+ * Resets the {@link IStreamsProxy} of this process. After calling this method,
+ * {@link #getStreamsProxy()} will always return null
.
+ */
+ protected void resetStreamsProxy() {
+
+ resetStreamsProxy = true;
+ }
+
+ /**
+ * Re-attaches the process to its launch and completes termination of the process. This ensures
+ * that the launch can properly terminate.
+ *
+ * @see RuntimeProcess#terminated()
+ */
+ protected void terminated() {
+
+ LocalTerminalProcessRegistry.addProcessBackToFinishedLaunch(getLaunch());
+ super.terminated();
+ }
+
+ //------------------------------------- PRIVATE SECTION --------------------------------------//
+
+ private static Map setProcessType(Map attributes) {
+
+ // The process type used to be set by the LocalTerminalProcessFactory. However, if some
+ // client code managed to instantiate a LocalTerminalProcess directly (instead of going
+ // through the factory) this would result in IProcess objects with an incorrect process type
+ // attribute. A better solution is to set the process type attribute at the time when the
+ // LocalTerminalProcess object is actually created:
+ //
+ if (attributes == null) {
+
+ attributes = new HashMap(1);
+ }
+ attributes.put(IProcess.ATTR_PROCESS_TYPE, PROCESS_TYPE);
+ return attributes;
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessFactory.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessFactory.java
new file mode 100644
index 00000000000..7faff8dfa83
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessFactory.java
@@ -0,0 +1,40 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.process;
+
+import java.util.Map;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.IProcessFactory;
+import org.eclipse.debug.core.model.IProcess;
+
+/**
+ * The class {@link LocalTerminalProcessFactory} is an {@link IProcessFactory} that produces
+ * {@link LocalTerminalProcess} objects.
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.2 $
+ */
+public class LocalTerminalProcessFactory implements IProcessFactory {
+
+ /**
+ * The ID of this factory (as used in plugin.xml
).
+ */
+ public final static String ID = "org.eclipse.tm.terminal.localProcess.factory"; //$NON-NLS-1$
+
+ /**
+ * @see IProcessFactory#newProcess(ILaunch, Process, String, Map)
+ */
+ public IProcess newProcess(ILaunch launch, Process process, String label, Map attributes) {
+
+ return new LocalTerminalProcess(launch, process, label, attributes);
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessRegistry.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessRegistry.java
new file mode 100644
index 00000000000..8383ba600b0
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessRegistry.java
@@ -0,0 +1,158 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.process;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchesListener2;
+import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities;
+
+/**
+ * The {@link LocalTerminalProcessRegistry} keeps a map between {@link ILaunch} objects and the
+ * {@link LocalTerminalProcess} objects that were associated with them. To prevent standard consoles
+ * from being opened, a {@link LocalTerminalProcess} is immediately removed from its {@link ILaunch}
+ * when the process is created (see {@link LocalTerminalProcess} for details).
+ * {@link LocalTerminalProcessRegistry} is a singleton (without lazy initialization).
+ *
+ * @author mirko
+ * @version $Revision: 1.1 $
+ */
+public class LocalTerminalProcessRegistry implements ILaunchesListener2 {
+
+ private final static LocalTerminalProcessRegistry INSTANCE = new LocalTerminalProcessRegistry();
+
+ private Map processes;
+
+ private LocalTerminalProcessRegistry() {
+
+ // The ILaunch interface does not make any statements about the suitability of implementing
+ // objects as hash keys. There might be ILaunch implementations that return a different
+ // hash code value if the object changes internally. To be safe in those cases, an
+ // IdentityHashMap is used:
+ //
+ processes = new IdentityHashMap();
+ }
+
+ /**
+ * Gets the {@link LocalTerminalProcess} that was originally associated with a given
+ * {@link ILaunch} object.
+ *
+ * @param launch the {@link ILaunch} that was used for creating the process
+ * @return the corresponding {@link LocalTerminalProcess}, or null
if no process
+ * could be found
+ */
+ public static LocalTerminalProcess getFromLaunch(ILaunch launch) {
+
+ return (LocalTerminalProcess)INSTANCE.processes.get(launch);
+ }
+
+ /**
+ * Adds a {@link LocalTerminalProcess} object back to its original {@link ILaunch}. This method
+ * will also perform a {@link LocalTerminalProcess#resetStreamsProxy()} on the process.
+ * The {@link #addProcessBackToFinishedLaunch(ILaunch)} method is necessary for properly
+ * terminating the launch of a terminal application (see {@link LocalTerminalProcess} for
+ * details).
+ *
+ * @param launch the {@link ILaunch} whose original process is to be re-attached
+ */
+ public static void addProcessBackToFinishedLaunch(ILaunch launch) {
+
+ LocalTerminalProcess process = getFromLaunch(launch);
+ if (process == null) {
+
+ // Maybe the process wasn't actually started in a terminal (can happen when a Terminal
+ // is launched from the External Tools menu):
+ //
+ return;
+ }
+ process.resetStreamsProxy();
+ if (launch.getProcesses().length == 0) {
+
+ launch.addProcess(process);
+ }
+ }
+
+ /**
+ * Registers a {@link LocalTerminalProcess} with a given {@link ILaunch} so that the process can
+ * be safely removed from the launch.
+ *
+ * @param launch the {@link ILaunch}
+ * @param process the {@link LocalTerminalProcess} originally associated with that launch
+ */
+ public static void registerWithLaunch(ILaunch launch, LocalTerminalProcess process) {
+
+ synchronized (INSTANCE) {
+
+ if (INSTANCE.processes.isEmpty()) {
+
+ // Start listening to terminated launches as soon as the first launch/process pair
+ // is registered:
+ //
+ LocalTerminalUtilities.LAUNCH_MANAGER.addLaunchListener(INSTANCE);
+ }
+ INSTANCE.processes.put(launch, process);
+ }
+ }
+
+ /**
+ * Handles the termination of launches. The {@link LocalTerminalProcessRegistry} acts as a
+ * {@link ILaunchesListener2} if there are monitored launches outstanding. It will automatically
+ * de-register itself (as a listener) when the last monitored launch was terminated.
+ *
+ * @param terminated the launches that were terminated
+ */
+ public void launchesTerminated(ILaunch[] terminated) {
+
+ synchronized (INSTANCE) {
+
+ int numberOfTerminatedLaunches = terminated.length;
+ for (int launch = 0; launch < numberOfTerminatedLaunches; launch++) {
+
+ INSTANCE.processes.remove(terminated[launch]);
+ }
+ if (INSTANCE.processes.isEmpty()) {
+
+ // If there are no more outstanding launches the listener can be removed again:
+ //
+ LocalTerminalUtilities.LAUNCH_MANAGER.removeLaunchListener(INSTANCE);
+ }
+ }
+ }
+
+ /**
+ * Not implemented.
+ * @see ILaunchesListener2#launchesAdded(ILaunch[])
+ */
+ public void launchesAdded(ILaunch[] launches) {
+
+ // Not implemented.
+ }
+
+ /**
+ * Not implemented.
+ * @see ILaunchesListener2#launchesRemoved(ILaunch[])
+ */
+ public void launchesRemoved(ILaunch[] launches) {
+
+ // Not implemented.
+ }
+
+ /**
+ * Not implemented.
+ * @see ILaunchesListener2#launchesChanged(ILaunch[])
+ */
+ public void launchesChanged(ILaunch[] launches) {
+
+ // Not implemented.
+ }
+}
diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ui/DependentHeightComposite.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ui/DependentHeightComposite.java
new file mode 100644
index 00000000000..7cdaa3e05cf
--- /dev/null
+++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ui/DependentHeightComposite.java
@@ -0,0 +1,88 @@
+/***************************************************************************************************
+ * Copyright (c) 2008 Mirko Raner.
+ * 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:
+ * Mirko Raner - initial implementation for Eclipse Bug 196337
+ **************************************************************************************************/
+
+package org.eclipse.tm.internal.terminal.local.ui;
+
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * The class {@link DependentHeightComposite} is a special kind of SWT {@link Composite} whose
+ * height depends on the height of some master {@link Composite} that does not have to be a direct
+ * parent or child of it. This class was introduced as a work-around for UI resizing problems with
+ * the Terminal API's PageBook class (which uses a StackLayout).
+ *
+ * @author Mirko Raner
+ * @version $Revision: 1.1 $
+ */
+public class DependentHeightComposite extends Composite implements ControlListener {
+
+ private Composite master;
+ private int preferredHeight = -1;
+ private int extraHeight;
+
+ /**
+ * Creates a new {@link DependentHeightComposite}.
+ *
+ * @param parent the parent {@link Composite}
+ * @param style the SWT style
+ * @param master the master {@link Composite} that determines the height
+ * @param extra the additional height in pixels (may be negative, to create a smaller height
+ * than the master {@link Composite})
+ */
+ public DependentHeightComposite(Composite parent, int style, Composite master, int extra) {
+
+ super(parent, style);
+ this.master = master;
+ this.extraHeight = extra;
+ master.addControlListener(this);
+ }
+
+ /**
+ * This method does nothing.
+ *
+ * @see ControlListener#controlMoved(ControlEvent)
+ */
+ public void controlMoved(ControlEvent event) {
+
+ // Does nothing...
+ }
+
+ /**
+ * Adjusts the {@link DependentHeightComposite} height if the master {@link Composite}'s size
+ * changed.
+ *
+ * @param event the {@link ControlEvent}
+ */
+ public void controlResized(ControlEvent event) {
+
+ setSize(getSize().x, master.getClientArea().height+extraHeight);
+ preferredHeight = master.getClientArea().height+extraHeight;
+ master.layout();
+ }
+
+ /**
+ * Computes the preferred size of this {@link DependentHeightComposite}.
+ *
+ * @see Composite#computeSize(int, int, boolean)
+ */
+ public Point computeSize(int widthHint, int heightHint, boolean changed) {
+
+ Point size = super.computeSize(widthHint, heightHint, changed);
+ if (preferredHeight != -1) {
+
+ size.y = preferredHeight;
+ }
+ return size;
+ }
+}