1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-26 02:15:31 +02:00

Bug 196337 - Initial contribution of local terminal feature

This commit is contained in:
Martin Oberhuber 2010-02-28 20:59:50 +00:00
parent d336005626
commit dd398b77bb
41 changed files with 4154 additions and 0 deletions

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.tm.terminal.local-feature</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.pde.FeatureBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.FeatureNature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,15 @@
####################################################################################################
# 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
####################################################################################################
bin.includes = feature.xml,\
feature.properties,\
epl-v10.html,\
license.html

View file

@ -0,0 +1,256 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Eclipse Public License - Version 1.0</title>
<style type="text/css">
body {
size: 8.5in 11.0in;
margin: 0.25in 0.5in 0.25in 0.5in;
tab-interval: 0.5in;
}
p {
margin-left: auto;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
p.list {
margin-left: 0.5in;
margin-top: 0.05em;
margin-bottom: 0.05em;
}
</style></head><body lang="EN-US">
<p align="center"><b>Eclipse Public License - v 1.0</b></p>
<p>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.</p>
<p><b>1. DEFINITIONS</b></p>
<p>"Contribution" means:</p>
<p class="list">a) in the case of the initial Contributor, the initial
code and documentation distributed under this Agreement, and</p>
<p class="list">b) in the case of each subsequent Contributor:</p>
<p class="list">i) changes to the Program, and</p>
<p class="list">ii) additions to the Program;</p>
<p class="list">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.</p>
<p>"Contributor" means any person or entity that distributes
the Program.</p>
<p>"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.</p>
<p>"Program" means the Contributions distributed in accordance
with this Agreement.</p>
<p>"Recipient" means anyone who receives the Program under
this Agreement, including all Contributors.</p>
<p><b>2. GRANT OF RIGHTS</b></p>
<p class="list">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.</p>
<p class="list">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.</p>
<p class="list">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.</p>
<p class="list">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.</p>
<p><b>3. REQUIREMENTS</b></p>
<p>A Contributor may choose to distribute the Program in object code
form under its own license agreement, provided that:</p>
<p class="list">a) it complies with the terms and conditions of this
Agreement; and</p>
<p class="list">b) its license agreement:</p>
<p class="list">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;</p>
<p class="list">ii) effectively excludes on behalf of all Contributors
all liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;</p>
<p class="list">iii) states that any provisions which differ from this
Agreement are offered by that Contributor alone and not by any other
party; and</p>
<p class="list">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.</p>
<p>When the Program is made available in source code form:</p>
<p class="list">a) it must be made available under this Agreement; and</p>
<p class="list">b) a copy of this Agreement must be included with each
copy of the Program.</p>
<p>Contributors may not remove or alter any copyright notices contained
within the Program.</p>
<p>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.</p>
<p><b>4. COMMERCIAL DISTRIBUTION</b></p>
<p>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.</p>
<p>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.</p>
<p><b>5. NO WARRANTY</b></p>
<p>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.</p>
<p><b>6. DISCLAIMER OF LIABILITY</b></p>
<p>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.</p>
<p><b>7. GENERAL</b></p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
</body></html>

View file

@ -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

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
# 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 id="org.eclipse.tm.terminal.local"
label="%featureName"
version="1.0.0.qualifier"
provider-name="Mirko Raner">
<description>
%description
</description>
<copyright>
%copyright
</copyright>
<license url="%licenseURL">
%license
</license>
<requires>
<import plugin="org.eclipse.tm.terminal" version="2.0.0" match="compatible"/>
<import plugin="org.eclipse.cdt.core" version="5.0.0" match="compatible"/>
<import plugin="org.eclipse.core.runtime"/>
<import plugin="org.eclipse.debug.core"/>
<import plugin="org.eclipse.debug.ui"/>
<import plugin="org.eclipse.jface"/>
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.ui.externaltools"/>
</requires>
<plugin id="org.eclipse.tm.terminal.local"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"
/>
</feature>

View file

@ -0,0 +1,79 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=iso-8859-1">
<title>Eclipse.org Software User Agreement</title>
</head>
<body lang="EN-US" link=blue vlink=purple>
<h2>Eclipse Foundation Software User Agreement</h2>
<p>March 17, 2005</p>
<h3>Usage Of Content</h3>
<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
(COLLECTIVELY &quot;CONTENT&quot;). 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.</p>
<h3>Applicable Licenses</h3>
<p>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
(&quot;EPL&quot;). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse.org CVS repository (&quot;Repository&quot;) in CVS
modules (&quot;Modules&quot;) and made available as downloadable archives (&quot;Downloads&quot;).</p>
<ul>
<li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins (&quot;Plug-ins&quot;), plug-in fragments (&quot;Fragments&quot;), and features (&quot;Features&quot;).</li>
<li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java&trade; ARchive) in a directory named &quot;plugins&quot;.</li>
<li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named &quot;features&quot;. Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of the Plug-ins
and/or Fragments associated with that Feature.</li>
<li>Features may also include other Features (&quot;Included Features&quot;). Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of Included Features.</li>
</ul>
<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named &quot;about.html&quot; (&quot;Abouts&quot;). The terms and conditions governing Features and
Included Features should be contained in files named &quot;license.html&quot; (&quot;Feature Licenses&quot;). Abouts and Feature Licenses may be located in any directory of a Download or Module
including, but not limited to the following locations:</p>
<ul>
<li>The top-level (root) directory</li>
<li>Plug-in and Fragment directories</li>
<li>Inside Plug-ins and Fragments packaged as JARs</li>
<li>Sub-directories of the directory named &quot;src&quot; of certain Plug-ins</li>
<li>Feature directories</li>
</ul>
<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Eclipse Update Manager, you must agree to a license (&quot;Feature Update License&quot;) 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 &quot;license&quot; property of files named &quot;feature.properties&quot; 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.</p>
<p>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):</p>
<ul>
<li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
<li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
<li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
<li>IBM Public License 1.0 (available at <a href="http://oss.software.ibm.com/developerworks/opensource/license10.html">http://oss.software.ibm.com/developerworks/opensource/license10.html</a>)</li>
<li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li>
<li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
</ul>
<p>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.</p>
<h3>Cryptography</h3>
<p>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.</p>
<small>Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.</small>
</body>
</html>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.tm.terminal.local</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,83 @@
#Tue Jul 29 00:26:52 PDT 2008
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.4
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.doc.comment.support=enabled
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
org.eclipse.jdt.core.compiler.problem.autoboxing=warning
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected
org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
org.eclipse.jdt.core.compiler.problem.nullReference=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.3

View file

@ -0,0 +1,20 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.tm.terminal.local;singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: org.eclipse.tm.internal.terminal.local.LocalTerminalActivator
Bundle-Localization: plugin
Bundle-Vendor: %providerName
Require-Bundle: org.eclipse.tm.terminal;bundle-version="2.0.0",
org.eclipse.cdt.core;bundle-version="5.0.0",
org.eclipse.core.runtime,
org.eclipse.debug.core,
org.eclipse.debug.ui,
org.eclipse.jface,
org.eclipse.ui,
org.eclipse.ui.externaltools
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-ActivationPolicy: lazy
Eclipse-LazyStart: true
Import-Package: org.eclipse.core.variables

View file

@ -0,0 +1,37 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<!--
# 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
-->
<HTML>
<HEAD>
<TITLE>About</TITLE>
<META HTTP-EQUIV=Content-Type CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY LANG="EN-US">
<H2>About This Content</H2>
<P>
August 4, 2008
<P>
<H3>License</H3>
Mirko Raner makes available all content in this plug-in (&quot;Content&quot;).
Unless otherwise indicated below, the Content is provided to you under the terms and conditions of
the Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available at
<A HREF="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</A>.
For purposes of the EPL, &quot;Program&quot; will mean the Content.
<P>
If you did not receive this Content directly from Mirko Raner, the Content is
being redistributed by another party (&quot;Redistributor&quot;) 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.
</BODY>
</HTML>

View file

@ -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

View file

@ -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@

View file

@ -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

View file

@ -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/,\
.

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -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

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.3"?>
<!--
# 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 API and implementation
-->
<plugin>
<!-- Terminal connector extension (the primary contribution of this plug-in): -->
<!-- -->
<extension point="org.eclipse.tm.terminal.terminalConnectors">
<connector name="%localConnection"
id="org.eclipse.tm.internal.terminal.local.LocalTerminalConnector"
class="org.eclipse.tm.internal.terminal.local.LocalTerminalConnector"
/>
</extension>
<!-- Process factory for terminal processes (necessary for preventing a standard console from -->
<!-- being created): -->
<!-- -->
<extension point="org.eclipse.debug.core.processFactories">
<processFactory id="org.eclipse.tm.terminal.localProcess.factory"
class="org.eclipse.tm.internal.terminal.local.process.LocalTerminalProcessFactory"
/>
</extension>
<!-- Launch configuration type for terminal launches: -->
<!-- -->
<extension point="org.eclipse.debug.core.launchConfigurationTypes">
<launchConfigurationType name="%terminalLaunch"
id="org.eclipse.tm.terminal.local.launch"
delegate="org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchDelegate"
category="org.eclipse.ui.externaltools"
modes="run"
/>
</extension>
<!-- Launch configuration icon used in the Launch Configuration dialog: -->
<!-- -->
<extension point="org.eclipse.debug.ui.launchConfigurationTypeImages">
<launchConfigurationTypeImage id="org.eclipse.tm.terminal.local.launch.icon"
configTypeID="org.eclipse.tm.terminal.local.launch"
icon="icons/terminal-launch.gif"
/>
</extension>
<!-- Launch configuration tab group for configuring workbench launches: -->
<!-- -->
<extension point="org.eclipse.debug.ui.launchConfigurationTabGroups">
<launchConfigurationTabGroup type="org.eclipse.tm.terminal.local.launch"
id="org.eclipse.tm.terminal.local.launch.tabs"
description="%terminalLaunchDescription"
class="org.eclipse.tm.internal.terminal.local.launch.ui.LocalTerminalLaunchTabGroup"
/>
</extension>
</plugin>

View file

@ -0,0 +1,75 @@
/***************************************************************************************************
* 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.tm.terminal.ssh/ISshSettings
**************************************************************************************************/
package org.eclipse.tm.internal.terminal.local;
import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore;
/**
* The interface {@link ILocalTerminalSettings} defines the public interface for connector-specific
* settings needed by the {@link LocalTerminalConnector}. The interface is implemented by class
* {@link LocalTerminalSettings}.
*
* @author Mirko Raner
* @version $Revision: 1.3 $
*/
public interface ILocalTerminalSettings {
/**
* The line separator setting CR (carriage return only; for example, used by Mac OS 9).
*/
public final static String LINE_SEPARATOR_CR = "\\r"; //$NON-NLS-1$
/**
* The line separator setting CRLF (carriage return and line feed; for example, used by
* Windows).
*/
public final static String LINE_SEPARATOR_CRLF = "\\r\\n"; //$NON-NLS-1$
/**
* The line separator setting LF (line feed only; used by all UNIX-based systems).
*/
public final static String LINE_SEPARATOR_LF = "\\n"; //$NON-NLS-1$
/**
* Loads the settings from a specified {@link ISettingsStore}.
*
* TODO: the {@link #load(ISettingsStore)} method should probably extracted to a super-interface
* as it appears to be common to all customized settings interfaces
*
* @param store the {@link ISettingsStore} to load the settings from
*/
public abstract void load(ISettingsStore store);
/**
* Saves the settings to a specified {@link ISettingsStore}.
*
* TODO: the {@link #save(ISettingsStore)} method should probably extracted to a super-interface
* as it appears to be common to all customized settings interfaces
*
* @param store the {@link ISettingsStore} for storing the settings
*/
public abstract void save(ISettingsStore store);
/**
* Gets the name of the launch configuration that will be started in the terminal.
*
* @return the launch configuration name
*/
public abstract String getLaunchConfigurationName();
/**
* Sets the name of the launch configuration that will be started in the terminal.
*
* @param configurationName the launch configuration name
*/
public abstract void setLaunchConfigurationName(String configurationName);
}

View file

@ -0,0 +1,88 @@
/***************************************************************************************************
* 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
**************************************************************************************************/
package org.eclipse.tm.internal.terminal.local;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The class {@link LocalTerminalActivator} is the bundle activator for the Local Terminal
* Connector plug-in.
*
* @author Mirko Raner
* @version $Revision: 1.2 $
*/
public class LocalTerminalActivator extends AbstractUIPlugin {
/**
* The plug-in ID of the Local Terminal Connector plug-in.
*/
public static final String PLUGIN_ID = "org.eclipse.tm.terminal.local"; //$NON-NLS-1$
private static LocalTerminalActivator plugin;
/**
* Creates a new {@link LocalTerminalActivator}.
*/
public LocalTerminalActivator() {
super();
}
/**
* Returns the shared plug-in instance.
*
* @return the shared instance
*/
public static LocalTerminalActivator getDefault() {
return plugin;
}
/**
* Returns an image descriptor for the image file at the given plug-in relative path.
*
* @param path the path to the image
* @return the image descriptor
*/
public static ImageDescriptor getImageDescriptor(String path) {
return imageDescriptorFromPlugin(PLUGIN_ID, path);
}
/**
* Starts the bundle and initializes the shared plug-in reference.
*
* @param context the {@link BundleContext}
*
* @see AbstractUIPlugin#start(BundleContext)
*/
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
}
/**
* Stops the bundle and resets the the shared plug-in reference.
*
* @param context the {@link BundleContext}
*
* @see AbstractUIPlugin#stop(BundleContext)
*/
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);
}
}

View file

@ -0,0 +1,319 @@
/***************************************************************************************************
* 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] initial implementation; some methods adapted from
* org.eclipse.tm.terminal.ssh/SshConnector
**************************************************************************************************/
package org.eclipse.tm.internal.terminal.local;
import java.io.OutputStream;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities;
import org.eclipse.tm.internal.terminal.local.process.LocalTerminalProcessFactory;
import org.eclipse.tm.internal.terminal.local.process.LocalTerminalProcessRegistry;
import org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage;
import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
import org.eclipse.tm.internal.terminal.provisional.api.Logger;
import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
import org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl;
/**
* The class {@link LocalTerminalConnector} provides a terminal connector implementation for
* connecting to local programs (for example, a locally running <code>bash</code> shell or
* <code>vi</code> 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
* <code>null</code> 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 <code>true</code> if the connection settings specify that local echo is enable,
* <code>false</code> 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
* <code>exit</code>, or if a <code>vi</code> editor is terminated by means of a
* <code>:q!</code> 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);
}
}

View file

@ -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 <code>null</code> 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 <code>null</code> 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);
}
}

View file

@ -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...
}
}

View file

@ -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;
}

View file

@ -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} <terminated, exit value: {1}>

View file

@ -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 java.io.PrintStream;
import org.eclipse.debug.core.IStreamListener;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
import org.eclipse.tm.internal.terminal.provisional.api.Logger;
/**
* The class {@link LocalTerminalOutputListener} is an {@link IStreamListener} that transfers output
* from a program's standard output and standard error streams to an {@link ITerminalControl}.
* It does so by listening for appended text and sending it to the terminal's
* {@link ITerminalControl#getRemoteToTerminalOutputStream()}. The class also performs line
* separator conversions as specified by the {@link ILocalTerminalSettings}.
*
* @author Mirko Raner
* @version $Revision: 1.2 $
*/
public class LocalTerminalOutputListener implements IStreamListener {
private PrintStream printStream;
private String lineSeparator;
/**
* Creates a new {@link LocalTerminalOutputListener}.
*
* @param control the {@link ITerminalControl} to which the received output is forwarded
* @param settings the {@link ILocalTerminalSettings}
*/
public LocalTerminalOutputListener(ITerminalControl control, ILocalTerminalSettings settings) {
printStream = new PrintStream(control.getRemoteToTerminalOutputStream(), true);
lineSeparator = LocalTerminalUtilities.getLineSeparator(settings);
if (lineSeparator == null) {
String defaultLS = System.getProperty(LocalTerminalUtilities.LINE_SEPARATOR_PROPERTY);
if (LocalTerminalUtilities.CRLF.equals(defaultLS)) {
lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_CRLF;
}
else if (LocalTerminalUtilities.LF.equals(defaultLS)) {
lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_LF;
}
else if (LocalTerminalUtilities.CR.equals(defaultLS)) {
lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_CR;
}
else {
Logger.log("Unknown default line separator: " + defaultLS); //$NON-NLS-1$
}
}
}
/**
* Processes new output that was appended to the intercepted stream.
*
* @param text the new output
* @param monitor the {@link IStreamMonitor} from which the output was received (this parameter
* is currently not evaluated because each {@link IStreamMonitor} has its own dedicated instance
* of {@link LocalTerminalOutputListener} attached)
*/
public void streamAppended(String text, IStreamMonitor monitor) {
// The VT100TerminalControl apparently adheres to a strict interpretation of the CR and
// LF control codes, i.e., CR moves the caret to the beginning of the line (but does not
// move down to the next line), and LF moves down to the next line (but not to the
// beginning of the line). Therefore, if the program launched in the terminal does not use
// CRLF as its line terminator the line terminators have to be converted to CRLF before
// being passed on to the terminal control:
//
if (!ILocalTerminalSettings.LINE_SEPARATOR_CRLF.equals(lineSeparator)) {
text = text.replaceAll(lineSeparator, LocalTerminalUtilities.CRLF);
}
printStream.print(text);
}
}

View file

@ -0,0 +1,184 @@
/***************************************************************************************************
* 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 java.io.IOException;
import java.io.OutputStream;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.tm.internal.terminal.local.process.LocalTerminalProcess;
/**
* The class {@link LocalTerminalOutputStream} is an {@link OutputStream} that copies data that is
* typed into the terminal to the standard input of the active process. Data that is written to the
* stream is directly forwarded to the {@link IStreamsProxy} of the process. CRLF line separators
* that are received from the terminal will be automatically converted to the line separator that is
* specified in the {@link ILocalTerminalSettings}. The Terminal Control generally sends CR line
* separators if the local echo is disabled and CRLF if enabled. The reason for this idiosyncrasy
* is not entirely clear right now and the line separator behavior might change in the future.
*
* TODO: research as to whether the CR/CRLF distinction in VT100TerminalControl.TerminalKeyHandler
* (based on the local echo setting) is really necessary
*
* @author Mirko Raner
* @version $Revision: 1.4 $
*/
public class LocalTerminalOutputStream extends OutputStream {
private final static String NOTHING = ""; //$NON-NLS-1$
private final static String CRLF = LocalTerminalUtilities.CRLF;
private final static char CR = '\r';
private final static char LF = '\n';
private final static char CTRL_C = '\03';
private final static int TERMINAL_SENDS_CR = 0;
private final static int TERMINAL_SENDS_CRLF = 1;
private final static int PROGRAM_EXPECTS_LF = 0;
private final static int PROGRAM_EXPECTS_CRLF = 1;
private final static int PROGRAM_EXPECTS_CR = 2;
private final static int NO_CHANGE = 0;
private final static int CHANGE_CR_TO_LF = 1;
private final static int INSERT_LF_AFTER_CR = 2;
private final static int REMOVE_CR = 3;
private final static int REMOVE_LF = 4;
// CRLF conversion table:
//
// Expected line separator --> | LF | CRLF | CR |
// ------------------------------------+-----------------+--------------------+----------------+
// Local echo off - control sends CR | change CR to LF | insert LF after CR | no change |
// ------------------------------------+-----------------+--------------------+----------------+
// Local echo on - control sends CRLF | remove CR | no change | remove LF |
//
private final static int[][] CRLF_REPLACEMENT = {
{CHANGE_CR_TO_LF, INSERT_LF_AFTER_CR, NO_CHANGE},
{REMOVE_CR, NO_CHANGE, REMOVE_LF}
};
private final boolean sendSIGINTOnCtrlC;
private IStreamsProxy streamsProxy;
private IProcess process;
private int replacement;
/**
* Creates a new {@link LocalTerminalOutputStream}.
*
* @param process the {@link IProcess} object of the terminal process
* @param settings the {@link ILocalTerminalSettings} (currently only used for the line
* separator settings)
*/
public LocalTerminalOutputStream(IProcess process, ILocalTerminalSettings settings) {
this.process = process;
streamsProxy = process.getStreamsProxy();
sendSIGINTOnCtrlC = LocalTerminalUtilities.getCtrlC(settings);
boolean localEcho = LocalTerminalUtilities.getLocalEcho(settings);
int terminalSends = localEcho? TERMINAL_SENDS_CRLF:TERMINAL_SENDS_CR;
int programExpects;
String lineSeparator = LocalTerminalUtilities.getLineSeparator(settings);
if (lineSeparator == null) {
lineSeparator = System.getProperty(LocalTerminalUtilities.LINE_SEPARATOR_PROPERTY);
if (LocalTerminalUtilities.CR.equals(lineSeparator)) {
programExpects = PROGRAM_EXPECTS_CR;
}
else if (LocalTerminalUtilities.LF.equals(lineSeparator)) {
programExpects = PROGRAM_EXPECTS_LF;
}
else {
programExpects = PROGRAM_EXPECTS_CRLF;
}
}
else if (lineSeparator.equals(ILocalTerminalSettings.LINE_SEPARATOR_LF)) {
programExpects = PROGRAM_EXPECTS_LF;
}
else if (lineSeparator.equals(ILocalTerminalSettings.LINE_SEPARATOR_CR)) {
programExpects = PROGRAM_EXPECTS_CR;
}
else {
programExpects = PROGRAM_EXPECTS_CRLF;
}
replacement = CRLF_REPLACEMENT[terminalSends][programExpects];
}
/**
* Writes the specified byte to this output stream.
*
* @param data the byte
* @throws IOException if an I/O error occurs
*/
public void write(int data) throws IOException {
write(new byte[] {(byte)data}, 0, 1);
}
/**
* Writes a specified number of bytes from the specified byte array starting at a given offset.
*
* @param data the array containing the data
* @param offset the offset into the array
* @param length the number of bytes to be written
* @throws IOException of an I/O error occurs
*/
public void write(byte[] data, int offset, int length) throws IOException {
String text = new String(data, offset, length);
//
// TODO: check whether this is correct! new String(byte[], int, int) always uses the default
// encoding!
if (replacement == CHANGE_CR_TO_LF) {
text = text.replace(CR, LF);
}
else if (replacement == INSERT_LF_AFTER_CR) {
text = text.replaceAll(ILocalTerminalSettings.LINE_SEPARATOR_CR, CRLF);
}
else if (replacement == REMOVE_CR) {
text = text.replaceAll(ILocalTerminalSettings.LINE_SEPARATOR_CR, NOTHING);
}
else if (replacement == REMOVE_LF) {
text = text.replaceAll(ILocalTerminalSettings.LINE_SEPARATOR_LF, NOTHING);
}
// Process Ctrl-C in the proper order:
//
int positionOfCtrlC = -1;
while (sendSIGINTOnCtrlC && (positionOfCtrlC = text.indexOf(CTRL_C)) != -1) {
// Send text up to (and including) the Ctrl-C to the process, then send a SIGINT:
//
streamsProxy.write(text.substring(0, positionOfCtrlC+1));
if (process instanceof LocalTerminalProcess) {
((LocalTerminalProcess)process).interrupt();
}
// Remove the part of the text that was already sent:
//
text = text.substring(positionOfCtrlC+1);
}
if (text.length() > 0) {
streamsProxy.write(text);
}
}
}

View file

@ -0,0 +1,108 @@
/***************************************************************************************************
* 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.tm.terminal.ssh/SshSettings
**************************************************************************************************/
package org.eclipse.tm.internal.terminal.local;
import java.lang.reflect.Field;
import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore;
import org.eclipse.tm.internal.terminal.provisional.api.Logger;
/**
* The class {@link LocalTerminalSettings} is the default implementation of the
* {@link ILocalTerminalSettings} interface.
*
* @author Mirko Raner
* @version $Revision: 1.3 $
*/
public class LocalTerminalSettings implements ILocalTerminalSettings {
private String launchConfiguration;
/**
* Loads the settings from the given {@link ISettingsStore}.
* This method loads the store contents by means of reflection. This is clearly overkill for
* the few settings supported by this class, but the code is much more reusable. Pretty much
* every implementation of a custom settings store is implemented in the same fashion and
* might be replace by a single centralized implementation.
*
* TODO: check for possibilities to reuse this code!
*
* @param store the {@link ISettingsStore}
* @see ILocalTerminalSettings#load(ISettingsStore)
*/
public void load(ISettingsStore store) {
Field[] declaredField = getClass().getDeclaredFields();
int numberOfFields = declaredField.length;
for (int index = 0; index < numberOfFields; index++) {
Field field = declaredField[index];
Class type = field.getType();
Object value = store.get(field.getName());
if (type.equals(boolean.class)) {
value = Boolean.valueOf((String)value);
}
// TODO: further conversions need to be added as new settings types are introduced
try {
field.set(this, value);
}
catch (IllegalAccessException illegalAccess) {
Logger.logException(illegalAccess);
}
}
}
/**
* Saves the settings to the specified {@link ISettingsStore}.
* See {@link #load(ISettingsStore)} for further implementation notes.
*
* @param store the {@link ISettingsStore}
*
* @see ILocalTerminalSettings#save(ISettingsStore)
*/
public void save(ISettingsStore store) {
Field[] declaredField = getClass().getDeclaredFields();
int numberOfFields = declaredField.length;
for (int index = 0; index < numberOfFields; index++) {
Field field = declaredField[index];
try {
field.setAccessible(true);
store.put(field.getName(), String.valueOf(field.get(this)));
}
catch (IllegalAccessException illegalAccess) {
Logger.logException(illegalAccess);
}
}
}
/**
* @see ILocalTerminalSettings#getLaunchConfigurationName()
*/
public String getLaunchConfigurationName() {
return launchConfiguration;
}
/**
* @see ILocalTerminalSettings#setLaunchConfigurationName(String)
*/
public void setLaunchConfigurationName(String launchConfiguration) {
this.launchConfiguration = launchConfiguration;
}
}

View file

@ -0,0 +1,287 @@
/***************************************************************************************************
* 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 - [196337] Adapted from org.eclipse.tm.terminal.ssh/SshSettingsPage
**************************************************************************************************/
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.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.ILaunchGroup;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.tm.internal.terminal.local.ui.DependentHeightComposite;
import org.eclipse.tm.internal.terminal.provisional.api.ISettingsPage;
import org.eclipse.tm.internal.terminal.provisional.api.Logger;
/**
* The class {@link LocalTerminalSettingsPage} is an implementation {@link ISettingsPage} for
* local program connections.
*
* @author Mirko Raner
* @version $Revision: 1.4 $
*/
public class LocalTerminalSettingsPage
implements ISettingsPage, ISelectionChangedListener, SelectionListener {
private ILocalTerminalSettings settings;
private TableViewer viewer;
private Button buttonEdit;
private Button buttonNew;
/**
* Creates a new {@link LocalTerminalSettingsPage} that reflects the settings of the specified
* {@link ILocalTerminalSettings} object.
*
* @param settings the {@link ILocalTerminalSettings}
*/
public LocalTerminalSettingsPage(ILocalTerminalSettings settings) {
this.settings = settings;
}
/**
* Creates the {@link org.eclipse.swt.widgets.Control} for the settings page.
* (NOTE: contrary to the common pattern, this method does not actually return the Control it
* created)
*
* @param parent the parent {@link Composite} into which the control is to be inserted
*
* @see ISettingsPage#createControl(Composite)
*/
public void createControl(Composite parent) {
Composite enclosing = parent.getParent();
Layout enclosingLayout = enclosing.getLayout();
int extra = 0;
if (enclosingLayout instanceof GridLayout) {
extra = -2*((GridLayout)enclosingLayout).marginHeight-2;
}
Composite composite = new DependentHeightComposite(parent, SWT.NONE, enclosing, extra);
//
// TODO: This is a HACK that ensures proper resizing of the settings page inside the
// StackLayout of the PageBook. The following code makes implicit assumptions about
// the internal layout of surrounding widgets. This is something that should be
// properly addressed in the framework (maybe in the PageBook class).
GridLayout layout = new GridLayout();
layout.marginWidth = layout.marginHeight = 0;
layout.horizontalSpacing = layout.verticalSpacing = 0;
composite.setLayout(layout);
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
Label label = new Label(composite, SWT.NONE);
label.setText(LocalTerminalMessages.launchConfiguration);
label.setLayoutData(new GridData());
// Create list of available launch configurations:
//
Composite tableAndButtons = new Composite(composite, SWT.NONE);
tableAndButtons.setLayoutData(new GridData(GridData.FILL_BOTH));
layout = new GridLayout(2, false);
layout.marginWidth = 0;
tableAndButtons.setLayout(layout);
Table table = new Table(tableAndButtons, SWT.BORDER);
viewer = new TableViewer(table);
viewer.setLabelProvider(new LocalTerminalLaunchLabelProvider());
viewer.setContentProvider(new LocalTerminalLaunchListProvider());
viewer.setInput(new Object());
viewer.addSelectionChangedListener(this);
table.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true, 0, 2));
buttonNew = pushButton(tableAndButtons, LocalTerminalMessages.labelNew, false);
buttonEdit = pushButton(tableAndButtons, LocalTerminalMessages.labelEdit, true);
buttonEdit.setEnabled(settings.getLaunchConfigurationName() != null);
//
// NOTE: echo and line separator settings were moved to the launch configuration!
// NOTE: loadSettings() is actually NOT called by the framework but needs to be called
// by the settings page itself
// TODO: this should be fixed in the framework; otherwise there is really no point
// in having it be a part of the ISettingsPage interface
//
loadSettings();
}
/**
* Loads the settings from the internal {@link ILocalTerminalSettings} object.
* This method will update the UI to reflect the current settings.
*
* @see ISettingsPage#loadSettings()
*/
public void loadSettings() {
String configurationName = settings.getLaunchConfigurationName();
ILaunchConfiguration configuration;
try {
configuration = LocalTerminalUtilities.findLaunchConfiguration(configurationName);
}
catch (CoreException couldNotFindLaunchConfiguration) {
configuration = null;
}
if (settings.getLaunchConfigurationName() != null && configuration != null) {
viewer.setSelection(new StructuredSelection(configuration), true);
}
}
/**
* Saves the settings that are currently displayed in the UI to the internal
* {@link ILocalTerminalSettings} object.
*
* @see ISettingsPage#saveSettings()
*/
public void saveSettings() {
if (viewer != null && !viewer.getSelection().isEmpty()) {
IStructuredSelection selection = (IStructuredSelection)viewer.getSelection();
Object element = selection.getFirstElement();
if (element instanceof ILaunchConfiguration) {
String launchConfiguration = ((ILaunchConfiguration)element).getName();
settings.setLaunchConfigurationName(launchConfiguration);
}
}
}
/**
* Checks if the current settings are valid for starting a terminal session.
* This method will only return <code>true</code> if a launch configuration is selected.
*
* @return <code>true</code> if a launch configuration has been selected, <code>false</code>
* 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;
}
}

View file

@ -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., "<code>line.separator</code>"). */
public final static String LINE_SEPARATOR_PROPERTY = "line.separator"; //$NON-NLS-1$
/** The line separator CRLF (i.e., "<code>\r\n</code>"). */
public final static String CRLF = "\r\n"; //$NON-NLS-1$
/** The line separator CR (i.e., "<code>\r</code>"). */
public final static String CR = "\r"; //$NON-NLS-1$
/** The line separator LF (i.e., "<code>\n</code>"). */
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 <code>null</code> 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 <code>true</code> for local echo enabled, <code>false</code> 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 <code>true</code> if sending SIGINT for Ctrl-C is enabled,
* <code>false</code> 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 <code>null</code> 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;
}
}
}

View file

@ -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 <code>ProgramLaunchDelegate</code> class in the
* <code>org.eclipse.ui.externaltools</code> 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 <code>null</code> 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();
}
}

View file

@ -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 <code>ExternalToolsUtil</code>
* class in the <code>org.eclipse.ui.externaltools</code> 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 <code>null</code> 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 <code>null</code> 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 <code>null</code> 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
* <code>null</code> if arguments were not specified.
*
* @param configuration the {@link ILaunchConfiguration}
* @return an array of resolved arguments, or <code>null</code> 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);
}
}

View file

@ -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
* <code>ProgramLaunchWindowListener</code> class inside <code>ProgramLaunchDelegate</code> in the
* <code>org.eclipse.ui.externaltools</code> 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 <code>true</code> if a forced shutdown occurred, <code>false</code> otherwise
* @return <code>true</code> to allow the workbench to proceed with shutdown, <code>false</code>
* 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;
}
/**
* <i>Not implemented</i>.
* @see IWorkbenchListener#postShutdown(IWorkbench)
*/
public void postShutdown(IWorkbench workbench) {
// Not implemented
}
}

View file

@ -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 <code>org.eclipse.ui.externaltools</code> plug-in), the
* custom {@link LocalTerminalSettingsTab}, and the {@link EnvironmentTab} and {@link CommonTab},
* which can be publicly accessed from the <code>org.eclipse.debug.ui</code> 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;
}
}

View file

@ -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 <code>null</code> if none
*
* @return the image for this tab, or <code>null</code> 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;
}
}

View file

@ -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 <code>true</code> to allow the workbench to proceed with shutdown, <code>false</code>
* 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);
}
/**
* <i>Not implemented</i>.
* @see ILaunchesListener2#launchesAdded(ILaunch[])
*/
public void launchesAdded(ILaunch[] launches) {
// Not implemented...
}
/**
* <i>Not implemented</i>.
* @see ILaunchesListener2#launchesChanged(ILaunch[])
*/
public void launchesChanged(ILaunch[] launches) {
// Not implemented...
}
/**
* <i>Not implemented</i>.
* @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;
}
}

View file

@ -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. <p>
* Unfortunately, the Process Console Manager determines the need for a Console view by checking the
* {@link IStreamsProxy} of the process for <code>null</code>. 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. <p>
* 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
* <code>null</code>. 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. <p>
*
* 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 <code>SIGINT</code> signal to the underlying system {@link Process}. This is roughly
* equivalent to the user pressing Ctrl-C.
*
* @return <code>true</code> if the interrupt signal was sent successfully; <code>false</code>
* 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 <code>null</code>
* 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 <code>null</code>.
*/
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;
}
}

View file

@ -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 <code>plugin.xml</code>).
*/
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);
}
}

View file

@ -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 <code>null</code> 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);
}
}
}
/**
* <i>Not implemented</i>.
* @see ILaunchesListener2#launchesAdded(ILaunch[])
*/
public void launchesAdded(ILaunch[] launches) {
// Not implemented.
}
/**
* <i>Not implemented</i>.
* @see ILaunchesListener2#launchesRemoved(ILaunch[])
*/
public void launchesRemoved(ILaunch[] launches) {
// Not implemented.
}
/**
* <i>Not implemented</i>.
* @see ILaunchesListener2#launchesChanged(ILaunch[])
*/
public void launchesChanged(ILaunch[] launches) {
// Not implemented.
}
}

View file

@ -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;
}
}