From dd398b77bb50386513f5a85ed15289ded4550139 Mon Sep 17 00:00:00 2001 From: Martin Oberhuber Date: Sun, 28 Feb 2010 20:59:50 +0000 Subject: [PATCH] Bug 196337 - Initial contribution of local terminal feature --- .../.project | 17 + .../build.properties | 15 + .../epl-v10.html | 256 ++++++++++++++ .../feature.properties | 117 +++++++ .../feature.xml | 47 +++ .../license.html | 79 +++++ org.eclipse.tm.terminal.local/.classpath | 7 + org.eclipse.tm.terminal.local/.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 83 +++++ .../META-INF/MANIFEST.MF | 20 ++ org.eclipse.tm.terminal.local/about.html | 37 ++ org.eclipse.tm.terminal.local/about.ini | 13 + org.eclipse.tm.terminal.local/about.mappings | 12 + .../about.properties | 17 + .../build.properties | 11 + .../icons/terminal-launch.gif | Bin 0 -> 601 bytes .../icons/terminal-local.png | Bin 0 -> 2520 bytes .../plugin.properties | 16 + org.eclipse.tm.terminal.local/plugin.xml | 63 ++++ .../local/ILocalTerminalSettings.java | 75 ++++ .../local/LocalTerminalActivator.java | 88 +++++ .../local/LocalTerminalConnector.java | 319 ++++++++++++++++++ .../LocalTerminalLaunchLabelProvider.java | 72 ++++ .../LocalTerminalLaunchListProvider.java | 90 +++++ .../terminal/local/LocalTerminalMessages.java | 103 ++++++ .../local/LocalTerminalMessages.properties | 39 +++ .../local/LocalTerminalOutputListener.java | 90 +++++ .../local/LocalTerminalOutputStream.java | 184 ++++++++++ .../terminal/local/LocalTerminalSettings.java | 108 ++++++ .../local/LocalTerminalSettingsPage.java | 287 ++++++++++++++++ .../local/LocalTerminalUtilities.java | 158 +++++++++ .../launch/LocalTerminalLaunchDelegate.java | 265 +++++++++++++++ .../launch/LocalTerminalLaunchUtilities.java | 201 +++++++++++ .../LocalTerminalStillRunningListener.java | 119 +++++++ .../ui/LocalTerminalLaunchTabGroup.java | 113 +++++++ .../launch/ui/LocalTerminalSettingsTab.java | 265 +++++++++++++++ .../ui/LocalTerminalStillRunningDialog.java | 298 ++++++++++++++++ .../local/process/LocalTerminalProcess.java | 156 +++++++++ .../process/LocalTerminalProcessFactory.java | 40 +++ .../process/LocalTerminalProcessRegistry.java | 158 +++++++++ .../local/ui/DependentHeightComposite.java | 88 +++++ 41 files changed, 4154 insertions(+) create mode 100644 org.eclipse.tm.terminal.local-feature/.project create mode 100644 org.eclipse.tm.terminal.local-feature/build.properties create mode 100644 org.eclipse.tm.terminal.local-feature/epl-v10.html create mode 100644 org.eclipse.tm.terminal.local-feature/feature.properties create mode 100644 org.eclipse.tm.terminal.local-feature/feature.xml create mode 100644 org.eclipse.tm.terminal.local-feature/license.html create mode 100644 org.eclipse.tm.terminal.local/.classpath create mode 100644 org.eclipse.tm.terminal.local/.project create mode 100644 org.eclipse.tm.terminal.local/.settings/org.eclipse.jdt.core.prefs create mode 100644 org.eclipse.tm.terminal.local/META-INF/MANIFEST.MF create mode 100644 org.eclipse.tm.terminal.local/about.html create mode 100644 org.eclipse.tm.terminal.local/about.ini create mode 100644 org.eclipse.tm.terminal.local/about.mappings create mode 100644 org.eclipse.tm.terminal.local/about.properties create mode 100644 org.eclipse.tm.terminal.local/build.properties create mode 100644 org.eclipse.tm.terminal.local/icons/terminal-launch.gif create mode 100644 org.eclipse.tm.terminal.local/icons/terminal-local.png create mode 100644 org.eclipse.tm.terminal.local/plugin.properties create mode 100644 org.eclipse.tm.terminal.local/plugin.xml create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ILocalTerminalSettings.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalActivator.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalConnector.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchLabelProvider.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchListProvider.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.properties create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputListener.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputStream.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettings.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettingsPage.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalUtilities.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchDelegate.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchUtilities.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalStillRunningListener.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalLaunchTabGroup.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalSettingsTab.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalStillRunningDialog.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcess.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessFactory.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessRegistry.java create mode 100644 org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ui/DependentHeightComposite.java diff --git a/org.eclipse.tm.terminal.local-feature/.project b/org.eclipse.tm.terminal.local-feature/.project new file mode 100644 index 00000000000..c8b6195f159 --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/.project @@ -0,0 +1,17 @@ + + + org.eclipse.tm.terminal.local-feature + + + + + + org.eclipse.pde.FeatureBuilder + + + + + + org.eclipse.pde.FeatureNature + + diff --git a/org.eclipse.tm.terminal.local-feature/build.properties b/org.eclipse.tm.terminal.local-feature/build.properties new file mode 100644 index 00000000000..b722ad42340 --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/build.properties @@ -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 diff --git a/org.eclipse.tm.terminal.local-feature/epl-v10.html b/org.eclipse.tm.terminal.local-feature/epl-v10.html new file mode 100644 index 00000000000..9321f4082e7 --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/epl-v10.html @@ -0,0 +1,256 @@ + + + + + + +Eclipse Public License - Version 1.0 + + + +

Eclipse Public License - v 1.0

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and

+

b) in the case of each subsequent Contributor:

+

i) changes to the Program, and

+

ii) additions to the Program;

+

where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.

+ +

"Contributor" means any person or entity that distributes +the Program.

+ +

"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions distributed in accordance +with this Agreement.

+ +

"Recipient" means anyone who receives the Program under +this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.

+ +

b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.

+ +

d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the Program in object code +form under its own license agreement, provided that:

+ +

a) it complies with the terms and conditions of this +Agreement; and

+ +

b) its license agreement:

+ +

i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;

+ +

ii) effectively excludes on behalf of all Contributors +all liability for damages, including direct, indirect, special, +incidental and consequential damages, such as lost profits;

+ +

iii) states that any provisions which differ from this +Agreement are offered by that Contributor alone and not by any other +party; and

+ +

iv) states that source code for the Program is available +from such Contributor, and informs licensees how to obtain it in a +reasonable manner on or through a medium customarily used for software +exchange.

+ +

When the Program is made available in source code form:

+ +

a) it must be made available under this Agreement; and

+ +

b) a copy of this Agreement must be included with each +copy of the Program.

+ +

Contributors may not remove or alter any copyright notices contained +within the Program.

+ +

Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use of +the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create +potential liability for other Contributors. Therefore, if a Contributor +includes the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and +indemnify every other Contributor ("Indemnified Contributor") +against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the +acts or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In +order to qualify, an Indemnified Contributor must: a) promptly notify +the Commercial Contributor in writing of such claim, and b) allow the +Commercial Contributor to control, and cooperate with the Commercial +Contributor in, the defense and any related settlement negotiations. The +Indemnified Contributor may participate in any such claim at its own +expense.

+ +

For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT +NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.

+ +

All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.

+ +

This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No party +to this Agreement will bring a legal action under this Agreement more +than one year after the cause of action arose. Each party waives its +rights to a jury trial in any resulting litigation.

+ + \ No newline at end of file diff --git a/org.eclipse.tm.terminal.local-feature/feature.properties b/org.eclipse.tm.terminal.local-feature/feature.properties new file mode 100644 index 00000000000..e289100d181 --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/feature.properties @@ -0,0 +1,117 @@ +#################################################################################################### +# Copyright (c) 2008 Mirko Raner and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Mirko Raner - initial implementation for Eclipse Bug 196337 +#################################################################################################### + +featureName=Target Management Terminal Local Connector +description=A local connector implementation for the Terminal. +copyright=\ +Copyright (c) 2008 Mirko Raner and others.\n\ +All rights reserved. This program and the accompanying materials\n\ +are made available under the terms of the Eclipse Public License v1.0\n\ +which accompanies this distribution, and is available at\n\ +http://www.eclipse.org/legal/epl-v10.html +licenseURL=license.html +license=\ +ECLIPSE FOUNDATION SOFTWARE USER AGREEMENT\n\ +March 17, 2005\n\ +\n\ +Usage Of Content\n\ +\n\ +THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\ +OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\ +USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\ +AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\ +NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU\n\ +AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\ +AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\ +OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\ +TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\ +OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\ +BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\ +\n\ +Applicable Licenses\n\ +\n\ +Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\ +is provided to you under the terms and conditions of the Eclipse Public\n\ +License Version 1.0 ("EPL"). A copy of the EPL is provided with this\n\ +Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\ +For purposes of the EPL, "Program" will mean the Content.\n\ +\n\ +Content includes, but is not limited to, source code, object code,\n\ +documentation and other files maintained in the Eclipse.org CVS\n\ +repository ("Repository") in CVS modules ("Modules") and made available\n\ +as downloadable archives ("Downloads").\n\ +\n\ + - Content may be structured and packaged into modules to facilitate delivering,\n\ + extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\ + plug-in fragments ("Fragments"), and features ("Features").\n\ + - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java(TM) ARchive)\n\ + in a directory named "plugins".\n\ + - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\ + Each Feature may be packaged as a sub-directory in a directory named "features".\n\ + Within a Feature, files named "feature.xml" may contain a list of the names and version\n\ + numbers of the Plug-ins and/or Fragments associated with that Feature.\n\ + - Features may also include other Features ("Included Features"). Within a Feature, files\n\ + named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\ +\n\ +Features may also include other Features ("Included Features"). Files named\n\ +"feature.xml" may contain a list of the names and version numbers of\n\ +Included Features.\n\ +\n\ +The terms and conditions governing Plug-ins and Fragments should be\n\ +contained in files named "about.html" ("Abouts"). The terms and\n\ +conditions governing Features and Included Features should be contained\n\ +in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\ +Licenses may be located in any directory of a Download or Module\n\ +including, but not limited to the following locations:\n\ +\n\ + - The top-level (root) directory\n\ + - Plug-in and Fragment directories\n\ + - Inside Plug-ins and Fragments packaged as JARs\n\ + - Sub-directories of the directory named "src" of certain Plug-ins\n\ + - Feature directories\n\ +\n\ +Note: if a Feature made available by the Eclipse Foundation is installed using the\n\ +Eclipse Update Manager, you must agree to a license ("Feature Update\n\ +License") during the installation process. If the Feature contains\n\ +Included Features, the Feature Update License should either provide you\n\ +with the terms and conditions governing the Included Features or inform\n\ +you where you can locate them. Feature Update Licenses may be found in\n\ +the "license" property of files named "feature.properties". Such Abouts,\n\ +Feature Licenses and Feature Update Licenses contain the terms and\n\ +conditions (or references to such terms and conditions) that govern your\n\ +use of the associated Content in that directory.\n\ +\n\ +THE ABOUTS, FEATURE LICENSES AND FEATURE UPDATE LICENSES MAY REFER\n\ +TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\ +SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\ +\n\ + - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\ + - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\ + - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\ + - IBM Public License 1.0 (available at http://oss.software.ibm.com/developerworks/opensource/license10.html)\n\ + - Metro Link Public License 1.00 (available at http://www.opengroup.org/openmotif/supporters/metrolink/license.html)\n\ + - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\ +\n\ +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\ +TO USE OF THE CONTENT. If no About, Feature License or Feature Update License\n\ +is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\ +govern that particular Content.\n\ +\n\ +Cryptography\n\ +\n\ +Content may contain encryption software. The country in which you are\n\ +currently may have restrictions on the import, possession, and use,\n\ +and/or re-export to another country, of encryption software. BEFORE\n\ +using any encryption software, please check the country's laws,\n\ +regulations and policies concerning the import, possession, or use,\n\ +and re-export of encryption software, to see if this is permitted.\n\ +\n\ +Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.\n diff --git a/org.eclipse.tm.terminal.local-feature/feature.xml b/org.eclipse.tm.terminal.local-feature/feature.xml new file mode 100644 index 00000000000..3f4a937aa82 --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/feature.xml @@ -0,0 +1,47 @@ + + + + + + %description + + + + %copyright + + + + %license + + + + + + + + + + + + + + + + diff --git a/org.eclipse.tm.terminal.local-feature/license.html b/org.eclipse.tm.terminal.local-feature/license.html new file mode 100644 index 00000000000..c6af966b61e --- /dev/null +++ b/org.eclipse.tm.terminal.local-feature/license.html @@ -0,0 +1,79 @@ + + + + +Eclipse.org Software User Agreement + + + +

Eclipse Foundation Software User Agreement

+

March 17, 2005

+ +

Usage Of Content

+ +

THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS + (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND + CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE + OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR + NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND + CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.

+ +

Applicable Licenses

+ +

Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0 + ("EPL"). A copy of the EPL is provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html. + For purposes of the EPL, "Program" will mean the Content.

+ +

Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse.org CVS repository ("Repository") in CVS + modules ("Modules") and made available as downloadable archives ("Downloads").

+ + + +

The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and +Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module +including, but not limited to the following locations:

+ + + +

Note: if a Feature made available by the Eclipse Foundation is installed using the Eclipse Update Manager, you must agree to a license ("Feature Update License") during the +installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or +inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature. +Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in +that directory.

+ +

THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE +OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):

+ + + +

IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please +contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.

+ +

Cryptography

+ +

Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to + another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import, + possession, or use, and re-export of encryption software, to see if this is permitted.

+ +Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. + + diff --git a/org.eclipse.tm.terminal.local/.classpath b/org.eclipse.tm.terminal.local/.classpath new file mode 100644 index 00000000000..64c5e31b7a2 --- /dev/null +++ b/org.eclipse.tm.terminal.local/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.eclipse.tm.terminal.local/.project b/org.eclipse.tm.terminal.local/.project new file mode 100644 index 00000000000..64ecfb91f82 --- /dev/null +++ b/org.eclipse.tm.terminal.local/.project @@ -0,0 +1,28 @@ + + + org.eclipse.tm.terminal.local + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.eclipse.tm.terminal.local/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.tm.terminal.local/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..d75669d46ce --- /dev/null +++ b/org.eclipse.tm.terminal.local/.settings/org.eclipse.jdt.core.prefs @@ -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 diff --git a/org.eclipse.tm.terminal.local/META-INF/MANIFEST.MF b/org.eclipse.tm.terminal.local/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..c380db7ab4c --- /dev/null +++ b/org.eclipse.tm.terminal.local/META-INF/MANIFEST.MF @@ -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 diff --git a/org.eclipse.tm.terminal.local/about.html b/org.eclipse.tm.terminal.local/about.html new file mode 100644 index 00000000000..b2a8401691f --- /dev/null +++ b/org.eclipse.tm.terminal.local/about.html @@ -0,0 +1,37 @@ + + + + + About + + + +

About This Content

+

+ August 4, 2008 +

+

License

+ + Mirko Raner makes available all content in this plug-in ("Content"). + Unless otherwise indicated below, the Content is provided to you under the terms and conditions of + the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available at + http://www.eclipse.org/legal/epl-v10.html. + For purposes of the EPL, "Program" will mean the Content. +

+ If you did not receive this Content directly from Mirko Raner, the Content is + being redistributed by another party ("Redistributor") and different terms and + conditions may apply to your use of any object code in the Content. Check the Redistributor's + license that was provided with the Content. If no such license exists, contact the Redistributor. + Unless otherwise indicated below, the terms and conditions of the EPL still apply to any source + code in the Content. + + diff --git a/org.eclipse.tm.terminal.local/about.ini b/org.eclipse.tm.terminal.local/about.ini new file mode 100644 index 00000000000..2d185b182ff --- /dev/null +++ b/org.eclipse.tm.terminal.local/about.ini @@ -0,0 +1,13 @@ +#################################################################################################### +# Copyright (c) 2008 Mirko Raner and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Mirko Raner - initial implementation for Eclipse Bug 196337 +#################################################################################################### + +featureImage=icons/terminal-local.png +aboutText=%feature.information diff --git a/org.eclipse.tm.terminal.local/about.mappings b/org.eclipse.tm.terminal.local/about.mappings new file mode 100644 index 00000000000..14f8af2448b --- /dev/null +++ b/org.eclipse.tm.terminal.local/about.mappings @@ -0,0 +1,12 @@ +#################################################################################################### +# Copyright (c) 2008 Mirko Raner and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Mirko Raner - initial implementation for Eclipse Bug 196337 +#################################################################################################### + +0=@build@ diff --git a/org.eclipse.tm.terminal.local/about.properties b/org.eclipse.tm.terminal.local/about.properties new file mode 100644 index 00000000000..5e2a9b09682 --- /dev/null +++ b/org.eclipse.tm.terminal.local/about.properties @@ -0,0 +1,17 @@ +#################################################################################################### +# Copyright (c) 2008 Mirko Raner and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Mirko Raner - initial implementation for Eclipse Bug 196337 +#################################################################################################### + +feature.information=Target Management Terminal Local Connector\n\ +\n\ +Version: {featureVersion}\n\ +\n\ +(c) Copyright Mirko Raner and others, 2008. All rights reserved.\n\ +Visit http://www.eclipse.org/dsdp/tm diff --git a/org.eclipse.tm.terminal.local/build.properties b/org.eclipse.tm.terminal.local/build.properties new file mode 100644 index 00000000000..15b26046ff5 --- /dev/null +++ b/org.eclipse.tm.terminal.local/build.properties @@ -0,0 +1,11 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + plugin.properties,\ + about.html,\ + about.ini,\ + about.mappings,\ + about.properties,\ + icons/,\ + META-INF/,\ + . diff --git a/org.eclipse.tm.terminal.local/icons/terminal-launch.gif b/org.eclipse.tm.terminal.local/icons/terminal-launch.gif new file mode 100644 index 0000000000000000000000000000000000000000..adfb9c2c80ffdc4b8095cf5838443120db655f75 GIT binary patch literal 601 zcmZ?wbhEHb6krfwIF`X+<=3bcK0!OYUmHk9Owfv$tQ|2~D{`_{)O4-r>DtlL9m6`b z;umViFLaLR)=FNZm9knhZG%>=ky)9QR@z40Y8TCnty&pdt;+56>rK59CK*+GYUb?I z%sHp8mJ*1X3$EO(w;@mjAAYnxsV*Ipl&37(FVf?`XT`S!*pl&=Ws_jjHi>M_G7 zs(!ui^suN&Az_n}{bodY&5jP55tmlKK4d{m?A(aZCDEyiJ))N+$1cgpSsaAy!I`5lXh0Lp3|GQt8vEu+__g1)}C6s z_Vn5f=QggpQ?~A2(e?}5w_Vz?EvFyOrL#OW?J^%Li!jM~ zUO_xcLynIjC0VTAU&q?Y%tA0KDKY6^f(T2hVo?%X2YXc33XX+|2gN!1B{CjfI?6p= jH%`h%XyJlnCKg#&8>x^5Pfj>0aR>+qBs83GV6X-NoFXk- literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal.local/icons/terminal-local.png b/org.eclipse.tm.terminal.local/icons/terminal-local.png new file mode 100644 index 0000000000000000000000000000000000000000..f04a52044ac00010a3c4a41d30c4785a4c6b1b47 GIT binary patch literal 2520 zcmV;}2`Bc6P)f^S3rE^IN9Xnb z@BjL*wbwTMKOav#zid^tCQ4I8ZSsHl_`!$w&H;A(lMA$vfw^?VjAUX|~a5jwJ?zBKY80SvlW7yR_K7 zaPH)(e9-;-GVeVhBBvfXa43H3<;y6fBD6&`iwYu&h<+e}o9nHK-{bTes61j z_BtAs8mUXL7K~A%1iX0iP?GzSt5>gb;`m#fJ$>x(xs!*#2z>1mAOB9Ns?8`QqDqu? z0`7kSPCk6k);lJqw>`G!_MhE<>n-l*A?^&f%=VIY_IH zR%eQKXMQtDa(CYEEo83oYP)@11d9Ty!(oi6y7vKhF0FiKa_&Q4+4s>;Zrio}M%!t0 zXrv8lu1;oaq^3e@DmW@wGOT16O)-*!I*f}XHA7=!OEs;J-E!=|pW3@}dGVDkGk2}5 z5wMtwwY0Ax@Zo#5-Z?S5^RI5aegD*TGdpNjn$(jzRaZwfQgbbw$w*9svkqr0LoXN< z5HZAHiDD2};%Y6#rn48%{#PeU#-44p7Chjrp$$0qBeTDu-kJWp8}GPpVPSTGcBMrv zsZ(=JYOcxRmG5$?IL=r$P1QBA#;)7gD58q05=U|NUWs_9wrn8$#HpkIp);HJeZN*6 zYXRv{iFNR5Qfof2`TCo8O-)WwNm86iu$tgV$y^1D@aFO}ymJ0=&JT{FC?Z%L<;l4+}ICp)0^W1||^V`!(k_=^)p`>O_ zW);ROJ21Av_m&>z`7>W-wY-F=j6z^WnLQi^vku`B+S_)`t@Pji&! z3?z-V)q1-B)H?^a0Vt4+OEXGJ>trXv!b*86W z)#0_IU*^P^Q8fjH%NV#6Md|rI#u+S*TH2=S8nov(v$eg4=T3f&Z~Wj(?C$&wTgPr4 znouHMB0+=@BZV(y@RB4EW&jF`(9%5C$o0CIko) z$zx75iZ3%%dL-733X4!i&uZRdlZ;;j9@TTDyvUK|mw5WU<8c~jKWpZMN|k9DU*_J(f=U@6s3rLF68g<-4l=V zpC=EJ=rp?~?EZzF+ovEMBQWtxZ!m>5t6%A6oF zG*D7yH6MmRfuNqQzs%uFFY=w^|ICStrIIDnWO(s#muv_ z^9#JXc#Jn*yXD8|tv){U?7trS_O0U=)(P0vm3NEY{1#JJ5>p^C8CJHD2SSYGJ`go< zWcekYcD@3&0j)nR4VfJ=s% zy}LQQWkxk0V@4MW7?Q+cHWEPfgj?x-CB3}#r=dAsk!qk=-HeqKX9XvQMtvKt`ew4E zMiB)QOV)m=s+j0WN7eoJ4?M}BKVbFBRaURAGU)et`ky|BVlb*Ci9-v&PT-LPhx~&N z?|saj9eh;B^N-o~DzWN`#W)h{1QKes86t)vhS5^X4-ruDr0MXNU--lOS)cFl@Nty9 z3Tc{P1{)O^k~s6==lA|WdNMpE*In3QYCU5ZN8%V7n|Q5(WEdP^*X313beO(rHtaPX z`r=c=jL8f7{XX5*E`xrbA}{#*qYn*$Y7@JqjUeDS3}uWZ#lOok+PeTwo5A%KV_ibW#CC`gQ8B8VqRh6KDQMh%z| z>;?m37%AY_1G&--MpfHUq~U$2#2{lQPQSkG*_R*vwQl9o&&N%^O;ZVJqzrvZUl2k- z1&nbduEKNw@ax>aCmGFp*fFl{zjTejd)WX_y%~qkkDXXb5|>P&>D>8i%(oGn4T8+iOby>z zdUv^|!p`8syu`RI1V0~q+!TG7mJmAJw>HU*vAu^Lf9tlTr8A>C0Q0l6eEw4(`Hg;QSNf$Hh$yC)N25whL|Y;1Sd3B+ zF{UBJB!-xY#59DMsc0G{rYf3>s5MH(T3c7e3|m(lRf(Vd+ZX}K&-~)u_x=xYpG7Fs3U8|b0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ILocalTerminalSettings.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ILocalTerminalSettings.java new file mode 100644 index 00000000000..4e41e3bc99b --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ILocalTerminalSettings.java @@ -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); +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalActivator.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalActivator.java new file mode 100644 index 00000000000..26ba5ce996c --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalActivator.java @@ -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); + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalConnector.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalConnector.java new file mode 100644 index 00000000000..fb3953d491e --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalConnector.java @@ -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 bash shell or + * vi editor). + * + * @author Mirko Raner + * @version $Revision: 1.4 $ + */ +public class LocalTerminalConnector extends TerminalConnectorImpl +implements IDebugEventSetListener { + + // Shorthand for attribute names: + // + private final static String ATTR_CAPTURE_IN_CONSOLE = IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE; + private final static String ATTR_CAPTURE_OUTPUT = DebugPlugin.ATTR_CAPTURE_OUTPUT; + private final static String ATTR_PROCESS_FACTORY_ID = DebugPlugin.ATTR_PROCESS_FACTORY_ID; + + private LocalTerminalOutputStream terminalToLocalProcessStream; + private LocalTerminalOutputListener outputListener; + private LocalTerminalOutputListener errorListener; + private ILocalTerminalSettings settings; + private IStreamMonitor outputMonitor; + private IStreamMonitor errorMonitor; + private IProcess process; + private ILaunch launch; + + /** + * Creates a new {@link LocalTerminalConnector}. This constructor is invoked by the framework. + */ + public LocalTerminalConnector() { + + settings = new LocalTerminalSettings(); + } + + /** + * Loads the connector's settings from the specified store. + * + * @param store the {@link ISettingsStore} + * + * @see TerminalConnectorImpl#load(ISettingsStore) + * + * TODO: the load(ISettingsStore) method should probably be made abstract in + * TerminalConnectorImpl, otherwise it is not immediately clear that clients need to + * override this method if custom settings are necessary (which they usually are). + * Maybe the whole settings store mechanism should be redesigned. The current scheme + * requires clients to implement load/save methods in their connector implementation + * classes (necessity to override is not immediately obvious) and in the settings store + * implementations (not enforced at all; merely expected by convention). Structurally, + * all client implementations look more or less the same, and probably could be handled + * by the framework in a uniform way. Maybe a configuration mechanism using attributes + * (like, for example, ILaunchConfiguration) might be beneficial here. + */ + public void load(ISettingsStore store) { + + settings.load(store); + } + + /** + * Stores the connector's settings into the specified store. + * See {@link #load(ISettingsStore)} for additional notes. + * + * @param store the {@link ISettingsStore} + * + * @see TerminalConnectorImpl#save(ISettingsStore) + */ + public void save(ISettingsStore store) { + + settings.save(store); + } + + /** + * Creates the {@link ISettingsPage} for the settings of this connector. + * + * @return a new page that can be used in a dialog to setup this connection, or + * null if the connection cannot be customized or configured + * + * @see TerminalConnectorImpl#makeSettingsPage() + */ + public ISettingsPage makeSettingsPage() { + + return new LocalTerminalSettingsPage(settings); + } + + /** + * Returns a string that represents the settings of the connection. + * + * @return the name of the launch configuration that is running in the terminal + * + * @see TerminalConnectorImpl#getSettingsSummary() + * @see ILocalTerminalSettings#getLaunchConfigurationName() + */ + public String getSettingsSummary() { + + return settings.getLaunchConfigurationName(); + } + + /** + * Checks if local echo is required. + * + * @return true if the connection settings specify that local echo is enable, + * false otherwise + * + * @see TerminalConnectorImpl#isLocalEcho() + * @see LocalTerminalLaunchUtilities#ATTR_LOCAL_ECHO + */ + public boolean isLocalEcho() { + + return LocalTerminalUtilities.getLocalEcho(settings); + } + + /** + * Returns an {@link OutputStream} that writes to the local program's standard input. For the + * stream in the other direction (remote to terminal) see + * {@link ITerminalControl#getRemoteToTerminalOutputStream()}. + * + * @return the terminal-to-remote-stream (bytes written to this stream will be sent to the + * local program) + */ + public OutputStream getTerminalToRemoteStream() { + + return terminalToLocalProcessStream; + } + + /** + * Connects a locally launched program to the {@link ITerminalControl}. + * + * @param control the {@link ITerminalControl} through which the user can interact with the + * program + */ + public void connect(ITerminalControl control) { + + super.connect(control); + control.setState(TerminalState.CONNECTING); + ILaunchConfigurationWorkingCopy workingCopy = null; + ILaunchConfiguration configuration = null; + try { + + String configurationName = settings.getLaunchConfigurationName(); + configuration = LocalTerminalUtilities.findLaunchConfiguration(configurationName); + + // Always set the the process factory ID and enable console output (there is no need + // to restore these attributes afterwards; disabling console output does not make + // sense for terminal launches and will be overridden when the configuration is + // actually launched): + // + workingCopy = configuration.getWorkingCopy(); + workingCopy.setAttribute(ATTR_CAPTURE_OUTPUT, true); + workingCopy.setAttribute(ATTR_CAPTURE_IN_CONSOLE, true); + workingCopy.setAttribute(ATTR_PROCESS_FACTORY_ID, LocalTerminalProcessFactory.ID); + configuration = workingCopy.doSave(); + launch = configuration.launch(ILaunchManager.RUN_MODE, null); + + // To prevent a console from being allocated, the launch will actually not contain a + // reference to the runtime process. The process has to be obtained from the + // LocalTerminalProcessRegistry instead: + // + process = LocalTerminalProcessRegistry.getFromLaunch(launch); + IStreamsProxy streamsProxy = process.getStreamsProxy(); + + // Hook up standard input: + // + terminalToLocalProcessStream = new LocalTerminalOutputStream(process, settings); + + // Hook up standard output: + // + outputMonitor = streamsProxy.getOutputStreamMonitor(); + outputListener = new LocalTerminalOutputListener(control, settings); + outputMonitor.addListener(outputListener); + outputListener.streamAppended(outputMonitor.getContents(), outputMonitor); + + // Hook up standard error: + // + errorMonitor = streamsProxy.getErrorStreamMonitor(); + errorListener = new LocalTerminalOutputListener(control, settings); + errorMonitor.addListener(errorListener); + errorListener.streamAppended(errorMonitor.getContents(), errorMonitor); + // + // TODO: add proper synchronization for incoming data from stdout and stderr: + // currently, the data gets sometimes processed in the wrong order, for example, + // the next prompt (which shells like bash print to stderr) sometimes appears + // before the command's proper output that was sent to stdout. For example, + // you get: + // + // $ echo hello + // $ hello + // + // instead of the correct output of: + // + // $ echo hello + // hello + // $ + + // Listen for process termination and update the terminal state: + // + DebugPlugin.getDefault().addDebugEventListener(this); + control.setState(TerminalState.CONNECTED); + } + catch (CoreException exception) { + + Logger.logException(exception); + } + } + + /** + * Disconnects the connector if it is currently connected or does nothing otherwise. This method + * will try to terminate the underlying launched process and will remove all registered + * listeners. + */ + public void doDisconnect() { + + try { + + removeAllListeners(); + + // To prevent a console from being allocated, Terminal launches don't have an IProcess + // associated with them while they are running. However, to properly terminate a launch + // the launch has to contain at least one process that can be terminated (launches + // without processes effectively cannot be terminated): + // + LocalTerminalProcessRegistry.addProcessBackToFinishedLaunch(launch); + + // Now, terminate the process if it hasn't been terminated already: + // + if (launch.canTerminate()) { + + launch.terminate(); + // + // NOTE: canTerminate() merely indicates that the launch has not been terminated + // previously already + } + } + catch (DebugException couldNotTerminate) { + + Logger.logException(couldNotTerminate); + } + } + + /** + * Listens for self-induced termination of the launched process. For example, this method will + * be notified if a launched shell is terminated by pressing Control-D or by calling + * exit, or if a vi editor is terminated by means of a + * :q! command. + * + * @param event the debug events + * + * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[]) + */ + public void handleDebugEvents(DebugEvent[] event) { + + int numberOfEvents = event.length; + for (int index = 0; index < numberOfEvents; index++) { + + if (event[index].getSource().equals(process) + && (event[index].getKind() == DebugEvent.TERMINATE)) { + + fControl.setState(TerminalState.CLOSED); + removeAllListeners(); + return; + } + } + } + + /** + * Removes any listeners that the {@link LocalTerminalConnector} might have registered in its + * {@link #connect(ITerminalControl)} method. This method is necessary for clean-up when a + * connection is closed. It prevents that orphaned or meaningless listeners keep accumulating + * on certain objects. + */ + protected void removeAllListeners() { + + if (outputMonitor != null && outputListener != null) { + + outputMonitor.removeListener(outputListener); + } + if (errorMonitor != null && errorListener != null) { + + errorMonitor.removeListener(errorListener); + } + DebugPlugin.getDefault().removeDebugEventListener(this); + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchLabelProvider.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchLabelProvider.java new file mode 100644 index 00000000000..ebac822d946 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchLabelProvider.java @@ -0,0 +1,72 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local; + +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.jface.viewers.BaseLabelProvider; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.swt.graphics.Image; +import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities; + +/** + * The class {@link LocalTerminalLaunchLabelProvider} is an {@link ILabelProvider} for lists (or + * tables) of {@link ILaunchConfiguration}s. It returns a configuration's name as the text label, + * and the configuration type's regular icon as the image label. + * + * @author Mirko Raner + * @version $Revision: 1.2 $ + */ +public class LocalTerminalLaunchLabelProvider extends BaseLabelProvider implements ILabelProvider { + + /** + * Creates a new {@link LocalTerminalLaunchLabelProvider}. + */ + public LocalTerminalLaunchLabelProvider() { + + super(); + } + + /** + * Returns the image for the label of the given element. + * + * @param element the element for which the image was requested + * @return the image, or null if no image could be found + * + * @see ILabelProvider#getImage(Object) + */ + public Image getImage(Object element) { + + if (element instanceof ILaunchConfiguration) { + + return LocalTerminalLaunchUtilities.getImage((ILaunchConfiguration)element); + } + return null; + } + + /** + * Returns the text for the label of the given element. + * + * @param element the element for which to provide the label text + * @return the text string used to label the element, or null if there is no text + * label for the given object + * + * @see ILabelProvider#getText(Object) + */ + public String getText(Object element) { + + if (element instanceof ILaunchConfiguration) { + + return ((ILaunchConfiguration)element).getName(); + } + return String.valueOf(element); + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchListProvider.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchListProvider.java new file mode 100644 index 00000000000..63738ec529e --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalLaunchListProvider.java @@ -0,0 +1,90 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +/** + * The class {@link LocalTerminalLaunchListProvider} is a {@link IStructuredContentProvider} that + * provides a list of all {@link ILaunchConfiguration}s of the type "External Tools"/"Program". + * Those launch configurations can be used to start a new session in the Terminal View. + * + * @author Mirko Raner + * @version $Revision: 1.1 $ + */ +public class LocalTerminalLaunchListProvider implements IStructuredContentProvider { + + /** + * Creates a new {@link LocalTerminalLaunchListProvider}. + */ + public LocalTerminalLaunchListProvider() { + + super(); + } + + /** + * Returns the matching {@link ILaunchConfiguration}s for the given input element. This content + * provider does not really use the concept of "input" because the input can only be obtained in + * one way (from the {@link ILaunchManager}. + * + * @param input the input element (not checked or used by this method) + * @return the matching {@link ILaunchConfiguration}s + * + * @see IStructuredContentProvider#getElements(Object) + */ + public Object[] getElements(Object input) { + + ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); + ILaunchConfigurationType type = LocalTerminalUtilities.PROGRAM_LAUNCH_TYPE; + ILaunchConfiguration[] configurations = null; + try { + + configurations = launchManager.getLaunchConfigurations(type); + } + catch (CoreException couldNotObtainLaunchConfigurations) { + + Logger.logException(couldNotObtainLaunchConfigurations); + } + return configurations; + } + + /** + * Disposes of this {@link LocalTerminalLaunchListProvider}. Currently, there is no additional + * clean-up necessary, and this method is empty. + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + + // Does nothing... + } + + /** + * Notifies the {@link LocalTerminalLaunchListProvider} that its input has changed. This method + * is currently empty because {@link LocalTerminalLaunchListProvider} is not aware of the + * concept of "input" + * + * @see #getElements(Object) + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, Object, Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + + // Does nothing... + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.java new file mode 100644 index 00000000000..38c7e9d5a47 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.java @@ -0,0 +1,103 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local; + +import org.eclipse.osgi.util.NLS; + +/** + * The class {@link LocalTerminalMessages} provides localization keys to internationalized display + * messages used by the Local Terminal Connector. + * + * @author Mirko Raner + * @version $Revision: 1.3 $ + */ +public class LocalTerminalMessages extends NLS { + + static { + + NLS.initializeMessages(LocalTerminalMessages.class.getName(), LocalTerminalMessages.class); + } + + private LocalTerminalMessages() { + + super(); + } + + /** The title for the launch configuration selection: "Launch configuration:". */ + public static String launchConfiguration; + + /** The label for the "New..." button. */ + public static String labelNew; + + /** The label for the "Edit..." button. */ + public static String labelEdit; + + /** The label for the "Enable terminal echo" check box. */ + public static String enableLocalEcho; + + /** The label for the "Send SIGINT when Ctrl-C is pressed" check box. */ + public static String sendInterruptOnCtrlC; + + /** The line separator option "LF". */ + public static String lineSeparatorLF; + + /** The line separator option "CR". */ + public static String lineSeparatorCR; + + /** The line separator option "CRLF". */ + public static String lineSeparatorCRLF; + + /** The default line separator option. */ + public static String lineSeparatorDefault; + + /** The base name for new launch configurations. */ + public static String newTerminalLaunchName; + + /** The error message to be issued if a launch configuration could not be found. */ + public static String noSuchLaunchConfiguration; + + /** The message displayed while launching a configuration. */ + public static String launchingConfiguration; + + /** The error message displayed when process creation failed. */ + public static String couldNotCreateIProcess; + + /** The error message for a missing executable path. */ + public static String locationNotSpecified; + + /** The error message for a specified but invalid executable path. */ + public static String invalidLocation; + + /** The error message for an invalid working directory location. */ + public static String invalidWorkingDirectory; + + /** The title string of the warning displayed when terminal launches are still running. */ + public static String warningTitleTerminalsStillRunning; + + /** The warning message displayed when terminal launches are still running. */ + public static String warningMessageTerminalsStillRunning; + + /** The label for the button that quits the workbench anyway. */ + public static String quitWorkbenchAnyway; + + /** The label for the button that vetoes a shutdown of the workbench. */ + public static String doNoQuitWorkbench; + + /** The label for a terminal process that was terminated during workbench shut-down. */ + public static String terminatedProcess; + + /** The name of the launch configuration tab for terminal settings. */ + public static String terminalTabName; + + /** The group label for the terminal settings on the launch configuration page. */ + public static String terminalSettings; +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.properties b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.properties new file mode 100644 index 00000000000..e54522aca35 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalMessages.properties @@ -0,0 +1,39 @@ +#################################################################################################### +# Copyright (c) 2008 Mirko Raner. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Mirko Raner - initial implementation for Eclipse Bug 196337 +#################################################################################################### + +launchConfiguration=Launch configuration\: +enableLocalEcho=Enable terminal echo +sendInterruptOnCtrlC=Send SIGINT when Ctrl-C is pressed +lineSeparatorDefault=Program uses default line separator - or\: +lineSeparatorCRLF=CRLF +lineSeparatorLF=LF +lineSeparatorCR=CR +labelNew=New... +labelEdit=Edit... +terminalTabName=Terminal +terminalSettings=Terminal settings\: +newTerminalLaunchName=New Terminal Launch +noSuchLaunchConfiguration=A launch configuration called ''{0}'' does not exist +launchingConfiguration=Launching {0}... +couldNotCreateIProcess=The IProcess could not be created +locationNotSpecified=Executable location was not specified in configuration ''{0}'' +invalidLocation=Executable does not exist for the external tool named ''{0}'' +invalidWorkingDirectory=The path {0} is not a directory and cannot be used as working directory \ + for ''{1}'' +warningTitleTerminalsStillRunning=Warning: Terminals with active processes are still running +warningMessageTerminalsStillRunning=The workbench is about to be shut down even though one or more \ + terminals with active processes are still running. You may abort the shut-down of the workbench \ + or you may quit the workbench nonetheless, in which case processes might be terminated in an \ + unexpected fashion. You may also terminate processes individually. The workbench shut-down will \ + continue automatically once no more terminal processes are running. +quitWorkbenchAnyway=Quit Workbench Anyway +doNoQuitWorkbench=Do Not Quit Workbench +terminatedProcess={0} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputListener.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputListener.java new file mode 100644 index 00000000000..b6d0c734677 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputListener.java @@ -0,0 +1,90 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local; + +import 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); + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputStream.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputStream.java new file mode 100644 index 00000000000..1bdcb1587b2 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalOutputStream.java @@ -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); + } + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettings.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettings.java new file mode 100644 index 00000000000..3d0d834cefb --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettings.java @@ -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; + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettingsPage.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettingsPage.java new file mode 100644 index 00000000000..18e2c672877 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalSettingsPage.java @@ -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 true if a launch configuration is selected. + * + * @return true if a launch configuration has been selected, false + * otherwise + */ + public boolean validateSettings() { + + return viewer != null && !viewer.getSelection().isEmpty(); + } + + /** + * Enables or disables the Edit... button depending on whether a launch configuration is + * currently selected in the viewer. + * + * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent) + */ + public void selectionChanged(SelectionChangedEvent event) { + + buttonEdit.setEnabled(!event.getSelection().isEmpty()); + } + + /** + * Handles default button clicks for the Edit... and New.. buttons. This method will simply + * pass on the call to {@link #widgetSelected(SelectionEvent)}. + * + * @param event the {@link SelectionEvent} + * + * @see SelectionListener#widgetDefaultSelected(SelectionEvent) + */ + public void widgetDefaultSelected(SelectionEvent event) { + + widgetSelected(event); + } + + /** + * Handles default button clicks for the Edit... and New.. buttons. + * + * @param event the {@link SelectionEvent} + * + * @see SelectionListener#widgetSelected(SelectionEvent) + */ + public void widgetSelected(SelectionEvent event) { + + ILaunchConfiguration configuration = null; + Widget widget = event.widget; + if (widget == null) { + + return; + } + if (widget.equals(buttonNew)) { + + ILaunchConfigurationWorkingCopy newlyCreatedConfiguration; + ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); + String baseName = LocalTerminalMessages.newTerminalLaunchName; + String uniqueName = launchManager.generateUniqueLaunchConfigurationNameFrom(baseName); + ILaunchConfigurationType type = LocalTerminalUtilities.PROGRAM_LAUNCH_TYPE; + try { + + newlyCreatedConfiguration = type.newInstance(null, uniqueName); + configuration = newlyCreatedConfiguration.doSave(); + } + catch (CoreException couldNotCreateNewLaunchConfiguration) { + + Logger.logException(couldNotCreateNewLaunchConfiguration); + } + } + if (widget.equals(buttonEdit) || configuration != null) { + + ILaunchGroup group; + Shell shell = DebugUIPlugin.getShell(); + IStructuredSelection selection = (IStructuredSelection)viewer.getSelection(); + if (configuration == null) { + + configuration = (ILaunchConfiguration)selection.getFirstElement(); + } + group = DebugUITools.getLaunchGroup(configuration, ILaunchManager.RUN_MODE); + String groupID = group.getIdentifier(); + DebugUITools.openLaunchConfigurationDialog(shell, configuration, groupID, null); + // + // TODO: handle return value (maybe start terminal right away if "Run" was selected) + // - a return value of Window.CANCEL indicates that "Close" was selected + // - a return value of Window.OK indicates that "Run" was selected + // TODO: prevent "Run" button from launching in the regular console + // (maybe tweak process factory settings before opening the configuration in the + // dialog?) + + viewer.refresh(); + viewer.setSelection(new StructuredSelection(configuration), true); + // + // TODO: handle renamed configurations; setSelection(...) will not work if the user + // renamed the configuration in the dialog (apparently, because renaming actually + // creates a different ILaunchConfiguration object, rather than just renaming the + // existing one) + } + } + + //------------------------------------ PRIVATE SECTION ---------------------------------------// + + private Button pushButton(Composite parent, String label, boolean grabVertical) { + + GridData layoutData; + Button button = new Button(parent, SWT.PUSH); + button.setText(label); + layoutData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING|GridData.HORIZONTAL_ALIGN_FILL); + layoutData.grabExcessVerticalSpace = grabVertical; + button.setLayoutData(layoutData); + button.addSelectionListener(this); + return button; + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalUtilities.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalUtilities.java new file mode 100644 index 00000000000..54bc719d405 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/LocalTerminalUtilities.java @@ -0,0 +1,158 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchDelegate; +import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +/** + * The class {@link LocalTerminalUtilities} is a collection of commonly used constants and utility + * methods. + * + * @author Mirko Raner + * @version $Revision: 1.3 $ + */ +public class LocalTerminalUtilities { + + private static String NULL = null; + private static String LOCAL_TERMINAL = LocalTerminalLaunchDelegate.LAUNCH_CONFIGURATION_TYPE_ID; + + /** The name of the line separator system property (i.e., "line.separator"). */ + public final static String LINE_SEPARATOR_PROPERTY = "line.separator"; //$NON-NLS-1$ + + /** The line separator CRLF (i.e., "\r\n"). */ + public final static String CRLF = "\r\n"; //$NON-NLS-1$ + + /** The line separator CR (i.e., "\r"). */ + public final static String CR = "\r"; //$NON-NLS-1$ + + /** The line separator LF (i.e., "\n"). */ + public final static String LF = "\n"; //$NON-NLS-1$ + + private LocalTerminalUtilities() { + + super(); + } + + /** The {@link DebugPlugin}'s {@link ILaunchManager} instance. */ + public final static ILaunchManager LAUNCH_MANAGER = DebugPlugin.getDefault().getLaunchManager(); + + /** + * The {@link ILaunchConfigurationType} for "Program" launches (in the "External Tools" + * category). + */ + public final static ILaunchConfigurationType PROGRAM_LAUNCH_TYPE = + LAUNCH_MANAGER.getLaunchConfigurationType(LOCAL_TERMINAL); + + /** + * Finds a launch configuration by its name. + * + * @param name the name of the launch configuration + * @return the corresponding {@link ILaunchConfiguration} object or null if the + * configuration could not be found + * @throws CoreException if there was a general problem accessing launch configurations + */ + public static ILaunchConfiguration findLaunchConfiguration(String name) throws CoreException { + + ILaunchConfiguration[] configuration; + ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); + configuration = manager.getLaunchConfigurations(LocalTerminalUtilities.PROGRAM_LAUNCH_TYPE); + int numberOfConfigurations = configuration.length; + for (int index = 0; index < numberOfConfigurations; index++) { + + if (configuration[index].getName().equals(name)) { + + return configuration[index]; + } + } + String error = NLS.bind(LocalTerminalMessages.noSuchLaunchConfiguration, name); + throw new CoreException(new Status(IStatus.ERROR, LocalTerminalActivator.PLUGIN_ID, error)); + } + + /** + * Gets the local echo setting that is stored in the launch configuration for the given + * {@link ILocalTerminalSettings}. + * + * @param settings the {@link ILocalTerminalSettings} + * @return true for local echo enabled, false otherwise + */ + public static boolean getLocalEcho(ILocalTerminalSettings settings) { + + return getBooleanSetting(settings, LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO); + } + + /** + * Gets the Ctrl-C/SIGINT setting that is stored in the launch configuration for the given + * {@link ILocalTerminalSettings}. + * + * @param settings the {@link ILocalTerminalSettings} + * @return true if sending SIGINT for Ctrl-C is enabled, + * false otherwise + */ + public static boolean getCtrlC(ILocalTerminalSettings settings) { + + return getBooleanSetting(settings, LocalTerminalLaunchUtilities.ATTR_CTRL_C); + } + + /** + * Gets the line separator setting that is stored in the launch configuration for the given + * {@link ILocalTerminalSettings}. + * + * @param settings the {@link ILocalTerminalSettings} + * @return {@link ILocalTerminalSettings#LINE_SEPARATOR_LF}, + * {@link ILocalTerminalSettings#LINE_SEPARATOR_CRLF}, + * {@link ILocalTerminalSettings#LINE_SEPARATOR_CR}, or null for the platform's + * default line separator + */ + public static String getLineSeparator(ILocalTerminalSettings settings) { + + String configurationName = settings.getLaunchConfigurationName(); + try { + + String ls; + ILaunchConfiguration configuration = findLaunchConfiguration(configurationName); + ls = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, NULL); + return ls; + } + catch (CoreException exception) { + + Logger.logException(exception); + return null; + } + } + + //------------------------------------- PRIVATE SECTION --------------------------------------// + + private static boolean getBooleanSetting(ILocalTerminalSettings settings, String attribute) { + + String configurationName = settings.getLaunchConfigurationName(); + try { + + ILaunchConfiguration configuration = findLaunchConfiguration(configurationName); + return configuration.getAttribute(attribute, false); + } + catch (CoreException exception) { + + Logger.logException(exception); + return false; + } + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchDelegate.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchDelegate.java new file mode 100644 index 00000000000..a6974da1268 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchDelegate.java @@ -0,0 +1,265 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - [196337] Adapted from org.eclipse.ui.externaltools/ProgramLaunchDelegate + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.launch; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.LaunchConfigurationDelegate; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tm.internal.terminal.local.LocalTerminalActivator; +import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages; +import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.ui.PlatformUI; + +/** + * The class {@link LocalTerminalLaunchDelegate} provides a launch configuration delegate for local + * terminal launches. It is based on the ProgramLaunchDelegate class in the + * org.eclipse.ui.externaltools plug-in. In contrast to the original class, + * {@link LocalTerminalLaunchDelegate} creates its low-level {@link Process} object using the CDT + * {@link ProcessFactory}, which allows the process to run with a pseudo-terminal ({@link PTY}). + * + * @author Mirko Raner and others + * @version $Revision: 1.2 $ + */ +public class LocalTerminalLaunchDelegate extends LaunchConfigurationDelegate { + + private final static String EMPTY = ""; //$NON-NLS-1$ + private final static String PLUGIN_ID = LocalTerminalActivator.PLUGIN_ID; + + /** + * The launch configuration type ID for terminal launches. + */ + public final static String LAUNCH_CONFIGURATION_TYPE_ID = PLUGIN_ID + ".launch"; //$NON-NLS-1$ + + private static LocalTerminalStillRunningListener workbenchCloseListener; + + /** + * Creates a new {@link LocalTerminalLaunchDelegate}. + */ + public LocalTerminalLaunchDelegate() { + + super(); + } + + /** + * Launches a new Local Terminal configuration in the specified mode. The launch object has + * already been registered with the launch manager. + * + * @param configuration the {@link ILaunchConfiguration} to launch + * @param mode the mode in which to launch; currently, Local Terminal launches only support the + * mode {@link org.eclipse.debug.core.ILaunchManager#RUN_MODE} + * @param progressMonitor an {@link IProgressMonitor}, or null for no progress + * monitoring + * @param launch the {@link ILaunch} object + * @exception CoreException if launching fails + */ + public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, + IProgressMonitor progressMonitor) throws CoreException { + + // Extract all relevant information from the ILaunchConfiguration; the original + // ProgramLaunchDelegate class checks for cancellation again and again after each step, + // which is a somewhat suspect pattern; however, for now, LocalTerminalLaunchDelegate + // handles cancellation in the same way: + // + if (progressMonitor.isCanceled()) { + + return; + } + IPath location = LocalTerminalLaunchUtilities.getLocation(configuration); + if (progressMonitor.isCanceled()) { + + return; + } + IPath workingDirectory = LocalTerminalLaunchUtilities.getWorkingDirectory(configuration); + if (progressMonitor.isCanceled()) { + + return; + } + String[] arguments = LocalTerminalLaunchUtilities.getArguments(configuration); + if (progressMonitor.isCanceled()) { + + return; + } + String[] commandLine = new String[arguments != null? arguments.length+1:1]; + commandLine[0] = location.toOSString(); + if (arguments != null) { + + System.arraycopy(arguments, 0, commandLine, 1, arguments.length); + } + File workingDirectoryAsFile = null; + if (workingDirectory != null) { + + workingDirectoryAsFile = workingDirectory.toFile(); + } + if (progressMonitor.isCanceled()) { + + return; + } + String[] environment = LocalTerminalUtilities.LAUNCH_MANAGER.getEnvironment(configuration); + if (progressMonitor.isCanceled()) { + + return; + } + // + // TODO: check if there is a better way of handling cancellation of terminal launches! + + // Install an IWindowListener that checks for left-over terminal processes when the + // workbench is closed: + // + if (workbenchCloseListener == null) { + + workbenchCloseListener = new LocalTerminalStillRunningListener(); + PlatformUI.getWorkbench().addWorkbenchListener(workbenchCloseListener); + } + + // Create the low-level Process object: + // + Process spawner; + try { + + ProcessFactory factory = ProcessFactory.getFactory(); + if (PTY.isSupported()) { + + spawner = factory.exec(commandLine, environment, workingDirectoryAsFile, new PTY()); + } + else { + + spawner = factory.exec(commandLine, environment, workingDirectoryAsFile); + } + } + catch (IOException exception) { + + Status error; + String message = exception.getMessage(); + error = new Status(IStatus.ERROR, LocalTerminalActivator.PLUGIN_ID, message, exception); + throw new CoreException(error); + } + + // Use program name as "process type" attribute: + // + Map processAttributes = new HashMap(); + String programName = location.lastSegment(); + String extension = location.getFileExtension(); + if (extension != null) { + + programName = programName.substring(0, programName.length()-extension.length()-1); + } + processAttributes.put(IProcess.ATTR_PROCESS_TYPE, programName.toLowerCase()); + + // Create the IProcess: + // + IProcess process = null; + if (spawner != null) { + + String[] configurationName = {configuration.getName()}; + String task = NLS.bind(LocalTerminalMessages.launchingConfiguration, configurationName); + progressMonitor.beginTask(task, IProgressMonitor.UNKNOWN); + process = DebugPlugin.newProcess(launch, spawner, commandLine[0], processAttributes); + } + if (spawner == null || process == null) { + + if (spawner != null) { + + spawner.destroy(); + } + String pluginID = LocalTerminalActivator.PLUGIN_ID; + String errorMessage = LocalTerminalMessages.couldNotCreateIProcess; + Status error = new Status(IStatus.ERROR, pluginID, IStatus.ERROR, errorMessage, null); + throw new CoreException(error); + } + process.setAttribute(IProcess.ATTR_CMDLINE, generateCommandLine(commandLine)); + + // Wait for process termination if necessary (though probably highly unusual for terminal + // launches); again, the busy waiting pattern was copied from ProgramLaunchDelegate and is + // somewhat suspect: + // + if (!CommonTab.isLaunchInBackground(configuration)) { + + while (!process.isTerminated()) { + + try { + + if (progressMonitor.isCanceled()) { + + process.terminate(); + break; + } + Thread.sleep(50); + } + catch (InterruptedException interrupt) { + + Logger.logException(interrupt); + } + } + } + // + // TODO: find a better replacement for the busy waiting loop + } + + //------------------------------------- PRIVATE SECTION --------------------------------------// + + private String generateCommandLine(String[] commandLine) { + + if (commandLine.length < 1) { + + return EMPTY; + } + StringBuffer buffer = new StringBuffer(); + for (int element = 0; element < commandLine.length; element++) { + + if (element > 0) { + + buffer.append(' '); + } + StringBuffer argument = new StringBuffer(); + char[] characters = commandLine[element].toCharArray(); + boolean argumentContainsSpace = false; + for (int index = 0; index < characters.length; index++) { + + char character = characters[index]; + if (character == '"') { + + argument.append('\\'); + } + else if (character == ' ') { + + argumentContainsSpace = true; + } + argument.append(character); + } + if (argumentContainsSpace) { + + buffer.append('"').append(argument).append('"'); + } + else { + + buffer.append(argument); + } + } + return buffer.toString(); + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchUtilities.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchUtilities.java new file mode 100644 index 00000000000..8eed6da5555 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalLaunchUtilities.java @@ -0,0 +1,201 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - [196337] Adapted from org.eclipse.ui.externaltools/ExternalToolsUtil + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.launch; + +import java.io.File; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.variables.IStringVariableManager; +import org.eclipse.core.variables.VariablesPlugin; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.graphics.Image; +import org.eclipse.tm.internal.terminal.local.LocalTerminalActivator; +import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +/** + * The class {@link LocalTerminalLaunchUtilities} provides some utility methods that are used by the + * {@link LocalTerminalLaunchDelegate}. The class is based on the ExternalToolsUtil + * class in the org.eclipse.ui.externaltools plug-in. This code had to be duplicated + * because the original class is not part of the public API of its plug-in. + * + * @author Mirko Raner and others + * @version $Revision: 1.2 $ + */ +public class LocalTerminalLaunchUtilities { + + /** The launch configuration attribute for the local echo setting. */ + public final static String ATTR_LOCAL_ECHO = LocalTerminalActivator.PLUGIN_ID + + ".echo"; //$NON-NLS-1$ + + /** The launch configuration attribute for the Ctrl-C/SIGINT setting. */ + public final static String ATTR_CTRL_C = LocalTerminalActivator.PLUGIN_ID + + ".sigint"; //$NON-NLS-1$ + + /** The launch configuration attribute for the line terminator setting. */ + public final static String ATTR_LINE_SEPARATOR = LocalTerminalActivator.PLUGIN_ID + + ".lineseparator"; //$NON-NLS-1$ + + private final static String[] EMPTY = {}; + private final static String STRING = null; + + // These constants were copied from IExternalToolConstants to avoid references to internal API: + // + private final static String XT = "org.eclipse.ui.externaltools"; //$NON-NLS-1$; + private final static String ATTR_LOCATION = XT+".ATTR_LOCATION"; //$NON-NLS-1$ + private final static String ATTR_TOOL_ARGUMENTS = XT+".ATTR_TOOL_ARGUMENTS"; //$NON-NLS-1$ + private final static String ATTR_WORKING_DIRECTORY = XT+".ATTR_WORKING_DIRECTORY"; //$NON-NLS-1$ + + private LocalTerminalLaunchUtilities() { + + super(); + } + + /** + * Gets the image that should be used for representing the given launch configuration. + * + * @param configuration the {@link ILaunchConfiguration} + * @return an SWT {@link Image} or null if no suitable image was found + */ + public static Image getImage(ILaunchConfiguration configuration) { + + String identifier; + try { + + identifier = configuration.getType().getIdentifier(); + } + catch (CoreException couldNotDetermineConfigurationType) { + + identifier = null; + Logger.logException(couldNotDetermineConfigurationType); + } + if (identifier != null) { + + return DebugUITools.getImage(identifier); + } + return null; + } + + /** + * Expands and returns the location attribute of the given launch configuration. The location is + * verified to point to an existing file in the local file system. + * + * @param configuration the {@link ILaunchConfiguration} + * @return an absolute path to a file in the local file system + * @throws CoreException if unable to retrieve the associated launch configuration attribute, or + * if unable to resolve any variables, or if the resolved location does not point to an existing + * file in the local file system + */ + public static IPath getLocation(ILaunchConfiguration configuration) throws CoreException { + + Object[] configurationName = {configuration.getName()}; + String location = configuration.getAttribute(ATTR_LOCATION, STRING); + if (location == null) { + + abort(NLS.bind(LocalTerminalMessages.locationNotSpecified, configurationName), null, 0); + } + String expandedLocation = getStringVariableManager().performStringSubstitution(location); + if (expandedLocation == null || expandedLocation.length() == 0) { + + abort(NLS.bind(LocalTerminalMessages.invalidLocation, configurationName), null, 0); + } + File file = new File(expandedLocation); + if (!file.isFile()) { + + abort(NLS.bind(LocalTerminalMessages.invalidLocation, configurationName), null, 0); + } + return new Path(expandedLocation); + } + + /** + * Expands and returns the working directory attribute of the given launch configuration. + * Returns null if a working directory is not specified. If specified, the working + * directory is guaranteed to point to an existing directory in the local file system. + * + * @param configuration the {@link ILaunchConfiguration} + * @return an absolute path to a directory in the local file system, or null if + * no working directory was specified + * @throws CoreException if unable to retrieve the associated launch configuration attribute, + * or if unable to resolve any variables, or if the resolved location does not point to an + * existing directory in the local file system + */ + public static IPath getWorkingDirectory(ILaunchConfiguration configuration) + throws CoreException { + + String location = configuration.getAttribute(ATTR_WORKING_DIRECTORY, STRING); + if (location != null) { + + String expandedLocation; + expandedLocation = getStringVariableManager().performStringSubstitution(location); + if (expandedLocation.length() > 0) { + + File path = new File(expandedLocation); + if (!path.isDirectory()) { + + Object[] detail = {expandedLocation, configuration.getName()}; + abort(NLS.bind(LocalTerminalMessages.invalidWorkingDirectory, detail), null, 0); + } + } + return new Path(expandedLocation); + } + return null; + } + + /** + * Expands and returns the arguments attribute of the given launch configuration. Returns + * null if arguments were not specified. + * + * @param configuration the {@link ILaunchConfiguration} + * @return an array of resolved arguments, or null if no arguments were specified + * @throws CoreException if unable to retrieve the associated launch configuration attribute, + * or if unable to resolve any variables + */ + public static String[] getArguments(ILaunchConfiguration configuration) throws CoreException { + + String arguments = configuration.getAttribute(ATTR_TOOL_ARGUMENTS, STRING); + if (arguments != null) { + + String expanded = getStringVariableManager().performStringSubstitution(arguments); + return parseStringIntoList(expanded); + } + return null; + } + + //------------------------------------- PRIVATE SECTION --------------------------------------// + + private static IStringVariableManager getStringVariableManager() { + + return VariablesPlugin.getDefault().getStringVariableManager(); + } + + private static String[] parseStringIntoList(String arguments) { + + if (arguments == null || arguments.length() == 0) { + + return EMPTY; + } + return DebugPlugin.parseArguments(arguments); + } + + private static void abort(String text, Throwable exception, int code) throws CoreException { + + Status status; + status = new Status(IStatus.ERROR, LocalTerminalActivator.PLUGIN_ID, code, text, exception); + throw new CoreException(status); + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalStillRunningListener.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalStillRunningListener.java new file mode 100644 index 00000000000..9014b5fb389 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/LocalTerminalStillRunningListener.java @@ -0,0 +1,119 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - [196337] Adapted from org.eclipse.ui.externaltools/ProgramLaunchDelegate + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.launch; + +import java.util.ArrayList; +import java.util.List; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities; +import org.eclipse.tm.internal.terminal.local.launch.ui.LocalTerminalStillRunningDialog; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchListener; +import org.eclipse.ui.IWorkbenchWindow; + +/** + * The class {@link LocalTerminalStillRunningListener} is an {@link IWorkbenchListener} that warns + * the user about any terminal launches that are still running when the workbench closes. The user + * might want to take specific action to deal with such left-over processes. Typically, this + * listener will trigger only on very rare cases because the + * {@link org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector} implementation will + * terminate left-over launches when the workbench window is closed. However, it is possible that + * a terminal launch does not get automatically terminated, for example, if it was started through + * an External Tools launch rather than through the terminal. + * + * The class {@link LocalTerminalStillRunningListener} is inspired by the + * ProgramLaunchWindowListener class inside ProgramLaunchDelegate in the + * org.eclipse.ui.externaltools plug-in, though it works through a slightly different + * mechanism. + * + * @author Mirko Raner + * @version $Revision: 1.1 $ + */ +public class LocalTerminalStillRunningListener implements IWorkbenchListener { + + /** + * Creates a new {@link LocalTerminalStillRunningListener}. + */ + public LocalTerminalStillRunningListener() { + + super(); + } + + /** + * Gets notified when the workbench is closed and informs the user about any left-over + * terminal launches. + * + * @param workbench the {@link IWorkbench} + * @param forced true if a forced shutdown occurred, false otherwise + * @return true to allow the workbench to proceed with shutdown, false + * to prevent a shutdown (only for non-forced shutdown) + */ + public boolean preShutdown(IWorkbench workbench, boolean forced) { + + if (forced) { + + return true; + } + ILaunchConfigurationType launchType; + String launchTypeID = LocalTerminalLaunchDelegate.LAUNCH_CONFIGURATION_TYPE_ID; + launchType = LocalTerminalUtilities.LAUNCH_MANAGER.getLaunchConfigurationType(launchTypeID); + if (launchType == null) { + + return true; + } + List notTerminated = new ArrayList(); + ILaunch launches[] = LocalTerminalUtilities.LAUNCH_MANAGER.getLaunches(); + ILaunchConfigurationType configurationType; + ILaunchConfiguration configuration; + for (int launch = 0; launch < launches.length; launch++) { + + try { + + configuration = launches[launch].getLaunchConfiguration(); + if (configuration == null) { + + continue; + } + configurationType= configuration.getType(); + } + catch (CoreException exception) { + + Logger.logException(exception); + continue; + } + if (configurationType.equals(launchType) && !launches[launch].isTerminated()) { + + notTerminated.add(launches[launch]); + } + } + if (!notTerminated.isEmpty()) { + + IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); + ILaunch[] launch = (ILaunch[])notTerminated.toArray(new ILaunch[notTerminated.size()]); + return LocalTerminalStillRunningDialog.openDialog(window.getShell(), launch); + } + return true; + } + + /** + * Not implemented. + * @see IWorkbenchListener#postShutdown(IWorkbench) + */ + public void postShutdown(IWorkbench workbench) { + + // Not implemented + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalLaunchTabGroup.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalLaunchTabGroup.java new file mode 100644 index 00000000000..4779739d606 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalLaunchTabGroup.java @@ -0,0 +1,113 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.launch.ui; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.EnvironmentTab; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.ILaunchConfigurationTabGroup; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +/** +* The class {@link LocalTerminalLaunchTabGroup} defines the tabs for the launch configuration +* dialog that is used for terminal-based launches. The tab groups consists of the main tab for +* a standard program launch (lifted from the org.eclipse.ui.externaltools plug-in), the +* custom {@link LocalTerminalSettingsTab}, and the {@link EnvironmentTab} and {@link CommonTab}, +* which can be publicly accessed from the org.eclipse.debug.ui plug-in. +* +* @author Mirko Raner +* @version $Revision: 1.1 $ +**/ +public class LocalTerminalLaunchTabGroup extends AbstractLaunchConfigurationTabGroup +implements IDebugUIConstants { + + private final static String ID = "id"; //$NON-NLS-1$ + private final static String CLASS = "class"; //$NON-NLS-1$ + private final static String LC_TAB_GROUPS = EXTENSION_POINT_LAUNCH_CONFIGURATION_TAB_GROUPS; + private final static String PROGRAM_TAB_GROUP = + "org.eclipse.ui.externaltools.launchConfigurationTabGroup.program"; //$NON-NLS-1$ + + /** + * Creates a new {@link LocalTerminalLaunchTabGroup}. + **/ + public LocalTerminalLaunchTabGroup() { + + super(); + } + + /** + * Creates the tabs contained in the local terminal launch configuration dialog for the specified + * launch mode. The tabs control's are not yet created. This is the first method called in the + * life-cycle of a tab group. + * + * @param dialog the launch configuration dialog this tab group is contained in + * @param mode the mode the launch configuration dialog was opened in + * @see AbstractLaunchConfigurationTabGroup#createTabs(ILaunchConfigurationDialog, String) + **/ + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + + ILaunchConfigurationTab main = getMainTab(dialog, mode); + ILaunchConfigurationTab terminal = new LocalTerminalSettingsTab(); + ILaunchConfigurationTab environment = new EnvironmentTab(); + ILaunchConfigurationTab common = new CommonTab(); + ILaunchConfigurationTab[] tabs = {main, terminal, environment, common}; + setTabs(tabs); + } + + //-------------------------------------- PRIVATE SECTION -------------------------------------// + + private ILaunchConfigurationTab getMainTab(ILaunchConfigurationDialog dialog, String mode) { + + // Find the main tab for the external program launch in the registry (a direct search is + // only possible for extensions that actually declare a unique ID, which most extensions + // don't; the search for the "id" attribute of a configuration element has to be done + // manually): + // + IConfigurationElement[] element; + IExtensionRegistry registry = Platform.getExtensionRegistry(); + element = registry.getConfigurationElementsFor(IDebugUIConstants.PLUGIN_ID, LC_TAB_GROUPS); + int numberOfElements = element.length; + for (int index = 0; index < numberOfElements; index++) { + + if (element[index].getAttribute(ID).equals(PROGRAM_TAB_GROUP)) { + + try { + + ILaunchConfigurationTabGroup tabGroup; + Object executable = element[index].createExecutableExtension(CLASS); + tabGroup = (ILaunchConfigurationTabGroup)executable; + tabGroup.createTabs(dialog, mode); + + // It's not possible to make assumptions about the class name of the program + // main tab (without over-stepping API boundaries), but it's usually the very + // first tab in the group (which is an assumption that actually also over-steps + // API boundaries, but it's the best possible solution, short of copying the + // whole source code): + // + return tabGroup.getTabs()[0]; + } + catch (CoreException exception) { + + Logger.logException(exception); + } + } + } + return null; + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalSettingsTab.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalSettingsTab.java new file mode 100644 index 00000000000..295cc7b8427 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalSettingsTab.java @@ -0,0 +1,265 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.launch.ui; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.tm.internal.terminal.local.ILocalTerminalSettings; +import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages; +import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchDelegate; +import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +/** + * The class {@link LocalTerminalSettingsTab} provides the UI for custom settings that are specific + * to terminal-based launches. Currently, the tab allows the user to control the local echo settings + * and the line separator string. + * + * @author Mirko Raner + * @version $Revision: 1.2 $ + **/ +public class LocalTerminalSettingsTab extends AbstractLaunchConfigurationTab +implements SelectionListener { + + private final static String NULL = null; + + private Button buttonEcho; + private Button buttonCtrlC; + private Button separatorDefault; + private Button separatorLF; + private Button separatorCRLF; + private Button separatorCR; + + /** + * Creates a new {@link LocalTerminalSettingsTab}. + **/ + public LocalTerminalSettingsTab() { + + super(); + } + + /** + * Creates the top-level control for this launch configuration tab under the given parent + * composite. This method is called once on tab creation. + * + * @param parent the parent {@link Composite} + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(Composite) + **/ + public void createControl(Composite parent) { + + Composite container = new Composite(parent, SWT.NONE); + setControl(container); + container.setLayout(new GridLayout()); + Group composite = new Group(container, SWT.NONE); + composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + composite.setText(LocalTerminalMessages.terminalSettings); + composite.setLayout(new GridLayout()); + + // Create echo check box: + // + buttonEcho = button(composite, LocalTerminalMessages.enableLocalEcho, SWT.CHECK); + buttonEcho.setLayoutData(new GridData()); + + // Create Ctrl-C/SIGINT check box: + // + buttonCtrlC = button(composite, LocalTerminalMessages.sendInterruptOnCtrlC, SWT.CHECK); + buttonCtrlC.setLayoutData(new GridData()); + + // Create radio buttons for line separator settings: + // + Composite separator = new Composite(composite, SWT.NONE); + RowLayout rowLayout = new RowLayout(); + rowLayout.wrap = false; + separator.setLayout(rowLayout); + separatorDefault = button(separator, LocalTerminalMessages.lineSeparatorDefault, SWT.RADIO); + separatorLF = button(separator, LocalTerminalMessages.lineSeparatorLF, SWT.RADIO); + separatorCRLF = button(separator, LocalTerminalMessages.lineSeparatorCRLF, SWT.RADIO); + separatorCR = button(separator, LocalTerminalMessages.lineSeparatorCR, SWT.RADIO); + separator.setLayoutData(new GridData()); + } + + /** + * Returns the name of this tab. + * + * @return the name of this tab + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + **/ + public String getName() { + + return LocalTerminalMessages.terminalTabName; + } + + /** + * Returns the image for this tab, or null if none + * + * @return the image for this tab, or null if none + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() + **/ + public Image getImage() { + + return DebugUITools.getImage(LocalTerminalLaunchDelegate.LAUNCH_CONFIGURATION_TYPE_ID); + } + + /** + * Initializes this tab's controls with values from the given launch configuration. This method + * is called when a configuration is selected to view or edit, after the tab's control has been + * created. + * + * @param configuration the launch configuration + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(ILaunchConfiguration) + **/ + public void initializeFrom(ILaunchConfiguration configuration) { + + boolean echo; + try { + + echo = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO, false); + } + catch (CoreException exception) { + + Logger.logException(exception); + echo = false; + } + boolean ctrlC; + try { + + ctrlC = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_CTRL_C, false); + } + catch (CoreException exception) { + + Logger.logException(exception); + ctrlC = false; + } + String ls; + try { + + ls = configuration.getAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, NULL); + } + catch (CoreException exception) { + + Logger.logException(exception); + ls = null; + } + buttonEcho.setSelection(echo); + buttonCtrlC.setSelection(ctrlC); + if (ILocalTerminalSettings.LINE_SEPARATOR_LF.equals(ls)) { + + separatorLF.setSelection(true); + } + else if (ILocalTerminalSettings.LINE_SEPARATOR_LF.equals(ls)) { + + separatorLF.setSelection(true); + } + else if (ILocalTerminalSettings.LINE_SEPARATOR_CRLF.equals(ls)) { + + separatorCRLF.setSelection(true); + } + else if (ILocalTerminalSettings.LINE_SEPARATOR_CR.equals(ls)) { + + separatorCR.setSelection(true); + } + else { + + separatorDefault.setSelection(true); + } + } + + /** + * Copies values from this tab into the given launch configuration. + * + * @param configuration the launch configuration + * @see AbstractLaunchConfigurationTab#performApply(ILaunchConfigurationWorkingCopy) + **/ + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + + boolean echo = buttonEcho.getSelection(); + configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO, echo); + boolean ctrlC = buttonCtrlC.getSelection(); + configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_CTRL_C, ctrlC); + String lineSeparator = null; + if (separatorCRLF.getSelection()) { + + lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_CRLF; + } + else if (separatorCR.getSelection()) { + + lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_CR; + } + else if (separatorLF.getSelection()) { + + lineSeparator = ILocalTerminalSettings.LINE_SEPARATOR_LF; + } + configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, lineSeparator); + } + + /** + * Initializes the given launch configuration with default values for this tab. This method is + * called when a new launch configuration is created such that the configuration can be + * initialized with meaningful values. This method may be called before the tab's control is + * created. + * + * @param configuration the launch configuration + * @see AbstractLaunchConfigurationTab#setDefaults(ILaunchConfigurationWorkingCopy) + **/ + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + + configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LOCAL_ECHO, false); + configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_CTRL_C, false); + configuration.setAttribute(LocalTerminalLaunchUtilities.ATTR_LINE_SEPARATOR, NULL); + } + + /** + * Handles selection of any of the buttons in the tab. + * + * @param event the {@link SelectionEvent} + * @see SelectionListener#widgetSelected(SelectionEvent) + **/ + public void widgetSelected(SelectionEvent event) { + + setDirty(true); + getLaunchConfigurationDialog().updateButtons(); + } + + /** + * Handles default selection of any of the buttons in the tab. + * + * @param event the {@link SelectionEvent} + * @see SelectionListener#widgetDefaultSelected(SelectionEvent) + **/ + public void widgetDefaultSelected(SelectionEvent event) { + + widgetSelected(event); + } + + //-------------------------------------- PRIVATE SECTION -------------------------------------// + + private Button button(Composite parent, String label, int buttonType) { + + Button button = new Button(parent, buttonType); + button.addSelectionListener(this); + button.setText(label); + return button; + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalStillRunningDialog.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalStillRunningDialog.java new file mode 100644 index 00000000000..f2b554e2474 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/launch/ui/LocalTerminalStillRunningDialog.java @@ -0,0 +1,298 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.launch.ui; + +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.tm.internal.terminal.local.LocalTerminalMessages; +import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities; +import org.eclipse.tm.internal.terminal.local.launch.LocalTerminalLaunchUtilities; +import org.eclipse.tm.internal.terminal.local.process.LocalTerminalProcessRegistry; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +/** + * The class {@link LocalTerminalStillRunningDialog} is a dialog that is shown when the workbench is + * about to exit and one or more terminal launches are still running. It gives the user a choice + * between aborting the workbench shut-down, proceeding, or terminating the outstanding launches + * individually. When no more launches are running the dialog will automatically disappear and + * workbench shut-down will proceed. + * + * @author Mirko Raner + * @version $Revision: 1.1 $ + */ +public class LocalTerminalStillRunningDialog extends MessageDialog +implements Runnable, SelectionListener, ILaunchesListener2 { + + private final static String TITLE = LocalTerminalMessages.warningTitleTerminalsStillRunning; + private final static String MESSAGE = LocalTerminalMessages.warningMessageTerminalsStillRunning; + private final static String QUIT_ANYWAY = LocalTerminalMessages.quitWorkbenchAnyway; + private final static String DO_NOT_QUIT = LocalTerminalMessages.doNoQuitWorkbench; + private final static String[] BUTTONS = {QUIT_ANYWAY, DO_NOT_QUIT}; + private final static RGB WHITE = new RGB(255, 255, 255); + private final static int SCROLLABLE_HEIGHT = 100; + + // Image key copied from IInternalDebugUIConstants: + // + private final static String IMG_LCL_TERMINATE = "IMG_LCL_TERMINATE"; //$NON-NLS-1$ + + private ILaunch[] unterminated; + private Composite content; + + private LocalTerminalStillRunningDialog(Shell parentShell, ILaunch[] launches) { + + super(parentShell, TITLE, null, MESSAGE, WARNING, BUTTONS, 0); + setShellStyle(SWT.BORDER|SWT.TITLE|SWT.APPLICATION_MODAL|SWT.RESIZE|SWT.MAX); + unterminated = launches; + } + + /** + * Opens a dialog that lists all terminal launches that have not yet terminated. + * + * @param shell the parent {@link Shell} for the dialog + * @param launches the launches that have not yet terminated + * @return true to allow the workbench to proceed with shutdown, false + * to prevent a shutdown (only for non-forced shutdown) + */ + public static boolean openDialog(Shell shell, ILaunch[] launches) { + + LocalTerminalStillRunningDialog dialog; + dialog = new LocalTerminalStillRunningDialog(shell, launches); + dialog.setBlockOnOpen(true); + try { + + LocalTerminalUtilities.LAUNCH_MANAGER.addLaunchListener(dialog); + return dialog.open() == 0; + } + finally { + + LocalTerminalUtilities.LAUNCH_MANAGER.removeLaunchListener(dialog); + } + } + + /** + * Creates the dialog buttons and sets the focus on the default button. This is done because + * otherwise the focus might land on one of the stop buttons of the unterminated launches, which + * looks somewhat funny. + * + * @param parent the parent {@link Composite} + */ + protected void createButtonsForButtonBar(Composite parent) { + + super.createButtonsForButtonBar(parent); + getButton(1).forceFocus(); + } + + /** + * Creates the custom area of the dialog that shows the list of terminal launches that have not + * yet been terminated. + * + * @param parent the parent {@link Composite} into which the custom area is inserted + * @return the {@link ScrolledComposite} for the custom area + * + * @see MessageDialog#createCustomArea(Composite) + */ + protected Control createCustomArea(Composite parent) { + + ScrolledComposite scrollable = new ScrolledComposite(parent, SWT.BORDER|SWT.V_SCROLL); + GridData gridData = new GridData(GridData.FILL_BOTH); + gridData.heightHint = SCROLLABLE_HEIGHT; + scrollable.setLayoutData(gridData); + scrollable.setExpandHorizontal(true); + scrollable.setExpandVertical(true); + GridLayout gridLayout = new GridLayout(); + gridLayout.marginWidth = gridLayout.marginHeight = gridLayout.verticalSpacing = 0; + content = new Composite(scrollable, SWT.NONE); + content.setLayout(gridLayout); + content.setBackground(new Color(parent.getDisplay(), WHITE)); + scrollable.setContent(content); + for (int index = 0; index < unterminated.length; index++) { + + Composite item = createItem(content, unterminated[index]); + item.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + } + content.pack(); + scrollable.setMinHeight(content.getBounds().height); + return scrollable; + } + + /** + * Handles the {@link SelectionEvent}s that are sent when the user clicks the stop button of a + * launch. The stop button will immediately be disabled to indicate that the stop request is + * being processed. The actual launch termination will be confirmed in an asynchronous fashion + * by the {@link #launchesTerminated(ILaunch[])} method. + * + * @param event the {@link SelectionEvent} + * + * @see #launchesTerminated(ILaunch[]) + */ + public void widgetSelected(SelectionEvent event) { + + ToolItem item = (ToolItem)event.widget; + ILaunch launch = (ILaunch)item.getParent().getParent().getData(); + item.setEnabled(false); + try { + + LocalTerminalProcessRegistry.addProcessBackToFinishedLaunch(launch); + launch.terminate(); + } + catch (DebugException exception) { + + Logger.logException(exception); + } + } + + /** + * Handles default selection events by passing them to {@link #widgetSelected(SelectionEvent)}. + * + * @param event the {@link SelectionEvent} + * + * @see #widgetSelected(SelectionEvent) + * @see SelectionListener#widgetSelected(SelectionEvent) + */ + public void widgetDefaultSelected(SelectionEvent event) { + + widgetSelected(event); + } + + /** + * Removes terminated launches from the list displayed by the dialog and closes the dialog once + * all outstanding launches have been terminated. + * + * @see #launchesTerminated(ILaunch[]) + */ + public void run() { + + boolean allLaunchesTerminated = true; + Control[] child = content.getChildren(); + int numberOfChildren = child.length; + for (int number = 0; number < numberOfChildren; number++) { + + ILaunch launch = (ILaunch)child[number].getData(); + if (launch != null && launch.isTerminated()) { + + child[number].setData(null); + String exitValue; + try { + + exitValue = String.valueOf(launch.getProcesses()[0].getExitValue()); + } + catch (DebugException couldNotGetExitValue) { + + exitValue = '(' + couldNotGetExitValue.getMessage() + ')'; + } + Label label = (Label)((Composite)child[number]).getChildren()[1]; + String process = label.getText(); + process = NLS.bind(LocalTerminalMessages.terminatedProcess, process, exitValue); + label.setText(process); + + // In case the launch terminated by itself (and not because the user pressed the + // stop button) disable the stop button so that no attempt can be made to stop the + // process twice: + // + ((Composite)child[number]).getChildren()[2].setEnabled(false); + } + if (child[number].getData() != null) { + + allLaunchesTerminated = false; + } + } + if (allLaunchesTerminated) { + + setReturnCode(0); + close(); + } + } + + /** + * Removes a recently terminated launch from the list displayed by the dialog. The actual work + * needs to be done in the UI thread and is performed by the {@link #run()} method. + * + * @param terminated a list of terminated launches + * + * @see #run() + */ + public void launchesTerminated(ILaunch[] terminated) { + + Display.getDefault().syncExec(this); + } + + /** + * Not implemented. + * @see ILaunchesListener2#launchesAdded(ILaunch[]) + */ + public void launchesAdded(ILaunch[] launches) { + + // Not implemented... + } + + /** + * Not implemented. + * @see ILaunchesListener2#launchesChanged(ILaunch[]) + */ + public void launchesChanged(ILaunch[] launches) { + + // Not implemented... + } + + /** + * Not implemented. + * @see ILaunchesListener2#launchesRemoved(ILaunch[]) + */ + public void launchesRemoved(ILaunch[] launches) { + + // Not implemented... + } + + //-------------------------------------- PRIVATE SECTION -------------------------------------// + + private Composite createItem(Composite parent, ILaunch launch) { + + Composite item = new Composite(parent, SWT.NULL); + GridLayout gridLayout = new GridLayout(3, false); + item.setData(launch); + item.setLayout(gridLayout); + Image processImage = LocalTerminalLaunchUtilities.getImage(launch.getLaunchConfiguration()); + Label icon = new Label(item, SWT.NULL); + icon.setImage(processImage); + Label label = new Label(item, SWT.NULL); + label.setText(launch.getLaunchConfiguration().getName()); + ToolItem stopButton = new ToolItem(new ToolBar(item, SWT.FLAT), SWT.PUSH); + stopButton.addSelectionListener(this); + Image deleteImage = DebugUITools.getImage(IMG_LCL_TERMINATE); + stopButton.setImage(deleteImage); + GridData gridData = new GridData(GridData.FILL_HORIZONTAL); + gridData.verticalAlignment = GridData.CENTER; + gridData.grabExcessHorizontalSpace = true; + label.setLayoutData(gridData); + return item; + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcess.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcess.java new file mode 100644 index 00000000000..76e89302180 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcess.java @@ -0,0 +1,156 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.process; + +import java.util.HashMap; +import java.util.Map; +import org.eclipse.cdt.utils.spawner.Spawner; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IStreamsProxy; +import org.eclipse.debug.core.model.RuntimeProcess; + +/** + * The class {@link LocalTerminalProcess} is a customized {@link RuntimeProcess} for use by the + * {@link org.eclipse.tm.internal.terminal.local.LocalTerminalConnector}. It serves the purpose of + * preventing the {@link org.eclipse.debug.internal.ui.DebugUIPlugin DebugUIPlugin}'s + * {@link org.eclipse.debug.internal.ui.views.console.ProcessConsoleManager ProcessConsoleManager} + * from allocating a Console view in addition to the Terminal view that serves as the program's main + * I/O device.

+ * Unfortunately, the Process Console Manager determines the need for a Console view by checking the + * {@link IStreamsProxy} of the process for null. If the process has a non-null + * {@link IStreamsProxy} a console will be automatically allocated. This is problematic because + * the Local Terminal Connector requires an {@link IStreamsProxy} but obviously not an additional + * console view. It would have been better if the Process Console Manager would check the + * corresponding attributes in the launch configuration rather than checking the + * {@link IStreamsProxy} of the process. The work-around for now is to remove the underlying + * process from the launch. No console will be allocated for a launch that doesn't have a process + * associated with it. Consequently, a currently running terminal launch will appear in the Debug + * view's list of active launches but the view will not show any child elements (and the element + * cannot be expanded, either). The {@link LocalTerminalProcessRegistry} keeps track of which + * {@link LocalTerminalProcess} is associated with a particular launch. Client code that needs to + * find the process of a launch can obtain it through that registry.

+ * 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 + * null. After the launch is terminated it will appear in the Debug view with the + * terminated process as its child element. The exit value of the terminal process can also be seen + * in that view.

+ * + * 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 SIGINT signal to the underlying system {@link Process}. This is roughly + * equivalent to the user pressing Ctrl-C. + * + * @return true if the interrupt signal was sent successfully; false + * if the signal was not sent successfully or if no signal was sent because the underlying + * process is not a CDT {@link Spawner} + */ + public boolean interrupt() { + + Process process = getSystemProcess(); + if (process instanceof Spawner) { + + return ((Spawner)process).interrupt() == 0; + } + return false; + } + + /** + * Returns the {@link IStreamsProxy} of the process. + * + * @return the original result of {@link RuntimeProcess#getStreamsProxy()}, or null + * after {@link #resetStreamsProxy()} has been called. + */ + public IStreamsProxy getStreamsProxy() { + + if (resetStreamsProxy) { + + return null; + } + return super.getStreamsProxy(); + } + + /** + * Resets the {@link IStreamsProxy} of this process. After calling this method, + * {@link #getStreamsProxy()} will always return null. + */ + protected void resetStreamsProxy() { + + resetStreamsProxy = true; + } + + /** + * Re-attaches the process to its launch and completes termination of the process. This ensures + * that the launch can properly terminate. + * + * @see RuntimeProcess#terminated() + */ + protected void terminated() { + + LocalTerminalProcessRegistry.addProcessBackToFinishedLaunch(getLaunch()); + super.terminated(); + } + + //------------------------------------- PRIVATE SECTION --------------------------------------// + + private static Map setProcessType(Map attributes) { + + // The process type used to be set by the LocalTerminalProcessFactory. However, if some + // client code managed to instantiate a LocalTerminalProcess directly (instead of going + // through the factory) this would result in IProcess objects with an incorrect process type + // attribute. A better solution is to set the process type attribute at the time when the + // LocalTerminalProcess object is actually created: + // + if (attributes == null) { + + attributes = new HashMap(1); + } + attributes.put(IProcess.ATTR_PROCESS_TYPE, PROCESS_TYPE); + return attributes; + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessFactory.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessFactory.java new file mode 100644 index 00000000000..7faff8dfa83 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessFactory.java @@ -0,0 +1,40 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.process; + +import java.util.Map; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.IProcessFactory; +import org.eclipse.debug.core.model.IProcess; + +/** + * The class {@link LocalTerminalProcessFactory} is an {@link IProcessFactory} that produces + * {@link LocalTerminalProcess} objects. + * + * @author Mirko Raner + * @version $Revision: 1.2 $ + */ +public class LocalTerminalProcessFactory implements IProcessFactory { + + /** + * The ID of this factory (as used in plugin.xml). + */ + public final static String ID = "org.eclipse.tm.terminal.localProcess.factory"; //$NON-NLS-1$ + + /** + * @see IProcessFactory#newProcess(ILaunch, Process, String, Map) + */ + public IProcess newProcess(ILaunch launch, Process process, String label, Map attributes) { + + return new LocalTerminalProcess(launch, process, label, attributes); + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessRegistry.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessRegistry.java new file mode 100644 index 00000000000..8383ba600b0 --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/process/LocalTerminalProcessRegistry.java @@ -0,0 +1,158 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.process; + +import java.util.IdentityHashMap; +import java.util.Map; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.tm.internal.terminal.local.LocalTerminalUtilities; + +/** + * The {@link LocalTerminalProcessRegistry} keeps a map between {@link ILaunch} objects and the + * {@link LocalTerminalProcess} objects that were associated with them. To prevent standard consoles + * from being opened, a {@link LocalTerminalProcess} is immediately removed from its {@link ILaunch} + * when the process is created (see {@link LocalTerminalProcess} for details). + * {@link LocalTerminalProcessRegistry} is a singleton (without lazy initialization). + * + * @author mirko + * @version $Revision: 1.1 $ + */ +public class LocalTerminalProcessRegistry implements ILaunchesListener2 { + + private final static LocalTerminalProcessRegistry INSTANCE = new LocalTerminalProcessRegistry(); + + private Map processes; + + private LocalTerminalProcessRegistry() { + + // The ILaunch interface does not make any statements about the suitability of implementing + // objects as hash keys. There might be ILaunch implementations that return a different + // hash code value if the object changes internally. To be safe in those cases, an + // IdentityHashMap is used: + // + processes = new IdentityHashMap(); + } + + /** + * Gets the {@link LocalTerminalProcess} that was originally associated with a given + * {@link ILaunch} object. + * + * @param launch the {@link ILaunch} that was used for creating the process + * @return the corresponding {@link LocalTerminalProcess}, or null if no process + * could be found + */ + public static LocalTerminalProcess getFromLaunch(ILaunch launch) { + + return (LocalTerminalProcess)INSTANCE.processes.get(launch); + } + + /** + * Adds a {@link LocalTerminalProcess} object back to its original {@link ILaunch}. This method + * will also perform a {@link LocalTerminalProcess#resetStreamsProxy()} on the process. + * The {@link #addProcessBackToFinishedLaunch(ILaunch)} method is necessary for properly + * terminating the launch of a terminal application (see {@link LocalTerminalProcess} for + * details). + * + * @param launch the {@link ILaunch} whose original process is to be re-attached + */ + public static void addProcessBackToFinishedLaunch(ILaunch launch) { + + LocalTerminalProcess process = getFromLaunch(launch); + if (process == null) { + + // Maybe the process wasn't actually started in a terminal (can happen when a Terminal + // is launched from the External Tools menu): + // + return; + } + process.resetStreamsProxy(); + if (launch.getProcesses().length == 0) { + + launch.addProcess(process); + } + } + + /** + * Registers a {@link LocalTerminalProcess} with a given {@link ILaunch} so that the process can + * be safely removed from the launch. + * + * @param launch the {@link ILaunch} + * @param process the {@link LocalTerminalProcess} originally associated with that launch + */ + public static void registerWithLaunch(ILaunch launch, LocalTerminalProcess process) { + + synchronized (INSTANCE) { + + if (INSTANCE.processes.isEmpty()) { + + // Start listening to terminated launches as soon as the first launch/process pair + // is registered: + // + LocalTerminalUtilities.LAUNCH_MANAGER.addLaunchListener(INSTANCE); + } + INSTANCE.processes.put(launch, process); + } + } + + /** + * Handles the termination of launches. The {@link LocalTerminalProcessRegistry} acts as a + * {@link ILaunchesListener2} if there are monitored launches outstanding. It will automatically + * de-register itself (as a listener) when the last monitored launch was terminated. + * + * @param terminated the launches that were terminated + */ + public void launchesTerminated(ILaunch[] terminated) { + + synchronized (INSTANCE) { + + int numberOfTerminatedLaunches = terminated.length; + for (int launch = 0; launch < numberOfTerminatedLaunches; launch++) { + + INSTANCE.processes.remove(terminated[launch]); + } + if (INSTANCE.processes.isEmpty()) { + + // If there are no more outstanding launches the listener can be removed again: + // + LocalTerminalUtilities.LAUNCH_MANAGER.removeLaunchListener(INSTANCE); + } + } + } + + /** + * Not implemented. + * @see ILaunchesListener2#launchesAdded(ILaunch[]) + */ + public void launchesAdded(ILaunch[] launches) { + + // Not implemented. + } + + /** + * Not implemented. + * @see ILaunchesListener2#launchesRemoved(ILaunch[]) + */ + public void launchesRemoved(ILaunch[] launches) { + + // Not implemented. + } + + /** + * Not implemented. + * @see ILaunchesListener2#launchesChanged(ILaunch[]) + */ + public void launchesChanged(ILaunch[] launches) { + + // Not implemented. + } +} diff --git a/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ui/DependentHeightComposite.java b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ui/DependentHeightComposite.java new file mode 100644 index 00000000000..7cdaa3e05cf --- /dev/null +++ b/org.eclipse.tm.terminal.local/src/org/eclipse/tm/internal/terminal/local/ui/DependentHeightComposite.java @@ -0,0 +1,88 @@ +/*************************************************************************************************** + * Copyright (c) 2008 Mirko Raner. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mirko Raner - initial implementation for Eclipse Bug 196337 + **************************************************************************************************/ + +package org.eclipse.tm.internal.terminal.local.ui; + +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; + +/** + * The class {@link DependentHeightComposite} is a special kind of SWT {@link Composite} whose + * height depends on the height of some master {@link Composite} that does not have to be a direct + * parent or child of it. This class was introduced as a work-around for UI resizing problems with + * the Terminal API's PageBook class (which uses a StackLayout). + * + * @author Mirko Raner + * @version $Revision: 1.1 $ + */ +public class DependentHeightComposite extends Composite implements ControlListener { + + private Composite master; + private int preferredHeight = -1; + private int extraHeight; + + /** + * Creates a new {@link DependentHeightComposite}. + * + * @param parent the parent {@link Composite} + * @param style the SWT style + * @param master the master {@link Composite} that determines the height + * @param extra the additional height in pixels (may be negative, to create a smaller height + * than the master {@link Composite}) + */ + public DependentHeightComposite(Composite parent, int style, Composite master, int extra) { + + super(parent, style); + this.master = master; + this.extraHeight = extra; + master.addControlListener(this); + } + + /** + * This method does nothing. + * + * @see ControlListener#controlMoved(ControlEvent) + */ + public void controlMoved(ControlEvent event) { + + // Does nothing... + } + + /** + * Adjusts the {@link DependentHeightComposite} height if the master {@link Composite}'s size + * changed. + * + * @param event the {@link ControlEvent} + */ + public void controlResized(ControlEvent event) { + + setSize(getSize().x, master.getClientArea().height+extraHeight); + preferredHeight = master.getClientArea().height+extraHeight; + master.layout(); + } + + /** + * Computes the preferred size of this {@link DependentHeightComposite}. + * + * @see Composite#computeSize(int, int, boolean) + */ + public Point computeSize(int widthHint, int heightHint, boolean changed) { + + Point size = super.computeSize(widthHint, heightHint, changed); + if (preferredHeight != -1) { + + size.y = preferredHeight; + } + return size; + } +}