From f1f305dbc1eda3866b7838011d837f4b9753de4d Mon Sep 17 00:00:00 2001 From: Ted Williams Date: Wed, 4 Oct 2006 01:28:30 +0000 Subject: [PATCH] Initial contribution of Wind River's terminal view. --- org.eclipse.tm.terminal/.project | 28 + .../.settings/org.eclipse.jdt.core.prefs | 8 + .../.settings/org.eclipse.jdt.ui.prefs | 3 + org.eclipse.tm.terminal/META-INF/MANIFEST.MF | 18 + .../icons/clcl16/connect_co.gif | Bin 0 -> 139 bytes .../icons/clcl16/disconnect_co.gif | Bin 0 -> 146 bytes .../icons/clcl16/newterminal.gif | Bin 0 -> 351 bytes .../icons/clcl16/properties_tsk.gif | Bin 0 -> 118 bytes .../icons/cview16/terminal_view.gif | Bin 0 -> 938 bytes .../icons/dlcl16/connect_co.gif | Bin 0 -> 874 bytes .../icons/dlcl16/disconnect_co.gif | Bin 0 -> 90 bytes .../icons/dlcl16/newterminal.gif | Bin 0 -> 351 bytes .../icons/dlcl16/properties_tsk.gif | Bin 0 -> 90 bytes .../icons/elcl16/connect_co.gif | Bin 0 -> 890 bytes .../icons/elcl16/disconnect_co.gif | Bin 0 -> 118 bytes .../icons/elcl16/newterminal.gif | Bin 0 -> 351 bytes .../icons/elcl16/properties_tsk.gif | Bin 0 -> 118 bytes .../icons/eview16/terminal_view.gif | Bin 0 -> 938 bytes org.eclipse.tm.terminal/plugin.properties | 17 + org.eclipse.tm.terminal/plugin.xml | 128 + .../src/org/eclipse/tm/terminal/Logger.java | 117 + .../src/org/eclipse/tm/terminal/Messages.java | 40 + .../eclipse/tm/terminal/Messages.properties | 33 + .../org/eclipse/tm/terminal/TelnetCodes.java | 92 + .../eclipse/tm/terminal/TelnetConnection.java | 796 ++++++ .../org/eclipse/tm/terminal/TelnetOption.java | 756 ++++++ .../eclipse/tm/terminal/TerminalAction.java | 119 + .../tm/terminal/TerminalActionClearAll.java | 34 + .../tm/terminal/TerminalActionConnect.java | 31 + .../tm/terminal/TerminalActionCopy.java | 38 + .../tm/terminal/TerminalActionCut.java | 38 + .../tm/terminal/TerminalActionDisconnect.java | 33 + .../terminal/TerminalActionNewTerminal.java | 36 + .../tm/terminal/TerminalActionPaste.java | 38 + .../tm/terminal/TerminalActionSelectAll.java | 30 + .../tm/terminal/TerminalActionSettings.java | 30 + .../eclipse/tm/terminal/TerminalConsts.java | 114 + .../org/eclipse/tm/terminal/TerminalCtrl.java | 1573 ++++++++++++ .../org/eclipse/tm/terminal/TerminalMsg.java | 44 + .../tm/terminal/TerminalNetworkPortMap.java | 104 + .../eclipse/tm/terminal/TerminalPlugin.java | 294 +++ .../tm/terminal/TerminalPreferencePage.java | 265 ++ .../tm/terminal/TerminalProperties.java | 278 +++ .../eclipse/tm/terminal/TerminalSettings.java | 425 ++++ .../tm/terminal/TerminalSettingsDlg.java | 911 +++++++ .../eclipse/tm/terminal/TerminalTarget.java | 19 + .../org/eclipse/tm/terminal/TerminalText.java | 2145 +++++++++++++++++ .../org/eclipse/tm/terminal/TerminalView.java | 1047 ++++++++ 48 files changed, 9682 insertions(+) create mode 100644 org.eclipse.tm.terminal/.project create mode 100644 org.eclipse.tm.terminal/.settings/org.eclipse.jdt.core.prefs create mode 100644 org.eclipse.tm.terminal/.settings/org.eclipse.jdt.ui.prefs create mode 100644 org.eclipse.tm.terminal/META-INF/MANIFEST.MF create mode 100644 org.eclipse.tm.terminal/icons/clcl16/connect_co.gif create mode 100644 org.eclipse.tm.terminal/icons/clcl16/disconnect_co.gif create mode 100644 org.eclipse.tm.terminal/icons/clcl16/newterminal.gif create mode 100644 org.eclipse.tm.terminal/icons/clcl16/properties_tsk.gif create mode 100644 org.eclipse.tm.terminal/icons/cview16/terminal_view.gif create mode 100644 org.eclipse.tm.terminal/icons/dlcl16/connect_co.gif create mode 100644 org.eclipse.tm.terminal/icons/dlcl16/disconnect_co.gif create mode 100644 org.eclipse.tm.terminal/icons/dlcl16/newterminal.gif create mode 100644 org.eclipse.tm.terminal/icons/dlcl16/properties_tsk.gif create mode 100644 org.eclipse.tm.terminal/icons/elcl16/connect_co.gif create mode 100644 org.eclipse.tm.terminal/icons/elcl16/disconnect_co.gif create mode 100644 org.eclipse.tm.terminal/icons/elcl16/newterminal.gif create mode 100644 org.eclipse.tm.terminal/icons/elcl16/properties_tsk.gif create mode 100644 org.eclipse.tm.terminal/icons/eview16/terminal_view.gif create mode 100644 org.eclipse.tm.terminal/plugin.properties create mode 100644 org.eclipse.tm.terminal/plugin.xml create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Logger.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.properties create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetCodes.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetConnection.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetOption.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalAction.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionClearAll.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionConnect.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCopy.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCut.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionDisconnect.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionNewTerminal.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionPaste.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSelectAll.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSettings.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalConsts.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalCtrl.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalMsg.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalNetworkPortMap.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPlugin.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPreferencePage.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalProperties.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettings.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettingsDlg.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalTarget.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalText.java create mode 100644 org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalView.java diff --git a/org.eclipse.tm.terminal/.project b/org.eclipse.tm.terminal/.project new file mode 100644 index 00000000000..621a3a690cf --- /dev/null +++ b/org.eclipse.tm.terminal/.project @@ -0,0 +1,28 @@ + + + org.eclipse.tm.terminal + + + + + + 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/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.tm.terminal/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..175992f9e31 --- /dev/null +++ b/org.eclipse.tm.terminal/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Mon Jul 31 14:55:18 CEST 2006 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4 +org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.source=1.4 diff --git a/org.eclipse.tm.terminal/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.tm.terminal/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000000..9a20fca52a4 --- /dev/null +++ b/org.eclipse.tm.terminal/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,3 @@ +#Mon Jul 31 14:55:17 CEST 2006 +eclipse.preferences.version=1 +internal.default.compliance=user diff --git a/org.eclipse.tm.terminal/META-INF/MANIFEST.MF b/org.eclipse.tm.terminal/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..3e3cd8e261f --- /dev/null +++ b/org.eclipse.tm.terminal/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.tm.terminal; singleton:=true +Bundle-Version: 2.6.0 +Bundle-Activator: org.eclipse.tm.terminal.TerminalPlugin +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.ui.ide, + org.eclipse.ui.views, + org.eclipse.ui.workbench.texteditor, + org.eclipse.ui.editors, + org.eclipse.core.resources, + org.eclipse.ui, + org.eclipse.jface.text +Eclipse-LazyStart: true +Bundle-RequiredExecutionEnvironment: J2SE-1.4 diff --git a/org.eclipse.tm.terminal/icons/clcl16/connect_co.gif b/org.eclipse.tm.terminal/icons/clcl16/connect_co.gif new file mode 100644 index 0000000000000000000000000000000000000000..556b230da5693996a541f64393a5cfb91b4bcd33 GIT binary patch literal 139 zcmZ?wbhEHb6krfw*v!Dd@SlPEEH^^~gT8_P{FCz!-aVMSH2FUm03|Vj;!hSv1_pKp z9gq~r3Qu)m%S5)Zch7sbWtx2K>xBK@v;FkGS@-1G W&gcrAxidxV#@Wd$qt**DSOWlPf-bNC literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/clcl16/disconnect_co.gif b/org.eclipse.tm.terminal/icons/clcl16/disconnect_co.gif new file mode 100644 index 0000000000000000000000000000000000000000..f1d5fb31e51476a24e289e7cf2d083058fb3fa2a GIT binary patch literal 146 zcmZ?wbhEHb6krfw*v!Ci-#-3+e0>80!+v}F|MBtjf#m%8_kn2t{)PsiFvI`<|A8bN zDE?$&WMJTC&;f~n%wS-#FF5JBdhd-z496C>ib@<2@H=YBn5x*&y^Cj=!u(ec#pJbA lyIR&OHT#;)l?%|2-kM+()~m7jt&Y*$wbNf#?G|LP1^^IEGNAwf literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/clcl16/newterminal.gif b/org.eclipse.tm.terminal/icons/clcl16/newterminal.gif new file mode 100644 index 0000000000000000000000000000000000000000..80201e0d46944f559b4a73b846886d8c999dc5d1 GIT binary patch literal 351 zcmV-l0igazNk%w1VGsZi0M!5h|NsC0{{a60|Nj600RR60{{R30{{R303;_R5MgMtu z%T%-WN4)<^zW+|Z|4_mIQNjOG!v9mk|5d~Ph|l(7%Ku}_|7FVmXUqR+%>QW3|8UIx zaLoLF&-sPV_iWGqchmoQ)&F_c|9aK`d({4Z*8Ygv{*2oFliB!+-T#Z;|BBlElHB`@ z;QyE6{g&kaoag_Z=>MMM`kv|kq3Zvs@c*yu`nl}&!1@2d`TxiL|H}UV%>Vz-|NsBw z0RR90A^8LW002J#EC2ui01yBW000Jjz@KnP{2`9Tq!8gl5Eam<;>e^56&{o9rrR-n z;O5n{a@lx2Y7_axCK5DUu7;{UJ*O8e7MroGLkbiLEpI6*B^(n06TG|z`pYHf!VrZ z*PVa54W6%gxH@FFeRoLf4R)Jzc;%{xj64N8hjkY$)1H0yPr!Z4xvR~8FKk*a6_}>- Pph=f=WmBVr5Q8-Uq17^g literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/cview16/terminal_view.gif b/org.eclipse.tm.terminal/icons/cview16/terminal_view.gif new file mode 100644 index 0000000000000000000000000000000000000000..bbb6a9e153e146f57903aa2b5e0d2bc58cd641c8 GIT binary patch literal 938 zcmZ?wbhEHb6krfw_|Cxa|NsA)GiL(HKmWeGdG{tQEzQ{2n8nEC$+IgA3}@2L{AWn} zZ#?sVS{lO{5MTg;pFe-joXIe2)~xRC?%A_v`}+F+{reXrF$zXQV5o(F;!hSvkehWt z1Sn59aI`Zpa>#gWP&mlIC&%G%V1r^46B`$sK!HM|Yd62LSV6;uMg|TcDTyf>3sO!p zvT!)4L>4xBvq>`gln5|79hzvws<1?00YeLyg({nbSILTlo$QmedMhU+F|q2;J|?;5|=b=70_{LC}2!?U|_HY0P>7m*#H0l literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/dlcl16/connect_co.gif b/org.eclipse.tm.terminal/icons/dlcl16/connect_co.gif new file mode 100644 index 0000000000000000000000000000000000000000..ae5e64b9f6f1b090da34d65bd48784886e8e2305 GIT binary patch literal 874 zcmZ?wbhEHb6krfw_|CwvfB*jZ^XLEn|DRzLjE2C-3jxKSEQ|~cObj|8uY>Z01BW{U zD~F8728DwR?CeT95(x_rwKK_BwPb8iXlP?(Q0cMwkmTGe!|LZ^v2oGK$r|2mF((UE aj!xCHPGXTXQajxtY@6nCa&v+MgEavEOe8}9 literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/dlcl16/disconnect_co.gif b/org.eclipse.tm.terminal/icons/dlcl16/disconnect_co.gif new file mode 100644 index 0000000000000000000000000000000000000000..24a628713dca636bfe498223f8382f189140cf93 GIT binary patch literal 90 zcmZ?wbhEHb6krfwn8?7efB*jZ^XLEn|DS<@LGdRGBLf2ygAR}llobH7wWsv2Jk7<( s$kioP{n2(-nO*KN;Z~JpouWlvOix;0c{hLlefKNJ?z8-VL>U;Y0qcJs5C8xG literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/dlcl16/newterminal.gif b/org.eclipse.tm.terminal/icons/dlcl16/newterminal.gif new file mode 100644 index 0000000000000000000000000000000000000000..80201e0d46944f559b4a73b846886d8c999dc5d1 GIT binary patch literal 351 zcmV-l0igazNk%w1VGsZi0M!5h|NsC0{{a60|Nj600RR60{{R30{{R303;_R5MgMtu z%T%-WN4)<^zW+|Z|4_mIQNjOG!v9mk|5d~Ph|l(7%Ku}_|7FVmXUqR+%>QW3|8UIx zaLoLF&-sPV_iWGqchmoQ)&F_c|9aK`d({4Z*8Ygv{*2oFliB!+-T#Z;|BBlElHB`@ z;QyE6{g&kaoag_Z=>MMM`kv|kq3Zvs@c*yu`nl}&!1@2d`TxiL|H}UV%>Vz-|NsBw z0RR90A^8LW002J#EC2ui01yBW000Jjz@KnP{2`9Tq!8gl5Eam<;>e^56&{o9rrR-n z;O5n{a@lx2Y7_axCK5DUu7;{UJ*O8e7MroGLkbiLEpI6*B^(n06TG|z`pjqWXXeWH$Wh%qo&0|4{v9aI1S literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/elcl16/connect_co.gif b/org.eclipse.tm.terminal/icons/elcl16/connect_co.gif new file mode 100644 index 0000000000000000000000000000000000000000..c0de0d32dd66e4efb5e94661dd3f180de819a25d GIT binary patch literal 890 zcmZ?wbhEHb6krfw_|CwvfB*jZ^XE4-G{ncpGcYjx|NkFIj)Kt;7$G5`_>+Z^fq|7l z2jpK+o^ap@XW-_L@z|i?$jHvFq@$7W@BkyD6rX^F!y}hAHe)6Mg9L_#7FHRi7cT-A zFfa(}&*GW6ap~#l3jB+n%(#%)*d-IXDQ4w_2gmy4D{r-EOk8+&qC=vVtb_oAH2~QW3|8UIx zaLoLF&-sPV_iWGqchmoQ)&F_c|9aK`d({4Z*8Ygv{*2oFliB!+-T#Z;|BBlElHB`@ z;QyE6{g&kaoag_Z=>MMM`kv|kq3Zvs@c*yu`nl}&!1@2d`TxiL|H}UV%>Vz-|NsBw z0RR90A^8LW002J#EC2ui01yBW000Jjz@KnP{2`9Tq!8gl5Eam<;>e^56&{o9rrR-n z;O5n{a@lx2Y7_axCK5DUu7;{UJ*O8e7MroGLkbiLEpI6*B^(n06TG|z`pYHf!VrZ z*PVa54W6%gxH@FFeRoLf4R)Jzc;%{xj64N8hjkY$)1H0yPr!Z4xvR~8FKk*a6_}>- Pph=f=WmBVr5Q8-Uq17^g literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/icons/eview16/terminal_view.gif b/org.eclipse.tm.terminal/icons/eview16/terminal_view.gif new file mode 100644 index 0000000000000000000000000000000000000000..bbb6a9e153e146f57903aa2b5e0d2bc58cd641c8 GIT binary patch literal 938 zcmZ?wbhEHb6krfw_|Cxa|NsA)GiL(HKmWeGdG{tQEzQ{2n8nEC$+IgA3}@2L{AWn} zZ#?sVS{lO{5MTg;pFe-joXIe2)~xRC?%A_v`}+F+{reXrF$zXQV5o(F;!hSvkehWt z1Sn59aI`Zpa>#gWP&mlIC&%G%V1r^46B`$sK!HM|Yd62LSV6;uMg|TcDTyf>3sO!p zvT!)4L>4xBvq>`gln5|79hzvws<1?00YeLyg({nbSILTlo$QmedMhU+F|q2;J|?;5|=b=70_{LC}2!?U|_HY0P>7m*#H0l literal 0 HcmV?d00001 diff --git a/org.eclipse.tm.terminal/plugin.properties b/org.eclipse.tm.terminal/plugin.properties new file mode 100644 index 00000000000..e4f0b2c67f0 --- /dev/null +++ b/org.eclipse.tm.terminal/plugin.properties @@ -0,0 +1,17 @@ +########################################################################## +# Copyright 2004 Wind River Systems, Inc. All rights reserved. +########################################################################## + +pluginName = Terminal +providerName = Eclipse.org +terminal.views.category.name = Device Debug +terminal.views.view.name = Terminal +terminal.views.view.font.description = The font for the terminal console. +terminal.views.view.font.label = Terminal Console Font + +terminal.view.context.name=Terminal view context +terminal.view.context.description=control-q override + +terminal.view.insertion.description=Terminal view insertion +terminal.view.insertion.name=Terminal view insert +terminal.view.insertion.category.name=Terminal view commands diff --git a/org.eclipse.tm.terminal/plugin.xml b/org.eclipse.tm.terminal/plugin.xml new file mode 100644 index 00000000000..28229ed8aa1 --- /dev/null +++ b/org.eclipse.tm.terminal/plugin.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %terminal.views.view.font.description + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Logger.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Logger.java new file mode 100644 index 00000000000..1c319e18fa1 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Logger.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; + + +/** + * A simple logger class. Every method in this class is static, so they can be called + * from both class and instance methods. To use this class, write code like this:

+ * + *

+ *      Logger.log("something has happened");
+ *      Logger.log("counter is " + counter);
+ * 
+ * + * @author Fran Litterio + */ +public final class Logger +{ + /** + * UNDER CONSTRUCTION + */ + private static PrintStream logStream; + + static + { + String logFile = null; + File logDirWindows = new File("C:\\wblogs"); //$NON-NLS-1$ + File logDirUNIX = new File("/tmp/wblogs"); //$NON-NLS-1$ + + if (logDirWindows.isDirectory()) + { + logFile = logDirWindows + "\\wbterminal.log"; //$NON-NLS-1$ + } + else if (logDirUNIX.isDirectory()) + { + logFile = logDirUNIX + "/wbterminal.log"; //$NON-NLS-1$ + } + + if (logFile != null) + { + try + { + logStream = new PrintStream(new FileOutputStream(logFile, true)); + } + catch (Exception ex) + { + logStream = System.err; + logStream.println("Exception when opening log file -- logging to stderr!"); //$NON-NLS-1$ + ex.printStackTrace(logStream); + } + } + } + + /** + * Logs the specified message. Do not append a newline to parameter + * message. This method does that for you. + * + * @param message A String containing the message to log. + */ + public static final void log(String message) + { + if (logStream != null) + { + // Read my own stack to get the class name, method name, and line number of + // where this method was called. + + StackTraceElement caller = new Throwable().getStackTrace()[1]; + int lineNumber = caller.getLineNumber(); + String className = caller.getClassName(); + String methodName = caller.getMethodName(); + className = className.substring(className.lastIndexOf('.') + 1); + + logStream.println(className + "." + methodName + ":" + lineNumber + ": " + message); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + logStream.flush(); + } + } + + /** + * Writes a stack trace for an exception to both Standard Error and to the log file. + */ + public static final void logException(Exception ex) + { + // Read my own stack to get the class name, method name, and line number of + // where this method was called. + + StackTraceElement caller = new Throwable().getStackTrace()[1]; + int lineNumber = caller.getLineNumber(); + String className = caller.getClassName(); + String methodName = caller.getMethodName(); + className = className.substring(className.lastIndexOf('.') + 1); + + PrintStream tmpStream = System.err; + + if (logStream != null) + { + tmpStream = logStream; + } + + tmpStream.println(className + "." + methodName + ":" + lineNumber + ": " + //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + "Caught exception: " + ex); //$NON-NLS-1$ + ex.printStackTrace(tmpStream); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.java new file mode 100644 index 00000000000..9defcc72685 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Messages +{ + private static final String BUNDLE_NAME = "org.eclipse.tm.terminal.Messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + + private Messages() + { + // Empty. + } + + public static String getString(String key) + { + try + { + return RESOURCE_BUNDLE.getString(key); + } + catch (MissingResourceException e) + { + return '!' + key + '!'; + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.properties b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.properties new file mode 100644 index 00000000000..32475e6c669 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Messages.properties @@ -0,0 +1,33 @@ +TerminalConsts.Terminal_7=Terminal +TerminalConsts.Limit_terminal_output_16=Limit terminal output +TerminalConsts.Terminal_buffer_lines__17=Terminal buffer lines: +TerminalConsts.Serial_timeout_(seconds)__18=Serial timeout (seconds): +TerminalConsts.Network_timeout_(seconds)__19=Network timeout (seconds): +TerminalConsts.Terminal_Settings_1=Terminal Settings +TerminalConsts.Connection_Type_2=Connection Type +TerminalConsts.Settings_3=Settings +TerminalConsts.Port_4=Port +TerminalConsts.Baud_Rate_5=Baud Rate +TerminalConsts.Data_Bits_6=Data Bits +TerminalConsts.Stop_Bits_7=Stop Bits +TerminalConsts.Parity_8=Parity +TerminalConsts.Flow_Out_10=Flow Out +TerminalConsts.Host_11=Host +TerminalConsts.Terminal_Error_12=Terminal Error +TerminalConsts.Socket_Error_13=Socket Error +TerminalConsts.IO_Error_14=IO Error +TerminalConsts.Serial_port___{0}___is_currently_in_use_!_nDo_you_want_to_close_the_port__15=Serial port \''{0}\'' is currently in use\!\nDo you want to close the port? +TerminalConsts.Error_16=Error +TerminalConsts.Emulator_is_not_supported._17=Emulator is not supported. + +TerminalConsts.New_terminal=New Terminal +TerminalConsts.Connect_2=Connect +TerminalConsts.Disconnect_3=Disconnect +TerminalConsts.Settings..._4=Settings... +TerminalConsts.Copy_5=Copy +TerminalConsts.0=Cut +TerminalConsts.1=Flow Control +TerminalConsts.Paste_6=Paste +TerminalConsts.Select_All_7=Select All +TerminalConsts.Clear_All_8=Clear All +TerminalConsts.Break_9=Break diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetCodes.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetCodes.java new file mode 100644 index 00000000000..c696786c753 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetCodes.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +/** + * This interface defines symbolic constants for numeric TELNET protocol command and + * option codes. Any class that needs to use these constants must implement this + * interface. The meanings of these constants are defined in the various TELNET RFCs + * (RFC 854 to RFC 861, and others). + */ +interface TelnetCodes +{ + /** Command code: Subnegotiation End. */ + static final byte TELNET_SE = (byte)240; + + /** Command code: No-op. */ + static final byte TELNET_NOP = (byte)241; + + /** Command code: Data Mark. */ + static final byte TELNET_DM = (byte)242; + + /** Command code: Break. */ + static final byte TELNET_BREAK = (byte)243; + + /** Command code: Interrupt Process. */ + static final byte TELNET_IP = (byte)244; + + /** Command code: Abort Output. */ + static final byte TELNET_AO = (byte)245; + + /** Command code: Are You There. */ + static final byte TELNET_AYT = (byte)246; + + /** Command code: Erase Character. */ + static final byte TELNET_EC = (byte)247; + + /** Command code: Erase Line. */ + static final byte TELNET_EL = (byte)248; + + /** Command code: Go Ahead. */ + static final byte TELNET_GA = (byte)249; + + /** Command code: Subnegotiation Begin. */ + static final byte TELNET_SB = (byte)250; + + /** Command code: Will. */ + static final byte TELNET_WILL = (byte)251; + + /** Command code: Won't. */ + static final byte TELNET_WONT = (byte)252; + + /** Command code: Do. */ + static final byte TELNET_DO = (byte)253; + + /** Command code: Don't. */ + static final byte TELNET_DONT = (byte)254; + + /** Command code: Interpret As Command. */ + static final byte TELNET_IAC = (byte)255; + + /** Command code: IS. */ + static final byte TELNET_IS = 0; + + /** Command code: SEND. */ + static final byte TELNET_SEND = 1; + + + /** Option code: Transmit Binary option. */ + static final byte TELNET_OPTION_TRANSMIT_BINARY = 0; + + /** Option code: Echo option. */ + static final byte TELNET_OPTION_ECHO = 1; + + /** Option code: Suppress Go Ahead option. */ + static final byte TELNET_OPTION_SUPPRESS_GA = 3; + + /** Option code: Terminal Type */ + static final byte TELNET_OPTION_TERMINAL_TYPE = 24; + + /** Option code: Negotitate About Window Size (NAWS) */ + static final byte TELNET_OPTION_NAWS = 31; +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetConnection.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetConnection.java new file mode 100644 index 00000000000..745419b7cd0 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetConnection.java @@ -0,0 +1,796 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + + +package org.eclipse.tm.terminal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketException; + +import org.eclipse.swt.widgets.Display; + + +/** + * This class encapsulates a TELNET connection to a remote server. It processes + * incoming TELNET protocol data and generates outbound TELNET protocol data. It also + * manages two sets of TelnetOption objects: one for the local endpoint and one for the + * remote endpoint.

+ * + * IMPORTANT: Understanding this code requires understanding the TELNET protocol and + * TELNET option processing, as defined in the RFCs listed below.

+ * + * @author Fran Litterio (francis.litterio@windriver.com) + * + * @see RFC 854 + * @see RFC 855 + * @see RFC 856 + * @see RFC 857 + * @see RFC 858 + * @see RFC 859 + * @see RFC 860 + * @see RFC 861 + * @see RFC 1091 + * @see RFC 1096 + * @see RFC 1073 + * @see RFC 1079 + * @see RFC 1143 + * @see RFC 1572 + */ +class TelnetConnection extends Thread implements TelnetCodes, TerminalMsg +{ + /** + * TELNET connection state: Initial state. + */ + protected static final int STATE_INITIAL = 0; + + /** + * TELNET connection state: Last byte processed was IAC code. + * code. + */ + protected static final int STATE_IAC_RECEIVED = 1; + + /** + * TELNET connection state: Last byte processed was WILL code. + * code. + */ + protected static final int STATE_WILL_RECEIVED = 2; + + /** + * TELNET connection state: Last byte processed was WONT code. + */ + protected static final int STATE_WONT_RECEIVED = 3; + + /** + * TELNET connection state: Last byte processed was DO code. + */ + protected static final int STATE_DO_RECEIVED = 4; + + /** + * TELNET connection state: Last byte processed was DONT code. + */ + protected static final int STATE_DONT_RECEIVED = 5; + + /** + * TELNET connection state: Last byte processed was SB. + */ + protected static final int STATE_SUBNEGOTIATION_STARTED = 6; + + /** + * TELNET connection state: Currently receiving sub-negotiation data. + */ + protected static final int STATE_RECEIVING_SUBNEGOTIATION = 7; + + /** + * Size of buffer for processing data received from remote endpoint. + */ + protected static final int BUFFER_SIZE = 2048; + + /** + * Holds raw bytes received from the remote endpoint, prior to any TELNET protocol + * processing. + */ + protected byte[] rawBytes = new byte[BUFFER_SIZE]; + + /** + * Holds incoming network data after the TELNET protocol bytes have been + * processed and removed. + */ + protected byte[] processedBytes = new byte[BUFFER_SIZE]; + + /** + * This field holds a StringBuffer containing text recently received from the + * remote endpoint (after all TELNET protocol bytes have been processed and + * removed). + */ + protected StringBuffer processedStringBuffer = new StringBuffer(BUFFER_SIZE); + + /** + * Holds the current state of the TELNET protocol processor. + */ + protected int telnetState = STATE_INITIAL; + + /** + * This field is true if the remote endpoint is a TELNET server, false if not. We + * set this to true if and only if the remote endpoint sends recognizable TELNET + * protocol data. We do not assume that the remote endpoint is a TELNET server + * just because it is listening on port 23. This allows us to successfully connect + * to a TELNET server listening on a port other than 23.

+ * + * When this field first changes from false to true, we send all WILL or DO + * commands to the remote endpoint.

+ * + * @see #telnetServerDetected() + */ + protected boolean remoteIsTelnetServer = false; + + /** + * An array of TelnetOption objects representing the local endpoint's TELNET + * options. The array is indexed by the numeric TELNET option code. + */ + protected TelnetOption[] localOptions = new TelnetOption[256]; + + /** + * An array of TelnetOption objects representing the remote endpoint's TELNET + * options. The array is indexed by the numeric TELNET option code. + */ + protected TelnetOption[] remoteOptions = new TelnetOption[256]; + + /** + * An array of bytes that holds the TELNET subnegotiation command most recently + * received from the remote endpoint. This array does _not_ include the leading + * IAC SB bytes, nor does it include the trailing IAC SE bytes. The first byte of + * this array is always a TELNET option code. + */ + protected byte[] receivedSubnegotiation = new byte[128]; + + /** + * This field holds the index into array {@link receivedSubnegotiation} of the next + * unused byte. This is used by method {@link #processTelnetProtocol(int)} when + * the state machine is in states {@link #STATE_SUBNEGOTIATION_STARTED} and {@link + * STATE_RECEIVING_SUBNEGOTIATION}. + */ + protected int nextSubnegotiationByteIndex = 0; + + /** + * This field is true if an error occurs while processing a subnegotiation + * command. + * + * @see #processTelnetProtocol(int) + */ + protected boolean ignoreSubnegotiation = false; + + /** + * This field holds the width of the Terminal screen in columns. + */ + protected int width = 0; + + /** + * This field holds the height of the Terminal screen in rows. + */ + protected int height = 0; + + /** + * This field holds a reference to the {@link TerminalCtrl} singleton. + */ + protected TerminalCtrl terminalControl; + + /** + * This method holds the Socket object for the TELNET connection. + */ + protected Socket socket; + + /** + * This field holds a reference to a {@link TerminalText} object, which displays + * text to the user. + */ + protected TerminalText terminalText; + + /** + * This field holds a reference to an {@link InputStream} object used to receive + * data from the remote endpoint. + */ + protected InputStream inputStream; + + /** + * This field holds a reference to an {@link OutputStream} object used to send data + * to the remote endpoint. + */ + protected OutputStream outputStream; + + /** + * This field holds the SWT Display object for the GUI. We use this to execute a + * TerminalText method on the display thread, so that it can draw text in the view. + */ + protected Display display; + + /** + * UNDER CONSTRUCTION + */ + protected boolean localEcho = true; + + /** + * This constructor just initializes some internal object state from its + * arguments. + */ + public TelnetConnection(TerminalCtrl terminalControl, Socket socket, + TerminalText terminalText) + throws IOException + { + super(); + + Logger.log("entered"); //$NON-NLS-1$ + + this.terminalControl = terminalControl; + this.socket = socket; + this.terminalText = terminalText; + + inputStream = socket.getInputStream(); + outputStream = socket.getOutputStream(); + display = terminalControl.getTextWidget().getDisplay(); + + initializeOptions(); + } + + /** + * Returns true if the TCP connection represented by this object is connected, + * false otherwise. + */ + public boolean isConnected() + { + return socket != null && socket.isConnected(); + } + + /** + * Returns true if the TCP connection represented by this object is connected and + * the remote endpoint is a TELNET server, false otherwise. + */ + public boolean isRemoteTelnetServer() + { + return remoteIsTelnetServer; + } + + /** + * This method sets the terminal width and height to the supplied values. If + * either new value differs from the corresponding old value, we initiate a NAWS + * subnegotiation to inform the remote endpoint of the new terminal size. + */ + public void setTerminalSize(int newWidth, int newHeight) + { + Logger.log("Setting new size: width = " + newWidth + ", height = " + newHeight); //$NON-NLS-1$ //$NON-NLS-2$ + + boolean sizeChanged = false; + + if (newWidth != width || newHeight != height) + sizeChanged = true; + + width = newWidth; + height = newHeight; + + if (sizeChanged && remoteIsTelnetServer && localOptions[TELNET_OPTION_NAWS].isEnabled()) + { + Integer[] sizeData = { new Integer(width), new Integer(height) }; + + localOptions[TELNET_OPTION_NAWS].sendSubnegotiation(sizeData); + } + } + + /** + * Returns true if local echoing is enabled for this TCP connection, false otherwise. + */ + public boolean localEcho() + { + return localEcho; + } + + /** + * This method runs in its own thread. It reads raw bytes from the TELNET + * connection socket, processes any TELNET protocol bytes (and removes them), and + * passes the remaining bytes to a TerminalDisplay object for display. + */ + public void run() + { + Logger.log("Entered"); //$NON-NLS-1$ + + try + { + while (socket.isConnected()) + { + int nRawBytes = inputStream.read(rawBytes); + + if (nRawBytes == -1) + { + // End of input on inputStream. + Logger.log("End of input reading from socket!"); //$NON-NLS-1$ + + // Announce to the user that the remote endpoint has closed the + // connection. + + processedStringBuffer.replace(0, processedStringBuffer.length(), + "\rConnection closed by foreign host.\r\n"); //$NON-NLS-1$ + + terminalText.setNewText(processedStringBuffer); + + // See the large comment below for an explaination of why we must + // call Display.syncExec() instead of Display.asyncExec(). + + display.syncExec(terminalText); + + // Tell the TerminalCtrl object that the connection is closed. + + terminalControl.setOpened(false); + terminalControl.setConnected(false); + + // Update the Terminal view UI to show a disconnected state. This + // ugliness involving Display.asyncExec() is forced on us by the + // bad design of class TerminalCtrl, which requires in certain + // cases (maybe all?) that TerminalCtrl.execute() is called only + // from the display thread. + + Runnable disconnectNotifier = new Thread() + { + public void run() + { + terminalControl.execute(ON_TERMINAL_DISCONNECT, null); + } + }; + + display.asyncExec(disconnectNotifier); + break; + } + else + { + Logger.log("Received " + nRawBytes + " bytes: '" + //$NON-NLS-1$ //$NON-NLS-2$ + new String(rawBytes, 0, nRawBytes) + "'"); //$NON-NLS-1$ + + // Process any TELNET protocol data that we receive. Don't send + // any TELNET protocol data until we are sure the remote endpoint + // is a TELNET server. + + int nProcessedBytes = processTelnetProtocol(nRawBytes); + + if (nProcessedBytes > 0) + { + String strBuffer = new String(processedBytes, 0, nProcessedBytes); + + // An earlier version of this code created a new StringBuffer + // object here, but that was unnecessary. Instead, we reuse an + // existing object (processedStringBuffer). + + processedStringBuffer.replace(0, processedStringBuffer.length(), + strBuffer); + + // Use the TerminalText object to display the text to the + // user. + + terminalText.setNewText(processedStringBuffer); + + // Now make the TerminalText object display the processesed + // bytes. Its code has to run in the display thread, so we + // call syncExec() to do that. We do _not_ call asyncExec() + // because asyncExec() does not wait for the Runnable + // (TerminalText) to finish. If we were to call asynchExec(), + // this loop might race around and call setNewText() on the + // terminalText object again before the Display thread gets to + // display the previous buffer held by that object. By + // blocking here, we avoid that race and also delay the next + // call to read(), which keeps the unread data in the kernel, + // where it belongs (i.e., we don't have to manage it). + // + // + // The original implementation of this package used asyncExec() + // and created a new TerminalText object for each trip through + // this loop. Yes, that allowed this thread to speed ahead of + // the Display thread. But that only served to make the + // Display thread queue all unexecuted TerminalText objects + // until they could be executed. That was simply buffering the + // undisplayed data in the Display thread instead of in the + // kernel, where it belongs. It also rapidly consumed JVM heap + // space with all those TerminalText objects waiting to be + // garbage collected. + // + + display.syncExec(terminalText); + } + } + } + } + catch (SocketException ex) + { + String message = ex.getMessage(); + + // A "socket closed" exception is normal here. It's caused by the user + // clicking the disconnect button on the Terminal view toolbar. + + if (message != null && !message.equals("socket closed")) //$NON-NLS-1$ + { + Logger.logException(ex); + } + } + catch (Exception ex) + { + Logger.logException(ex); + } + } + + /** + * This method initializes the localOptions[] and remoteOptions[] arrays so that + * they contain references to TelnetOption objects representing our desired state + * for each option. The goal is to achieve server-side echoing, suppression of Go + * Aheads, and to send the local terminal type and size to the remote endpoint. + */ + protected void initializeOptions() + { + // First, create all the TelnetOption objects in the "undesired" state. + + for (int i = 0; i < localOptions.length; ++i) + { + localOptions[i] = new TelnetOption((byte)i, false, true, outputStream); + } + + for (int i = 0; i < localOptions.length; ++i) + { + remoteOptions[i] = new TelnetOption((byte)i, false, false, outputStream); + } + + // Next, set some of the options to the "desired" state. The options we desire + // to be enabled are as follows: + // + // TELNET Option Desired for Desired for + // Name and Code Local Endpoint Remote Endpoint + // --------------------- -------------- --------------- + // Echo (1) No Yes + // Suppress Go Ahead (3) Yes Yes + // Terminal Type (24) Yes Yes + // NAWS (31) Yes Yes + // + // All other options remain in the "undesired" state, and thus will be disabled + // (since either endpoint can force any option to be disabled by simply + // answering WILL with DONT and DO with WONT). + + localOptions[TELNET_OPTION_ECHO].setDesired(false); + remoteOptions[TELNET_OPTION_ECHO].setDesired(true); + + localOptions[TELNET_OPTION_SUPPRESS_GA].setDesired(true); + remoteOptions[TELNET_OPTION_SUPPRESS_GA].setDesired(true); + + localOptions[TELNET_OPTION_TERMINAL_TYPE].setDesired(true); + remoteOptions[TELNET_OPTION_TERMINAL_TYPE].setDesired(true); + + localOptions[TELNET_OPTION_NAWS].setDesired(true); + remoteOptions[TELNET_OPTION_NAWS].setDesired(true); + } + + /** + * Process TELNET protocol data contained in the first count bytes of + * rawBytes. This function preserves its state between calls, because a + * multi-byte TELNET command might be split between two (or more) calls to this + * function. The state is preserved in field telnetState. This function + * implements an FSA that recognizes TELNET option codes. TELNET option state is + * stored in instances of {@link TelnetOption}. TELNET option subnegotiation is + * delegated to instances of TelnetOption. + * + * @return The number of bytes remaining in the buffer after removing all TELNET + * protocol bytes. + */ + protected int processTelnetProtocol(int count) + { + // This is too noisy to leave on all the time. + // Logger.log("Processing " + count + " bytes of data."); + + int nextProcessedByte = 0; + + for (int byteIndex = 0; byteIndex < count; ++byteIndex) + { + // It is possible for control to flow through the below code such that + // nothing happens. This happens when array rawBytes[] contains no TELNET + // protocol data. + + byte inputByte = rawBytes[byteIndex]; + + switch (telnetState) + { + case STATE_INITIAL: + if (inputByte == TELNET_IAC) + { + telnetState = STATE_IAC_RECEIVED; + } + else + { + // It's not an IAC code, so just append it to processedBytes. + + processedBytes[nextProcessedByte++] = rawBytes[byteIndex]; + } + break; + + case STATE_IAC_RECEIVED: + switch (inputByte) + { + case TELNET_IAC: + // Two IAC bytes in a row are translated into one byte with the + // value 0xff. + + processedBytes[nextProcessedByte++] = (byte)0xff; + telnetState = STATE_INITIAL; + break; + + case TELNET_WILL: + telnetState = STATE_WILL_RECEIVED; + break; + + case TELNET_WONT: + telnetState = STATE_WONT_RECEIVED; + break; + + case TELNET_DO: + telnetState = STATE_DO_RECEIVED; + break; + + case TELNET_DONT: + telnetState = STATE_DONT_RECEIVED; + break; + + case TELNET_SB: + telnetState = STATE_SUBNEGOTIATION_STARTED; + break; + + // Commands to consume and ignore. + + // Data Mark (DM). This is sent by a TELNET server following an + // IAC sent as TCP urgent data. It should cause the client to skip + // all not yet processed non-TELNET-protocol data preceding the DM + // byte. However, Java 1.4.x has no way to inform clients of class + // Socket that urgent data is available, so we simply ignore the + // "IAC DM" command. Since the IAC is sent as TCP urgent data, the + // Socket must be put into OOB-inline mode via a call to + // setOOBInline(true), otherwise the IAC is silently dropped by + // Java and only the DM arrives (leaving the user to see a spurious + // ISO Latin-1 character). + case TELNET_DM: + + case TELNET_NOP: // No-op. + case TELNET_GA: // Go Ahead command. Meaningless on a full-duplex link. + case TELNET_IP: // Interupt Process command. Server should never send this. + case TELNET_AO: // Abort Output command. Server should never send this. + case TELNET_AYT: // Are You There command. Server should never send this. + case TELNET_EC: // Erase Character command. Server should never send this. + case TELNET_EL: // Erase Line command. Server should never send this. + telnetState = STATE_INITIAL; + break; + + default: + // Unrecognized command! This should never happen. + Logger.log("processTelnetProtocol: UNRECOGNIZED TELNET PROTOCOL COMMAND: " + //$NON-NLS-1$ + inputByte); + telnetState = STATE_INITIAL; + assert false; + break; + } + break; + + // For the next four cases, WILL and WONT commands affect the state of + // remote options, and DO and DONT commands affect the state of local + // options. + + case STATE_WILL_RECEIVED: + Logger.log("Received WILL " + localOptions[inputByte].optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + remoteOptions[inputByte].handleWill(); + telnetState = STATE_INITIAL; + telnetServerDetected(); + break; + + case STATE_WONT_RECEIVED: + Logger.log("Received WONT " + localOptions[inputByte].optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + remoteOptions[inputByte].handleWont(); + telnetState = STATE_INITIAL; + telnetServerDetected(); + break; + + case STATE_DO_RECEIVED: + Logger.log("Received DO " + localOptions[inputByte].optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + localOptions[inputByte].handleDo(); + telnetState = STATE_INITIAL; + telnetServerDetected(); + break; + + case STATE_DONT_RECEIVED: + Logger.log("Received DONT " + localOptions[inputByte].optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + localOptions[inputByte].handleDont(); + telnetState = STATE_INITIAL; + telnetServerDetected(); + break; + + case STATE_SUBNEGOTIATION_STARTED: + Logger.log("Starting subnegotiation for option " + //$NON-NLS-1$ + localOptions[inputByte].optionName() + "."); //$NON-NLS-1$ + + // First, zero out the array of received subnegotiation butes. + + for (int i = 0; i < receivedSubnegotiation.length; ++i) + receivedSubnegotiation[i] = 0; + + // Forget about any previous subnegotiation errors. + + ignoreSubnegotiation = false; + + // Then insert this input byte into the array and enter state + // STATE_RECEIVING_SUBNEGOTIATION, where we will gather the remaining + // subnegotiation bytes. + + nextSubnegotiationByteIndex = 0; + receivedSubnegotiation[nextSubnegotiationByteIndex++] = inputByte; + telnetState = STATE_RECEIVING_SUBNEGOTIATION; + break; + + case STATE_RECEIVING_SUBNEGOTIATION: + if (inputByte == TELNET_IAC) + { + // Handle double IAC bytes. From RFC 855: "if parameters + // in an option 'subnegotiation' include a byte with a value of + // 255, it is necessary to double this byte in accordance the + // general TELNET rules." + + if (nextSubnegotiationByteIndex > 0 && + receivedSubnegotiation[nextSubnegotiationByteIndex - 1] == TELNET_IAC) + { + // The last input byte we received in this subnegotiation was + // IAC, so this is a double IAC. Leave the previous IAC in the + // receivedSubnegotiation[] array and drop the current one + // (thus translating a double IAC into a single IAC). + + Logger.log("Double IAC in subnegotiation translated into single IAC."); //$NON-NLS-1$ + break; + } + + // Append the IAC byte to receivedSubnegotiation[]. If there is no + // room for the IAC byte, it overwrites the last byte, because we + // need to know when the subnegotiation ends, and that is marked by + // an "IAC SE" command. + + if (nextSubnegotiationByteIndex < receivedSubnegotiation.length) + { + receivedSubnegotiation[nextSubnegotiationByteIndex++] = inputByte; + } + else + { + receivedSubnegotiation[receivedSubnegotiation.length - 1] = inputByte; + } + break; + } + + // Handle an "IAC SE" command, which marks the end of the + // subnegotiation. An SE byte by itself might be a legitimate part of + // the subnegotiation data, so don't do anything unless the SE is + // immediately preceded by an IAC. + + if (inputByte == TELNET_SE && + receivedSubnegotiation[nextSubnegotiationByteIndex - 1] == TELNET_IAC) + { + Logger.log("Found SE code marking end of subnegotiation."); //$NON-NLS-1$ + + // We are done receiving the subnegotiation command. Now process + // it. We always use the option object stored in array + // localOptions[] to process the received subnegotiation. This is + // an arbitrary decision, but it is sufficient for handling options + // TERMINAL-TYPE and NAWS, which are the only options that we + // subnegotiate (presently). If, in the future, subnegotiations + // need to be handled by option objects stored in both + // localOptions[] and remoteOptions[], then some mechanism to + // choose the correct option object must be implemented. + // + // Also, if ignoreSubnegotiation is true, there was an error while + // receiving the subnegotiation, so we must not process the + // command, and instead just return to the initial state. + + if (!ignoreSubnegotiation) + { + // Remove the trailing IAC byte from receivedSubnegotiation[]. + + receivedSubnegotiation[nextSubnegotiationByteIndex - 1] = 0; + + int subnegotiatedOption = receivedSubnegotiation[0]; + + localOptions[subnegotiatedOption]. + handleSubnegotiation(receivedSubnegotiation, + nextSubnegotiationByteIndex); + } + else + { + Logger.log("NOT CALLING handleSubnegotiation() BECAUSE OF ERRORS!"); //$NON-NLS-1$ + } + + // Return to the initial state. + + telnetState = STATE_INITIAL; + } + + // Check whether the receivedSubnegotiation[] array is full. + + if (nextSubnegotiationByteIndex >= receivedSubnegotiation.length) + { + // This should not happen. Array receivedSubnegotiation can hold + // 128 bytes, and no TELNET option that we perform subnegotiation + // for requires that many bytes in a subnegotiation command. In + // the interest of robustness, we handle this case by ignoring all + // remaining subnegotiation bytes until we receive the IAC SE + // command that ends the subnegotiation. Also, we set + // ignoreSubnegotiation to true to prevent a call to + // handleSubnegotiation() when the IAC SE command arrives. + + Logger.log("SUBNEGOTIATION BUFFER FULL!"); //$NON-NLS-1$ + ignoreSubnegotiation = true; + } + else + { + Logger.log("Recording subnegotiation byte " + (inputByte & 0xff)); //$NON-NLS-1$ + + receivedSubnegotiation[nextSubnegotiationByteIndex++] = inputByte; + } + break; + + default: + // This should _never_ happen! If it does, it means there is a bug in + // this FSA. For robustness, we return to the initial state. + + Logger.log("INVALID TELNET STATE: " + telnetState); //$NON-NLS-1$ + telnetState = STATE_INITIAL; + assert false; + break; + } + } + + // Return the number of bytes of processed data (i.e., number of bytes of + // raw data minus TELNET control bytes). This value can be zero. + + return nextProcessedByte; + } + + /** + * This method is called whenever we receive a valid TELNET protocol command from + * the remote endpoint. When it is called for the first time for this connection, + * we negotiate all options that we desire to be enabled.

+ * + * This method does not negotiate options that we do not desire to be enabled, + * because all options are initially disabled.

+ */ + protected void telnetServerDetected() + { + if (!remoteIsTelnetServer) + { + // This block only executes once per TelnetConnection instance. + + localEcho = false; + + Logger.log("Detected TELNET server."); //$NON-NLS-1$ + + remoteIsTelnetServer = true; + + for (int i = 0; i < localOptions.length; ++i) + { + if (localOptions[i].isDesired()) + { + localOptions[i].negotiate(); + } + } + + for (int i = 0; i < remoteOptions.length; ++i) + { + if (remoteOptions[i].isDesired()) + { + remoteOptions[i].negotiate(); + } + } + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetOption.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetOption.java new file mode 100644 index 00000000000..60712d6eeef --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetOption.java @@ -0,0 +1,756 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + + +package org.eclipse.tm.terminal; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +/** + * This class represents a single TELNET protocol option at one endpoint of a TELNET + * connection. This class encapsulates the endpoint associated with the option (local + * or remote), the current state of the option (enabled or disabled), the desired state + * of the option, the current state of the negotiation, an OutputStream that allows + * communication with the remote endpoint, and the number of negotiations that have + * started within this connection.

+ * + * In addition to encapsulating the above state, this class performs option negotiation + * to attempt to achieve the desired option state. For some options, this class also + * performs option sub-negotiation.

+ * + * IMPORTANT: Understanding this code requires understanding the TELNET protocol and + * TELNET option processing.

+ * + * @author Fran Litterio (francis.litterio@windriver.com) + */ +class TelnetOption implements TelnetCodes +{ + /** + * This array of Strings maps an integer TELNET option code value to the symbolic + * name of the option. Array elements of the form "?" represent unassigned option + * values. + */ + protected static final String[] optionNames = + { + "BINARY", // 0 //$NON-NLS-1$ + "ECHO", // 1 //$NON-NLS-1$ + "RECONNECTION", // 2 //$NON-NLS-1$ + "SUPPRESS GO AHEAD", // 3 //$NON-NLS-1$ + "MSG SIZE NEGOTIATION", // 4 //$NON-NLS-1$ + "STATUS", // 5 //$NON-NLS-1$ + "TIMING MARK", // 6 //$NON-NLS-1$ + "REMOTE CTRL TRANS+ECHO", // 7 //$NON-NLS-1$ + "OUTPUT LINE WIDTH", // 8 //$NON-NLS-1$ + "OUTPUT PAGE SIZE", // 9 //$NON-NLS-1$ + "OUTPUT CR DISPOSITION", // 10 //$NON-NLS-1$ + "OUTPUT HORIZ TABSTOPS", // 11 //$NON-NLS-1$ + "OUTPUT HORIZ TAB DISPOSITION", // 12 //$NON-NLS-1$ + "OUTPUT FORMFEED DISPOSITION", // 13 //$NON-NLS-1$ + "OUTPUT VERTICAL TABSTOPS", // 14 //$NON-NLS-1$ + "OUTPUT VT DISPOSITION", // 15 //$NON-NLS-1$ + "OUTPUT LF DISPOSITION", // 16 //$NON-NLS-1$ + "EXTENDED ASCII", // 17 //$NON-NLS-1$ + "LOGOUT", // 18 //$NON-NLS-1$ + "BYTE MACRO", // 19 //$NON-NLS-1$ + "DATA ENTRY TERMINAL", // 20 //$NON-NLS-1$ + "SUPDUP", // 21 //$NON-NLS-1$ + "SUPDUP OUTPUT", // 22 //$NON-NLS-1$ + "SEND LOCATION", // 23 //$NON-NLS-1$ + "TERMINAL TYPE", // 24 //$NON-NLS-1$ + "END OF RECORD", // 25 //$NON-NLS-1$ + "TACACS USER IDENTIFICATION", // 26 //$NON-NLS-1$ + "OUTPUT MARKING", // 27 //$NON-NLS-1$ + "TERMINAL LOCATION NUMBER", // 28 //$NON-NLS-1$ + "3270 REGIME", // 29 //$NON-NLS-1$ + "X.3 PAD", // 30 //$NON-NLS-1$ + "NEGOTIATE ABOUT WINDOW SIZE", // 31 //$NON-NLS-1$ + "TERMINAL SPEED", // 32 //$NON-NLS-1$ + "REMOTE FLOW CONTROL", // 33 //$NON-NLS-1$ + "LINEMODE", // 34 //$NON-NLS-1$ + "X DISPLAY LOCATION", // 35 //$NON-NLS-1$ + "ENVIRONMENT OPTION", // 36 //$NON-NLS-1$ + "AUTHENTICATION OPTION", // 37 //$NON-NLS-1$ + "ENCRYPTION OPTION", // 38 //$NON-NLS-1$ + "NEW ENVIRONMENT OPTION", // 39 //$NON-NLS-1$ + "TN3270E", // 40 //$NON-NLS-1$ + "XAUTH", // 41 //$NON-NLS-1$ + "CHARSET", // 42 //$NON-NLS-1$ + "REMOTE SERIAL PORT", // 43 //$NON-NLS-1$ + "COM PORT CONTROL OPTION", // 44 //$NON-NLS-1$ + "SUPPRESS LOCAL ECHO", // 45 //$NON-NLS-1$ + "START TLS", // 46 //$NON-NLS-1$ + "KERMIT", // 47 //$NON-NLS-1$ + "SEND URL", // 48 //$NON-NLS-1$ + "FORWARD X", // 49 //$NON-NLS-1$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 50 ... //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", // ... 137 //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ + "TELOPT PRAGMA LOGON", // 138 //$NON-NLS-1$ + "TELOPT SSPI LOGON", // 139 //$NON-NLS-1$ + "TELOPT PRAGMA HEARTBEAT", // 140 //$NON-NLS-1$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 141 ... //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ + "?", "?", "?", "?", // ... 254 //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "EXTENDED OPTIONS LIST" // 255 //$NON-NLS-1$ + }; + + /** + * Negotiation state: Negotiation not yet started for this option.

+ * + * This constant and the others having similar names represent the states of a + * finite state automaton (FSA) that tracks the negotiation state of this option. + * The initial state is NEGOTIATION_NOT_STARTED. The state machine is as follows + * (with transitions labelled with letters in parentheses):

+ * + *

+     *     NEGOTIATION_NOT_STARTED -----> {@link #NEGOTIATION_IN_PROGRESS}
+     *                         |    (A)      |        ^
+     *                      (C)|          (B)|        |(D)
+     *                         |             V        |
+     *                         +--------> {@link #NEGOTIATION_DONE}
+     * 

+ * + * Once the FSA leaves state NEGOTIATION_NOT_STARTED, it never returns to that + * state. Transition A happens when the local endpoint sends an option command + * before receiving a command for the same option from the remote endpoint.

+ * + * Transition B happens when the local endpoint receives a reply to an option + * command sent earlier by the local endpoint. Receipt of that reply terminates + * the negotiation.

+ * + * Transition D happens after negotiation is done and "something changes" (see the + * RFCs for the definition of "something changes"). Either endpoint can + * re-negotiate an option after a previous negotiation, but only if some external + * influence (such as the user or the OS) causes it to do so. Re-negotiation must + * start more than {@link #NEGOTIATION_IGNORE_DURATION} milliseconds after the FSA + * enters state NEGOTIATION_DONE or it will be ignored. This is how this client + * prevents negotiation loops.

+ * + * Transition C happens when the local endpoint receives an option command from the + * remote endpoint before sending a command for the same option. In that case, the + * local endpoint replies immediately with an option command and the negotitation + * terminates.

+ * + * Some TELNET servers (e.g., the Solaris server), after sending WILL and receiving + * DONT, will reply with a superfluous WONT. Any such superfluous option command + * received from the remote endpoint while the option's FSA is in state + * {@link #NEGOTIATION_DONE} will be ignored by the local endpoint. + */ + protected static final int NEGOTIATION_NOT_STARTED = 0; + + /** Negotiation state: Negotiation is in progress for this option. */ + protected static final int NEGOTIATION_IN_PROGRESS = 1; + + /** Negotiation state: Negotiation has terminated for this option. */ + protected static final int NEGOTIATION_DONE = 2; + + /** + * The number of milliseconds following the end of negotiation of this option + * before which the remote endpoint can re-negotiate the option. Any option + * command received from the remote endpoint before this time passes is ignored. + * This is used to prevent option negotiation loops. + * + * @see #ignoreNegotiation() + * @see #negotiationCompletionTime + */ + protected static final int NEGOTIATION_IGNORE_DURATION = 30000; + + /** + * This field holds the current negotiation state for this option. + */ + protected int negotiationState = NEGOTIATION_NOT_STARTED; + + /** + * This field holds the time when negotiation of this option most recently + * terminated (i.e., entered state {@link #NEGOTIATION_DONE}). This is used to + * determine whether an option command received from the remote endpoint after + * negotiation has terminated for this option is to be ignored or interpreted as + * the start of a new negotiation. + * + * @see #NEGOTIATION_IGNORE_DURATION + */ + protected Date negotiationCompletionTime = new Date(0); + + /** + * Holds the total number of negotiations that have completed for this option. + */ + protected int negotiationCount = 0; + + /** + * Holds the integer code representing the option. + */ + protected byte option = 0; + + /** + * Holds the OutputStream object that allows data to be sent to the remote endpoint + * of the TELNET connection. + */ + protected OutputStream outputStream; + + /** + * True if this option is for the local endpoint, false for the remote endpoint. + */ + protected boolean local = true; + + /** + * This field is true if the option is enabled, false if it is disabled. All + * options are initially disabled until they are negotiated to be enabled.

+ */ + protected boolean enabled = false; + + /** + * This field is true if the client desires the option to be enabled, false if the + * client desires the option to be disabled. This field does not represent the + * remote's endpoints desire (as expressed via WILL and WONT commands) -- it + * represnet the local endpoint's desire.

+ * + * @see #setDesired(boolean) + */ + protected boolean desired = false; + + /** + * Constructor.

+ * + * @param option The integer code of this option. + * @param desired Whether we desire this option to be enabled. + * @param local Whether this option is for the local or remote endpoint. + * @param outputStream A stream used to negotiate with the remote endpoint. + */ + TelnetOption(byte option, boolean desired, boolean local, OutputStream outputStream) + { + this.option = option; + this.desired = desired; + this.local = local; + this.outputStream = outputStream; + } + + /** + * @return Returns a String containing the name of the TELNET option specified in + * parameter option. + */ + public String optionName() + { + return optionNames[option]; + } + + /** + * Returns true if this option is enabled, false if it is disabled.

+ * + * @return Returns true if this option is enabled, false if it is disabled. + */ + public boolean isEnabled() + { + return enabled; + } + + /** + * Enables this option if newValue is true, otherwise disables this + * option.

+ * + * @param newValue True if this option is to be enabled, false otherwise. + */ + public void setEnabled(boolean newValue) + { + Logger.log("Enabling " + (local ? "local" : "remote") + " option " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + optionName()); + enabled = newValue; + } + + /** + * Returns true if the local endpoint desires this option to be enabled, false if + * not. It is not an error for the value returned by this method to differ from + * the value returned by isEnabled(). The value returned by this method can change + * over time, reflecting the local endpoint's changing desire regarding the + * option.

+ * + * NOTE: Even if this option represents a remote endpoint option, the return value + * of this method represents the local endpint's desire regarding the remote + * option.

+ * + * @return Returns true if the local endpoint desires this option to be enabled, + * false if not. + */ + public boolean isDesired() + { + return desired; + } + + /** + * Sets our desired value for this option. Note that the option can be desired + * when enabled is false, and the option can be undesired when + * enabled is true, though the latter state should not persist, since either + * endpoint can disable any option at any time.

+ * + * @param newValue True if we desire this option to be enabled, false if + * we desire this option to be disabled. + */ + public void setDesired(boolean newValue) + { + if (newValue) + Logger.log("Setting " + (local ? "local" : "remote") + " option " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + optionName() + " as desired."); //$NON-NLS-1$ + + desired = newValue; + } + + /** + * Call this method to request that negotiation begin for this option. This method + * does nothing if negotiation for this option has already started or is already + * complete. If negotiation has not yet started for this option and the local + * endpoint desires this option to be enabled, then we send a WILL or DO command to + * the remote endpoint. + */ + public void negotiate() + { + if (negotiationState == NEGOTIATION_NOT_STARTED && desired) + { + if (local) + { + Logger.log("Starting negotiation for local option " + optionName()); //$NON-NLS-1$ + sendWill(); + } + else + { + Logger.log("Starting negotiation for remote option " + optionName()); //$NON-NLS-1$ + sendDo(); + } + + negotiationState = NEGOTIATION_IN_PROGRESS; + } + } + + /** + * This method is called whenever we receive a WILL command from the remote + * endpoint. + */ + public void handleWill() + { + if (negotiationState == NEGOTIATION_DONE && ignoreNegotiation()) + { + Logger.log("Ignoring superfluous WILL command from remote endpoint."); //$NON-NLS-1$ + return; + } + + if (negotiationState == NEGOTIATION_IN_PROGRESS) + { + if (desired) + { + // We sent DO and server replied with WILL. Enable the option, and end + // this negotiation. + + enabled = true; + Logger.log("Enabling remote option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + else + { + // This should never happen! We sent DONT and the server replied with + // WILL. Bad server. No soup for you. Disable the option, and end + // this negotiation. + + Logger.log("Server answered DONT with WILL!"); //$NON-NLS-1$ + enabled = false; + Logger.log("Disabling remote option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + } + else + { + if (desired) + { + // Server sent WILL, so we reply with DO. Enable the option, and end + // this negotiation. + + sendDo(); + enabled = true; + Logger.log("Enabling remote option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + else + { + // Server sent WILL, so we reply with DONT. Disable the option, and + // end this negotiation. + + sendDont(); + enabled = false; + Logger.log("Disabling remote option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + } + } + + /** + * Handles a WONT command sent by the remote endpoint for this option. The value + * of desired doesn't matter in this method, because the remote endpoint is + * forcing the option to be disabled. + */ + public void handleWont() + { + if (negotiationState == NEGOTIATION_DONE && ignoreNegotiation()) + { + Logger.log("Ignoring superfluous WONT command from remote endpoint."); //$NON-NLS-1$ + return; + } + + if (negotiationState == NEGOTIATION_IN_PROGRESS) + { + // We sent DO or DONT and server replied with WONT. Disable the + // option, and end this negotiation. + + enabled = false; + Logger.log("Disabling remote option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + else + { + // Server sent WONT, so we reply with DONT. Disable the option, and + // end this negotiation. + + sendDont(); + enabled = false; + Logger.log("Disabling remote option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + } + + /** + * Handles a DO command sent by the remote endpoint for this option. + */ + public void handleDo() + { + if (negotiationState == NEGOTIATION_DONE && ignoreNegotiation()) + { + Logger.log("Ignoring superfluous DO command from remote endpoint."); //$NON-NLS-1$ + return; + } + + if (negotiationState == NEGOTIATION_IN_PROGRESS) + { + if (desired) + { + // We sent WILL and server replied with DO. Enable the option, and end + // this negotiation. + + enabled = true; + Logger.log("Enabling local option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + else + { + // We sent WONT and server replied with DO. This should never happen! + // Bad server. No soup for you. Disable the option, and end this + // negotiation. + + Logger.log("Server answered WONT with DO!"); //$NON-NLS-1$ + enabled = false; + Logger.log("Disabling local option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + } + else + { + if (desired) + { + // Server sent DO, so we reply with WILL. Enable the option, and end + // this negotiation. + + sendWill(); + enabled = true; + Logger.log("Enabling local option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + else + { + // Server sent DO, so we reply with WONT. Disable the option, and end + // this negotiation. + + sendWont(); + enabled = false; + Logger.log("Disabling local option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + } + } + + /** + * Handles a DONT command sent by the remote endpoint for this option. The value + * of desired doesn't matter in this method, because the remote endpoint is + * forcing the option to be disabled. + */ + public void handleDont() + { + if (negotiationState == NEGOTIATION_DONE && ignoreNegotiation()) + { + Logger.log("Ignoring superfluous DONT command from remote endpoint."); //$NON-NLS-1$ + return; + } + + if (negotiationState == NEGOTIATION_IN_PROGRESS) + { + // We sent WILL or WONT and server replied with DONT. Disable the + // option, and end this negotiation. + + enabled = false; + Logger.log("Disabling local option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + else + { + // Server sent DONT, so we reply with WONT. Disable the option, and end + // this negotiation. + + sendWont(); + enabled = false; + Logger.log("Disabling local option " + optionName() + "."); //$NON-NLS-1$ //$NON-NLS-2$ + endNegotiation(); + } + } + + /** + * This method handles a subnegotiation command received from the remote endpoint. + * Currently, the only subnegotiation we handle is when the remote endpoint + * commands us to send our terminal type (which is "ansi"). + * + * @param subnegotiationData An array of bytes containing a TELNET + * subnegotiation command received from the + * remote endpoint. + * @param count The number of bytes in array + * subnegotiationData to examine. + */ + public void handleSubnegotiation(byte[] subnegotiationData, int count) + { + switch (option) + { + case TELNET_OPTION_TERMINAL_TYPE: + if (subnegotiationData[1] != TELNET_SEND) + { + // This should never happen! + Logger.log("Invalid TERMINAL-TYPE subnegotiation command from remote endpoint: " + //$NON-NLS-1$ + (subnegotiationData[1] & 0xff)); + break; + } + + // Tell the remote endpoint our terminal type is "ansi" using this sequence + // of TELNET protocol bytes: + // + // IAC SB TERMINAL-TYPE IS a n s i IAC SE + + byte[] terminalTypeData = { TELNET_IAC, TELNET_SB, TELNET_OPTION_TERMINAL_TYPE, + TELNET_IS, (byte)'a', (byte)'n', (byte)'s', (byte)'i', + TELNET_IAC, TELNET_SE }; + + try + { + outputStream.write(terminalTypeData); + } + catch (IOException ex) + { + Logger.log("IOException sending TERMINAL-TYPE subnegotiation!"); //$NON-NLS-1$ + Logger.logException(ex); + } + break; + + default: + // This should never happen! + Logger.log("SHOULD NOT BE REACHED: Called for option " + optionName()); //$NON-NLS-1$ + assert false; + break; + } + } + + /** + * This method sends a subnegotiation command to the remote endpoint. + * + * @param subnegotiationData An array of Objects holding data to be used + * when generating the outbound subnegotiation + * command. + */ + public void sendSubnegotiation(Object[] subnegotiationData) + { + switch (option) + { + case TELNET_OPTION_NAWS: + // Get the width and height of the view and send it to the remote + // endpoint using this sequence of TELNET protocol bytes: + // + // IAC SB NAWS + // IAC SE + + byte[] NAWSData = { TELNET_IAC, TELNET_SB, TELNET_OPTION_NAWS, + 0, 0, 0, 0, TELNET_IAC, TELNET_SE }; + int width = ((Integer)subnegotiationData[0]).intValue(); + int height = ((Integer)subnegotiationData[1]).intValue(); + + NAWSData[3] = (byte)((width >>> 8) & 0xff); // High order byte of width. + NAWSData[4] = (byte)(width & 0xff); // Low order byte of width. + NAWSData[5] = (byte)((height >>> 8) & 0xff); // High order byte of height. + NAWSData[6] = (byte)(height & 0xff); // Low order byte of height. + + Logger.log("sending terminal size to remote endpoint: width = " + width + //$NON-NLS-1$ + ", height = " + height + "."); //$NON-NLS-1$ //$NON-NLS-2$ + + // This final local variable is a hack to get around the fact that inner + // classes cannot reference a non-final local variable in a lexically + // enclosing scope. + + final byte[] NAWSDataFinal = NAWSData; + + // Send the NAWS data in a new thread. The current thread is the display + // thread, and calls to write() can block, but blocking the display thread + // is _bad_ (it hangs the GUI). + + new Thread() + { + public void run() + { + try + { + outputStream.write(NAWSDataFinal); + } + catch (IOException ex) + { + Logger.log("IOException sending NAWS subnegotiation!"); //$NON-NLS-1$ + Logger.logException(ex); + } + } + }.start(); + break; + + default: + // This should never happen! + Logger.log("SHOULD NOT BE REACHED: Called for option " + optionName()); //$NON-NLS-1$ + assert false; + break; + } + } + + + /** + * This method returns true if there has not yet been any negotiation of this + * option. + * + * @return Returns true if there has not yet been any negotiation of this option. + */ + protected boolean notYetNegotiated() + { + return negotiationState == NEGOTIATION_NOT_STARTED; + } + + /** + * This method terminates the current negotiation and records the time at which the + * negotiation terminated. + */ + protected void endNegotiation() + { + Logger.log("Ending negotiation #" + negotiationCount + " for " + //$NON-NLS-1$ //$NON-NLS-2$ + (local ? "local" : "remote") + " option " + optionName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + negotiationState = NEGOTIATION_DONE; + negotiationCompletionTime.setTime(System.currentTimeMillis()); + ++negotiationCount; + } + + /** + * This method determines whether or not to ignore what appears to be a new + * negotiation initiated by the remote endpoint. This is needed because some + * TELNET servers send superfluous option commands that a naive client might + * interpret as the start of a new negotiation. If the superfluous command is not + * ignored, an option negotiation loop can result (which is bad). For details + * about the superfluous commands sent by some servers, see the documentation for + * {@link #NEGOTIATION_NOT_STARTED}.

+ * + * The current implementation of this method returns true if the new negotiation + * starts within NEGOTIATION_IGNORE_DURATION seconds of the end of the previous + * negotiation of this option.

+ * + * @return Returns true if the new negotiation should be ignored, false if not. + */ + protected boolean ignoreNegotiation() + { + return (System.currentTimeMillis() - negotiationCompletionTime.getTime()) > + NEGOTIATION_IGNORE_DURATION; + } + + /** + * Sends a DO command to the remote endpoint for this option. + */ + protected void sendDo() + { + Logger.log("Sending DO " + optionName()); //$NON-NLS-1$ + sendCommand(TELNET_DO); + } + + /** + * Sends a DONT command to the remote endpoint for this option. + */ + protected void sendDont() + { + Logger.log("Sending DONT " + optionName()); //$NON-NLS-1$ + sendCommand(TELNET_DONT); + } + + /** + * Sends a WILL command to the remote endpoint for this option. + */ + protected void sendWill() + { + Logger.log("Sending WILL " + optionName()); //$NON-NLS-1$ + sendCommand(TELNET_WILL); + } + + /** + * Sends a WONT command to the remote endpoint for this option. + */ + protected void sendWont() + { + Logger.log("Sending WONT " + optionName()); //$NON-NLS-1$ + sendCommand(TELNET_WONT); + } + + /** + * This method sends a WILL/WONT/DO/DONT command to the remote endpoint for this + * option. + */ + protected void sendCommand(byte command) + { + byte[] data = { TELNET_IAC, 0, 0 }; + + data[1] = command; + data[2] = option; + + try + { + outputStream.write(data); + } + catch (IOException ex) + { + Logger.log("IOException sending command " + command); //$NON-NLS-1$ + Logger.logException(ex); + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalAction.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalAction.java new file mode 100644 index 00000000000..e1b786d4867 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalAction.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + + +package org.eclipse.tm.terminal; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; + +public class TerminalAction extends Action + implements TerminalMsg, TerminalConsts +{ + /** + * + */ + protected TerminalTarget m_Target; + protected String m_strMsg; + + /** + * + */ + public TerminalAction(TerminalTarget target, + String strMsg, + String strId) + { + super(""); //$NON-NLS-1$ + + m_Target = target; + m_strMsg = strMsg; + + setId(strId); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Action interface + // + + /** + * + */ + public void run() + { + m_Target.execute(m_strMsg,this); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Operations + // + + /** + * + */ + protected void setupAction(String strText, + String strToolTip, + String strImage, + String strEnabledImage, + String strDisabledImage, + boolean bEnabled) + { + TerminalPlugin plugin; + ImageRegistry imageRegistry; + + plugin = TerminalPlugin.getDefault(); + imageRegistry = plugin.getImageRegistry(); + setupAction(strText, + strToolTip, + strImage, + strEnabledImage, + strDisabledImage, + bEnabled, + imageRegistry); + } + + /** + * + */ + protected void setupAction(String strText, + String strToolTip, + String strImage, + String strEnabledImage, + String strDisabledImage, + boolean bEnabled, + ImageRegistry imageRegistry) + { + ImageDescriptor imageDescriptor; + + setText(strText); + setToolTipText(strToolTip); + setEnabled(bEnabled); + + imageDescriptor = imageRegistry.getDescriptor(strEnabledImage); + if (imageDescriptor != null) + { + setImageDescriptor(imageDescriptor); + } + + imageDescriptor = imageRegistry.getDescriptor(strDisabledImage); + if (imageDescriptor != null) + { + setDisabledImageDescriptor(imageDescriptor); + } + + imageDescriptor = imageRegistry.getDescriptor(strImage); + if (imageDescriptor != null) + { + setHoverImageDescriptor(imageDescriptor); + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionClearAll.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionClearAll.java new file mode 100644 index 00000000000..889715198e7 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionClearAll.java @@ -0,0 +1,34 @@ + +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + + +package org.eclipse.tm.terminal; + +public class TerminalActionClearAll extends TerminalAction +{ + protected TerminalActionClearAll(TerminalTarget target) + { + super(target, + ON_EDIT_CLEARALL, + TerminalActionClearAll.class.getName()); + + setupAction(TERMINAL_TEXT_CLEARALL, + TERMINAL_TEXT_CLEARALL, + null, + null, + null, + false); + } +} + + diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionConnect.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionConnect.java new file mode 100644 index 00000000000..078338e1c34 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionConnect.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + + +package org.eclipse.tm.terminal; + +public class TerminalActionConnect extends TerminalAction +{ + protected TerminalActionConnect(TerminalTarget target) + { + super(target, + ON_TERMINAL_CONNECT, + TerminalActionConnect.class.getName()); + + setupAction(TERMINAL_TEXT_CONNECT, + TERMINAL_TEXT_CONNECT, + TERMINAL_IMAGE_CLCL_CONNECT, + TERMINAL_IMAGE_ELCL_CONNECT, + TERMINAL_IMAGE_DLCL_CONNECT, + true); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCopy.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCopy.java new file mode 100644 index 00000000000..47c900f8a11 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCopy.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.internal.WorkbenchImages; + +public class TerminalActionCopy extends TerminalAction +{ + protected TerminalActionCopy(TerminalTarget target) + { + super(target, + ON_EDIT_COPY, + TerminalActionCopy.class.getName()); + + ImageRegistry imageRegistry; + + imageRegistry = WorkbenchImages.getImageRegistry(); + setupAction(TERMINAL_TEXT_COPY, + TERMINAL_TEXT_COPY, + ISharedImages.IMG_TOOL_COPY, + ISharedImages.IMG_TOOL_COPY, + ISharedImages.IMG_TOOL_COPY_DISABLED, + true, + imageRegistry); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCut.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCut.java new file mode 100644 index 00000000000..2a06c87748d --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionCut.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.internal.WorkbenchImages; + +public class TerminalActionCut extends TerminalAction +{ + protected TerminalActionCut(TerminalTarget target) + { + super(target, + ON_EDIT_CUT, + TerminalActionCut.class.getName()); + + ImageRegistry imageRegistry; + + imageRegistry = WorkbenchImages.getImageRegistry(); + setupAction(TERMINAL_TEXT_CUT, + TERMINAL_TEXT_CUT, + ISharedImages.IMG_TOOL_CUT, + ISharedImages.IMG_TOOL_CUT, + ISharedImages.IMG_TOOL_CUT_DISABLED, + true, + imageRegistry); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionDisconnect.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionDisconnect.java new file mode 100644 index 00000000000..e1f294da96e --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionDisconnect.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +public class TerminalActionDisconnect extends TerminalAction +{ + /** + * + */ + protected TerminalActionDisconnect(TerminalTarget target) + { + super(target, + ON_TERMINAL_DISCONNECT, + TerminalActionDisconnect.class.getName()); + + setupAction(TERMINAL_TEXT_DISCONNECT, + TERMINAL_TEXT_DISCONNECT, + TERMINAL_IMAGE_CLCL_DISCONNECT, + TERMINAL_IMAGE_ELCL_DISCONNECT, + TERMINAL_IMAGE_DLCL_DISCONNECT, + false); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionNewTerminal.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionNewTerminal.java new file mode 100644 index 00000000000..80bfdccadb6 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionNewTerminal.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +/** + * UNDER CONSTRUCTION + * + * @author Fran Litterio + */ +public class TerminalActionNewTerminal extends TerminalAction +{ + /** + * UNDER CONSTRUCTION + */ + protected TerminalActionNewTerminal(TerminalTarget target) + { + super(target, ON_TERMINAL_NEW_TERMINAL, TerminalActionNewTerminal.class.getName()); + + setupAction(TERMINAL_TEXT_NEW_TERMINAL, + TERMINAL_TEXT_NEW_TERMINAL, + TERMINAL_IMAGE_NEW_TERMINAL, + TERMINAL_IMAGE_NEW_TERMINAL, + TERMINAL_IMAGE_NEW_TERMINAL, + true); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionPaste.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionPaste.java new file mode 100644 index 00000000000..6d14e5612dd --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionPaste.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.internal.WorkbenchImages; + +public class TerminalActionPaste extends TerminalAction +{ + protected TerminalActionPaste(TerminalTarget target) + { + super(target, + ON_EDIT_PASTE, + TerminalActionPaste.class.getName()); + + ImageRegistry imageRegistry; + + imageRegistry = WorkbenchImages.getImageRegistry(); + setupAction(TERMINAL_TEXT_PASTE, + TERMINAL_TEXT_PASTE, + ISharedImages.IMG_TOOL_PASTE, + ISharedImages.IMG_TOOL_PASTE_DISABLED, + ISharedImages.IMG_TOOL_PASTE, + false, + imageRegistry); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSelectAll.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSelectAll.java new file mode 100644 index 00000000000..19903bda345 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSelectAll.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +public class TerminalActionSelectAll extends TerminalAction +{ + protected TerminalActionSelectAll(TerminalTarget target) + { + super(target, + ON_EDIT_SELECTALL, + TerminalActionSelectAll.class.getName()); + + setupAction(TERMINAL_TEXT_SELECTALL, + TERMINAL_TEXT_SELECTALL, + null, + null, + null, + false); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSettings.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSettings.java new file mode 100644 index 00000000000..b0389b62cb1 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalActionSettings.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +public class TerminalActionSettings extends TerminalAction +{ + protected TerminalActionSettings(TerminalTarget target) + { + super(target, + ON_TERMINAL_SETTINGS, + TerminalActionSettings.class.getName()); + + setupAction(TERMINAL_TEXT_SETTINGS_ELLIPSE, + TERMINAL_TEXT_SETTINGS, + TERMINAL_IMAGE_CLCL_SETTINGS, + TERMINAL_IMAGE_ELCL_SETTINGS, + TERMINAL_IMAGE_DLCL_SETTINGS, + true); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalConsts.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalConsts.java new file mode 100644 index 00000000000..f8968d42572 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalConsts.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +public interface TerminalConsts +{ + public static final String TERMINAL_CONNTYPE_SERIAL = "Serial"; //$NON-NLS-1$ + public static final String TERMINAL_CONNTYPE_NETWORK = "Network"; //$NON-NLS-1$ + + public final static String TERMINAL_IMAGE_DIR_ROOT = "icons/"; //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_CTOOL = "ctool16/"; // basic colors - size 16x16 //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_LOCALTOOL = "clcl16/"; // basic colors - size 16x16 //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_DLCL = "dlcl16/"; // disabled - size 16x16 //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_ELCL = "elcl16/"; // enabled - size 16x16 //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_OBJECT = "obj16/"; // basic colors - size 16x16 //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_WIZBAN = "wizban/"; // basic colors - size 16x16 //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_OVR = "ovr16/"; // basic colors - size 7x8 //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_VIEW = "cview16/"; // views //$NON-NLS-1$ + public final static String TERMINAL_IMAGE_DIR_EVIEW = "eview16/"; // views //$NON-NLS-1$ + + public static final String TERMINAL_IMAGE_NEW_TERMINAL = "TerminalViewNewTerminal"; //$NON-NLS-1$ + public static final String TERMINAL_IMAGE_CLCL_CONNECT = "ImageClclConnect"; //$NON-NLS-1$ + public static final String TERMINAL_IMAGE_CLCL_DISCONNECT = "ImageClclDisconnect"; //$NON-NLS-1$ + public static final String TERMINAL_IMAGE_CLCL_SETTINGS = "ImageClclSettings"; //$NON-NLS-1$ + + public static final String TERMINAL_IMAGE_DLCL_CONNECT = "ImageDlclConnect"; //$NON-NLS-1$ + public static final String TERMINAL_IMAGE_DLCL_DISCONNECT = "ImageDlclDisconnect"; //$NON-NLS-1$ + public static final String TERMINAL_IMAGE_DLCL_SETTINGS = "ImageDlclSettings"; //$NON-NLS-1$ + + public static final String TERMINAL_IMAGE_ELCL_CONNECT = "ImageElclConnect"; //$NON-NLS-1$ + public static final String TERMINAL_IMAGE_ELCL_DISCONNECT = "ImageElclDisconnect"; //$NON-NLS-1$ + public static final String TERMINAL_IMAGE_ELCL_SETTINGS = "ImageElclSettings"; //$NON-NLS-1$ + + public static final String TERMINAL_PROP_TITLE = Messages.getString("TerminalConsts.Terminal_7"); //$NON-NLS-1$ + public static final String TERMINAL_PROP_NAMENET = "net"; //$NON-NLS-1$ + public static final String TERMINAL_PROP_NAMETGTCONST = "tgtcons"; //$NON-NLS-1$ + public static final String TERMINAL_PROP_NAMETELNET = "telnet"; //$NON-NLS-1$ + public static final String TERMINAL_PROP_VALUENET = "1233"; //$NON-NLS-1$ + public static final String TERMINAL_PROP_VALUETGTCONST = "1232"; //$NON-NLS-1$ + public static final String TERMINAL_PROP_VALUETELNET = "23"; //$NON-NLS-1$ + + public static final String TERMINAL_PREF_LIMITOUTPUT = "TerminalPrefLimitOutput"; //$NON-NLS-1$ + public static final String TERMINAL_PREF_BUFFERLINES = "TerminalPrefBufferLines"; //$NON-NLS-1$ + public static final String TERMINAL_PREF_TIMEOUT_SERIAL = "TerminalPrefTimeoutSerial"; //$NON-NLS-1$ + public static final String TERMINAL_PREF_TIMEOUT_NETWORK = "TerminalPrefTimeoutNetwork"; //$NON-NLS-1$ + + public static final String PLUGIN_HOME = "org.eclipse.tm.terminal"; //$NON-NLS-1$ + public static final String HELP_VIEW = PLUGIN_HOME + ".terminal_view"; //$NON-NLS-1$ + + public static final String TERMINAL_TEXT_NEW_TERMINAL = Messages.getString("TerminalConsts.New_terminal"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_CONNECT = Messages.getString("TerminalConsts.Connect_2"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_DISCONNECT = Messages.getString("TerminalConsts.Disconnect_3"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_SETTINGS_ELLIPSE = Messages.getString("TerminalConsts.Settings..._4"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_COPY = Messages.getString("TerminalConsts.Copy_5"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_CUT = Messages.getString("TerminalConsts.0"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_PASTE = Messages.getString("TerminalConsts.Paste_6"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_SELECTALL = Messages.getString("TerminalConsts.Select_All_7"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_CLEARALL = Messages.getString("TerminalConsts.Clear_All_8"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_TERMINALSETTINGS = Messages.getString("TerminalConsts.Terminal_Settings_1"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_CONNECTIONTYPE = Messages.getString("TerminalConsts.Connection_Type_2"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_SETTINGS = Messages.getString("TerminalConsts.Settings_3"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_PORT = Messages.getString("TerminalConsts.Port_4"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_BAUDRATE = Messages.getString("TerminalConsts.Baud_Rate_5"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_DATABITS = Messages.getString("TerminalConsts.Data_Bits_6"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_STOPBITS = Messages.getString("TerminalConsts.Stop_Bits_7"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_PARITY = Messages.getString("TerminalConsts.Parity_8"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_FLOWCONTROL = Messages.getString("TerminalConsts.1"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_HOST = Messages.getString("TerminalConsts.Host_11"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_LIMITOUTPUT = Messages.getString("TerminalConsts.Limit_terminal_output_16"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_BUFFERLINES = Messages.getString("TerminalConsts.Terminal_buffer_lines__17"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_SERIALTIMEOUT = Messages.getString("TerminalConsts.Serial_timeout_(seconds)__18"); //$NON-NLS-1$ + public static final String TERMINAL_TEXT_NETWORKTIMEOUT = Messages.getString("TerminalConsts.Network_timeout_(seconds)__19"); //$NON-NLS-1$ + + public static final String TERMINAL_MSG_ERROR_1 = Messages.getString("TerminalConsts.Terminal_Error_12"); //$NON-NLS-1$ + public static final String TERMINAL_MSG_ERROR_2 = Messages.getString("TerminalConsts.Socket_Error_13"); //$NON-NLS-1$ + public static final String TERMINAL_MSG_ERROR_3 = Messages.getString("TerminalConsts.IO_Error_14"); //$NON-NLS-1$ + public static final String TERMINAL_MSG_ERROR_4 = Messages.getString("TerminalConsts.Serial_port___{0}___is_currently_in_use_!_nDo_you_want_to_close_the_port__15"); //$NON-NLS-1$ + public static final String TERMINAL_MSG_ERROR_5 = Messages.getString("TerminalConsts.Error_16"); //$NON-NLS-1$ + public static final String TERMINAL_MSG_ERROR_6 = Messages.getString("TerminalConsts.Emulator_is_not_supported._17"); //$NON-NLS-1$ + + public static final String TERMINAL_FONT_DEFINITION = "terminal.views.view.font.definition"; //$NON-NLS-1$ + + public static final String TERMINAL_TRACE_DEBUG_LOG = "org.eclipse.tm.terminal/debug/log"; //$NON-NLS-1$ + public static final String TERMINAL_TRACE_DEBUG_LOG_ERROR = "org.eclipse.tm.terminal/debug/log/error"; //$NON-NLS-1$ + public static final String TERMINAL_TRACE_DEBUG_LOG_INFO = "org.eclipse.tm.terminal/debug/log/info"; //$NON-NLS-1$ + public static final String TERMINAL_TRACE_DEBUG_LOG_CHAR = "org.eclipse.tm.terminal/debug/log/char"; //$NON-NLS-1$ + public static final String TERMINAL_TRACE_DEBUG_LOG_BUFFER_SIZE = "org.eclipse.tm.terminal/debug/log/buffer/size"; //$NON-NLS-1$ + + public static final boolean TERMINAL_DEFAULT_LIMITOUTPUT = true; + public static final int TERMINAL_DEFAULT_BUFFERLINES = 1000; + public static final int TERMINAL_DEFAULT_TIMEOUT_SERIAL = 5; + public static final int TERMINAL_DEFAULT_TIMEOUT_NETWORK = 5; + + public static final int TERMINAL_ID_OK = 0; + public static final int TERMINAL_ID_CANCEL = 1; + public static final int TERMINAL_ID_CONNECT = 2; + + public static final int TERMINAL_KEY_ESCAPE = 27; + public static final int TERMINAL_KEY_H = 104; + public static final int TERMINAL_KEY_J = 106; + public static final int TERMINAL_KEY_K = 107; + public static final int TERMINAL_KEY_L = 108; + public static final int TERMINAL_KEY_CR = 13; +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalCtrl.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalCtrl.java new file mode 100644 index 00000000000..0423376d1f1 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalCtrl.java @@ -0,0 +1,1573 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.net.ConnectException; +import java.text.MessageFormat; + +import javax.comm.CommPortIdentifier; +import javax.comm.CommPortOwnershipListener; +import javax.comm.PortInUseException; +import javax.comm.SerialPort; +import javax.comm.SerialPortEvent; +import javax.comm.SerialPortEventListener; + +import org.eclipse.core.runtime.Preferences; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.ConfigurableLineTracker; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.TextViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.custom.VerifyKeyListener; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.part.ViewPart; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.keys.IBindingService; + +/** + * UNDER CONSTRUCTION + * + * This class was originally written to use nested classes, which unfortunately makes + * this source file larger and more complex than it needs to be. In particular, the + * methods in the nested classes directly access the fields of the enclosing class. + * One day we should pull the nested classes out into their own source files (but still + * in this package). + * + * @author Chris Thew + */ +class TerminalCtrl implements TerminalTarget, TerminalConsts +{ + /** + * UNDER CONSTRUCTION + */ + protected final static String[] LINE_DELIMITERS = { "\n" }; //$NON-NLS-1$ + + /** + * This field holds a reference to a TerminalText object that performs all ANSI + * text processing on data received from the remote host and controls how text is + * displayed using the view's StyledText widget. + */ + protected TerminalText m_TerminalText; + + protected Display m_Display; + protected StyledText m_ctlText; + protected TextViewer m_Viewer; + protected Composite m_wndParent; + protected CommPortIdentifier m_SerialPortIdentifier; + protected SerialPort m_SerialPort; + protected Socket m_Socket; + protected InputStream m_InputStream; + protected OutputStream m_OutputStream; + protected Clipboard m_Clipboard; + protected TerminalSerialPortHandler m_SerialPortHandler; + protected VerifyListener m_VerifyHandler; + protected TerminalModifyListener m_ModifyListener; + protected KeyListener m_KeyHandler; + protected TerminalSettings m_TerminalSettings; + protected TerminalTarget m_Target; + protected ViewPart m_ViewPart; + protected String m_strMsg = ""; //$NON-NLS-1$ + protected boolean m_bConnecting = false; + protected boolean m_bConnected = false; + protected boolean m_bOpened = false; + protected boolean m_bPortInUse = false; + protected boolean m_bCaretReset = false; + protected TelnetConnection m_telnetConnection; + protected VerifyKeyListener m_VerifyKeyListener; + protected FocusListener m_FocusListener; + + /** + * UNDER CONSTRUCTION + */ + public TerminalCtrl(TerminalTarget target, Composite wndParent) + throws Exception + { + super(); + + m_Target = target; + m_ViewPart = (ViewPart)target; + m_wndParent = wndParent; + + try + { + m_TerminalText = new TerminalText(this); + } + catch (Exception ex) + { + Logger.logException(ex); + + throw ex; + } + + setupTerminal(); + } + + /** + * UNDER CONSTRUCTION + */ + public void execute(String strMsg, Object data) + { + if (strMsg.equals(ON_TERMINAL_CONNECT)) + { + onTerminalConnect(data); + } + else if (strMsg.equals(ON_TERMINAL_CONNECTING)) + { + onTerminalConnecting(data); + } + else if (strMsg.equals(ON_TERMINAL_DISCONNECT)) + { + onTerminalDisconnect(data); + } + else if (strMsg.equals(ON_TERMINAL_STATUS)) + { + onTerminalStatus(data); + } + } + + /** + * UNDER CONSTRUCTION + */ + protected void onTerminalConnect(Object data) + { + m_Target.execute(ON_TERMINAL_CONNECT, data); + } + + /** + * UNDER CONSTRUCTION + */ + protected void onTerminalConnecting(Object data) + { + m_Target.execute(ON_TERMINAL_CONNECTING, data); + } + + /** + * UNDER CONSTRUCTION + */ + protected void onTerminalDisconnect(Object data) + { + m_Target.execute(ON_TERMINAL_DISCONNECT, data); + } + + /** + * UNDER CONSTRUCTION + */ + protected void onTerminalStatus(Object data) + { + m_Target.execute(ON_TERMINAL_STATUS, data); + } + + /** + * UNDER CONSTRUCTION + */ + public void copy() + { + m_ctlText.copy(); + } + + /** + * UNDER CONSTRUCTION + */ + public void paste() + { + TextTransfer textTransfer; + String strText; + + textTransfer = TextTransfer.getInstance(); + strText = (String)m_Clipboard.getContents(textTransfer); + + if (strText == null) + return; + + for (int i=0;i tags are present in the plugin.xml file + // for the Terminal view. Do not delete those tags. + + switch (event.keyCode) + { + case 0x1000001: // Up arrow. + sendString("\u001b[A"); //$NON-NLS-1$ + break; + + case 0x1000002: // Down arrow. + sendString("\u001b[B"); //$NON-NLS-1$ + break; + + case 0x1000003: // Left arrow. + sendString("\u001b[D"); //$NON-NLS-1$ + break; + + case 0x1000004: // Right arrow. + sendString("\u001b[C"); //$NON-NLS-1$ + break; + + case 0x1000005: // PgUp key. + sendString("\u001b[I"); //$NON-NLS-1$ + break; + + case 0x1000006: // PgDn key. + sendString("\u001b[G"); //$NON-NLS-1$ + break; + + case 0x1000007: // Home key. + sendString("\u001b[H"); //$NON-NLS-1$ + break; + + case 0x1000008: // End key. + sendString("\u001b[F"); //$NON-NLS-1$ + break; + + case 0x100000a: // F1 key. + sendString("\u001b[M"); //$NON-NLS-1$ + break; + + case 0x100000b: // F2 key. + sendString("\u001b[N"); //$NON-NLS-1$ + break; + + case 0x100000c: // F3 key. + sendString("\u001b[O"); //$NON-NLS-1$ + break; + + case 0x100000d: // F4 key. + sendString("\u001b[P"); //$NON-NLS-1$ + break; + + case 0x100000e: // F5 key. + sendString("\u001b[Q"); //$NON-NLS-1$ + break; + + case 0x100000f: // F6 key. + sendString("\u001b[R"); //$NON-NLS-1$ + break; + + case 0x1000010: // F7 key. + sendString("\u001b[S"); //$NON-NLS-1$ + break; + + case 0x1000011: // F8 key. + sendString("\u001b[T"); //$NON-NLS-1$ + break; + + case 0x1000012: // F9 key. + sendString("\u001b[U"); //$NON-NLS-1$ + break; + + case 0x1000013: // F10 key. + sendString("\u001b[V"); //$NON-NLS-1$ + break; + + case 0x1000014: // F11 key. + sendString("\u001b[W"); //$NON-NLS-1$ + break; + + case 0x1000015: // F12 key. + sendString("\u001b[X"); //$NON-NLS-1$ + break; + + default: + // Ignore other special keys. Control flows through this case when + // the user presses SHIFT, CONTROL, ALT, and any other key not + // handled by the above cases. + break; + } + + // It's ok to return here, because we never locally echo special keys. + + return; + } + + // To fix SPR 110341, we consider the Alt key to be pressed only when the + // Ctrl key is _not_ also pressed. This works around a bug in SWT where, + // on European keyboards, the AltGr key being pressed appears to us as Ctrl + // + Alt being pressed simultaneously. + + Logger.log("stateMask = " + event.stateMask); //$NON-NLS-1$ + + boolean altKeyPressed = (((event.stateMask & SWT.ALT) != 0) && + ((event.stateMask & SWT.CTRL) == 0)); + + if (!altKeyPressed && (event.stateMask & SWT.CTRL) != 0 && character == ' ') + { + // Send a NUL character -- many terminal emulators send NUL when + // Ctrl-Space is pressed. This is used to set the mark in Emacs. + + character='\u0000'; + } + + sendChar(character, altKeyPressed); + + // Special case: When we are in a TCP connection and echoing characters + // locally, send a LF after sending a CR. + // ISSUE: Is this absolutely required? + + if (character == '\r' && + m_telnetConnection != null && + m_telnetConnection.isConnected() && + m_telnetConnection.localEcho()) + { + sendChar('\n', false); + } + + // Now decide if we should locally echo the character we just sent. We do + // _not_ locally echo the character if any of these conditions are true: + // + // o This is a serial connection. + // + // o This is a TCP connection (i.e., m_telnetConnection is not null) and + // the remote endpoint is not a TELNET server. + // + // o The ALT (or META) key is pressed. + // + // o The character is any of the first 32 ISO Latin-1 characters except + // Control-I or Control-M. + // + // o The character is the DELETE character. + + if (m_telnetConnection == null || + m_telnetConnection.isConnected() == false || + m_telnetConnection.localEcho() == false || + altKeyPressed || + (character >= '\u0001' && character < '\t') || + (character > '\t' && character < '\r') || + (character > '\r' && character <= '\u001f') || + character == '\u007f') + { + // No local echoing. + return; + } + + // Locally echo the character. + + StringBuffer charBuffer = new StringBuffer(); + charBuffer.append(character); + + // If the character is a carriage return, we locally echo it as a CR + LF + // combination. + + if (character == '\r') + charBuffer.append('\n'); + + m_TerminalText.setNewText(charBuffer); + m_Display.syncExec(m_TerminalText); + } + } + + /** + * UNDER CONSTRUCTION + */ + protected class TerminalSerialPortHandler implements SerialPortEventListener, + CommPortOwnershipListener + { + protected byte[] bytes = new byte[2048]; + + /** + * UNDER CONSTRUCTION + */ + protected TerminalSerialPortHandler() + { + super(); + } + + // Message handlers + + /** + * UNDER CONSTRUCTION + */ + protected void onSerialDataAvailable(Object data) + { + Display display; + StringBuffer buffer; + String strBuffer; + int nBytes; + + display = m_ctlText.getDisplay(); + + try + { + while (m_InputStream != null && m_InputStream.available() > 0) + { + nBytes = m_InputStream.read(bytes); + strBuffer = new String(bytes, 0, nBytes); + buffer = new StringBuffer(strBuffer); + + m_TerminalText.setNewText(buffer); + + // Do _not_ use asyncExec() here. Class TerminalText requires that + // its run() and setNewText() methods be called in strictly + // alternating order. If we were to call asyncExec() here, this + // loop might race around and call setNewText() twice in a row, + // which would lose data. + + display.syncExec(m_TerminalText); + } + } + catch (IOException ex) + { + displayTextInTerminal(ex.getMessage()); + } + catch (Exception exception) + { + Logger.logException(exception); + } + } + + /** + * UNDER CONSTRUCTION + */ + protected void onSerialOwnershipRequested(Object data) + { + TerminalSerialOwnershipRequestedWorker ownershipRequestedWorker; + Display display; + + if (m_bPortInUse) + { + m_bPortInUse = false; + return; + } + + display = m_ctlText.getDisplay(); + ownershipRequestedWorker = new TerminalSerialOwnershipRequestedWorker(); + display.asyncExec(ownershipRequestedWorker); + } + + // SerialPortEventListener interface + + /** + * UNDER CONSTRUCTION + */ + public void serialEvent(SerialPortEvent event) + { + switch (event.getEventType()) + { + case SerialPortEvent.DATA_AVAILABLE: + onSerialDataAvailable(null); + break; + } + } + + // CommPortOwnershipListener interface + + /** + * UNDER CONSTRUCTION + */ + public void ownershipChange(int nType) + { + switch (nType) + { + case CommPortOwnershipListener.PORT_OWNERSHIP_REQUESTED: + onSerialOwnershipRequested(null); + break; + } + } + } + + /** + * UNDER CONSTRUCTION + */ + protected class TerminalDocument extends Document + { + /** + * UNDER CONSTRUCTION + */ + protected TerminalDocument() + { + super(); + + setupDocument(); + } + + // Operations + + /** + * UNDER CONSTRUCTION + */ + protected void setupDocument() + { + ConfigurableLineTracker lineTracker; + + lineTracker = new ConfigurableLineTracker(LINE_DELIMITERS); + setLineTracker(lineTracker); + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalMsg.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalMsg.java new file mode 100644 index 00000000000..158832aeb24 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalMsg.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +public interface TerminalMsg +{ + public static final String ON_TERMINAL_FOCUS = "OnTerminalFocus"; //$NON-NLS-1$ + public static final String ON_TERMINAL_NEW_TERMINAL = "OnTerminalNew"; //$NON-NLS-1$ + public static final String ON_TERMINAL_CONNECT = "OnTerminalConnect"; //$NON-NLS-1$ + public static final String ON_TERMINAL_CONNECTING = "OnTerminalConnecting"; //$NON-NLS-1$ + public static final String ON_TERMINAL_DISCONNECT = "OnTerminalDisconnect"; //$NON-NLS-1$ + public static final String ON_TERMINAL_SETTINGS = "OnTerminalSettings"; //$NON-NLS-1$ + public static final String ON_TERMINAL_STATUS = "OnTerminalStatus"; //$NON-NLS-1$ + public static final String ON_TERMINAL_DATAAVAILABLE = "OnTerminalDataAvailable"; //$NON-NLS-1$ + public static final String ON_TERMINAL_FONTCHANGED = "OnTerminalFontChanged"; //$NON-NLS-1$ + public static final String ON_EDIT_COPY = "OnEditCopy"; //$NON-NLS-1$ + public static final String ON_EDIT_CUT = "OnEditCut"; //$NON-NLS-1$ + public static final String ON_EDIT_PASTE = "OnEditPaste"; //$NON-NLS-1$ + public static final String ON_EDIT_CLEARALL = "OnEditClearAll"; //$NON-NLS-1$ + public static final String ON_EDIT_SELECTALL = "OnEditSelectAll"; //$NON-NLS-1$ + public static final String ON_UPDATE_TERMINAL_CONNECT = "OnUpdateTerminalConnect"; //$NON-NLS-1$ + public static final String ON_UPDATE_TERMINAL_DISCONNECT = "OnUpdateTerminalDisconnect"; //$NON-NLS-1$ + public static final String ON_UPDATE_TERMINAL_SETTINGS = "OnUpdateTerminalSettings"; //$NON-NLS-1$ + public static final String ON_UPDATE_EDIT_COPY = "OnUpdateEditCopy"; //$NON-NLS-1$ + public static final String ON_UPDATE_EDIT_CUT = "OnUpdateEditCut"; //$NON-NLS-1$ + public static final String ON_UPDATE_EDIT_PASTE = "OnUpdateEditPaste"; //$NON-NLS-1$ + public static final String ON_UPDATE_EDIT_CLEARALL = "OnUpdateEditClearAll"; //$NON-NLS-1$ + public static final String ON_UPDATE_EDIT_SELECTALL = "OnUpdateEditSelectAll"; //$NON-NLS-1$ + public static final String ON_CONNTYPE_SELECTED = "OnConnTypeSelected"; //$NON-NLS-1$ + public static final String ON_LIMITOUTPUT_SELECTED = "OnLimitOutputSelected"; //$NON-NLS-1$ + public static final String ON_OK = "OnOK"; //$NON-NLS-1$ + public static final String ON_CANCEL = "OnCancel"; //$NON-NLS-1$ + public static final String ON_HELP = "OnHelp"; //$NON-NLS-1$ +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalNetworkPortMap.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalNetworkPortMap.java new file mode 100644 index 00000000000..47392cacf26 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalNetworkPortMap.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Set; +import java.util.Vector; + + +public class TerminalNetworkPortMap extends HashMap + implements TerminalConsts +{ + static final long serialVersionUID = 0; + + public TerminalNetworkPortMap() + { + super(); + + setupMap(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Operations + // + + /** + * + */ + public String getDefaultNetworkPort() + { + return (String) get(TERMINAL_PROP_NAMETELNET); + } + + /** + * + */ + public String findPortName(String strPort) + { + Collection values; + Vector portTable; + Vector nameTable; + String strPortName; + int nIndex; + + values = values(); + portTable = new Vector(values); + nIndex = portTable.indexOf(strPort); + nameTable = getNameTable(); + + if (nIndex == -1) + return strPort; + + strPortName = (String) nameTable.get(nIndex); + return strPortName; + } + + /** + * + */ + public String findPort(String strPortName) + { + String strPort; + + strPort = (String) get(strPortName); + if (strPort == null) + return strPortName; + + return strPort; + } + + /** + * + */ + public Vector getNameTable() + { + Set keySet; + Vector nameTable; + + keySet = keySet(); + nameTable = new Vector(keySet); + + return nameTable; + } + + /** + * + */ + protected void setupMap() + { + put(TERMINAL_PROP_NAMETGTCONST, TERMINAL_PROP_VALUETGTCONST); + put(TERMINAL_PROP_NAMETELNET, TERMINAL_PROP_VALUETELNET); + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPlugin.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPlugin.java new file mode 100644 index 00000000000..c7489fa4032 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPlugin.java @@ -0,0 +1,294 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +public class TerminalPlugin extends AbstractUIPlugin + implements TerminalConsts +{ + protected static TerminalPlugin m_Default; + + protected TerminalProperties m_Properties; + protected ResourceBundle m_ResourceBundle; + + /** + * The constructor. + */ + public TerminalPlugin() + { + super(); + + m_Default = this; + + setupPlugin(); + } + + public void start(BundleContext context) throws Exception + { + super.start(context); + } + + public void stop(BundleContext context) throws Exception + { + super.stop(context); + } + + // AbstractUIPlugin interface + + /** + * + */ + protected void initializeImageRegistry(ImageRegistry imageRegistry) + { + HashMap map; + + map = new HashMap(); + + try + { + // Local toolbars + map.put(TERMINAL_IMAGE_NEW_TERMINAL, "newterminal.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_CLCL_CONNECT, "connect_co.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_CLCL_DISCONNECT, "disconnect_co.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_CLCL_SETTINGS, "properties_tsk.gif"); //$NON-NLS-1$ + + loadImageRegistry(imageRegistry, TERMINAL_IMAGE_DIR_LOCALTOOL, map); + + map.clear(); + + // Enabled local toolbars + map.put(TERMINAL_IMAGE_NEW_TERMINAL, "newterminal.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_ELCL_CONNECT, "connect_co.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_ELCL_DISCONNECT, "disconnect_co.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_ELCL_SETTINGS, "properties_tsk.gif"); //$NON-NLS-1$ + + loadImageRegistry(imageRegistry, TERMINAL_IMAGE_DIR_ELCL, map); + + map.clear(); + + // Disabled local toolbars + map.put(TERMINAL_IMAGE_NEW_TERMINAL, "newterminal.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_DLCL_CONNECT, "connect_co.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_DLCL_DISCONNECT, "disconnect_co.gif"); //$NON-NLS-1$ + map.put(TERMINAL_IMAGE_DLCL_SETTINGS, "properties_tsk.gif"); //$NON-NLS-1$ + + loadImageRegistry(imageRegistry, TERMINAL_IMAGE_DIR_DLCL, map); + + map.clear(); + } + catch(MalformedURLException malformedURLException) + { + malformedURLException.printStackTrace(); + } + } + + /** + * + */ + protected void initializeDefaultPreferences(IPreferenceStore store) + { + store.setDefault(TERMINAL_PREF_LIMITOUTPUT, TERMINAL_DEFAULT_LIMITOUTPUT); + store.setDefault(TERMINAL_PREF_BUFFERLINES, TERMINAL_DEFAULT_BUFFERLINES); + store.setDefault(TERMINAL_PREF_TIMEOUT_SERIAL, TERMINAL_DEFAULT_TIMEOUT_SERIAL); + store.setDefault(TERMINAL_PREF_TIMEOUT_NETWORK, TERMINAL_DEFAULT_TIMEOUT_NETWORK); + } + + // Operations + + /** + * Returns the shared instance. + */ + public static TerminalPlugin getDefault() + { + return m_Default; + } + + /** + * Returns the workspace instance. + */ + public static IWorkspace getWorkspace() + { + return ResourcesPlugin.getWorkspace(); + } + + /** + * Returns the string from the plugin's resource bundle, or 'key' if not found. + */ + public static String getResourceString(String strKey) + { + ResourceBundle resourceBundle; + + resourceBundle = m_Default.getResourceBundle(); + + try + { + return resourceBundle.getString(strKey); + } + catch(MissingResourceException missingResourceException) + { + return strKey; + } + } + + /** + * + */ + public static boolean isLogInfoEnabled() + { + return isOptionEnabled(TERMINAL_TRACE_DEBUG_LOG_INFO); + } + + /** + * + */ + public static boolean isLogErrorEnabled() + { + return isOptionEnabled(TERMINAL_TRACE_DEBUG_LOG_ERROR); + } + + /** + * + */ + public static boolean isLogEnabled() + { + return isOptionEnabled(TERMINAL_TRACE_DEBUG_LOG); + } + + /** + * + */ + public static boolean isOptionEnabled(String strOption) + { + String strEnabled; + Boolean boolEnabled; + boolean bEnabled; + + strEnabled = Platform.getDebugOption(strOption); + if (strEnabled == null) + return false; + + boolEnabled = new Boolean(strEnabled); + bEnabled = boolEnabled.booleanValue(); + + return bEnabled; + } + + /** + * + */ + public TerminalProperties getTerminalProperties() + { + return m_Properties; + } + + /** + * Returns the plugin's resource bundle, + */ + public ResourceBundle getResourceBundle() + { + return m_ResourceBundle; + } + + /** + * + */ + protected void loadImageRegistry(ImageRegistry imageRegistry, + String strDir, + HashMap map) + throws MalformedURLException + { + URL url; + ImageDescriptor imageDescriptor; + Iterator keys; + String strKey; + String strFile; + + keys = map.keySet().iterator(); + + while(keys.hasNext()) + { + strKey = (String) keys.next(); + strFile = (String) map.get(strKey); + + if (strFile != null) + { + url = TerminalPlugin.getDefault().getBundle().getEntry(TERMINAL_IMAGE_DIR_ROOT + strDir + strFile); + imageDescriptor = ImageDescriptor.createFromURL(url); + imageRegistry.put(strKey,imageDescriptor); + } + } + } + + /** + * + */ + protected void setupPlugin() + { + setupData(); + setupLog(); + setupResources(); + } + + /** + * + */ + protected void setupData() + { + m_Properties = new TerminalProperties(); + } + + /** + * + */ + protected void setupLog() + { + } + + /** + * + */ + protected void setupResources() + { + Package pkg; + String strPkg; + String strBundle; + + pkg = TerminalPlugin.class.getPackage(); + strPkg = pkg.getName(); + strBundle = strPkg + ".PluginResources"; //$NON-NLS-1$ + + try + { + m_ResourceBundle = ResourceBundle.getBundle(strBundle); + } + catch(MissingResourceException missingResourceException) + { + m_ResourceBundle = null; + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPreferencePage.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPreferencePage.java new file mode 100644 index 00000000000..4071b47f6d5 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalPreferencePage.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +public class TerminalPreferencePage extends FieldEditorPreferencePage + implements IWorkbenchPreferencePage, + TerminalTarget, + TerminalConsts +{ + /** + * + */ + protected TerminalBooleanFieldEditor m_editorLimitOutput; + protected IntegerFieldEditor m_editorBufferSize; + protected IntegerFieldEditor m_editorSerialTimeout; + protected IntegerFieldEditor m_editorNetworkTimeout; + + /** + * + */ + public TerminalPreferencePage() + { + super(GRID); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // TerminalTarget interface + // + + /** + * + */ + public void execute(String strMsg,Object data) + { + if (strMsg.equals(ON_LIMITOUTPUT_SELECTED)) + { + onLimitOutputSelected(data); + } + else + { + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Message handlers + // + + /** + * + */ + protected void onLimitOutputSelected(Object data) + { + Button ctlButton; + Text ctlText; + Label ctlLabel; + boolean bEnabled; + + ctlButton = m_editorLimitOutput.getChangeControl(getFieldEditorParent()); + ctlText = m_editorBufferSize.getTextControl(getFieldEditorParent()); + ctlLabel = m_editorBufferSize.getLabelControl(getFieldEditorParent()); + bEnabled = ctlButton.getSelection(); + + ctlText.setEnabled(bEnabled); + ctlLabel.setEnabled(bEnabled); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FieldEditorPreferencePage interface + // + + /** + * + */ + protected void createFieldEditors() + { + setupPage(); + } + + /** + * + */ + protected void initialize() + { + super.initialize(); + + execute(ON_LIMITOUTPUT_SELECTED,null); + } + + /** + * + */ + protected void performDefaults() + { + super.performDefaults(); + + execute(ON_LIMITOUTPUT_SELECTED,null); + } + + /** + * + */ + public void init(IWorkbench workbench) + { + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Operations + // + + /** + * + */ + protected void setupPage() + { + setupData(); + setupEditors(); + setupListeners(); + } + + /** + * + */ + protected void setupData() + { + TerminalPlugin plugin; + IPreferenceStore preferenceStore; + + plugin = TerminalPlugin.getDefault(); + preferenceStore = plugin.getPreferenceStore(); + setPreferenceStore(preferenceStore); + } + + /** + * + */ + protected void setupEditors() + { + m_editorLimitOutput = new TerminalBooleanFieldEditor(TERMINAL_PREF_LIMITOUTPUT, + TERMINAL_TEXT_LIMITOUTPUT, + getFieldEditorParent()); + m_editorBufferSize = new IntegerFieldEditor(TERMINAL_PREF_BUFFERLINES, + TERMINAL_TEXT_BUFFERLINES, + getFieldEditorParent()); + m_editorSerialTimeout = new IntegerFieldEditor(TERMINAL_PREF_TIMEOUT_SERIAL, + TERMINAL_TEXT_SERIALTIMEOUT, + getFieldEditorParent()); + m_editorNetworkTimeout = new IntegerFieldEditor(TERMINAL_PREF_TIMEOUT_NETWORK, + TERMINAL_TEXT_NETWORKTIMEOUT, + getFieldEditorParent()); + + m_editorBufferSize.setValidRange(0,Integer.MAX_VALUE); + m_editorSerialTimeout.setValidRange(0,Integer.MAX_VALUE); + m_editorNetworkTimeout.setValidRange(0,Integer.MAX_VALUE); + + addField(m_editorLimitOutput); + addField(m_editorBufferSize); + addField(m_editorSerialTimeout); + addField(m_editorNetworkTimeout); + } + + /** + * + */ + protected void setupListeners() + { + TerminalSelectionHandler selectionHandler; + Button ctlButton; + + selectionHandler = new TerminalSelectionHandler(); + ctlButton = m_editorLimitOutput.getChangeControl(getFieldEditorParent()); + ctlButton.addSelectionListener(selectionHandler); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Inner classes + // + + /** + * + */ + public class TerminalBooleanFieldEditor extends BooleanFieldEditor + { + /** + * + */ + public TerminalBooleanFieldEditor(String strName, + String strLabel, + Composite ctlParent) + { + super(strName,strLabel,ctlParent); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // BooleanFieldEditor interface + // + + /** + * + */ + public Button getChangeControl(Composite parent) + { + return super.getChangeControl(parent); + } + } + + + /** + * + */ + protected class TerminalSelectionHandler extends SelectionAdapter + { + /** + * + */ + protected TerminalSelectionHandler() + { + super(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // SelectionAdapter interface + // + + /** + * + */ + public void widgetSelected(SelectionEvent event) + { + Object source; + Button ctlButton; + + source = event.getSource(); + ctlButton = m_editorLimitOutput.getChangeControl(getFieldEditorParent()); + + if (source == ctlButton) + { + execute(ON_LIMITOUTPUT_SELECTED,null); + } + } + + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalProperties.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalProperties.java new file mode 100644 index 00000000000..54435d95ff0 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalProperties.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Vector; + +import javax.comm.CommPortIdentifier; + +public class TerminalProperties implements TerminalConsts +{ + protected TerminalNetworkPortMap m_NetworkPortMap; + protected Vector m_ConnTypeTable; + protected Vector m_SerialPortTable; + protected Vector m_BaudRateTable; + protected Vector m_DataBitsTable; + protected Vector m_StopBitsTable; + protected Vector m_ParityTable; + protected Vector m_FlowControlTable; + protected String m_strDefaultConnType; + protected String m_strDefaultSerialPort; + protected String m_strDefaultBaudRate; + protected String m_strDefaultDataBits; + protected String m_strDefaultStopBits; + protected String m_strDefaultParity; + protected String m_strDefaultFlowControl; + protected String m_strDefaultHost; + protected String m_strDefaultNetworkPort; + + /** + * + */ + public TerminalProperties() + { + super(); + + setupProperties(); + } + + //////////////////////////////////////////////////////////////////////////// + // Operations + // + + /** + * + */ + public Vector getConnTypeTable() + { + return m_ConnTypeTable; + } + + /** + * + */ + public Vector getSerialPortTable() + { + return m_SerialPortTable; + } + + /** + * + */ + public Vector getBaudRateTable() + { + return m_BaudRateTable; + } + + /** + * + */ + public Vector getDataBitsTable() + { + return m_DataBitsTable; + } + + /** + * + */ + public Vector getStopBitsTable() + { + return m_StopBitsTable; + } + + /** + * + */ + public Vector getParityTable() + { + return m_ParityTable; + } + + /** + * + */ + public Vector getFlowControlTable() + { + return m_FlowControlTable; + } + + /** + * + */ + public TerminalNetworkPortMap getNetworkPortMap() + { + return m_NetworkPortMap; + } + + /** + * + */ + public String getDefaultConnType() + { + return m_strDefaultConnType; + } + + /** + * + */ + public String getDefaultSerialPort() + { + return m_strDefaultSerialPort; + } + + /** + * + */ + public String getDefaultBaudRate() + { + return m_strDefaultBaudRate; + } + + /** + * + */ + public String getDefaultDataBits() + { + return m_strDefaultDataBits; + } + + /** + * + */ + public String getDefaultStopBits() + { + return m_strDefaultStopBits; + } + + /** + * + */ + public String getDefaultParity() + { + return m_strDefaultParity; + } + + /** + * + */ + public String getDefaultFlowControl() + { + return m_strDefaultFlowControl; + } + + /** + * + */ + public String getDefaultHost() + { + return m_strDefaultHost; + } + + /** + * + */ + public String getDefaultNetworkPort() + { + return m_strDefaultNetworkPort; + } + + /** + * + */ + protected void setupProperties() + { + Enumeration portIdEnum; + CommPortIdentifier identifier; + String strName; + int nPortType; + + portIdEnum = CommPortIdentifier.getPortIdentifiers(); + m_NetworkPortMap = new TerminalNetworkPortMap(); + m_ConnTypeTable = new Vector(); + m_SerialPortTable = new Vector(); + m_BaudRateTable = new Vector(); + m_DataBitsTable = new Vector(); + m_StopBitsTable = new Vector(); + m_ParityTable = new Vector(); + m_FlowControlTable = new Vector(); + m_strDefaultConnType = ""; //$NON-NLS-1$ + m_strDefaultSerialPort = ""; //$NON-NLS-1$ + m_strDefaultBaudRate = ""; //$NON-NLS-1$ + m_strDefaultDataBits = ""; //$NON-NLS-1$ + m_strDefaultStopBits = ""; //$NON-NLS-1$ + m_strDefaultParity = ""; //$NON-NLS-1$ + m_strDefaultFlowControl = ""; //$NON-NLS-1$ + m_strDefaultHost = ""; //$NON-NLS-1$ + m_strDefaultNetworkPort = ""; //$NON-NLS-1$ + + m_ConnTypeTable.add(TERMINAL_CONNTYPE_SERIAL); + m_ConnTypeTable.add(TERMINAL_CONNTYPE_NETWORK); + + m_BaudRateTable.add("300"); //$NON-NLS-1$ + m_BaudRateTable.add("1200"); //$NON-NLS-1$ + m_BaudRateTable.add("2400"); //$NON-NLS-1$ + m_BaudRateTable.add("4800"); //$NON-NLS-1$ + m_BaudRateTable.add("9600"); //$NON-NLS-1$ + m_BaudRateTable.add("19200"); //$NON-NLS-1$ + m_BaudRateTable.add("38400"); //$NON-NLS-1$ + m_BaudRateTable.add("57600"); //$NON-NLS-1$ + m_BaudRateTable.add("115200"); //$NON-NLS-1$ + + m_DataBitsTable.add("5"); //$NON-NLS-1$ + m_DataBitsTable.add("6"); //$NON-NLS-1$ + m_DataBitsTable.add("7"); //$NON-NLS-1$ + m_DataBitsTable.add("8"); //$NON-NLS-1$ + + m_StopBitsTable.add("1"); //$NON-NLS-1$ + m_StopBitsTable.add("1_5"); //$NON-NLS-1$ + m_StopBitsTable.add("2"); //$NON-NLS-1$ + + m_ParityTable.add("None"); //$NON-NLS-1$ + m_ParityTable.add("Even"); //$NON-NLS-1$ + m_ParityTable.add("Odd"); //$NON-NLS-1$ + m_ParityTable.add("Mark"); //$NON-NLS-1$ + m_ParityTable.add("Space"); //$NON-NLS-1$ + + m_FlowControlTable.add("None"); //$NON-NLS-1$ + m_FlowControlTable.add("RTS/CTS"); //$NON-NLS-1$ + m_FlowControlTable.add("Xon/Xoff"); //$NON-NLS-1$ + + m_strDefaultNetworkPort = m_NetworkPortMap.getDefaultNetworkPort(); + m_strDefaultConnType = (String) m_ConnTypeTable.get(0); + m_strDefaultBaudRate = (String) m_BaudRateTable.get(4); + m_strDefaultDataBits = (String) m_DataBitsTable.get(3); + m_strDefaultStopBits = (String) m_StopBitsTable.get(0); + m_strDefaultParity = (String) m_ParityTable.get(0); + m_strDefaultFlowControl = (String) m_FlowControlTable.get(0); + m_strDefaultHost = ""; //$NON-NLS-1$ + + while(portIdEnum.hasMoreElements()) + { + identifier = (CommPortIdentifier)portIdEnum.nextElement(); + strName = identifier.getName(); + nPortType = identifier.getPortType(); + + if (nPortType == CommPortIdentifier.PORT_SERIAL) + m_SerialPortTable.addElement(strName); + } + + Collections.sort(m_SerialPortTable); + + if (!m_SerialPortTable.isEmpty()) + { + m_strDefaultSerialPort = (String) m_SerialPortTable.get(0); + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettings.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettings.java new file mode 100644 index 00000000000..516ec202843 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettings.java @@ -0,0 +1,425 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import javax.comm.SerialPort; + +import org.eclipse.jface.dialogs.IDialogSettings; + +public class TerminalSettings + implements TerminalConsts +{ + protected String m_strConnType; + protected String m_strSerialPort; + protected String m_strBaudRate; + protected String m_strDataBits; + protected String m_strStopBits; + protected String m_strParity; + protected String m_strFlowControl; + protected String m_strHost; + protected String m_strNetworkPort; + + /** + * + */ + public TerminalSettings(String terminalPartName) + { + importSettings(terminalPartName); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Operations + // + + /** + * + */ + public String getConnType() + { + return m_strConnType; + } + + /** + * + */ + public void setConnType(String strConnType) + { + m_strConnType = strConnType; + } + + /** + * + */ + public String getSerialPort() + { + return m_strSerialPort; + } + + /** + * + */ + public void setSerialPort(String strSerialPort) + { + m_strSerialPort = strSerialPort; + } + + /** + * + */ + public String getBaudRate() + { + return m_strBaudRate; + } + + /** + * + */ + public int getBaudRateValue() + { + int nBaudRate; + + try + { + nBaudRate = Integer.parseInt(m_strBaudRate); + } + catch(NumberFormatException numberFormatException) + { + nBaudRate = 9600; + } + + return nBaudRate; + } + + /** + * + */ + public void setBaudRate(String strBaudRate) + { + m_strBaudRate = strBaudRate; + } + + /** + * + */ + public String getDataBits() + { + return m_strDataBits; + } + + /** + * + */ + public int getDataBitsValue() + { + if (m_strDataBits.equals("5")) //$NON-NLS-1$ + { + return SerialPort.DATABITS_5; + } + else if (m_strDataBits.equals("6")) //$NON-NLS-1$ + { + return SerialPort.DATABITS_6; + } + else if (m_strDataBits.equals("7")) //$NON-NLS-1$ + { + return SerialPort.DATABITS_7; + } + else // 8 + { + return SerialPort.DATABITS_8; + } + } + + /** + * + */ + public void setDataBits(String strDataBits) + { + m_strDataBits = strDataBits; + } + + /** + * + */ + public String getStopBits() + { + return m_strStopBits; + } + + /** + * + */ + public int getStopBitsValue() + { + if (m_strStopBits.equals("1_5")) //$NON-NLS-1$ + { + return SerialPort.STOPBITS_1_5; + } + else if (m_strStopBits.equals("2")) //$NON-NLS-1$ + { + return SerialPort.STOPBITS_2; + } + else // 1 + { + return SerialPort.STOPBITS_1; + } + } + + /** + * + */ + public void setStopBits(String strStopBits) + { + m_strStopBits = strStopBits; + } + + /** + * + */ + public String getParity() + { + return m_strParity; + } + + /** + * + */ + public int getParityValue() + { + if (m_strParity.equals("Even")) //$NON-NLS-1$ + { + return SerialPort.PARITY_EVEN; + } + else if (m_strParity.equals("Odd")) //$NON-NLS-1$ + { + return SerialPort.PARITY_ODD; + } + else if (m_strParity.equals("Mark")) //$NON-NLS-1$ + { + return SerialPort.PARITY_MARK; + } + else if (m_strParity.equals("Space")) //$NON-NLS-1$ + { + return SerialPort.PARITY_SPACE; + } + else // None + { + return SerialPort.PARITY_NONE; + } + } + + /** + * + */ + public void setParity(String strParity) + { + m_strParity = strParity; + } + + /** + * + */ + public String getFlowControl() + { + return m_strFlowControl; + } + + /** + * + */ + public int getFlowControlValue() + { + if (m_strFlowControl.equals("RTS/CTS")) //$NON-NLS-1$ + { + return SerialPort.FLOWCONTROL_RTSCTS_IN; + } + else if (m_strFlowControl.equals("Xon/Xoff")) //$NON-NLS-1$ + { + return SerialPort.FLOWCONTROL_XONXOFF_IN; + } + else // None + { + return SerialPort.FLOWCONTROL_NONE; + } + } + + /** + * + */ + public void setFlowControl(String strFlow) + { + m_strFlowControl = strFlow; + } + + /** + * + */ + public String getHost() + { + return m_strHost; + } + + /** + * + */ + public void setHost(String strHost) + { + m_strHost = strHost; + } + + /** + * + */ + public String getNetworkPort() + { + return m_strNetworkPort; + } + + /** + * + */ + public int getNetworkPortValue() + { + int nNetworkPort; + + try + { + nNetworkPort = Integer.parseInt(m_strNetworkPort); + } + catch(NumberFormatException numberFormatException) + { + nNetworkPort = 1313; + } + + return nNetworkPort; + } + + /** + * + */ + public void setNetworkPort(String strNetworkPort) + { + m_strNetworkPort = strNetworkPort; + } + + /** + * + */ + public void importSettings(String terminalPartName) + { + TerminalPlugin plugin; + TerminalProperties properties; + + plugin = TerminalPlugin.getDefault(); + properties = plugin.getTerminalProperties(); + m_strConnType = importSetting(terminalPartName, "ConnType", //$NON-NLS-1$ + properties.getDefaultConnType()); + m_strSerialPort = importSetting(terminalPartName, "SerialPort", //$NON-NLS-1$ + properties.getDefaultSerialPort()); + m_strBaudRate = importSetting(terminalPartName, "BaudRate", //$NON-NLS-1$ + properties.getDefaultBaudRate()); + m_strDataBits = importSetting(terminalPartName, "DataBits", //$NON-NLS-1$ + properties.getDefaultDataBits()); + m_strStopBits = importSetting(terminalPartName, "StopBits", //$NON-NLS-1$ + properties.getDefaultStopBits()); + m_strParity = importSetting(terminalPartName, "Parity", //$NON-NLS-1$ + properties.getDefaultParity()); + m_strFlowControl = importSetting(terminalPartName, "FlowControl", //$NON-NLS-1$ + properties.getDefaultFlowControl()); + m_strHost = importSetting(terminalPartName, "Host", //$NON-NLS-1$ + properties.getDefaultHost()); + m_strNetworkPort = importSetting(terminalPartName, "NetworkPort", //$NON-NLS-1$ + properties.getDefaultNetworkPort()); + } + + /** + * + */ + public void exportSettings(String terminalPartName) + { + TerminalPlugin plugin; + TerminalProperties properties; + + plugin = TerminalPlugin.getDefault(); + properties = plugin.getTerminalProperties(); + + exportSetting(terminalPartName, "ConnType", m_strConnType, //$NON-NLS-1$ + properties.getDefaultConnType()); + exportSetting(terminalPartName, "SerialPort", m_strSerialPort, //$NON-NLS-1$ + properties.getDefaultSerialPort()); + exportSetting(terminalPartName, "BaudRate", m_strBaudRate, //$NON-NLS-1$ + properties.getDefaultBaudRate()); + exportSetting(terminalPartName, "DataBits", m_strDataBits, //$NON-NLS-1$ + properties.getDefaultDataBits()); + exportSetting(terminalPartName, "StopBits", m_strStopBits, //$NON-NLS-1$ + properties.getDefaultStopBits()); + exportSetting(terminalPartName, "Parity", m_strParity, //$NON-NLS-1$ + properties.getDefaultParity()); + exportSetting(terminalPartName, "FlowControl", m_strFlowControl, //$NON-NLS-1$ + properties.getDefaultFlowControl()); + exportSetting(terminalPartName, "Host", m_strHost, //$NON-NLS-1$ + properties.getDefaultHost()); + exportSetting(terminalPartName, "NetworkPort", m_strNetworkPort, //$NON-NLS-1$ + properties.getDefaultNetworkPort()); + } + + /** + * + */ + protected String importSetting(String terminalPartName, String strName, String strDefault) + { + TerminalPlugin plugin; + IDialogSettings settings; + String strPrefix; + String strKey; + String strValue; + + plugin = TerminalPlugin.getDefault(); + settings = plugin.getDialogSettings(); + strPrefix = TerminalSettings.class.getName() + "."; //$NON-NLS-1$ + strKey = strPrefix + terminalPartName + "." + strName; //$NON-NLS-1$ + strValue = settings.get(strKey); + + if ((strValue == null) || + (strValue.equals(""))) //$NON-NLS-1$ + return strDefault; + + return strValue; + } + + /** + * + */ + protected void exportSetting(String terminalPartName, String strName, String strValue, + String strDefault) + { + TerminalPlugin plugin; + IDialogSettings settings; + String strPrefix; + String strKey; + + plugin = TerminalPlugin.getDefault(); + settings = plugin.getDialogSettings(); + strPrefix = TerminalSettings.class.getName() + "."; //$NON-NLS-1$ + strKey = strPrefix + terminalPartName + "." + strName; //$NON-NLS-1$ + + if ((strValue == null) || + (strValue.equals(""))) //$NON-NLS-1$ + { + settings.put(strKey,strDefault); + } + else + { + settings.put(strKey,strValue); + } + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettingsDlg.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettingsDlg.java new file mode 100644 index 00000000000..88d00c2ea9e --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalSettingsDlg.java @@ -0,0 +1,911 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Vector; + +import javax.comm.CommPortIdentifier; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + + +public class TerminalSettingsDlg extends org.eclipse.jface.dialogs.Dialog + implements TerminalTarget, + TerminalConsts +{ + protected static final String m_strHelpID = "hid_db_terminal"; //$NON-NLS-1$ + + protected Combo m_ctlConnTypeCombo; + protected Combo m_ctlSerialPortCombo; + protected Combo m_ctlBaudRateCombo; + protected Combo m_ctlDataBitsCombo; + protected Combo m_ctlStopBitsCombo; + protected Combo m_ctlParityCombo; + protected Combo m_ctlFlowControlCombo; + protected Text m_ctlHostText; + protected Combo m_ctlNetworkPortCombo; + protected Group m_wndSettingsGroup; + protected Composite m_wndSettingsPanel; + protected TerminalSettings m_TerminalSettings; + protected String m_strConnType; + protected int m_nStatus; + + /** + * + */ + public TerminalSettingsDlg(Shell wndParent) + { + super(wndParent); + + m_nStatus = TERMINAL_ID_CANCEL; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // TerminalTarget interface + // + + /** + * + */ + public void execute(String strMsg,Object data) + { + if (strMsg.equals(ON_CONNTYPE_SELECTED)) + { + onConnTypeSelected(data); + } + else if (strMsg.equals(ON_OK)) + { + onOk(data); + } + else if (strMsg.equals(ON_CANCEL)) + { + onCancel(data); + } + else if (strMsg.equals(ON_HELP)) + { + onHelp(data); + } + else + { + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Message handlers + // + + /** + * + */ + protected void onConnTypeSelected(Object data) + { + String strConnType; + + strConnType = m_ctlConnTypeCombo.getText(); + if (m_strConnType.equals(strConnType)) + return; + + m_strConnType = strConnType; + + if (strConnType.equals(TERMINAL_CONNTYPE_SERIAL)) + { + setupSerialPanel(); + } + else if (strConnType.equals(TERMINAL_CONNTYPE_NETWORK)) + { + setupNetworkPanel(); + } + else + { + setupConnTypeNotSupportedPanel(); + } + } + + /** + * + */ + protected void onOk(Object data) + { + if (!validateSettings()) + return; + + saveSettings(); + m_nStatus = TERMINAL_ID_CONNECT; + } + + /** + * + */ + protected void onCancel(Object data) + { + m_nStatus = TERMINAL_ID_CANCEL; + } + + /** + * + */ + protected void onHelp(Object data) + { + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Dialog interface + // + + /** + * + */ + protected void okPressed() + { + execute(ON_OK,null); + super.okPressed(); + } + + /** + * + */ + protected void cancelPressed() + { + execute(ON_CANCEL,null); + super.cancelPressed(); + + } + + /** + * + */ + public int open() + { + int nShellStyle; + + nShellStyle = getShellStyle(); + nShellStyle = nShellStyle|SWT.RESIZE; + setShellStyle(nShellStyle); + + return super.open(); + } + + /** + * + */ + protected void configureShell(Shell newShell) + { + super.configureShell(newShell); + + newShell.setText(TERMINAL_TEXT_TERMINALSETTINGS); + } + + /** + * + */ + protected Control createDialogArea(Composite parent) + { + Composite ctlComposite; + + ctlComposite = (Composite)super.createDialogArea(parent); + createDialog(ctlComposite); + + return ctlComposite; + } + + /** + * + */ + protected void initializeBounds() + { + setConnType(TERMINAL_CONNTYPE_SERIAL); + execute(ON_CONNTYPE_SELECTED,null); + super.initializeBounds(); + setConnType(m_TerminalSettings.getConnType()); + execute(ON_CONNTYPE_SELECTED,null); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Operations + // + + /** + * + */ + public void loadSettings(TerminalSettings terminalSettings) + { + m_TerminalSettings = terminalSettings; + } + + /** + * + */ + public void saveSettings() + { + String strConnType; + + strConnType = getConnType(); + + if (strConnType.equals(TERMINAL_CONNTYPE_SERIAL)) + { + m_TerminalSettings.setConnType(getConnType()); + m_TerminalSettings.setSerialPort(getSerialPort()); + m_TerminalSettings.setBaudRate(getBaudRate()); + m_TerminalSettings.setDataBits(getDataBits()); + m_TerminalSettings.setStopBits(getStopBits()); + m_TerminalSettings.setParity(getParity()); + m_TerminalSettings.setFlowControl(getFlowControl()); + } + else if (strConnType.equals(TERMINAL_CONNTYPE_NETWORK)) + { + m_TerminalSettings.setConnType(getConnType()); + m_TerminalSettings.setHost(getHost()); + m_TerminalSettings.setNetworkPort(getNetworkPort()); + } + } + + /** + * + */ + public int getStatus() + { + return m_nStatus; + } + + /** + * + */ + protected void setConnType(String strConnType) + { + int nIndex; + + nIndex = m_ctlConnTypeCombo.indexOf(strConnType); + if (nIndex == -1) + return; + + m_ctlConnTypeCombo.select(nIndex); + } + + /** + * + */ + protected void setSerialPort(String strSerialPort) + { + int nIndex; + + nIndex = m_ctlSerialPortCombo.indexOf(strSerialPort); + if (nIndex == -1) + return; + + m_ctlSerialPortCombo.select(nIndex); + } + + /** + * + */ + protected void setBaudRate(String strBaudRate) + { + int nIndex; + + nIndex = m_ctlBaudRateCombo.indexOf(strBaudRate); + if (nIndex == -1) + return; + + m_ctlBaudRateCombo.select(nIndex); + } + + /** + * + */ + protected void setDataBits(String strDataBits) + { + int nIndex; + + nIndex = m_ctlDataBitsCombo.indexOf(strDataBits); + if (nIndex == -1) + return; + + m_ctlDataBitsCombo.select(nIndex); + } + + /** + * + */ + protected void setStopBits(String strStopBits) + { + int nIndex; + + nIndex = m_ctlStopBitsCombo.indexOf(strStopBits); + if (nIndex == -1) + return; + + m_ctlStopBitsCombo.select(nIndex); + } + + /** + * + */ + protected void setParity(String strParity) + { + int nIndex; + + nIndex = m_ctlParityCombo.indexOf(strParity); + if (nIndex == -1) + return; + + m_ctlParityCombo.select(nIndex); + } + + /** + * + */ + protected void setFlowIn(String strFlowIn) + { + int nIndex; + + nIndex = m_ctlFlowControlCombo.indexOf(strFlowIn); + if (nIndex == -1) + return; + + m_ctlFlowControlCombo.select(nIndex); + } + + /** + * + */ + protected void setHost(String strHost) + { + m_ctlHostText.setText(strHost); + } + + /** + * + */ + protected void setNetworkPort(String strNetworkPort) + { + TerminalPlugin plugin; + TerminalProperties properties; + TerminalNetworkPortMap networkPortMap; + String strPortName; + int nIndex; + + plugin = TerminalPlugin.getDefault(); + properties = plugin.getTerminalProperties(); + networkPortMap = properties.getNetworkPortMap(); + strPortName = networkPortMap.findPortName(strNetworkPort); + nIndex = m_ctlNetworkPortCombo.indexOf(strPortName); + + if (nIndex == -1) + { + m_ctlNetworkPortCombo.setText(strNetworkPort); + } + else + { + m_ctlNetworkPortCombo.select(nIndex); + } + } + + /** + * + */ + protected String getConnType() + { + int nIndex; + + nIndex = m_ctlConnTypeCombo.getSelectionIndex(); + if (nIndex == -1) + return ""; //$NON-NLS-1$ + + return m_ctlConnTypeCombo.getItem(nIndex); + } + + /** + * + */ + protected String getSerialPort() + { + int nIndex; + + nIndex = m_ctlSerialPortCombo.getSelectionIndex(); + if (nIndex == -1) + return ""; //$NON-NLS-1$ + + return m_ctlSerialPortCombo.getItem(nIndex); + } + + /** + * + */ + protected String getBaudRate() + { + int nIndex; + + nIndex = m_ctlBaudRateCombo.getSelectionIndex(); + if (nIndex == -1) + return ""; //$NON-NLS-1$ + + return m_ctlBaudRateCombo.getItem(nIndex); + } + + /** + * + */ + protected String getDataBits() + { + int nIndex; + + nIndex = m_ctlDataBitsCombo.getSelectionIndex(); + if (nIndex == -1) + return ""; //$NON-NLS-1$ + + return m_ctlDataBitsCombo.getItem(nIndex); + } + + /** + * + */ + protected String getStopBits() + { + int nIndex; + + nIndex = m_ctlStopBitsCombo.getSelectionIndex(); + if (nIndex == -1) + return ""; //$NON-NLS-1$ + + return m_ctlStopBitsCombo.getItem(nIndex); + } + + /** + * + */ + protected String getParity() + { + int nIndex; + + nIndex = m_ctlParityCombo.getSelectionIndex(); + if (nIndex == -1) + return ""; //$NON-NLS-1$ + + return m_ctlParityCombo.getItem(nIndex); + } + + /** + * + */ + protected String getFlowControl() + { + int nIndex; + + nIndex = m_ctlFlowControlCombo.getSelectionIndex(); + if (nIndex == -1) + return ""; //$NON-NLS-1$ + + return m_ctlFlowControlCombo.getItem(nIndex); + } + + /** + * + */ + protected String getHost() + { + return m_ctlHostText.getText(); + } + + /** + * + */ + protected String getNetworkPort() + { + TerminalPlugin plugin; + TerminalProperties properties; + TerminalNetworkPortMap networkPortMap; + String strPortName; + String strPort; + + plugin = TerminalPlugin.getDefault(); + properties = plugin.getTerminalProperties(); + networkPortMap = properties.getNetworkPortMap(); + strPortName = m_ctlNetworkPortCombo.getText(); + strPort = networkPortMap.findPort(strPortName); + + return strPort; + } + + /** + * + */ + protected boolean validateSettings() + { + return true; + } + + /** + * + */ + protected void createDialog(Composite ctlComposite) + { + setupData(); + setupPanel(ctlComposite); + setupListeners(); + } + + /** + * + */ + protected void setupData() + { + m_strConnType = ""; //$NON-NLS-1$ + } + + /** + * + */ + protected void setupPanel(Composite wndParent) + { + setupConnTypePanel(wndParent); + setupSettingsGroup(wndParent); + } + + /** + * + */ + protected void setupConnTypePanel(Composite wndParent) + { + Group wndGroup; + GridLayout gridLayout; + GridData gridData; + + wndGroup = new Group(wndParent,SWT.NONE); + gridLayout = new GridLayout(1,true); + gridData = new GridData(GridData.FILL_HORIZONTAL); + + wndGroup.setLayout(gridLayout); + wndGroup.setLayoutData(gridData); + wndGroup.setText(TERMINAL_TEXT_CONNECTIONTYPE + ":"); //$NON-NLS-1$ + + m_ctlConnTypeCombo = new Combo(wndGroup,SWT.DROP_DOWN|SWT.READ_ONLY); + gridData = new GridData(GridData.FILL_HORIZONTAL); + gridData.widthHint = 200; + m_ctlConnTypeCombo.setLayoutData(gridData); + + // Load controls + m_ctlConnTypeCombo.add(TERMINAL_CONNTYPE_SERIAL); + m_ctlConnTypeCombo.add(TERMINAL_CONNTYPE_NETWORK); + } + + /** + * + */ + protected void setupSettingsGroup(Composite parent) + { + m_wndSettingsGroup = new Group(parent,SWT.NONE); + GridLayout gridLayout = new GridLayout(); + GridData gridData = new GridData(GridData.FILL_BOTH); + + m_wndSettingsGroup.setText(TERMINAL_TEXT_SETTINGS + ":"); //$NON-NLS-1$ + m_wndSettingsGroup.setLayout(gridLayout); + m_wndSettingsGroup.setLayoutData(gridData); + } + + /** + * + */ + protected void setupSerialPanel() + { + TerminalPlugin plugin; + TerminalProperties properties; + Label ctlLabel; + GridLayout gridLayout; + GridData gridData; + Vector table; + + if (m_wndSettingsPanel != null) + { + m_wndSettingsPanel.setVisible(false); + m_wndSettingsPanel.dispose(); + } + + plugin = TerminalPlugin.getDefault(); + properties = plugin.getTerminalProperties(); + m_wndSettingsPanel = new Composite(m_wndSettingsGroup,SWT.NONE); + gridLayout = new GridLayout(2,false); + gridData = new GridData(GridData.FILL_HORIZONTAL); + + m_wndSettingsPanel.setLayout(gridLayout); + m_wndSettingsPanel.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_PORT + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlSerialPortCombo = new Combo(m_wndSettingsPanel,SWT.DROP_DOWN|SWT.READ_ONLY); + m_ctlSerialPortCombo.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_BAUDRATE + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlBaudRateCombo = new Combo(m_wndSettingsPanel,SWT.DROP_DOWN|SWT.READ_ONLY); + m_ctlBaudRateCombo.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_DATABITS + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlDataBitsCombo = new Combo(m_wndSettingsPanel,SWT.DROP_DOWN|SWT.READ_ONLY); + m_ctlDataBitsCombo.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_STOPBITS + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlStopBitsCombo = new Combo(m_wndSettingsPanel,SWT.DROP_DOWN|SWT.READ_ONLY); + m_ctlStopBitsCombo.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_PARITY + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlParityCombo = new Combo(m_wndSettingsPanel,SWT.DROP_DOWN|SWT.READ_ONLY); + m_ctlParityCombo.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_FLOWCONTROL + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlFlowControlCombo = new Combo(m_wndSettingsPanel,SWT.DROP_DOWN|SWT.READ_ONLY); + m_ctlFlowControlCombo.setLayoutData(gridData); + + // Load controls + table = properties.getSerialPortTable(); + loadCombo(m_ctlSerialPortCombo,table); + + table = properties.getBaudRateTable(); + loadCombo(m_ctlBaudRateCombo,table); + + table = properties.getDataBitsTable(); + loadCombo(m_ctlDataBitsCombo,table); + + table = properties.getStopBitsTable(); + loadCombo(m_ctlStopBitsCombo,table); + + table = properties.getParityTable(); + loadCombo(m_ctlParityCombo,table); + + table = properties.getFlowControlTable(); + loadCombo(m_ctlFlowControlCombo,table); + + setSerialPort(m_TerminalSettings.getSerialPort()); + setBaudRate(m_TerminalSettings.getBaudRate()); + setDataBits(m_TerminalSettings.getDataBits()); + setStopBits(m_TerminalSettings.getStopBits()); + setParity(m_TerminalSettings.getParity()); + setFlowIn(m_TerminalSettings.getFlowControl()); + + m_wndSettingsGroup.layout(true); + } + + /** + * + */ + protected void setupNetworkPanel() + { + TerminalPlugin plugin; + TerminalProperties properties; + TerminalNetworkPortMap networkPortMap; + Label ctlLabel; + GridLayout gridLayout; + GridData gridData; + Vector table; + + if (m_wndSettingsPanel != null) + { + m_wndSettingsPanel.setVisible(false); + m_wndSettingsPanel.dispose(); + } + + plugin = TerminalPlugin.getDefault(); + properties = plugin.getTerminalProperties(); + m_wndSettingsPanel = new Composite(m_wndSettingsGroup,SWT.NONE); + gridLayout = new GridLayout(2,false); + gridData = new GridData(GridData.FILL_HORIZONTAL); + + m_wndSettingsPanel.setLayout(gridLayout); + m_wndSettingsPanel.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_HOST + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlHostText = new Text(m_wndSettingsPanel,SWT.BORDER); + m_ctlHostText.setLayoutData(gridData); + + // Add label + ctlLabel = new Label(m_wndSettingsPanel,SWT.RIGHT); + ctlLabel.setText(TERMINAL_TEXT_PORT + ":"); //$NON-NLS-1$ + + // Add control + gridData = new GridData(GridData.FILL_HORIZONTAL); + m_ctlNetworkPortCombo = new Combo(m_wndSettingsPanel,SWT.DROP_DOWN); + + m_ctlNetworkPortCombo.setLayoutData(gridData); + + networkPortMap = properties.getNetworkPortMap(); + table = networkPortMap.getNameTable(); + Collections.sort(table); + loadCombo(m_ctlNetworkPortCombo,table); + + setHost(m_TerminalSettings.getHost()); + setNetworkPort(m_TerminalSettings.getNetworkPort()); + + m_wndSettingsGroup.layout(true); + } + + /** + * + */ + protected void setupConnTypeNotSupportedPanel() + { + MessageDialog dlgError; + Shell parentShell; + Image imgTitle; + String labels[]; + String strTitle; + String strMessage; + int nImage; + int nIndex; + + if (m_wndSettingsPanel != null) + { + m_wndSettingsPanel.setVisible(false); + m_wndSettingsPanel.dispose(); + } + + m_wndSettingsPanel = new Composite(m_wndSettingsGroup,SWT.NONE); + parentShell = getShell(); + strTitle = TERMINAL_MSG_ERROR_5; + imgTitle = null; + strMessage = TERMINAL_MSG_ERROR_6; + nImage = SWT.ICON_ERROR; + labels = new String[]{"OK"}; //$NON-NLS-1$ + nIndex = 0; + dlgError = new MessageDialog(parentShell, + strTitle, + imgTitle, + strMessage, + nImage, + labels, + nIndex); + + m_wndSettingsGroup.layout(true); + dlgError.open(); + } + + /** + * + */ + protected void setupListeners() + { + TerminalSettingsSelectionHandler selectionHandler; + + selectionHandler = new TerminalSettingsSelectionHandler(); + m_ctlConnTypeCombo.addSelectionListener(selectionHandler); + } + + /** + * + */ + protected void loadCombo(Combo ctlCombo,Vector table) + { + String strData; + + for(int i=0;i + * + * @author Fran Litterio + * @author Chris Thew + */ +class TerminalText extends Thread implements ControlListener +{ + /** This is a character processing state: Initial state. */ + protected static final int ANSISTATE_INITIAL = 0; + + /** This is a character processing state: We've seen an escape character. */ + protected static final int ANSISTATE_ESCAPE = 1; + + /** + * This is a character processing state: We've seen a '[' after an escape + * character. Expecting a parameter character or a command character next. + */ + protected static final int ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND = 2; + + /** + * This is a character processing state: We've seen a ']' after an escape + * character. We are now expecting an operating system command that reprograms an + * intelligent terminal. + */ + protected static final int ANSISTATE_EXPECTING_OS_COMMAND = 3; + + /** + * This field holds the current state of the Finite State Automaton (FSA) that + * recognizes ANSI escape sequences. + * + * @see #processNewText() + */ + protected int ansiState = ANSISTATE_INITIAL; + + /** + * This field holds a reference to the {@link TerminalCtrl} object that + * instantiates this class. + */ + protected TerminalCtrl terminal; + + /** + * This field holds a reference to the StyledText widget that is used to display + * text to the user. + */ + protected StyledText text; + + /** + * This field holds the characters received from the remote host before they are + * displayed to the user. Method {@link #processNewText()} scans this text + * looking for ANSI control characters and escape sequences. + */ + protected StringBuffer newText; + + /** + * This field holds the index of the current character while the text stored in + * field {@link #newText} is being processed. + */ + protected int characterIndex = 0; + + /** + * This field holds the width of a character (in pixels) for the font used to + * display text. + */ + protected int characterPixelWidth = 0; + + /** + * This field holds the width of the terminal screen in columns. + */ + protected int widthInColumns = 0; + + /** + * This field holds the height of the terminal screen in visible lines. The + * StyledText widget can contain more lines than are visible. + */ + protected int heightInLines = 0; + + /** + * This field holds the number of the column in which the cursor is logically + * positioned. The leftmost column on the screen is column 0, and column numbers + * increase to the right. The maximum value of this field is {@link + * #widthInColumns} - 1. We track the cursor column using this field to avoid + * having to recompute it repeatly using StyledText method calls.

+ * + * The StyledText widget that displays text has a vertical bar (called the "caret") + * that appears _between_ character cells, but ANSI terminals have the concept of a + * cursor that appears _in_ a character cell, so we need a convention for which + * character cell the cursor logically occupies when the caret is physically + * between two cells. The convention used in this class is that the cursor is + * logically in column N when the caret is physically positioned immediately to the + * _left_ of column N.

+ * + * When cursorColumn is N, the next character output to the terminal appears in + * column N. When a character is output to the rightmost column on a given line + * (column widthInColumns - 1), the cursor moves to column 0 on the next line after + * the character is drawn (this is how line wrapping is implemented). If the + * cursor is in the bottommost line when line wrapping occurs, the topmost visible + * line is scrolled off the top edge of the screen.

+ */ + protected int cursorColumn = 0; + + /** + * This field holds the caret offset when we last moved it or wrote text to the + * terminal. The reason we need to remember this value is because, unlike with a + * normal terminal emulator, the user can move the caret by clicking anywhere in + * the terminal view. In a normal terminal emulator, the cursor only moves as the + * result of character output (i.e., escape sequences or normal characters). We + * use the value stored in this field to restore the position of the caret + * immediately before processing each chunk of output from the remote endpoint. + */ + protected int caretOffset = 0; + + /** + * This field hold the saved absolute line number of the cursor when processing the + * "ESC 7" and "ESC 8" command sequences. + */ + protected int savedCursorLine = 0; + + /** + * This field hold the saved column number of the cursor when processing the "ESC + * 7" and "ESC 8" command sequences. + */ + protected int savedCursorColumn = 0; + + /** + * This field holds an array of StringBuffer objects, each of which is one + * parameter from the current ANSI escape sequence. For example, when parsing the + * escape sequence "\e[20;10H", this array holds the strings "20" and "10". + */ + protected StringBuffer[] ansiParameters = new StringBuffer[16]; + + /** + * This field holds the OS-specific command found in an escape sequence of the form + * "\e]...\u0007". + */ + protected StringBuffer ansiOsCommand = new StringBuffer(128); + + /** + * This field holds the index of the next unused element of the array stored in + * field {@link #ansiParameters}. + */ + protected int nextAnsiParameter = 0; + + /** + * This field holds the Color object representing the current foreground color as + * set by the ANSI escape sequence "\e[m". + */ + protected Color currentForegroundColor; + + /** + * This field holds the Color object representing the current background color as + * set by the ANSI escape sequence "\e[m". + */ + protected Color currentBackgroundColor; + + /** + * This field holds an integer representing the current font style as set by the + * ANSI escape sequence "\e[m". + */ + protected int currentFontStyle = SWT.NORMAL; + + /** + * This field is true if we are currently outputing text in reverse video mode, + * false otherwise. + */ + protected boolean reverseVideo = false; + + /** + * This field holds the time (in milliseconds) of the previous call to method + * {@link #SetNewText()}. + */ + static long LastNewOutputTime = 0; + + /** + * Color object representing the color black. The Color class requires us to call + * dispose() on this object when we no longer need it. We do that in method {@link + * #dispose()}. + */ + protected final Color BLACK = new Color(Display.getCurrent(), 0, 0, 0); + + /** + * Color object representing the color red. The Color class requires us to call + * dispose() on this object when we no longer need it. We do that in method {@link + * #dispose()}. + */ + protected final Color RED = new Color(Display.getCurrent(), 255, 0, 0); + + /** + * Color object representing the color green. The Color class requires us to call + * dispose() on this object when we no longer need it. We do that in method {@link + * #dispose()}. + */ + protected final Color GREEN = new Color(Display.getCurrent(), 0, 255, 0); + + /** + * Color object representing the color yellow. The Color class requires us to call + * dispose() on this object when we no longer need it. We do that in method {@link + * #dispose()}. + */ + protected final Color YELLOW = new Color(Display.getCurrent(), 255, 255, 0); + + /** + * Color object representing the color blue. The Color class requires us to call + * dispose() on this object when we no longer need it. We do that in method {@link + * #dispose()}. + */ + protected final Color BLUE = new Color(Display.getCurrent(), 0, 0, 255); + + /** + * Color object representing the color magenta. The Color class requires us to + * call dispose() on this object when we no longer need it. We do that in method + * {@link #dispose()}. + */ + protected final Color MAGENTA = new Color(Display.getCurrent(), 255, 0, 255); + + /** + * Color object representing the color cyan. The Color class requires us to call + * dispose() on this object when we no longer need it. We do that in method {@link + * #dispose()}. + */ + protected final Color CYAN = new Color(Display.getCurrent(), 0, 255, 255); + + /** + * Color object representing the color white. The Color class requires us to call + * dispose() on this object when we no longer need it. We do that in method {@link + * #dispose()}. + */ + protected final Color WHITE = new Color(Display.getCurrent(), 255, 255, 255); + + /** + * The constructor. + */ + public TerminalText(TerminalCtrl terminal) + { + super(); + + Logger.log("entered"); //$NON-NLS-1$ + + this.terminal = terminal; + + for (int i = 0; i < ansiParameters.length; ++i) + { + ansiParameters[i] = new StringBuffer(); + } + } + + /** + * This method performs clean up when this TerminalText object is no longer + * needed. After calling this method, no other method on this object should be + * called. + */ + public void dispose() + { + Logger.log("entered"); //$NON-NLS-1$ + + // Call dispose() on the Color objects we created. + + BLACK.dispose(); + RED.dispose(); + GREEN.dispose(); + YELLOW.dispose(); + BLUE.dispose(); + MAGENTA.dispose(); + CYAN.dispose(); + WHITE.dispose(); + } + + /** + * This method is required by interface ControlListener. It allows us to know when + * the StyledText widget is moved. + */ + public void controlMoved(ControlEvent event) + { + Logger.log("entered"); //$NON-NLS-1$ + // Empty. + } + + /** + * This method is required by interface ControlListener. It allows us to know when + * the StyledText widget is resized. This method must be synchronized to prevent + * it from executing at the same time as run(), which displays new text. We can't + * have the fields that represent the dimensions of the terminal changing while we + * are rendering text. + */ + public synchronized void controlResized(ControlEvent event) + { + Logger.log("entered"); //$NON-NLS-1$ + adjustTerminalDimensions(); + } + + /** + * This method sets field {@link #newText} to a new value. This method must + * not execute at the same time as methods {@link #run()} and {@link + * #clearTerminal()}.

+ * + * IMPORTANT: This method must be called in strict alternation with method + * {@link #run()}.

+ * + * @param newBuffer The new buffer containing characters received from the + * remote host. + */ + public synchronized void setNewText(StringBuffer newBuffer) + { + Logger.log("new text: '" + newBuffer + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + newText = newBuffer; + + // When continuous output is being processed by the Terminal view code, it + // consumes nearly 100% of the CPU. This fixes that. If this method is called + // too frequently, we explicitly sleep for a short time so that the thread + // executing this function (which is the thread reading from the socket or + // serial port) doesn't consume 100% of the CPU. Without this code, the + // Workbench GUI is practically hung when there is continuous output in the + // Terminal view. + + long CurrentTime = System.currentTimeMillis(); + + if (CurrentTime - LastNewOutputTime < 250 && newBuffer.length() > 10) + { + try + { + Thread.sleep(50); + } + catch (InterruptedException ex) + { + // Ignore. + } + } + + LastNewOutputTime = CurrentTime; + } + + /** + * This method erases all text from the Terminal view. This method is called when + * the user chooses "Clear all" from the Terminal view context menu, so we need to + * serialize this method with methods {@link #run()} and {@link + * #setNewText(StringBuffer)}. + */ + public synchronized void clearTerminal() + { + Logger.log("entered"); //$NON-NLS-1$ + text.setText(""); //$NON-NLS-1$ + cursorColumn = 0; + } + + /** + * This method is called when the user changes the Terminal view's font. We + * attempt to recompute the pixel width of the new font's characters and fix the + * terminal's dimensions. This method must be synchronized to prevent it from + * executing at the same time as run(), which displays new text. We can't have the + * fields that represent the dimensions of the terminal changing while we are + * rendering text. + */ + public synchronized void fontChanged() + { + Logger.log("entered"); //$NON-NLS-1$ + + characterPixelWidth = 0; + + if (text != null) + adjustTerminalDimensions(); + } + + /** + * This method executes in the Display thread to process data received from the + * remote host by classes {@link TelnetConnection} and {@link + * TerminalSerialPortHandler}. This method must not execute at the same time as + * methods {@link #setNewText(StringBuffer)} and {@link #clearTerminal()}.

+ * + * IMPORTANT: This method must be called in strict alternation with method + * {@link #setNewText(StringBuffer)}.

+ */ + public synchronized void run() + { + Logger.log("entered"); //$NON-NLS-1$ + + try + { + if (text == null) + { + // We defer initialization of these fields until execution reaches + // here, because the StyledText widget doesn't exist when this class is + // first instantiated. + + text = terminal.getTextWidget(); + + // Register this class instance as a ControlListener so we can learn + // when the StyledText widget is resized. + + text.addControlListener(this); + + currentForegroundColor = text.getForeground(); + currentBackgroundColor = text.getBackground(); + currentFontStyle = SWT.NORMAL; + reverseVideo = false; + } + + // This method can be called just after the user closes the view, so we + // make sure not to cause a widget-disposed exception. + + if (text != null && text.isDisposed()) + return; + + // If the status bar is showing "OPENED", change it to "CONNECTED". + + if (terminal.isOpened()) + { + terminal.setOpened(false); + terminal.execute(TerminalMsg.ON_TERMINAL_STATUS, null); + } + + // Find the width and height of the terminal, and resize it to display an + // integral number of lines and columns. + + adjustTerminalDimensions(); + + // Restore the caret offset, process and display the new text, then save + // the caret offset. See the documentation for field caretOffset for + // details. + + // ISSUE: Is this causing the scroll-to-bottom-on-output behavior? + + text.setCaretOffset(caretOffset); + + processNewText(); + + caretOffset = text.getCaretOffset(); + } + catch (Exception ex) + { + Logger.logException(ex); + } + } + + /** + * This method scans the newly received text, processing ANSI control characters + * and escape sequences and displaying normal text. + */ + protected void processNewText() + { + Logger.log("entered"); //$NON-NLS-1$ + + // Stop the StyledText widget from redrawing while we manipulate its contents. + // This helps display performance. + + text.setRedraw(false); + + // Scan the newly received text. + + characterIndex = 0; + + while (characterIndex < newText.length()) + { + char character = newText.charAt(characterIndex); + + switch (ansiState) + { + case ANSISTATE_INITIAL: + switch (character) + { + case '\u0000': + break; // NUL character. Ignore it. + + case '\u0007': + processBEL(); // BEL (Ctrl-G) + break; + + case '\b': + processBackspace(); // Backspace + break; + + case '\t': + processTab(); // Tab. + break; + + case '\n': + processNewline(); // Newline (Ctrl-J) + break; + + case '\r': + processCarriageReturn(); // Carriage Return (Ctrl-M) + break; + + case '\u001b': + ansiState = ANSISTATE_ESCAPE; // Escape. + break; + + default: + processNonControlCharacters(); + break; + } + break; + + case ANSISTATE_ESCAPE: + // We've seen an escape character. Here, we process the character + // immediately following the escape. + + switch (character) + { + case '[': + ansiState = ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND; + nextAnsiParameter = 0; + + // Erase the parameter strings in preparation for optional + // parameter characters. + + for (int i = 0; i < ansiParameters.length; ++i) + { + ansiParameters[i].delete(0, ansiParameters[i].length()); + } + break; + + case ']': + ansiState = ANSISTATE_EXPECTING_OS_COMMAND; + ansiOsCommand.delete(0, ansiOsCommand.length()); + break; + + case '7': + // Save cursor position and character attributes + + ansiState = ANSISTATE_INITIAL; + savedCursorLine = absoluteCursorLine(); + savedCursorColumn = cursorColumn; + break; + + case '8': + // Restore cursor and attributes to previously saved position + + ansiState = ANSISTATE_INITIAL; + moveCursor(savedCursorLine, savedCursorColumn); + break; + + default: + Logger.log("Unsupported escape sequence: escape '" + character + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + ansiState = ANSISTATE_INITIAL; + break; + } + break; + + case ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND: + // Parameters can appear after the '[' in an escape sequence, but they + // are optional. + + if (character == '@' || + (character >= 'A' && character <= 'Z') || + (character >= 'a' && character <= 'z')) + { + ansiState = ANSISTATE_INITIAL; + processAnsiCommandCharacter(character); + } + else + { + processAnsiParameterCharacter(character); + } + break; + + case ANSISTATE_EXPECTING_OS_COMMAND: + // A BEL (\u0007) character marks the end of the OSC sequence. + + if (character == '\u0007') + { + ansiState = ANSISTATE_INITIAL; + processAnsiOsCommand(); + } + else + { + ansiOsCommand.append(character); + } + break; + + default: + // This should never happen! If it does happen, it means there is a + // bug in the FSA. For robustness, we return to the initial state. + + Logger.log("INVALID ANSI FSA STATE: " + ansiState); //$NON-NLS-1$ + ansiState = ANSISTATE_INITIAL; + assert false; + break; + } + + ++characterIndex; + } + + // Allow the StyledText widget to redraw itself. + + text.setRedraw(true); + } + + /** + * This method is called when we have parsed an OS Command escape sequence. The + * only one we support is "\e]0;...\u0007", which sets the terminal title. + */ + protected void processAnsiOsCommand() + { + if (ansiOsCommand.charAt(0) != '0' || ansiOsCommand.charAt(1) != ';') + { + Logger.log("Ignoring unsupported ANSI OSC sequence: '" + ansiOsCommand + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + return; + } + + terminal.execute(TerminalMsg.ON_TERMINAL_STATUS, ansiOsCommand.substring(2)); + } + + /** + * This method dispatches control to various processing methods based on the + * command character found in the most recently received ANSI escape sequence. + * This method only handles command characters that follow the ANSI standard + * Control Sequence Introducer (CSI), which is "\e[...", where "..." is an optional + * ';'-separated sequence of numeric parameters.

+ */ + protected void processAnsiCommandCharacter(char ansiCommandCharacter) + { + // If the width or height of the terminal is ridiculously small (one line or + // column or less), don't even try to process the escape sequence. This avoids + // throwing an exception (SPR 107450). The display will be messed up, but what + // did you user expect by making the terminal so small? + + if (heightInLines <= 1 || widthInColumns <= 1) + return; + + switch (ansiCommandCharacter) + { + case '@': + // Insert character(s). + processAnsiCommand_atsign(); + break; + + case 'A': + // Move cursor up N lines (default 1). + processAnsiCommand_A(); + break; + + case 'B': + // Move cursor down N lines (default 1). + processAnsiCommand_B(); + break; + + case 'C': + // Move cursor forward N columns (default 1). + processAnsiCommand_C(); + break; + + case 'D': + // Move cursor backward N columns (default 1). + processAnsiCommand_D(); + break; + + case 'E': + // Move cursor to first column of Nth next line (default 1). + processAnsiCommand_E(); + break; + + case 'F': + // Move cursor to first column of Nth previous line (default 1). + processAnsiCommand_F(); + break; + + case 'G': + // Move to column N of current line (default 1). + processAnsiCommand_G(); + break; + + case 'H': + // Set cursor Position. + processAnsiCommand_H(); + break; + + case 'J': + // Erase part or all of display. Cursor does not move. + processAnsiCommand_J(); + break; + + case 'K': + // Erase in line (cursor does not move). + processAnsiCommand_K(); + break; + + case 'L': + // Insert line(s) (current line moves down). + processAnsiCommand_L(); + break; + + case 'M': + // Delete line(s). + processAnsiCommand_M(); + break; + + case 'm': + // Set Graphics Rendition (SGR). + processAnsiCommand_m(); + break; + + case 'n': + // Device Status Report (DSR). + processAnsiCommand_n(); + break; + + case 'P': + // Delete character(s). + processAnsiCommand_P(); + break; + + case 'S': + // Scroll up. + // Emacs, vi, and GNU readline don't seem to use this command, so we ignore + // it for now. + break; + + case 'T': + // Scroll down. + // Emacs, vi, and GNU readline don't seem to use this command, so we ignore + // it for now. + break; + + case 'X': + // Erase character. + // Emacs, vi, and GNU readline don't seem to use this command, so we ignore + // it for now. + break; + + case 'Z': + // Cursor back tab. + // Emacs, vi, and GNU readline don't seem to use this command, so we ignore + // it for now. + break; + + default: + Logger.log("Ignoring unsupported ANSI command character: '" + //$NON-NLS-1$ + ansiCommandCharacter + "'"); //$NON-NLS-1$ + break; + } + } + + /** + * This method makes room for N characters on the current line at the cursor + * position. Text under the cursor moves right without wrapping at the end of hte + * line. + */ + protected void processAnsiCommand_atsign() + { + int charactersToInsert = getAnsiParameter(0); + int caretOffset = text.getCaretOffset(); + + text.replaceTextRange(caretOffset, 0, generateString(' ', charactersToInsert)); + + // If the current line extends past the right edge of the screen, delete the + // characters beyond the rightmost visible column. + + int currentLineAbsolute = absoluteCursorLine(); + int currentLineStartOffset = text.getOffsetAtLine(currentLineAbsolute); + int currentLineEndOffset; + + if (currentLineAbsolute == text.getLineCount() - 1) + { + // The cursor is on the bottommost line of text. + + currentLineEndOffset = text.getCharCount(); + } + else + { + // The cursor is not on the bottommost line of text. + + currentLineEndOffset = text.getOffsetAtLine(currentLineAbsolute + 1) - 1; + } + + if (currentLineEndOffset - currentLineStartOffset > widthInColumns) + { + int charactersToDelete = + currentLineEndOffset - currentLineStartOffset - widthInColumns; + + text.replaceTextRange(currentLineStartOffset + widthInColumns, + charactersToDelete, ""); //$NON-NLS-1$ + } + + // Is this necessary? + + text.setCaretOffset(caretOffset); + } + + /** + * This method moves the cursor up by the number of lines specified by the escape + * sequence parameter (default 1). + */ + protected void processAnsiCommand_A() + { + moveCursorUp(getAnsiParameter(0)); + } + + /** + * This method moves the cursor down by the number of lines specified by the escape + * sequence parameter (default 1). + */ + protected void processAnsiCommand_B() + { + moveCursorDown(getAnsiParameter(0)); + } + + /** + * This method moves the cursor forward by the number of columns specified by the + * escape sequence parameter (default 1). + */ + protected void processAnsiCommand_C() + { + moveCursorForward(getAnsiParameter(0)); + } + + /** + * This method moves the cursor backward by the number of columns specified by the + * escape sequence parameter (default 1). + */ + protected void processAnsiCommand_D() + { + moveCursorBackward(getAnsiParameter(0)); + } + + /** + * This method moves the cursor to the first column of the Nth next line, where N + * is specified by the ANSI parameter (default 1). + */ + protected void processAnsiCommand_E() + { + int linesToMove = getAnsiParameter(0); + + moveCursor(relativeCursorLine() + linesToMove, 0); + } + + /** + * This method moves the cursor to the first column of the Nth previous line, where + * N is specified by the ANSI parameter (default 1). + */ + protected void processAnsiCommand_F() + { + int linesToMove = getAnsiParameter(0); + + moveCursor(relativeCursorLine() - linesToMove, 0); + } + + /** + * This method moves the cursor within the current line to the column specified by + * the ANSI parameter (default is column 1). + */ + protected void processAnsiCommand_G() + { + int targetColumn = 1; + + if (ansiParameters[0].length() > 0) + targetColumn = getAnsiParameter(0) - 1; + + moveCursor(relativeCursorLine(), targetColumn); + } + + /** + * This method sets the cursor to a position specified by the escape sequence + * parameters (default is the upper left corner of the screen). + */ + protected void processAnsiCommand_H() + { + moveCursor(getAnsiParameter(0) - 1, getAnsiParameter(1) - 1); + } + + /** + * This method deletes some (or all) of the text on the screen without moving the + * cursor. + */ + protected void processAnsiCommand_J() + { + int ansiParameter; + + if (ansiParameters[0].length() == 0) + ansiParameter = 0; + else + ansiParameter = getAnsiParameter(0); + + switch (ansiParameter) + { + case 0: + // Erase from current position to end of screen (inclusive). + + int caretOffset = text.getCaretOffset(); + + text.replaceTextRange(caretOffset, + text.getCharCount() - caretOffset, + generateString('\n', heightInLines - relativeCursorLine() - 1)); + + // The above call moves the caret to the end of the text, so restore its + // position. + + text.setCaretOffset(caretOffset); + break; + + case 1: + // Erase from beginning to current position (inclusive). + + int currentRelativeLineNumber = relativeCursorLine(); + int topmostScreenLineStartOffset = text.getOffsetAtLine(absoluteLine(0)); + + text.replaceTextRange(topmostScreenLineStartOffset, + text.getCaretOffset() - topmostScreenLineStartOffset, + generateString('\n', currentRelativeLineNumber) + + generateString(' ', cursorColumn)); + + text.setCaretOffset(topmostScreenLineStartOffset + currentRelativeLineNumber + + cursorColumn); + break; + + case 2: + // Erase entire display. + + int currentLineNumber = relativeCursorLine(); + topmostScreenLineStartOffset = text.getOffsetAtLine(absoluteLine(0)); + + text.replaceTextRange(topmostScreenLineStartOffset, + text.getCharCount() - topmostScreenLineStartOffset, + generateString('\n', heightInLines - 1)); + + moveCursor(currentLineNumber, cursorColumn); + break; + + default: + Logger.log("Unexpected J-command parameter: " + ansiParameter); //$NON-NLS-1$ + assert false; + break; + } + } + + /** + * This method deletes some (or all) of the text in the current line without moving + * the cursor. + */ + protected void processAnsiCommand_K() + { + int ansiParameter = getAnsiParameter(0); + int originalCaretOffset = text.getCaretOffset(); + + switch (ansiParameter) + { + case 0: + // Erase from beginning to current position (inclusive). + + int currentLineStartOffset = text.getOffsetAtLine(absoluteCursorLine()); + + text.replaceTextRange(currentLineStartOffset, + cursorColumn, + generateString(' ', cursorColumn)); + break; + + case 1: + // Erase from current position to end (inclusive). + + int caretOffset = text.getCaretOffset(); + + if (absoluteCursorLine() == text.getLineCount() - 1) + { + text.replaceTextRange(caretOffset, text.getCharCount() - caretOffset, ""); //$NON-NLS-1$ + } + else + { + int nextLineStartOffset = text.getOffsetAtLine(absoluteCursorLine() + 1); + + text.replaceTextRange(caretOffset, nextLineStartOffset - caretOffset - 1, ""); //$NON-NLS-1$ + } + break; + + case 2: + // Erase entire line. + + currentLineStartOffset = text.getOffsetAtLine(absoluteCursorLine()); + + if (absoluteCursorLine() == text.getLineCount() - 1) + { + // The cursor is on the bottommost line of text. Replace its contents + // with enough spaces to leave the cursor in the current column. + + text.replaceTextRange(currentLineStartOffset, + text.getCharCount() - currentLineStartOffset, + generateString(' ', cursorColumn)); + } + else + { + // The cursor is not on the bottommost line of text. Replace the + // current line's contents with enough spaces to leave the cursor in + // the current column. + + int nextLineStartOffset = text.getOffsetAtLine(absoluteCursorLine() + 1); + + text.replaceTextRange(currentLineStartOffset, + nextLineStartOffset - currentLineStartOffset - 1, + generateString(' ', cursorColumn)); + } + break; + + default: + Logger.log("Unexpected K-command parameter: " + ansiParameter); //$NON-NLS-1$ + assert false; + break; + } + + // There is some undocumented strangeness with method + // StyledText.replaceTextRange() that requires us to manually reposition the + // caret after calling that method. If we don't do this, the caret sometimes + // moves to the very end of the text when deleting text within a line. + + text.setCaretOffset(originalCaretOffset); + } + + /** + * Insert one or more blank lines. The current line of text moves down. Text that + * falls off the bottom of the screen is deleted. + */ + protected void processAnsiCommand_L() + { + int linesToInsert = getAnsiParameter(0); + + int currentLineStartOffset = text.getOffsetAtLine(absoluteCursorLine()); + + // Compute how many of the bottommost lines of text to delete. This is + // necessary if those lines are being pushed off the bottom of the screen by + // the insertion of the blank lines. + + int totalLines = text.getLineCount(); + int linesToDelete = -1; + + if (heightInLines <= totalLines) + { + // There are more lines of text than are displayed, so delete as many lines + // at the end as we insert in the middle. + + linesToDelete = linesToInsert; + } + else + { + // There are fewer lines of text than the size of the terminal window, so + // compute how many lines will be pushed off the end of the screen by the + // insertion. NOTE: It is possible that we may not have to delete any + // lines at all, which will leave linesToDelete set to -1. + + if (totalLines + linesToInsert > heightInLines) + { + linesToDelete = (totalLines + linesToInsert) - heightInLines; + } + } + + if (linesToDelete != -1) + { + // Delete the bottomost linesToInsert lines plus the newline on the line + // immediately above the first line to be deleted. + + int firstLineToDeleteStartOffset = text.getOffsetAtLine(totalLines - linesToDelete); + + text.replaceTextRange(firstLineToDeleteStartOffset - 1, + text.getCharCount() - firstLineToDeleteStartOffset + 1, + ""); //$NON-NLS-1$ + } + + // Insert the new blank lines, leaving the cursor on the topmost of the new + // blank lines. + + int totalCharacters = text.getCharCount(); + + if (currentLineStartOffset > totalCharacters) + { + // We are inserting the blank lines at the very end of the text, so + // currentLineStartOffset is now out of range. It will be be in range + // again after these newlines are appended. + + text.replaceTextRange(totalCharacters, 0, generateString('\n', linesToInsert)); + } + else + { + // We are inserting the blank lines in the middle of the text, so + // currentLineStartOffset is not out of range. + + text.replaceTextRange(currentLineStartOffset, 0, generateString('\n', linesToInsert)); + } + + text.setCaretOffset(currentLineStartOffset); + } + + /** + * Delete one or more lines of text. Any lines below the deleted lines move up, + * which we implmement by appending newlines to the end of the text. + */ + protected void processAnsiCommand_M() + { + int totalLines = text.getLineCount(); + int linesToDelete = getAnsiParameter(0); + int currentLineAbsolute = absoluteCursorLine(); + int currentLineStartOffset = text.getOffsetAtLine(currentLineAbsolute); + + // Compute the offset of the character after the lines to be deleted. This + // might be the end of the text. + + if (linesToDelete >= totalLines - currentLineAbsolute) + { + // We are deleting all the lines to the bottom of the text. Replace them + // with blank lines. + + text.replaceTextRange(currentLineStartOffset, + text.getCharCount() - currentLineStartOffset, + generateString('\n', totalLines - currentLineAbsolute - 1)); + } + else + { + // Delete the next linesToDelete lines. + + int firstUndeletedLineStartOffset = + text.getOffsetAtLine(currentLineAbsolute + linesToDelete); + + text.replaceTextRange(currentLineStartOffset, + firstUndeletedLineStartOffset - currentLineStartOffset, + ""); //$NON-NLS-1$ + + // Add an equal number of blank lines to the end of the text. + + text.replaceTextRange(text.getCharCount(), 0, generateString('\n', linesToDelete)); + } + + text.setCaretOffset(currentLineStartOffset); + } + + /** + * This method sets a new graphics rendition mode, such as foreground/background + * color, bold/normal text, and reverse video. + */ + protected void processAnsiCommand_m() + { + if (ansiParameters[0].length() == 0) + { + // This a special case: when no ANSI parameter is specified, act like a + // single parameter equal to 0 was specified. + + ansiParameters[0].append('0'); + } + + // There are a non-zero number of ANSI parameters. Process each one in order. + + int totalParameters = ansiParameters.length; + int parameterIndex = 0; + + while (parameterIndex < totalParameters && ansiParameters[parameterIndex].length() > 0) + { + int ansiParameter = getAnsiParameter(parameterIndex); + + switch (ansiParameter) + { + case 0: + // Reset all graphics modes. + currentForegroundColor = text.getForeground(); + currentBackgroundColor = text.getBackground(); + currentFontStyle = SWT.NORMAL; + reverseVideo = false; + break; + + case 1: + currentFontStyle = SWT.BOLD; // Turn on bold. + break; + + case 7: + reverseVideo = true; // Reverse video. + break; + + case 10: // Set primary font. Ignored. + break; + + case 22: + currentFontStyle = SWT.NORMAL; // Cancel bold or dim attributes only. + break; + + case 27: + reverseVideo = false; // Cancel reverse video attribute only. + break; + + case 30: + currentForegroundColor = BLACK; // Foreground is black. + break; + + case 31: + currentForegroundColor = RED; // Foreground is red. + break; + + case 32: + currentForegroundColor = GREEN; // Foreground is green. + break; + + case 33: + currentForegroundColor = YELLOW; // Foreground is yellow. + break; + + case 34: + currentForegroundColor = BLUE; // Foreground is blue. + break; + + case 35: + currentForegroundColor = MAGENTA; // Foreground is magenta. + break; + + case 36: + currentForegroundColor = CYAN; // Foreground is cyan. + break; + + case 37: + currentForegroundColor = text.getForeground(); // Foreground is white. + break; + + case 40: + currentBackgroundColor = text.getBackground(); // Background is black. + break; + + case 41: + currentBackgroundColor = RED; // Background is red. + break; + + case 42: + currentBackgroundColor = GREEN; // Background is green. + break; + + case 43: + currentBackgroundColor = YELLOW; // Background is yellow. + break; + + case 44: + currentBackgroundColor = BLUE; // Background is blue. + break; + + case 45: + currentBackgroundColor = MAGENTA; // Background is magenta. + break; + + case 46: + currentBackgroundColor = CYAN; // Background is cyan. + break; + + case 47: + currentBackgroundColor = WHITE; // Background is white. + break; + + default: + Logger.log("Unsupported graphics rendition parameter: " + ansiParameter); //$NON-NLS-1$ + break; + } + + ++ parameterIndex; + } + } + + /** + * This method responds to an ANSI Device Status Report (DSR) command from the + * remote endpoint requesting the cursor position. Requests for other kinds of + * status are ignored. + */ + protected void processAnsiCommand_n() + { + // Do nothing if the numeric parameter was not 6 (which means report cursor + // position). + + if (getAnsiParameter(0) != 6) + return; + + // Send the ANSI cursor position (which is 1-based) to the remote endpoint. + + String positionReport = "\u001b[" + (relativeCursorLine() + 1) + ";" + //$NON-NLS-1$ //$NON-NLS-2$ + (cursorColumn + 1) + "R"; //$NON-NLS-1$ + + OutputStreamWriter streamWriter = + new OutputStreamWriter(terminal.getOutputStream(), Charset.forName("ISO-8859-1")); //$NON-NLS-1$ + + try + { + streamWriter.write(positionReport, 0, positionReport.length()); + streamWriter.flush(); + } + catch (IOException ex) + { + Logger.log("Caught IOException!"); //$NON-NLS-1$ + assert false; + } + } + + /** + * Deletes one or more characters starting at the current cursor position. + * Characters on the same line and to the right of the deleted characters move + * left. If there are no characters on the current line at or to the right of the + * cursor column, no text is deleted. + */ + protected void processAnsiCommand_P() + { + int currentLineEndOffset; + int currentLineAbsolute = absoluteCursorLine(); + + if (currentLineAbsolute == text.getLineCount() - 1) + { + // The cursor is on the bottommost line of text. + + currentLineEndOffset = text.getCharCount(); + } + else + { + // The cursor is not on the bottommost line of text. + + currentLineEndOffset = text.getOffsetAtLine(currentLineAbsolute + 1) - 1; + } + + int caretOffset = text.getCaretOffset(); + int remainingCharactersOnLine = currentLineEndOffset - caretOffset; + + if (remainingCharactersOnLine > 0) + { + // There are characters that can be deleted. + + int charactersToDelete = getAnsiParameter(0); + + if (charactersToDelete > remainingCharactersOnLine) + charactersToDelete = remainingCharactersOnLine; + + text.replaceTextRange(caretOffset, charactersToDelete, ""); //$NON-NLS-1$ + text.setCaretOffset(caretOffset); + } + } + + /** + * This method returns one of the numeric ANSI parameters received in the most + * recent escape sequence. + * + * @return The parameterIndexth numeric ANSI parameter or -1 if the index + * is out of range. + */ + protected int getAnsiParameter(int parameterIndex) + { + if (parameterIndex < 0 || parameterIndex >= ansiParameters.length) + { + // This should never happen. + assert false; + return -1; + } + + String parameter = ansiParameters[parameterIndex].toString(); + + if (parameter.length() == 0) + return 1; + + int parameterValue = 1; + + // Don't trust the remote endpoint to send well formed numeric parameters. + + try + { + parameterValue = Integer.parseInt(parameter); + } + catch (NumberFormatException ex) + { + parameterValue = 1; + } + + return parameterValue; + } + + /** + * This method processes a single parameter character in an ANSI escape sequence. + * Paramters are the (optional) characters between the leading "\e[" and the + * command character in an escape sequence (e.g., in the escape sequence + * "\e[20;10H", the paramter characters are "20;10"). Parameters are integers + * separated by one or more ';'s. + */ + protected void processAnsiParameterCharacter(char ch) + { + if (ch == ';') + { + ++nextAnsiParameter; + } + else + { + if (nextAnsiParameter < ansiParameters.length) + ansiParameters[nextAnsiParameter].append(ch); + } + } + + /** + * This method processes a contiguous sequence of non-control characters. This is + * a performance optimization, so that we don't have to insert or append each + * non-control character individually to the StyledText widget. A non-control + * character is any character that passes the condition in the below while loop. + */ + protected void processNonControlCharacters() + { + int firstNonControlCharacterIndex = characterIndex; + int newTextLength = newText.length(); + char character = newText.charAt(characterIndex); + + // Identify a contiguous sequence of non-control characters, starting at + // firstNonControlCharacterIndex in newText. + + while (character != '\u0000' && character != '\b' && character != '\t' && + character != '\u0007' && character != '\n' && character != '\r' && + character != '\u001b') + { + ++characterIndex; + + if (characterIndex >= newTextLength) + break; + + character = newText.charAt(characterIndex); + } + + // Move characterIndex back by one character because it gets incremented at the + // bottom of the loop in processNewText(). + + --characterIndex; + + int preDisplayCaretOffset = text.getCaretOffset(); + + // Now insert the sequence of non-control characters in the StyledText widget + // at the location of the cursor. + + displayNewText(firstNonControlCharacterIndex, characterIndex); + + // If any one of the current font style, foreground color or background color + // differs from the defaults, apply the current style to the newly displayed + // text. Since this method is only called for a contiguous sequence of + // non-control characters, the current style applies to the entire sequence of + // characters. + + if (!currentForegroundColor.equals(text.getForeground()) || + !currentBackgroundColor.equals(text.getBackground()) || + currentFontStyle != SWT.NORMAL || + reverseVideo == true) + { + StyleRange style = + new StyleRange(preDisplayCaretOffset, + text.getCaretOffset() - preDisplayCaretOffset, + reverseVideo ? currentBackgroundColor : currentForegroundColor, + reverseVideo ? currentForegroundColor : currentBackgroundColor, + currentFontStyle); + + text.setStyleRange(style); + } + } + + /** + * This method displays a subset of the newly-received text in the Terminal view, + * wrapping text at the right edge of the screen and overwriting text when the + * cursor is not at the very end of the screen's text.

+ * + * There are never any ANSI control characters or escape sequences in the text + * being displayed by this method (this includes newlines, carriage returns, and + * tabs).

+ * + * @param first The index (within newText) of the first character to + * display. + * @param last The index (within newText) of the last character to + * display. + */ + protected void displayNewText(int first, int last) + { + if (text.getCaretOffset() == text.getCharCount()) + { + // The cursor is at the very end of the terminal's text, so we append the + // new text to the StyledText widget. + + displayNewTextByAppending(first, last); + } + else + { + // The cursor is not at the end of the screen's text, so we have to + // overwrite existing text. + + displayNewTextByOverwriting(first, last); + } + } + + /** + * This method displays new text by appending it to the end of the existing text, + * wrapping text that extends past the right edge of the screen.

+ * + * There are never any ANSI control characters or escape sequences in the text + * being displayed by this method (this includes newlines, carriage returns, and + * tabs).

+ * + * @param first The index (within newText) of the first character to + * display. + * @param last The index (within newText) of the last character to + * display. + */ + protected void displayNewTextByAppending(int first, int last) + { + int numCharsToOutput = last - first + 1; + int availableSpaceOnLine = widthInColumns - cursorColumn; + + if (numCharsToOutput >= availableSpaceOnLine) + { + // We need to wrap the text, because it's longer than the available + // space on the current line. First, appends as many characters as + // will fit in the space remaining on the current line. + // + // NOTE: We don't line wrap the text in this method the same way we line + // wrap the text in method displayNewTextByOverwriting(), but this is by far + // the most common case, and it has to run the fastest. + + text.append(newText.substring(first, first + availableSpaceOnLine)); + first += availableSpaceOnLine; + + processCarriageReturn(); + processNewline(); + + while (first <= last) + { + availableSpaceOnLine = widthInColumns; + + if (availableSpaceOnLine > last - first + 1) + { + text.append(newText.substring(first, last + 1)); + cursorColumn = last - first + 1; + break; + } + else + { + text.append(newText.substring(first, first + availableSpaceOnLine)); + first += availableSpaceOnLine; + + processCarriageReturn(); + processNewline(); + } + } + } + else + { + // We don't need to wrap the text. + + text.append(newText.substring(first, last + 1)); + cursorColumn += last - first + 1; + } + } + + /** + * This method displays new text by overwriting existing text, wrapping text that + * extends past the right edge of the screen.

+ * + * There are never any ANSI control characters or escape sequences in the text + * being displayed by this method (this includes newlines, carriage returns, and + * tabs).

+ * + * @param first The index (within newText) of the first character to + * display. + * @param last The index (within newText) of the last character to + * display. + */ + protected void displayNewTextByOverwriting(int first, int last) + { + // First, break new text into segments, based on where it needs to line wrap, + // so that each segment contains text that will appear on a separate line. + + List textSegments = new ArrayList(100); + + int availableSpaceOnLine = widthInColumns - cursorColumn; + + while (first <= last) + { + String segment; + + if (last - first + 1 > availableSpaceOnLine) + segment = newText.substring(first, first + availableSpaceOnLine); + else + segment = newText.substring(first, last + 1); + + textSegments.add(segment); + + first += availableSpaceOnLine; + availableSpaceOnLine = widthInColumns; + } + + // Next, for each segment, if the cursor is at the end of the text, append the + // segment along with a newline character. If the cursor is not at the end of + // the text, replace the next N characters starting at the cursor position with + // the segment, where N is the minimum of the length of the segment or the + // length of the rest of the current line. + + Iterator iter = textSegments.iterator(); + + while (iter.hasNext()) + { + String segment = (String)iter.next(); + int caretOffset = text.getCaretOffset(); + + if (caretOffset == text.getCharCount()) + { + // The cursor is at the end of the text, so just append the current + // segement along with a newline. + + text.append(segment); + + // If there is another segment to display, move the cursor to a new + // line. + + if (iter.hasNext()) + { + processCarriageReturn(); + processNewline(); + } + } + else + { + // The cursor is not at the end of the text, so replace some or all of + // the text following the cursor on the current line with the current + // segment. + + int numCharactersAfterCursorOnLine; + + if (absoluteCursorLine() == text.getLineCount() - 1) + { + // The cursor is on the last line of text. + numCharactersAfterCursorOnLine = text.getCharCount() - caretOffset; + } + else + { + // The cursor is not on the last line of text. + numCharactersAfterCursorOnLine = + text.getOffsetAtLine(absoluteCursorLine() + 1) - caretOffset - 1; + } + + int segmentLength = segment.length(); + int numCharactersToReplace; + + if (segmentLength < numCharactersAfterCursorOnLine) + numCharactersToReplace = segmentLength; + else + numCharactersToReplace = numCharactersAfterCursorOnLine; + + text.replaceTextRange(caretOffset, numCharactersToReplace, segment); + text.setCaretOffset(caretOffset + segmentLength); + cursorColumn += segmentLength; + + // If there is another segment, move the cursor to the start of the + // next line. + + if (iter.hasNext()) + { + cursorColumn = 0; + text.setCaretOffset(caretOffset + segmentLength + 1); + } + else + { + // We just inserted the last segment. If the current line is full, + // wrap the cursor onto a new line. + + if (cursorColumn == widthInColumns) + { + processCarriageReturn(); + processNewline(); + } + } + } + } + } + + /** + * Process a BEL (Ctrl-G) character. + */ + protected void processBEL() + { + // ISSUE: Is there a better way to make a sound? This is not guaranteed to + // work on all platforms. + + java.awt.Toolkit.getDefaultToolkit().beep(); + } + + /** + * Process a backspace (Ctrl-H) character. + */ + protected void processBackspace() + { + moveCursorBackward(1); + } + + /** + * Process a tab (Ctrl-I) character. We don't insert a tab character into the + * StyledText widget. Instead, we move the cursor forward to the next tab stop, + * without altering any of the text. Tab stops are every 8 columns. The cursor + * will never move past the rightmost column. + */ + protected void processTab() + { + moveCursorForward(8 - (cursorColumn % 8)); + } + + /** + * Process a newline (Control-J) character. A newline (NL) character just moves + * the cursor to the same column on the next line, creating new lines when the + * cursor reaches the bottom edge of the terminal. This is counter-intuitive, + * especially to UNIX programmers who are taught that writing a single NL to a + * terminal is sufficient to move the cursor to the first column of the next line, + * as if a carriage return (CR) and a NL were written.

+ * + * UNIX terminals typically display a NL character as a CR followed by a NL because + * the terminal device typically has the ONLCR attribute bit set (see the + * termios(4) man page for details), which causes the terminal device driver to + * translate NL to CR + NL on output. The terminal itself (i.e., a hardware + * terminal or a terminal emulator, like xterm or this code) _always_ interprets a + * CR to mean "move the cursor to the beginning of the current line" and a NL to + * mean "move the cursor to the same column on the next line".

+ */ + protected void processNewline() + { + int totalLines = text.getLineCount(); + int currentLineAbsolute = absoluteCursorLine(); + + if (currentLineAbsolute < totalLines - 1) + { + // The cursor is not on the bottommost line of text, so we move the cursor + // to the same column on the next line. + + // TODO: If we can verify that the next character is a carriage return, we + // can optimize out the insertion of spaces that moveCursorDown() will do. + + moveCursorDown(1); + } + else if (currentLineAbsolute == totalLines - 1) + { + // The cursor is on the bottommost line of text, so we append a newline + // character to the end of the terminal's text (creating a new line on the + // screen) and insert cursorColumn spaces. + + text.append("\n"); //$NON-NLS-1$ + text.append(generateString(' ', cursorColumn)); + text.setCaretOffset(text.getCharCount()); + + // We may have scrolled a line off the top of the screen, so check if we + // need to delete some of the the oldest lines in the scroll buffer. + + deleteTopmostLines(); + } + else + { + // This should _never_ happen. If it does happen, it is a bug in this + // algorithm. + + Logger.log("SHOULD NOT BE REACHED!"); //$NON-NLS-1$ + assert false; + } + } + + /** + * Process a Carriage Return (Ctrl-M). + */ + protected void processCarriageReturn() + { + // Move the cursor to the beginning of the current line. + + text.setCaretOffset(text.getOffsetAtLine(text.getLineAtOffset(text.getCaretOffset()))); + cursorColumn = 0; + } + + /** + * This method computes the width of the terminal in columns and its height in + * lines, then adjusts the width and height of the view's StyledText widget so that + * it displays an integral number of lines and columns of text. The adjustment is + * always to shrink the widget vertically or horizontally, because if the control + * were to grow, it would be clipped by the edges of the view window (i.e., the + * view window does not become larger to accommodate its contents becoming + * larger).

+ * + * This method must be called immediately before each time text is written to the + * terminal so that we can properly line wrap text. Because it is called so + * frequently, it must be fast when there is no resizing to be done.

+ */ + protected void adjustTerminalDimensions() + { + // Compute how many pixels we need to shrink the StyledText control vertically + // to make it display an integral number of lines of text. + + int linePixelHeight = text.getLineHeight(); + Point textWindowDimensions = text.getSize(); + int verticalPixelsToShrink = textWindowDimensions.y % linePixelHeight; + + // Compute the current height of the terminal in lines. + + heightInLines = textWindowDimensions.y / linePixelHeight; + + // Compute how many pixels we need to shrink the StyledText control to make + // it display an integral number of columns of text. We can only do this if we + // know the pixel width of a character in the font used by the StyledText + // widget. + + int horizontalPixelsToShrink = 0; + + if (characterPixelWidth == 0) + computeCharacterPixelWidth(); + + if (characterPixelWidth != 0) + { + horizontalPixelsToShrink = textWindowDimensions.x % characterPixelWidth; + + // The width of the StyledText widget that text.getSize() returns includes + // the space occupied by the vertical scrollbar, so we have to fudge this + // calculation (by subtracting 3 columns) to account for the presence of + // the scrollbar. Ugh. + + widthInColumns = textWindowDimensions.x / characterPixelWidth - 3; + } + + // If necessary, resize the text widget. + + if (verticalPixelsToShrink > 0 || horizontalPixelsToShrink > 0) + { + // Remove this class instance from being a ControlListener on the + // StyledText widget, because we are about to resize and move the widget, + // and we don't want this method to be recursively invoked. + + text.removeControlListener(this); + + // Shrink the StyledText control so that it displays an integral number + // of lines of text and an integral number of columns of text. + + textWindowDimensions.y -= verticalPixelsToShrink; + textWindowDimensions.x -= horizontalPixelsToShrink; + text.setSize(textWindowDimensions); + + // Move the StyledText control down by the same number of pixels that + // we just shrank it vertically and right by the same number of pixels that + // we just shrank it horizontally. This makes the padding appear to the + // left and top of the widget, which is more visually appealing. This is + // only necessary because there is no way to programmatically shrink the + // view itself. + + Point textLocation = text.getLocation(); + textLocation.y += verticalPixelsToShrink; + textLocation.x += horizontalPixelsToShrink; + text.setLocation(textLocation); + + // Restore this class instance as the ControlListener on the StyledText + // widget so we know when the user resizes the Terminal view. + + text.addControlListener(this); + + // Make sure the exposed portion of the Composite canvas behind the + // StyledText control matches the background color of the StyledText + // control. + + Color textBackground = text.getBackground(); + text.getParent().setBackground(textBackground); + + // Scroll the StyledText widget to the bottommost position. + + text.setSelectionRange(text.getCharCount(), 0); + text.showSelection(); + + // Tell the parent object to redraw itself. This erases any partial + // line of text that might be left visible where the parent object is + // now exposed. This call only happens if the size needed to be changed, + // so it should not cause any flicker. + + text.getParent().redraw(); + } + + // If we are in a TELNET connection and we know the dimensions of the terminal, + // we give the size information to the TELNET connection object so it can + // communicate it to the TELNET server. If we are in a serial connection, + // there is nothing we can do to tell the remote host about the size of the + // terminal. + + TelnetConnection telnetConnection = terminal.getTelnetConnection(); + + if (telnetConnection != null && telnetConnection.isConnected() && + telnetConnection.isRemoteTelnetServer() && + widthInColumns != 0 && heightInLines != 0) + { + telnetConnection.setTerminalSize(widthInColumns, heightInLines); + } + } + + /** + * This method computes the the pixel width of a character in the current font. + * The Font object representing the font in the Terminal view doesn't provide the + * pixel width of the characters (even for a fixed width font). Instead, we get + * the pixel coordinates of the upper left corner of the bounding boxes for two + * adjacent characters on the same line and subtract the X coordinate of one from + * the X coordinate of the other. Simple, no? + */ + protected void computeCharacterPixelWidth() + { + // We can't assume there is any text in the terminal, so make sure there's at + // least two characters. + + text.replaceTextRange(0, 0, " "); //$NON-NLS-1$ + + Point firstCharLocation = text.getLocationAtOffset(0); + Point secondCharLocation = text.getLocationAtOffset(1); + + characterPixelWidth = secondCharLocation.x - firstCharLocation.x; + + text.replaceTextRange(0, 3, ""); //$NON-NLS-1$ + } + + /** + * This method deletes as many of the topmost lines of text as needed to keep the + * total number of lines of text in the Terminal view less than or equal to the + * limit configured in the preferences. If no limit is configured, this method + * does nothing. + */ + protected void deleteTopmostLines() + { + Preferences preferences = TerminalPlugin.getDefault().getPluginPreferences(); + boolean bLimitOutput = preferences.getBoolean(TerminalConsts.TERMINAL_PREF_LIMITOUTPUT); + + if (!bLimitOutput) + return; + + // Compute the number of lines to delete, but don't do anything if there are + // fewer lines in the terminal than the height of the terminal in lines. + + int totalLineCount = text.getLineCount(); + + if (totalLineCount <= heightInLines) + return; + + int bufferLineLimit = preferences.getInt(TerminalConsts.TERMINAL_PREF_BUFFERLINES); + + // Don't allow the user to set the buffer line limit to less than the height of + // the terminal in lines. + + if (bufferLineLimit <= heightInLines) + bufferLineLimit = heightInLines + 1; + + int linesToDelete = totalLineCount - bufferLineLimit; + + // Delete the lines. A small optimization here: don't do anything unless + // there's at least 5 lines to delete. + + if (linesToDelete >= 5) + text.replaceTextRange(0, text.getOffsetAtLine(linesToDelete), ""); //$NON-NLS-1$ + } + + /** + * This method returns the absolute line number of the line containing the + * cursor. The very first line of text (even if it is scrolled off the screen) is + * absolute line number 0. + * + * @return The absolute line number of the line containing the cursor. + */ + protected int absoluteCursorLine() + { + return text.getLineAtOffset(text.getCaretOffset()); + } + + /** + * This method returns the relative line number of the line comtaining the cursor. + * The returned line number is relative to the topmost visible line, which has + * relative line number 0. + * + * @return The relative line number of the line containing the cursor. + */ + protected int relativeCursorLine() + { + int totalLines = text.getLineCount(); + + if (totalLines <= heightInLines) + return text.getLineAtOffset(text.getCaretOffset()); + + return absoluteCursorLine() - totalLines + heightInLines; + } + + /** + * This method converts a visible line number (i.e., line 0 is the topmost visible + * line if the terminal is scrolled all the way down, and line number heightInLines + * - 1 is the bottommost visible line if the terminal is scrolled all the way down) + * to a line number as known to the StyledText widget. + */ + protected int absoluteLine(int visibleLineNumber) + { + int totalLines = text.getLineCount(); + + if (totalLines <= heightInLines) + return visibleLineNumber; + + return totalLines - heightInLines + visibleLineNumber; + } + + /** + * This method returns a String containing count ch characters. + * + * @return A String containing count ch characters. + */ + protected String generateString(char ch, int count) + { + char[] chars = new char[count]; + + for (int i = 0; i < chars.length; ++i) + chars[i] = ch; + + return new String(chars); + } + + /** + * This method moves the cursor to the specified line and column. Parameter + * targetLine is the line number of a screen line, so it has a minimum value + * of 0 (the topmost screen line) and a maximum value of heightInLines - 1 (the + * bottommost screen line). A line does not have to contain any text to move the + * cursor to any column in that line. + */ + protected void moveCursor(int targetLine, int targetColumn) + { + // Don't allow out of range target line and column values. + + if (targetLine < 0) targetLine = 0; + if (targetLine >= heightInLines) targetLine = heightInLines - 1; + + if (targetColumn < 0) targetColumn = 0; + if (targetColumn >= widthInColumns) targetColumn = widthInColumns - 1; + + // First, find out if we need to append newlines to the end of the text. This + // is necessary if there are fewer total lines of text than visible screen + // lines and the target line is below the bottommost line of text. + + int totalLines = text.getLineCount(); + + if (totalLines < heightInLines && targetLine >= totalLines) + text.append(generateString('\n', heightInLines - totalLines)); + + // Next, compute the offset of the start of the target line. + + int targetLineStartOffset = text.getOffsetAtLine(absoluteLine(targetLine)); + + // Next, find how many characters are in the target line. Be careful not to + // index off the end of the StyledText widget. + + int nextLineNumber = absoluteLine(targetLine + 1); + int targetLineLength; + + if (nextLineNumber >= totalLines) + { + // The target line is the bottommost line of text. + + targetLineLength = text.getCharCount() - targetLineStartOffset; + } + else + { + // The target line is not the bottommost line of text, so compute its + // length by subtracting the start offset of the target line from the start + // offset of the following line. + + targetLineLength = text.getOffsetAtLine(nextLineNumber) - targetLineStartOffset - 1; + } + + // Find out if we can just move the cursor without having to insert spaces at + // the end of the target line. + + if (targetColumn >= targetLineLength) + { + // The target line is not long enough to just move the cursor, so we have + // to append spaces to it before positioning the cursor. + + int spacesToAppend = targetColumn - targetLineLength; + + text.replaceTextRange(targetLineStartOffset + targetLineLength, 0, + generateString(' ', spacesToAppend)); + } + + // Now position the cursor. + + text.setCaretOffset(targetLineStartOffset + targetColumn); + + cursorColumn = targetColumn; + } + + /** + * This method moves the cursor down lines lines, but won't move the cursor + * past the bottom of the screen. This method does not cause any scrolling. + */ + protected void moveCursorDown(int lines) + { + moveCursor(relativeCursorLine() + lines, cursorColumn); + } + + /** + * This method moves the cursor up lines lines, but won't move the cursor + * past the top of the screen. This method does not cause any scrolling. + */ + protected void moveCursorUp(int lines) + { + moveCursor(relativeCursorLine() - lines, cursorColumn); + } + + /** + * This method moves the cursor forward columns columns, but won't move the + * cursor past the right edge of the screen, nor will it move the cursor onto the + * next line. This method does not cause any scrolling. + */ + protected void moveCursorForward(int columnsToMove) + { + moveCursor(relativeCursorLine(), cursorColumn + columnsToMove); + } + + /** + * This method moves the cursor backward columnsToMove columns, but won't + * move the cursor past the left edge of the screen, nor will it move the cursor + * onto the previous line. This method does not cause any scrolling. + */ + protected void moveCursorBackward(int columnsToMove) + { + // We don't call moveCursor() here, because this is optimized for backward + // cursor motion on a single line. + + if (columnsToMove > cursorColumn) + columnsToMove = cursorColumn; + + text.setCaretOffset(text.getCaretOffset() - columnsToMove); + + cursorColumn -= columnsToMove; + } +} diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalView.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalView.java new file mode 100644 index 00000000000..b5117f7f294 --- /dev/null +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalView.java @@ -0,0 +1,1047 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems, Inc. - initial implementation + * + *******************************************************************************/ + +package org.eclipse.tm.terminal; + +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.resource.FontRegistry; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.events.MenuListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.actions.RetargetAction; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.internal.WorkbenchWindow; +import org.eclipse.ui.part.ViewPart; + +public class TerminalView extends ViewPart implements TerminalTarget, TerminalConsts +{ + protected static final String m_SecondaryTerminalCountMutex = ""; //$NON-NLS-1$ + protected static int m_SecondaryTerminalCount = 0; + + protected TerminalCtrl m_ctlTerminal; + protected TerminalAction m_actionTerminalNewTerminal; + protected TerminalAction m_actionTerminalConnect; + protected TerminalAction m_actionTerminalDisconnect; + protected TerminalAction m_actionTerminalSettings; + protected TerminalAction m_actionEditCopy; + protected TerminalAction m_actionEditCut; + protected TerminalAction m_actionEditPaste; + protected TerminalAction m_actionEditClearAll; + protected TerminalAction m_actionEditSelectAll; + protected TerminalMenuHandlerEdit m_MenuHandlerEdit; + protected TerminalPropertyChangeHandler m_PropertyChangeHandler; + protected TerminalSettings m_TerminalSettings; + protected boolean m_bMenuAboutToShow; + /** Remember the item with which we contributed the shortcutt to unregister them again! */ + private IContextActivation fRememberedContextActivation; + + /** + * + */ + public TerminalView() + { + Logger.log("==============================================================="); //$NON-NLS-1$ + setupView(); + } + + // TerminalTarget interface + + /** + * + */ + public void execute(String strMsg,Object data) + { + if (strMsg.equals(ON_TERMINAL_FOCUS)) + { + onTerminalFocus(data); + } + else if (strMsg.equals(ON_TERMINAL_NEW_TERMINAL)) + { + onTerminalNewTerminal(data); + } + else if (strMsg.equals(ON_TERMINAL_CONNECT)) + { + onTerminalConnect(data); + } + else if (strMsg.equals(ON_UPDATE_TERMINAL_CONNECT)) + { + onUpdateTerminalConnect(data); + } + else if (strMsg.equals(ON_TERMINAL_CONNECTING)) + { + onTerminalConnecting(data); + } + else if (strMsg.equals(ON_TERMINAL_DISCONNECT)) + { + onTerminalDisconnect(data); + } + else if (strMsg.equals(ON_UPDATE_TERMINAL_DISCONNECT)) + { + onUpdateTerminalDisconnect(data); + } + else if (strMsg.equals(ON_TERMINAL_SETTINGS)) + { + onTerminalSettings(data); + } + else if (strMsg.equals(ON_UPDATE_TERMINAL_SETTINGS)) + { + onUpdateTerminalSettings(data); + } + else if (strMsg.equals(ON_TERMINAL_STATUS)) + { + onTerminalStatus(data); + } + else if (strMsg.equals(ON_TERMINAL_FONTCHANGED)) + { + onTerminalFontChanged(data); + } + else if (strMsg.equals(ON_EDIT_COPY)) + { + onEditCopy(data); + } + else if (strMsg.equals(ON_UPDATE_EDIT_COPY)) + { + onUpdateEditCopy(data); + } + else if (strMsg.equals(ON_EDIT_CUT)) + { + onEditCut(data); + } + else if (strMsg.equals(ON_UPDATE_EDIT_CUT)) + { + onUpdateEditCut(data); + } + else if (strMsg.equals(ON_EDIT_PASTE)) + { + onEditPaste(data); + } + else if (strMsg.equals(ON_UPDATE_EDIT_PASTE)) + { + onUpdateEditPaste(data); + } + else if (strMsg.equals(ON_EDIT_CLEARALL)) + { + onEditClearAll(data); + } + else if (strMsg.equals(ON_UPDATE_EDIT_CLEARALL)) + { + onUpdateEditClearAll(data); + } + else if (strMsg.equals(ON_EDIT_SELECTALL)) + { + onEditSelectAll(data); + } + else if (strMsg.equals(ON_UPDATE_EDIT_SELECTALL)) + { + onUpdateEditSelectAll(data); + } + else + { + } + } + + // Message handlers + + /** + * + */ + protected void onTerminalFocus(Object data) + { + m_ctlTerminal.setFocus(); + } + + /** + * Display a new Terminal view. This method is called when the user clicks the New + * Terminal button in any Terminal view's toolbar. + */ + protected void onTerminalNewTerminal(Object data) + { + Logger.log("creating new Terminal instance."); //$NON-NLS-1$ + + try + { + // The second argument to showView() is a unique String identifying the + // secondary view instance. If it ever matches a previously used secondary + // view identifier, then this call will not create a new Terminal view, + // which is undesireable. Therefore, we append the current time in + // milliseconds to the secondary view identifier to ensure it is always + // unique. This code runs only when the user clicks the New Terminal + // button, so there is no risk that this code will run twice in a single + // millisecond. + + getSite().getPage().showView("org.eclipse.tm.terminal.TerminalView",//$NON-NLS-1$ + "SecondaryTerminal" + System.currentTimeMillis(), //$NON-NLS-1$ + IWorkbenchPage.VIEW_ACTIVATE); + } + catch (PartInitException ex) + { + Logger.logException(ex); + } + } + + /** + * + */ + protected void onTerminalConnect(Object data) + { + if (m_ctlTerminal.isConnected()) + return; + + m_ctlTerminal.connectTerminal(m_TerminalSettings); + + execute(ON_UPDATE_TERMINAL_CONNECT,null); + execute(ON_UPDATE_TERMINAL_DISCONNECT,null); + execute(ON_UPDATE_TERMINAL_SETTINGS,null); + } + + /** + * + */ + protected void onTerminalConnecting(Object data) + { + execute(ON_UPDATE_TERMINAL_CONNECT,null); + execute(ON_UPDATE_TERMINAL_DISCONNECT,null); + execute(ON_UPDATE_TERMINAL_SETTINGS,null); + } + + /** + * + */ + protected void onUpdateTerminalConnect(Object data) + { + boolean bEnabled; + + bEnabled = ((!m_ctlTerminal.isConnecting()) && + (!m_ctlTerminal.isConnected())); + + m_actionTerminalConnect.setEnabled(bEnabled); + } + + /** + * + */ + protected void onTerminalDisconnect(Object data) + { + if ((!m_ctlTerminal.isConnecting()) && + (!m_ctlTerminal.isConnected())) + { + execute(ON_TERMINAL_STATUS, null); + execute(ON_UPDATE_TERMINAL_CONNECT, null); + execute(ON_UPDATE_TERMINAL_DISCONNECT, null); + execute(ON_UPDATE_TERMINAL_SETTINGS, null); + return; + } + + m_ctlTerminal.disconnectTerminal(); + + execute(ON_UPDATE_TERMINAL_CONNECT,null); + execute(ON_UPDATE_TERMINAL_DISCONNECT,null); + execute(ON_UPDATE_TERMINAL_SETTINGS,null); + } + + /** + * + */ + protected void onUpdateTerminalDisconnect(Object data) + { + boolean bEnabled; + + bEnabled = ((m_ctlTerminal.isConnecting()) || + (m_ctlTerminal.isConnected())); + + m_actionTerminalDisconnect.setEnabled(bEnabled); + } + + /** + * + */ + protected void onTerminalSettings(Object data) + { + TerminalSettingsDlg dlgTerminalSettings; + int nStatus; + + // When the settings dialog is opened, load the Terminal settings from the + // persistent settings. + + m_TerminalSettings.importSettings(getPartName()); + + dlgTerminalSettings = new TerminalSettingsDlg(getViewSite().getShell()); + dlgTerminalSettings.loadSettings(m_TerminalSettings); + + Logger.log("opening Settings dialog."); //$NON-NLS-1$ + + nStatus = dlgTerminalSettings.open(); + + if (nStatus == TerminalConsts.TERMINAL_ID_CANCEL) + { + Logger.log("Settings dialog cancelled."); //$NON-NLS-1$ + return; + } + + Logger.log("Settings dialog OK'ed."); //$NON-NLS-1$ + + // When the settings dialog is closed, we persist the Terminal settings. + + m_TerminalSettings.exportSettings(getPartName()); + + execute(ON_TERMINAL_CONNECT,null); + } + + /** + * + */ + protected void onUpdateTerminalSettings(Object data) + { + boolean bEnabled; + + bEnabled = ((!m_ctlTerminal.isConnecting()) && + (!m_ctlTerminal.isConnected())); + + m_actionTerminalSettings.setEnabled(bEnabled); + } + + /** + * + */ + protected void onTerminalStatus(Object data) + { + String strConnType; + String strConnected; + String strSerialPort; + String strBaudRate; + String strDataBits; + String strStopBits; + String strParity; + String strFlowControl; + String strHost; + String strNetworkPort; + String strText; + String strTitle; + + if (m_ctlTerminal.isDisposed()) + return; + + if (data != null) + { + // When parameter 'data' is not null, it is a String containing text to + // display in the view's content description line. This is used by class + // TerminalText when it processes an ANSI OSC escape sequence that commands + // the terminal to display text in its title bar. + + strTitle = (String)data; + } + else + { + // When parameter 'data' is null, we construct a descriptive string to + // display in the content description line. + + if (m_ctlTerminal.isConnecting()) + { + strConnected = "CONNECTING..."; //$NON-NLS-1$ + } + else if (m_ctlTerminal.isOpened()) + { + strConnected = "OPENED"; //$NON-NLS-1$ + } + else if (m_ctlTerminal.isConnected()) + { + strConnected = "CONNECTED"; //$NON-NLS-1$ + } + else + { + strConnected = "CLOSED"; //$NON-NLS-1$ + } + + strConnType = m_TerminalSettings.getConnType(); + if (strConnType.equals(TERMINAL_CONNTYPE_SERIAL)) + { + strSerialPort = m_TerminalSettings.getSerialPort(); + strBaudRate = m_TerminalSettings.getBaudRate(); + strDataBits = m_TerminalSettings.getDataBits(); + strStopBits = m_TerminalSettings.getStopBits(); + strParity = m_TerminalSettings.getParity(); + strFlowControl = m_TerminalSettings.getFlowControl(); + strText = " (" + //$NON-NLS-1$ + strSerialPort + + ", " + //$NON-NLS-1$ + strBaudRate + + ", " + //$NON-NLS-1$ + strDataBits + + ", " + //$NON-NLS-1$ + strStopBits + + ", " + //$NON-NLS-1$ + strParity + + ", " + //$NON-NLS-1$ + strFlowControl + + " - " + //$NON-NLS-1$ + strConnected + + ")"; //$NON-NLS-1$ + } + else if (strConnType.equals(TERMINAL_CONNTYPE_NETWORK)) + { + strHost = m_TerminalSettings.getHost(); + strNetworkPort = m_TerminalSettings.getNetworkPort(); + strText = " (" + //$NON-NLS-1$ + strHost + + ":" + //$NON-NLS-1$ + strNetworkPort + + " - " + //$NON-NLS-1$ + strConnected + + ")"; //$NON-NLS-1$ + } + else + { + strText = ""; //$NON-NLS-1$ + } + + strTitle = TERMINAL_PROP_TITLE + strText; + } + + setContentDescription(strTitle); + getViewSite().getActionBars().getStatusLineManager().setMessage(strTitle); + } + + /** + * + */ + protected void onTerminalFontChanged(Object data) + { + StyledText ctlText; + Font font; + + ctlText = m_ctlTerminal.getTextWidget(); + font = JFaceResources.getFont(TERMINAL_FONT_DEFINITION); + ctlText.setFont(font); + + // Tell the TerminalCtrl singleton that the font has changed. + + m_ctlTerminal.onFontChanged(); + } + + /** + * + */ + protected void onEditCopy(Object data) + { + ITextSelection selection; + String strText; + boolean bEnabled; + + selection = m_ctlTerminal.getSelection(); + strText = selection.getText(); + bEnabled = !strText.equals(""); //$NON-NLS-1$ + + if (bEnabled) + { + m_ctlTerminal.copy(); + } + else + { + m_ctlTerminal.sendKey('\u0003'); + } + } + + /** + * + */ + protected void onUpdateEditCopy(Object data) + { + ITextSelection selection; + String strText; + boolean bEnabled; + + if (m_bMenuAboutToShow) + { + selection = m_ctlTerminal.getSelection(); + strText = selection.getText(); + bEnabled = !strText.equals(""); //$NON-NLS-1$ + } + else + { + bEnabled = true; + } + + m_actionEditCopy.setEnabled(bEnabled); + } + + /** + * + */ + protected void onEditCut(Object data) + { + m_ctlTerminal.sendKey('\u0018'); + } + + /** + * + */ + protected void onUpdateEditCut(Object data) + { + boolean bEnabled; + + bEnabled = !m_bMenuAboutToShow; + m_actionEditCut.setEnabled(bEnabled); + } + + /** + * + */ + protected void onEditPaste(Object data) + { + m_ctlTerminal.paste(); + } + + /** + * + */ + protected void onUpdateEditPaste(Object data) + { + Clipboard clipboard; + TextTransfer textTransfer; + String strText; + boolean bConnected; + boolean bEnabled; + + clipboard = m_ctlTerminal.getClipboard(); + textTransfer = TextTransfer.getInstance(); + strText = (String)clipboard.getContents(textTransfer); + bConnected = m_ctlTerminal.isConnected(); + + bEnabled = ((strText != null) && + (!strText.equals("")) && //$NON-NLS-1$ + (bConnected)); + + m_actionEditPaste.setEnabled(bEnabled); + } + + /** + * + */ + protected void onEditClearAll(Object data) + { + m_ctlTerminal.clearTerminal(); + } + + /** + * + */ + protected void onUpdateEditClearAll(Object data) + { + boolean bEnabled; + + bEnabled = !m_ctlTerminal.isEmpty(); + m_actionEditClearAll.setEnabled(bEnabled); + } + + /** + * + */ + protected void onEditSelectAll(Object data) + { + m_ctlTerminal.selectAll(); + } + + /** + * + */ + protected void onUpdateEditSelectAll(Object data) + { + boolean bEnabled; + + bEnabled = !m_ctlTerminal.isEmpty(); + m_actionEditSelectAll.setEnabled(bEnabled); + } + + // ViewPart interface + + /** + * + */ + public void createPartControl(Composite wndParent) + { + // Bind plugin.xml key bindings to this plugin. Overrides global Ctrl-W key + // sequence. + + /** Activate the sy context allowing shortcuts like F3(open declaration) in the view */ + IContextService ctxtService = (IContextService)getSite().getService(IContextService.class); + fRememberedContextActivation = ctxtService.activateContext("org.eclipse.tm.terminal.TerminalPreferencePage"); //$NON-NLS-1$ + + setupControls(wndParent); + setupActions(); + setupMenus(); + setupToolBars(); + setupLocalMenus(); + setupLocalToolBars(); + setupContextMenus(); + setupListeners(wndParent); + + synchronized (m_SecondaryTerminalCountMutex) + { + setPartName(TERMINAL_PROP_TITLE + " " + m_SecondaryTerminalCount++); //$NON-NLS-1$ + } + + execute(ON_TERMINAL_STATUS,null); + } + + /** + * + */ + public void dispose() + { + Logger.log("entered."); //$NON-NLS-1$ + + setPartName("Terminal"); //$NON-NLS-1$ + + TerminalPlugin plugin; + FontRegistry fontRegistry; + IWorkbench workbench; + WorkbenchWindow workbenchWindow; + MenuManager menuMgr; + Menu menu; + + /** The context (for short cuts) was set above, now unset it again */ + if (fRememberedContextActivation != null) { + IContextService ctxService = (IContextService)getSite().getService(IContextService.class); + ctxService.deactivateContext(fRememberedContextActivation); + fRememberedContextActivation = null; + } + + fontRegistry = JFaceResources.getFontRegistry(); + plugin = TerminalPlugin.getDefault(); + workbench = plugin.getWorkbench(); + workbenchWindow = (WorkbenchWindow) workbench.getActiveWorkbenchWindow(); + menuMgr = workbenchWindow.getMenuManager(); + menuMgr = (MenuManager) menuMgr.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT); + menu = menuMgr.getMenu(); + + fontRegistry.removeListener(m_PropertyChangeHandler); + menuMgr.removeMenuListener(m_MenuHandlerEdit); + + if (menu != null) + menu.removeMenuListener(m_MenuHandlerEdit); + + m_ctlTerminal.disposeTerminal(); + } + + /** + * Passing the focus request to the viewer's control. + */ + public void setFocus() + { + execute(ON_TERMINAL_FOCUS,null); + } + + // Operations + + /** + * + */ + protected void setupView() + { + m_TerminalSettings = new TerminalSettings(getPartName()); + m_bMenuAboutToShow = false; + } + + /** + * This method creates the top-level control for the Terminal view. + */ + protected void setupControls(Composite wndParent) + { + try + { + m_ctlTerminal = new TerminalCtrl(this, wndParent); + } + catch (Exception ex) + { + Logger.logException(ex); + } + } + + /** + * + */ + protected void setupActions() + { + IViewSite viewSite; + IActionBars actionBars; + + viewSite = getViewSite(); + actionBars = viewSite.getActionBars(); + m_actionTerminalNewTerminal = new TerminalActionNewTerminal(this); + m_actionTerminalConnect = new TerminalActionConnect(this); + m_actionTerminalDisconnect = new TerminalActionDisconnect(this); + m_actionTerminalSettings = new TerminalActionSettings(this); + m_actionEditCopy = new TerminalActionCopy(this); + m_actionEditCut = new TerminalActionCut(this); + m_actionEditPaste = new TerminalActionPaste(this); + m_actionEditClearAll = new TerminalActionClearAll(this); + m_actionEditSelectAll = new TerminalActionSelectAll(this); + + actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), + m_actionEditCopy); + + actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), + m_actionEditCut); + + actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), + m_actionEditPaste); + + actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), + m_actionEditSelectAll); + } + + /** + * + */ + protected void setupMenus() + { + TerminalPlugin plugin; + IWorkbench workbench; + WorkbenchWindow workbenchWindow; + MenuManager menuMgr; + Menu menu; + + m_MenuHandlerEdit = new TerminalMenuHandlerEdit(); + plugin = TerminalPlugin.getDefault(); + workbench = plugin.getWorkbench(); + workbenchWindow = (WorkbenchWindow) workbench.getActiveWorkbenchWindow(); + menuMgr = workbenchWindow.getMenuManager(); + menuMgr = (MenuManager)menuMgr.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT); + menu = menuMgr.getMenu(); + + menuMgr.addMenuListener(m_MenuHandlerEdit); + menu.addMenuListener(m_MenuHandlerEdit); + } + + /** + * + */ + protected void setupToolBars() + { + } + + /** + * + */ + protected void setupLocalMenus() + { + } + + /** + * + */ + protected void setupLocalToolBars() + { + IViewSite viewSite; + IActionBars actionBars; + IToolBarManager toolBarMgr; + + viewSite = getViewSite(); + actionBars = viewSite.getActionBars(); + toolBarMgr = actionBars.getToolBarManager(); + + toolBarMgr.add(m_actionTerminalNewTerminal); + toolBarMgr.add(m_actionTerminalConnect); + toolBarMgr.add(m_actionTerminalDisconnect); + toolBarMgr.add(m_actionTerminalSettings); + } + + /** + * + */ + protected void setupContextMenus() + { + StyledText ctlText; + MenuManager menuMgr; + Menu menu; + TerminalContextMenuHandler contextMenuHandler; + + ctlText = m_ctlTerminal.getTextWidget(); + menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + menu = menuMgr.createContextMenu(ctlText); + contextMenuHandler = new TerminalContextMenuHandler(); + + ctlText.setMenu(menu); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(contextMenuHandler); + menu.addMenuListener(contextMenuHandler); + } + + /** + * + */ + protected void loadContextMenus(IMenuManager menuMgr) + { + menuMgr.add(m_actionEditCopy); + menuMgr.add(m_actionEditPaste); + menuMgr.add(new Separator()); + menuMgr.add(m_actionEditClearAll); + menuMgr.add(m_actionEditSelectAll); + + // Other plug-ins can contribute there actions here + menuMgr.add(new Separator("Additions")); //$NON-NLS-1$ + } + + /** + * + */ + protected void setupListeners(Composite wndParent) + { + getViewSite().getPage().addPartListener(new IPartListener() { + public void partClosed(IWorkbenchPart part) + { + if (part instanceof TerminalView) + part.dispose(); + } + + public void partActivated(IWorkbenchPart part) { } + public void partBroughtToTop(IWorkbenchPart part) { } + public void partDeactivated(IWorkbenchPart part) { } + public void partOpened(IWorkbenchPart part) { } + }); + + FontRegistry fontRegistry = JFaceResources.getFontRegistry(); + m_PropertyChangeHandler = new TerminalPropertyChangeHandler(); + fontRegistry.addListener(m_PropertyChangeHandler); + } + + // Inner classes + + /** + * + */ + protected class TerminalMenuHandlerEdit + implements MenuListener, + IMenuListener + { + /** + * + */ + protected String m_strActionDefinitionIdCopy; + protected String m_strActionDefinitionIdPaste; + protected String m_strActionDefinitionIdSelectAll; + + protected int m_nAcceleratorCopy; + protected int m_nAcceleratorPaste; + protected int m_nAcceleratorSelectAll; + + /** + * + */ + protected TerminalMenuHandlerEdit() + { + super(); + + m_strActionDefinitionIdCopy = ""; //$NON-NLS-1$ + m_strActionDefinitionIdPaste = ""; //$NON-NLS-1$ + m_strActionDefinitionIdSelectAll = ""; //$NON-NLS-1$ + + m_nAcceleratorCopy = 0; + m_nAcceleratorPaste = 0; + m_nAcceleratorSelectAll = 0; + } + + // IMenuListener interface + + /** + * + */ + public void menuAboutToShow(IMenuManager menuMgr) + { + ActionContributionItem item; + RetargetAction action; + + m_bMenuAboutToShow = true; + execute(ON_UPDATE_EDIT_COPY,null); + execute(ON_UPDATE_EDIT_CUT,null); + execute(ON_UPDATE_EDIT_PASTE,null); + execute(ON_UPDATE_EDIT_SELECTALL,null); + + item = (ActionContributionItem)menuMgr.find(ActionFactory.COPY.getId()); + action = (RetargetAction) item.getAction(); + m_strActionDefinitionIdCopy = action.getActionDefinitionId(); + m_nAcceleratorCopy = action.getAccelerator(); + action.setActionDefinitionId(null); + action.enableAccelerator(false); + item.update(); + + item = (ActionContributionItem)menuMgr.find(ActionFactory.PASTE.getId()); + action = (RetargetAction) item.getAction(); + m_strActionDefinitionIdPaste = action.getActionDefinitionId(); + m_nAcceleratorPaste = action.getAccelerator(); + action.setActionDefinitionId(null); + action.enableAccelerator(false); + item.update(); + + item = (ActionContributionItem)menuMgr.find(ActionFactory.SELECT_ALL.getId()); + action = (RetargetAction) item.getAction(); + m_strActionDefinitionIdSelectAll = action.getActionDefinitionId(); + m_nAcceleratorSelectAll = action.getAccelerator(); + action.setActionDefinitionId(null); + action.enableAccelerator(false); + item.update(); + } + + // MenuListener interface + + /** + * + */ + public void menuShown(MenuEvent event) + { + } + + /** + * + */ + public void menuHidden(MenuEvent event) + { + TerminalPlugin plugin; + IWorkbench workbench; + WorkbenchWindow workbenchWindow; + MenuManager menuMgr; + ActionContributionItem item; + RetargetAction action; + + m_bMenuAboutToShow = false; + execute(ON_UPDATE_EDIT_COPY,null); + execute(ON_UPDATE_EDIT_CUT,null); + + plugin = TerminalPlugin.getDefault(); + workbench = plugin.getWorkbench(); + workbenchWindow = (WorkbenchWindow) workbench.getActiveWorkbenchWindow(); + menuMgr = workbenchWindow.getMenuManager(); + menuMgr = (MenuManager) menuMgr.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT); + + item = (ActionContributionItem) menuMgr.find(ActionFactory.COPY.getId()); + action = (RetargetAction) item.getAction(); + action.setActionDefinitionId(m_strActionDefinitionIdCopy); + action.setAccelerator(m_nAcceleratorCopy); + action.enableAccelerator(true); + item.update(); + + item = (ActionContributionItem) menuMgr.find(ActionFactory.PASTE.getId()); + action = (RetargetAction) item.getAction(); + action.setActionDefinitionId(m_strActionDefinitionIdPaste); + action.setAccelerator(m_nAcceleratorPaste); + action.enableAccelerator(true); + item.update(); + + item = (ActionContributionItem) menuMgr.find(ActionFactory.SELECT_ALL.getId()); + action = (RetargetAction) item.getAction(); + action.setActionDefinitionId(m_strActionDefinitionIdSelectAll); + action.setAccelerator(m_nAcceleratorSelectAll); + action.enableAccelerator(true); + item.update(); + } + } + + /** + * + */ + protected class TerminalContextMenuHandler + implements MenuListener, IMenuListener + { + /** + * + */ + protected TerminalContextMenuHandler() + { + super(); + } + + // MenuListener interface + + /** + * + */ + public void menuHidden(MenuEvent event) + { + m_bMenuAboutToShow = false; + execute(ON_UPDATE_EDIT_COPY,null); + } + + public void menuShown(MenuEvent e) + { + } + + // IMenuListener interface + + /** + * + */ + public void menuAboutToShow(IMenuManager menuMgr) + { + m_bMenuAboutToShow = true; + execute(ON_UPDATE_EDIT_COPY,null); + execute(ON_UPDATE_EDIT_PASTE,null); + execute(ON_UPDATE_EDIT_CLEARALL,null); + execute(ON_UPDATE_EDIT_SELECTALL,null); + + loadContextMenus(menuMgr); + } + } + + /** + * + */ + protected class TerminalPropertyChangeHandler implements IPropertyChangeListener + { + /** + * + */ + protected TerminalPropertyChangeHandler() + { + super(); + } + + // IPropertyChangeListener interface + + public void propertyChange(PropertyChangeEvent event) + { + String strProperty; + + strProperty = event.getProperty(); + if (strProperty.equals(TERMINAL_FONT_DEFINITION)) + { + execute(ON_TERMINAL_FONTCHANGED,event); + } + else + { + } + } + } +}