diff --git a/org.eclipse.tm.terminal-feature/.project b/org.eclipse.tm.terminal-feature/.project
new file mode 100644
index 00000000000..c93208eb484
--- /dev/null
+++ b/org.eclipse.tm.terminal-feature/.project
@@ -0,0 +1,17 @@
+
+
+ * This class may be instantiated; it is not intended to be subclassed. + *
+ * + */ +public class PageBook extends Composite { + private Point minimumPageSize = new Point(0, 0); + /** + * Layout for the page container. + * + */ + private class PageLayout extends Layout { + public Point computeSize(Composite composite, int wHint, int hHint, boolean force) { + if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) + return new Point(wHint, hHint); + int x = minimumPageSize.x; + int y = minimumPageSize.y; + Control[] children = composite.getChildren(); + for (int i = 0; i < children.length; i++) { + Point size = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, force); + x = Math.max(x, size.x); + y = Math.max(y, size.y); + } + + if (wHint != SWT.DEFAULT) + x = wHint; + if (hHint != SWT.DEFAULT) + y = hHint; + return new Point(x, y); + } + + public void layout(Composite composite, boolean force) { + Rectangle rect = composite.getClientArea(); + Control[] children = composite.getChildren(); + for (int i = 0; i < children.length; i++) { + children[i].setSize(rect.width, rect.height); + } + } + } + /** + * Creates a new empty pagebook. + * + * @param parent the parent composite + * @param style the SWT style bits (use {@link SWT#NONE}) + */ + public PageBook(Composite parent, int style) { + super(parent, style); + setLayout(new PageLayout()); + } + /** + * The current control;null
if none.
+ */
+ private Control currentPage = null;
+
+ /**
+ * Shows the given page. This method has no effect if the given page is not
+ * contained in this pagebook.
+ *
+ * @param page the page to show
+ */
+ public void showPage(Control page) {
+
+ if (page == currentPage)
+ return;
+ if (page.getParent() != this)
+ return;
+
+ currentPage = page;
+
+ // show new page
+ if (page != null) {
+ if (!page.isDisposed()) {
+ page.setVisible(true);
+ layout(true);
+ // if (fRequestFocusOnShowPage)
+ // page.setFocus();
+ }
+ }
+
+ // hide old *after* new page has been made visible in order to avoid flashing
+ // we have to hide all other pages, because they might be visible
+ // by some other means...
+ Control[] pages=getChildren();
+ for (int i = 0; i < pages.length; i++) {
+ if(pages[i]!=page && !pages[i].isDisposed()) {
+ pages[i].setVisible(false);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/SettingStorePrefixDecorator.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/SettingStorePrefixDecorator.java
new file mode 100644
index 00000000000..e305e0a4ea8
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/SettingStorePrefixDecorator.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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.internal.view;
+
+import org.eclipse.tm.terminal.ISettingsStore;
+
+public class SettingStorePrefixDecorator implements ISettingsStore {
+ private final String fPrefix;
+ private final ISettingsStore fStore;
+ SettingStorePrefixDecorator(ISettingsStore store,String prefix) {
+ fPrefix=prefix;
+ fStore=store;
+ }
+ public String get(String key) {
+ return fStore.get(fPrefix+key);
+ }
+
+ public String get(String key, String defaultValue) {
+ return fStore.get(fPrefix+key,defaultValue);
+ }
+
+ public void put(String key, String value) {
+ fStore.put(fPrefix+key,value);
+ }
+
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/SettingsStore.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/SettingsStore.java
new file mode 100644
index 00000000000..ee715a4a54e
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/SettingsStore.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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.internal.view;
+
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.tm.terminal.ISettingsStore;
+
+/**
+ *
+ * A {@link IDialogSettings} based {@link ISettingsStore}.
+ *
+ * @author Michael Scharf
+ */
+class SettingsStore implements ISettingsStore {
+ final private IDialogSettings fDialogSettings;
+ final private String fPrefix;
+ public SettingsStore(String terminalPartName) {
+ fDialogSettings=TerminalViewPlugin.getDefault().getDialogSettings();
+ fPrefix=getClass().getName() + "." + terminalPartName + "."; //$NON-NLS-1$ //$NON-NLS-2$;
+ }
+
+ public String get(String key) {
+ return get(key,null);
+ }
+ public String get(String key, String defaultValue) {
+ String value = fDialogSettings.get(fPrefix + key);
+
+ if ((value == null) || (value.equals(""))) //$NON-NLS-1$
+ return defaultValue;
+
+ return value;
+ }
+
+ public void put(String key, String strValue) {
+ fDialogSettings.put(fPrefix + key , strValue);
+ }
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalPreferencePage.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalPreferencePage.java
new file mode 100644
index 00000000000..dddcdb38f82
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalPreferencePage.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * 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.internal.view;
+
+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 {
+ public static final String PREF_LIMITOUTPUT = "TerminalPrefLimitOutput"; //$NON-NLS-1$
+ public static final String PREF_BUFFERLINES = "TerminalPrefBufferLines"; //$NON-NLS-1$
+ public static final String PREF_TIMEOUT_SERIAL = "TerminalPrefTimeoutSerial"; //$NON-NLS-1$
+ public static final String PREF_TIMEOUT_NETWORK = "TerminalPrefTimeoutNetwork"; //$NON-NLS-1$
+ public static final boolean DEFAULT_LIMITOUTPUT = true;
+ public static final int DEFAULT_BUFFERLINES = 1000;
+ public static final int DEFAULT_TIMEOUT_SERIAL = 5;
+ public static final int DEFAULT_TIMEOUT_NETWORK = 5;
+
+
+ protected TerminalBooleanFieldEditor fEditorLimitOutput;
+
+ protected IntegerFieldEditor fEditorBufferSize;
+
+ protected IntegerFieldEditor fEditorSerialTimeout;
+
+ protected IntegerFieldEditor fEditorNetworkTimeout;
+ public TerminalPreferencePage() {
+ super(GRID);
+ }
+ public void onLimitOutputSelected() {
+ Button ctlButton;
+ Text ctlText;
+ Label ctlLabel;
+ boolean bEnabled;
+
+ ctlButton = fEditorLimitOutput.getChangeControl(getFieldEditorParent());
+ ctlText = fEditorBufferSize.getTextControl(getFieldEditorParent());
+ ctlLabel = fEditorBufferSize.getLabelControl(getFieldEditorParent());
+ bEnabled = ctlButton.getSelection();
+
+ ctlText.setEnabled(bEnabled);
+ ctlLabel.setEnabled(bEnabled);
+ }
+ protected void createFieldEditors() {
+ setupPage();
+ }
+ protected void initialize() {
+ super.initialize();
+
+ onLimitOutputSelected();
+ }
+ protected void performDefaults() {
+ super.performDefaults();
+
+ onLimitOutputSelected();
+ }
+ public void init(IWorkbench workbench) {
+ // do nothing
+ }
+ protected void setupPage() {
+ setupData();
+ setupEditors();
+ setupListeners();
+ }
+ protected void setupData() {
+ TerminalViewPlugin plugin;
+ IPreferenceStore preferenceStore;
+
+ plugin = TerminalViewPlugin.getDefault();
+ preferenceStore = plugin.getPreferenceStore();
+ setPreferenceStore(preferenceStore);
+ }
+ protected void setupEditors() {
+ fEditorLimitOutput = new TerminalBooleanFieldEditor(
+ PREF_LIMITOUTPUT, ViewMessages.LIMITOUTPUT,
+ getFieldEditorParent());
+ fEditorBufferSize = new IntegerFieldEditor(PREF_BUFFERLINES,
+ ViewMessages.BUFFERLINES, getFieldEditorParent());
+ fEditorSerialTimeout = new IntegerFieldEditor(
+ PREF_TIMEOUT_SERIAL, ViewMessages.SERIALTIMEOUT,
+ getFieldEditorParent());
+ fEditorNetworkTimeout = new IntegerFieldEditor(
+ PREF_TIMEOUT_NETWORK, ViewMessages.NETWORKTIMEOUT,
+ getFieldEditorParent());
+
+ fEditorBufferSize.setValidRange(0, Integer.MAX_VALUE);
+ fEditorSerialTimeout.setValidRange(0, Integer.MAX_VALUE);
+ fEditorNetworkTimeout.setValidRange(0, Integer.MAX_VALUE);
+
+ addField(fEditorLimitOutput);
+ addField(fEditorBufferSize);
+ addField(fEditorSerialTimeout);
+ addField(fEditorNetworkTimeout);
+ }
+ protected void setupListeners() {
+ TerminalSelectionHandler selectionHandler;
+ Button ctlButton;
+
+ selectionHandler = new TerminalSelectionHandler();
+ ctlButton = fEditorLimitOutput.getChangeControl(getFieldEditorParent());
+ ctlButton.addSelectionListener(selectionHandler);
+ }
+ public class TerminalBooleanFieldEditor extends BooleanFieldEditor {
+ public TerminalBooleanFieldEditor(String strName, String strLabel,
+ Composite ctlParent) {
+ super(strName, strLabel, ctlParent);
+ }
+ public Button getChangeControl(Composite parent) {
+ return super.getChangeControl(parent);
+ }
+ }
+ protected class TerminalSelectionHandler extends SelectionAdapter {
+ protected TerminalSelectionHandler() {
+ super();
+ }
+ public void widgetSelected(SelectionEvent event) {
+ Object source;
+ Button ctlButton;
+
+ source = event.getSource();
+ ctlButton = fEditorLimitOutput
+ .getChangeControl(getFieldEditorParent());
+
+ if (source == ctlButton) {
+ onLimitOutputSelected();
+ }
+ }
+
+ }
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalSettingsDlg.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalSettingsDlg.java
new file mode 100644
index 00000000000..608c0c7cce7
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalSettingsDlg.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * 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.internal.view;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tm.terminal.ISettingsPage;
+import org.eclipse.tm.terminal.ITerminalConnector;
+
+class TerminalSettingsDlg extends Dialog {
+ private Combo fCtlConnTypeCombo;
+ private final ITerminalConnector[] fConnectors;
+ private final ISettingsPage[] fPages;
+ private int fSelectedConnector;
+ private PageBook fPageBook;
+
+ public TerminalSettingsDlg(Shell shell, ITerminalConnector[] connectors, ITerminalConnector connector) {
+ super(shell);
+ fConnectors=connectors;
+ fPages=new ISettingsPage[fConnectors.length];
+ for (int i = 0; i < fConnectors.length; i++) {
+ fPages[i]=fConnectors[i].makeSettingsPage();
+ if(fConnectors[i]==connector)
+ fSelectedConnector=i;
+ }
+ }
+ protected void okPressed() {
+ if (!validateSettings())
+ return;
+ if(fSelectedConnector>=0) {
+ fPages[fSelectedConnector].saveSettings();
+ }
+ super.okPressed();
+ }
+ protected void cancelPressed() {
+ fSelectedConnector=-1;
+ super.cancelPressed();
+ }
+ public int open() {
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ return super.open();
+ }
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+
+ newShell.setText(ViewMessages.TERMINALSETTINGS);
+ }
+ protected Control createDialogArea(Composite parent) {
+ Composite ctlComposite = (Composite) super.createDialogArea(parent);
+ setupPanel(ctlComposite);
+ setupListeners();
+ initFields();
+
+ return ctlComposite;
+ }
+ private void initFields() {
+ // Load controls
+ for (int i = 0; i < fPages.length; i++) {
+ String name=fPages[i].getName();
+ fCtlConnTypeCombo.add(name);
+ if(fSelectedConnector==i) {
+ fCtlConnTypeCombo.select(i);
+ selectPage(i);
+ }
+ }
+ }
+ private boolean validateSettings() {
+ if(fSelectedConnector<0)
+ return true;
+ return fPages[fSelectedConnector].validateSettings();
+ }
+ private void setupPanel(Composite wndParent) {
+ setupConnTypePanel(wndParent);
+ setupSettingsGroup(wndParent);
+ }
+ private 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(ViewMessages.CONNECTIONTYPE + ":"); //$NON-NLS-1$
+
+ fCtlConnTypeCombo = new Combo(wndGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
+ gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.widthHint = 200;
+ fCtlConnTypeCombo.setLayoutData(gridData);
+ }
+ private void setupSettingsGroup(Composite parent) {
+ Group group = new Group(parent, SWT.NONE);
+ GridLayout gridLayout = new GridLayout();
+ GridData gridData = new GridData(GridData.FILL_BOTH);
+
+ group.setText(ViewMessages.SETTINGS + ":"); //$NON-NLS-1$
+ group.setLayout(gridLayout);
+ group.setLayoutData(gridData);
+ fPageBook=new PageBook(group,SWT.NONE);
+ for (int i = 0; i < fPages.length; i++) {
+ fPages[i].createControl(fPageBook);
+ }
+ }
+ private void setupListeners() {
+ fCtlConnTypeCombo.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent event) {
+ selectPage(fCtlConnTypeCombo.getSelectionIndex());
+ }
+ });
+ }
+ public ITerminalConnector getConnector() {
+ if(fSelectedConnector>=0)
+ return fConnectors[fSelectedConnector];
+ return null;
+ }
+ private void selectPage(int index) {
+ fSelectedConnector=index;
+ Control[] pages=fPageBook.getChildren();
+ fPageBook.showPage(pages[fSelectedConnector]);
+ }
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalView.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalView.java
new file mode 100644
index 00000000000..38cfea804a0
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalView.java
@@ -0,0 +1,657 @@
+/*******************************************************************************
+ * 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.internal.view;
+
+import org.eclipse.core.runtime.Preferences;
+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.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.window.Window;
+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.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.tm.terminal.ISettingsStore;
+import org.eclipse.tm.terminal.ITerminalConnector;
+import org.eclipse.tm.terminal.Logger;
+import org.eclipse.tm.terminal.TerminalConnectorExtension;
+import org.eclipse.tm.terminal.TerminalState;
+import org.eclipse.tm.terminal.internal.actions.TerminalAction;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionClearAll;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionConnect;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionCopy;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionCut;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionDisconnect;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionNewTerminal;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionPaste;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionSelectAll;
+import org.eclipse.tm.terminal.internal.actions.TerminalActionSettings;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPage;
+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;
+import org.eclipse.tm.terminal.control.ITerminalListener;
+import org.eclipse.tm.terminal.control.ITerminalViewControl;
+import org.eclipse.tm.terminal.control.TerminalViewControlFactory;
+
+public class TerminalView extends ViewPart implements ITerminalView, ITerminalListener {
+ public static final String FONT_DEFINITION = "terminal.views.view.font.definition"; //$NON-NLS-1$
+ protected static final String fSecondaryTerminalCountMutex = ""; //$NON-NLS-1$
+
+ protected static int fSecondaryTerminalCount = 0;
+
+ protected ITerminalViewControl fCtlTerminal;
+
+ protected TerminalAction fActionTerminalNewTerminal;
+
+ protected TerminalAction fActionTerminalConnect;
+
+ protected TerminalAction fActionTerminalDisconnect;
+
+ protected TerminalAction fActionTerminalSettings;
+
+ protected TerminalAction fActionEditCopy;
+
+ protected TerminalAction fActionEditCut;
+
+ protected TerminalAction fActionEditPaste;
+
+ protected TerminalAction fActionEditClearAll;
+
+ protected TerminalAction fActionEditSelectAll;
+
+ protected TerminalMenuHandlerEdit fMenuHandlerEdit;
+
+ protected TerminalPropertyChangeHandler fPropertyChangeHandler;
+
+ protected boolean fMenuAboutToShow;
+
+ private ISettingsStore fStore;
+
+ /** Remember the item with which we contributed the shortcut to unregister them again! */
+ private IContextActivation fRememberedContextActivation;
+
+ public TerminalView() {
+ Logger
+ .log("==============================================================="); //$NON-NLS-1$
+ }
+
+ private void XXXXX() {
+ Preferences preferences = TerminalViewPlugin.getDefault().getPluginPreferences();
+ boolean bLimitOutput = preferences.getBoolean(TerminalPreferencePage.PREF_LIMITOUTPUT);
+ int bufferLineLimit = preferences.getInt(TerminalPreferencePage.PREF_BUFFERLINES);
+
+ }
+ // TerminalTarget interface
+ public void setState(final TerminalState state) {
+ Runnable runnable=new Runnable() {
+ public void run() {
+ updateStatus();
+ onTerminalStatus();
+ }
+ };
+ if(Thread.currentThread()==Display.getDefault().getThread())
+ runnable.run();
+ else
+ Display.getDefault().syncExec(runnable);
+ }
+
+
+ /**
+ * Display a new Terminal view. This method is called when the user clicks the New
+ * Terminal button in any Terminal view's toolbar.
+ */
+ public void onTerminalNewTerminal() {
+ 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.view.TerminalView",//$NON-NLS-1$
+ "SecondaryTerminal" + System.currentTimeMillis(), //$NON-NLS-1$
+ IWorkbenchPage.VIEW_ACTIVATE);
+ } catch (PartInitException ex) {
+ Logger.logException(ex);
+ }
+ }
+
+ public void onTerminalConnect() {
+ if (isConnected())
+ return;
+ if(fCtlTerminal.getTerminalConnection()==null)
+ setConnector(showSettingsDialog());
+ fCtlTerminal.connectTerminal();
+ }
+
+ public void updateStatus() {
+ updateTerminalConnect();
+ updateTerminalDisconnect();
+ updateTerminalSettings();
+ }
+
+ public void updateTerminalConnect() {
+ boolean bEnabled = ((!isConnecting()) && (!fCtlTerminal.isConnected()));
+
+ fActionTerminalConnect.setEnabled(bEnabled);
+ }
+
+ private boolean isConnecting() {
+ return fCtlTerminal.getState()==TerminalState.CONNECTING;
+ }
+ private boolean isConnected() {
+ return fCtlTerminal.getState()==TerminalState.CONNECTED;
+ }
+ public void onTerminalDisconnect() {
+ fCtlTerminal.disconnectTerminal();
+ }
+
+ public void updateTerminalDisconnect() {
+ boolean bEnabled = ((isConnecting()) || (fCtlTerminal.isConnected()));
+ fActionTerminalDisconnect.setEnabled(bEnabled);
+ }
+
+ public void onTerminalSettings() {
+ setConnector(showSettingsDialog());
+
+ onTerminalConnect();
+ }
+
+ private ITerminalConnector showSettingsDialog() {
+ // When the settings dialog is opened, load the Terminal settings from the
+ // persistent settings.
+
+ TerminalSettingsDlg dlgTerminalSettings = new TerminalSettingsDlg(getViewSite().getShell(),fCtlTerminal.getConnectors(),fCtlTerminal.getTerminalConnection());
+
+ Logger.log("opening Settings dialog."); //$NON-NLS-1$
+
+ if (dlgTerminalSettings.open() == Window.CANCEL) {
+ Logger.log("Settings dialog cancelled."); //$NON-NLS-1$
+ return null;
+ }
+
+ Logger.log("Settings dialog OK'ed."); //$NON-NLS-1$
+
+ // When the settings dialog is closed, we persist the Terminal settings.
+
+ saveSettings();
+ return dlgTerminalSettings.getConnector();
+ }
+
+ private void setConnector(ITerminalConnector connector) {
+ fCtlTerminal.setConnector(connector);
+ }
+
+ public void updateTerminalSettings() {
+ boolean bEnabled;
+
+ bEnabled = ((!isConnecting()) && (!fCtlTerminal
+ .isConnected()));
+
+ fActionTerminalSettings.setEnabled(bEnabled);
+ }
+
+ public void setTerminalTitle(String strTitle) {
+ if (fCtlTerminal.isDisposed())
+ return;
+
+ if (strTitle != 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.
+ } else {
+ // When parameter 'data' is null, we construct a descriptive string to
+ // display in the content description line.
+ String strConnected = getStateDisplayName(fCtlTerminal.getState());
+ String status=""; //$NON-NLS-1$
+ status=fCtlTerminal.getStatusString(strConnected);
+ strTitle = ViewMessages.PROP_TITLE + status;
+ }
+
+ setContentDescription(strTitle);
+ getViewSite().getActionBars().getStatusLineManager().setMessage(
+ strTitle);
+ }
+ public void onTerminalStatus() {
+ setTerminalTitle(null);
+ }
+
+ private String getStateDisplayName(TerminalState state) {
+ if(state==TerminalState.CONNECTED) {
+ return ViewMessages.STATE_CONNECTED;
+ } else if(state==TerminalState.CONNECTING) {
+ return ViewMessages.STATE_CONNECTING;
+ } else if(state==TerminalState.OPENED) {
+ return ViewMessages.STATE_OPENED;
+ } else if(state==TerminalState.CLOSED) {
+ return ViewMessages.STATE_CLOSED;
+ } else {
+ throw new IllegalStateException(state.toString());
+ }
+ }
+
+ public void onTerminalFontChanged() {
+ fCtlTerminal.getCtlText().setFont(JFaceResources.getFont(FONT_DEFINITION));
+
+ // Tell the TerminalControl singleton that the font has changed.
+
+ fCtlTerminal.onFontChanged();
+ }
+
+ public void onEditCopy() {
+ String selection=fCtlTerminal.getSelection();
+
+ if (!selection.equals("")) {//$NON-NLS-1$
+ fCtlTerminal.copy();
+ } else {
+ fCtlTerminal.sendKey('\u0003');
+ }
+ }
+
+ public void updateEditCopy() {
+ boolean bEnabled=true;
+
+ if (fMenuAboutToShow) {
+ bEnabled = fCtlTerminal.getSelection().length()>0;
+ }
+
+ fActionEditCopy.setEnabled(bEnabled);
+ }
+
+ public void onEditCut() {
+ fCtlTerminal.sendKey('\u0018');
+ }
+
+ public void updateEditCut() {
+ boolean bEnabled;
+
+ bEnabled = !fMenuAboutToShow;
+ fActionEditCut.setEnabled(bEnabled);
+ }
+
+ public void onEditPaste() {
+ fCtlTerminal.paste();
+ }
+
+ public void updateEditPaste() {
+ String strText = (String) fCtlTerminal.getClipboard().getContents(TextTransfer.getInstance());
+
+ boolean bEnabled = ((strText != null) && (!strText.equals("")) && (isConnected()));//$NON-NLS-1$
+
+ fActionEditPaste.setEnabled(bEnabled);
+ }
+
+ public void onEditClearAll() {
+ fCtlTerminal.clearTerminal();
+ }
+
+ public void updateEditClearAll() {
+ fActionEditClearAll.setEnabled(!fCtlTerminal.isEmpty());
+ }
+
+ public void onEditSelectAll() {
+ fCtlTerminal.selectAll();
+ }
+
+ public void updateEditSelectAll() {
+ fActionEditSelectAll.setEnabled(!fCtlTerminal.isEmpty());
+ }
+
+ // ViewPart interface
+
+ public void createPartControl(Composite wndParent) {
+ // Bind plugin.xml key bindings to this plugin. Overrides global Control-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$
+
+ synchronized (fSecondaryTerminalCountMutex) {
+ setPartName(ViewMessages.PROP_TITLE + " " + fSecondaryTerminalCount++); //$NON-NLS-1$
+ }
+
+ setupControls(wndParent);
+ setupActions();
+ setupMenus();
+ setupLocalToolBars();
+ setupContextMenus();
+ setupListeners(wndParent);
+
+ onTerminalStatus();
+ }
+
+ public void dispose() {
+ Logger.log("entered."); //$NON-NLS-1$
+
+ setPartName("Terminal"); //$NON-NLS-1$
+
+ TerminalViewPlugin plugin;
+ 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;
+ }
+
+ JFaceResources.getFontRegistry().removeListener(fPropertyChangeHandler);
+ plugin = TerminalViewPlugin.getDefault();
+ workbench = plugin.getWorkbench();
+ workbenchWindow = (WorkbenchWindow) workbench
+ .getActiveWorkbenchWindow();
+ menuMgr = workbenchWindow.getMenuManager();
+ menuMgr = (MenuManager) menuMgr
+ .findMenuUsingPath(IWorkbenchActionConstants.M_EDIT);
+ menu = menuMgr.getMenu();
+
+ menuMgr.removeMenuListener(fMenuHandlerEdit);
+
+ if (menu != null)
+ menu.removeMenuListener(fMenuHandlerEdit);
+
+ fCtlTerminal.disposeTerminal();
+ }
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ public void setFocus() {
+ fCtlTerminal.setFocus();
+ }
+
+ /**
+ * This method creates the top-level control for the Terminal view.
+ */
+ protected void setupControls(Composite wndParent) {
+ ITerminalConnector[] connectors=TerminalConnectorExtension.getTerminalConnectors();
+ fCtlTerminal = TerminalViewControlFactory.makeControl(this, wndParent, connectors);
+ String connectionType=getStore().get("ConnectionType"); //$NON-NLS-1$
+ for (int i = 0; i < connectors.length; i++) {
+ connectors[i].load(getStore(connectors[i]));
+ if(connectors[i].getId().equals(connectionType))
+ fCtlTerminal.setConnector(connectors[i]);
+ }
+ }
+ private void saveSettings() {
+ ITerminalConnector[] connectors=fCtlTerminal.getConnectors();
+ for (int i = 0; i < connectors.length; i++) {
+ connectors[i].save(getStore(connectors[i]));
+ }
+ if(fCtlTerminal.getTerminalConnection()!=null) {
+ getStore().put("ConnectionType",fCtlTerminal.getTerminalConnection().getId()); //$NON-NLS-1$
+ }
+ }
+
+ private ISettingsStore getStore() {
+ if(fStore==null)
+ fStore=new SettingsStore(getPartName());
+ return fStore;
+ }
+
+ private ISettingsStore getStore(ITerminalConnector connector) {
+ return new SettingStorePrefixDecorator(getStore(),connector.getClass().getName()+"."); //$NON-NLS-1$
+ }
+
+ protected void setupActions() {
+ fActionTerminalNewTerminal = new TerminalActionNewTerminal(this);
+ fActionTerminalConnect = new TerminalActionConnect(this);
+ fActionTerminalDisconnect = new TerminalActionDisconnect(this);
+ fActionTerminalSettings = new TerminalActionSettings(this);
+ fActionEditCopy = new TerminalActionCopy(this);
+ fActionEditCut = new TerminalActionCut(this);
+ fActionEditPaste = new TerminalActionPaste(this);
+ fActionEditClearAll = new TerminalActionClearAll(this);
+ fActionEditSelectAll = new TerminalActionSelectAll(this);
+
+ IActionBars actionBars = getViewSite().getActionBars();
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), fActionEditCopy);
+
+ actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), fActionEditCut);
+
+ actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), fActionEditPaste);
+
+ actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), fActionEditSelectAll);
+ }
+
+ protected void setupMenus() {
+ TerminalViewPlugin plugin;
+ IWorkbench workbench;
+ WorkbenchWindow workbenchWindow;
+ MenuManager menuMgr;
+ Menu menu;
+
+ fMenuHandlerEdit = new TerminalMenuHandlerEdit();
+ plugin = TerminalViewPlugin.getDefault();
+ workbench = plugin.getWorkbench();
+ workbenchWindow = (WorkbenchWindow) workbench
+ .getActiveWorkbenchWindow();
+ menuMgr = workbenchWindow.getMenuManager();
+ menuMgr = (MenuManager) menuMgr
+ .findMenuUsingPath(IWorkbenchActionConstants.M_EDIT);
+ menu = menuMgr.getMenu();
+
+ menuMgr.addMenuListener(fMenuHandlerEdit);
+ menu.addMenuListener(fMenuHandlerEdit);
+ }
+
+ protected void setupLocalToolBars() {
+ IToolBarManager toolBarMgr = getViewSite().getActionBars().getToolBarManager();
+
+ toolBarMgr.add(fActionTerminalNewTerminal);
+ toolBarMgr.add(fActionTerminalConnect);
+ toolBarMgr.add(fActionTerminalDisconnect);
+ toolBarMgr.add(fActionTerminalSettings);
+ }
+
+ protected void setupContextMenus() {
+ StyledText ctlText;
+ MenuManager menuMgr;
+ Menu menu;
+ TerminalContextMenuHandler contextMenuHandler;
+
+ ctlText = fCtlTerminal.getCtlText();
+ 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(fActionEditCopy);
+ menuMgr.add(fActionEditPaste);
+ menuMgr.add(new Separator());
+ menuMgr.add(fActionEditClearAll);
+ menuMgr.add(fActionEditSelectAll);
+
+ // Other plug-ins can contribute there actions here
+ menuMgr.add(new Separator("Additions")); //$NON-NLS-1$
+ }
+
+ protected void setupListeners(Composite wndParent) {
+ fPropertyChangeHandler = new TerminalPropertyChangeHandler();
+ JFaceResources.getFontRegistry().addListener(fPropertyChangeHandler);
+ }
+
+ // Inner classes
+
+ protected class TerminalMenuHandlerEdit implements MenuListener, IMenuListener {
+ protected String fActionDefinitionIdCopy;
+
+ protected String fActionDefinitionIdPaste;
+
+ protected String fActionDefinitionIdSelectAll;
+
+ protected int fAcceleratorCopy;
+
+ protected int fAcceleratorPaste;
+
+ protected int fAcceleratorSelectAll;
+
+ protected TerminalMenuHandlerEdit() {
+ super();
+
+ fActionDefinitionIdCopy = ""; //$NON-NLS-1$
+ fActionDefinitionIdPaste = ""; //$NON-NLS-1$
+ fActionDefinitionIdSelectAll = ""; //$NON-NLS-1$
+
+ fAcceleratorCopy = 0;
+ fAcceleratorPaste = 0;
+ fAcceleratorSelectAll = 0;
+ }
+ public void menuAboutToShow(IMenuManager menuMgr) {
+
+ fMenuAboutToShow = true;
+ updateEditCopy();
+ updateEditCut();
+ updateEditPaste();
+ updateEditSelectAll();
+
+ ActionContributionItem item = (ActionContributionItem) menuMgr.find(ActionFactory.COPY.getId());
+ RetargetAction action = (RetargetAction) item.getAction();
+ fActionDefinitionIdCopy = action.getActionDefinitionId();
+ fAcceleratorCopy = action.getAccelerator();
+ action.setActionDefinitionId(null);
+ action.enableAccelerator(false);
+ item.update();
+
+ item = (ActionContributionItem) menuMgr.find(ActionFactory.PASTE.getId());
+ action = (RetargetAction) item.getAction();
+ fActionDefinitionIdPaste = action.getActionDefinitionId();
+ fAcceleratorPaste = action.getAccelerator();
+ action.setActionDefinitionId(null);
+ action.enableAccelerator(false);
+ item.update();
+
+ item = (ActionContributionItem) menuMgr.find(ActionFactory.SELECT_ALL.getId());
+ action = (RetargetAction) item.getAction();
+ fActionDefinitionIdSelectAll = action.getActionDefinitionId();
+ fAcceleratorSelectAll = action.getAccelerator();
+ action.setActionDefinitionId(null);
+ action.enableAccelerator(false);
+ item.update();
+ }
+ public void menuShown(MenuEvent event) {
+ // do nothing
+ }
+ public void menuHidden(MenuEvent event) {
+ TerminalViewPlugin plugin;
+ IWorkbench workbench;
+ WorkbenchWindow workbenchWindow;
+ MenuManager menuMgr;
+ ActionContributionItem item;
+ RetargetAction action;
+
+ fMenuAboutToShow = false;
+ updateEditCopy();
+ updateEditCut();
+
+ plugin = TerminalViewPlugin.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(fActionDefinitionIdCopy);
+ action.setAccelerator(fAcceleratorCopy);
+ action.enableAccelerator(true);
+ item.update();
+
+ item = (ActionContributionItem) menuMgr.find(ActionFactory.PASTE
+ .getId());
+ action = (RetargetAction) item.getAction();
+ action.setActionDefinitionId(fActionDefinitionIdPaste);
+ action.setAccelerator(fAcceleratorPaste);
+ action.enableAccelerator(true);
+ item.update();
+
+ item = (ActionContributionItem) menuMgr
+ .find(ActionFactory.SELECT_ALL.getId());
+ action = (RetargetAction) item.getAction();
+ action.setActionDefinitionId(fActionDefinitionIdSelectAll);
+ action.setAccelerator(fAcceleratorSelectAll);
+ action.enableAccelerator(true);
+ item.update();
+ }
+ }
+
+ protected class TerminalContextMenuHandler implements MenuListener, IMenuListener {
+ public void menuHidden(MenuEvent event) {
+ fMenuAboutToShow = false;
+ updateEditCopy();
+ }
+
+ public void menuShown(MenuEvent e) {
+ //
+ }
+ public void menuAboutToShow(IMenuManager menuMgr) {
+ fMenuAboutToShow = true;
+ updateEditCopy();
+ updateEditCut();
+ updateEditPaste();
+ updateEditClearAll();
+
+ loadContextMenus(menuMgr);
+ }
+ }
+
+ protected class TerminalPropertyChangeHandler implements IPropertyChangeListener {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(FONT_DEFINITION)) {
+ onTerminalFontChanged();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalViewPlugin.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalViewPlugin.java
new file mode 100644
index 00000000000..2a300c72ca6
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/TerminalViewPlugin.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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.internal.view;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+
+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.tm.terminal.Logger;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+public class TerminalViewPlugin extends AbstractUIPlugin {
+ protected static TerminalViewPlugin fDefault;
+ 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$
+
+ /**
+ * The constructor.
+ */
+ public TerminalViewPlugin() {
+ fDefault = this;
+ }
+ protected void initializeImageRegistry(ImageRegistry imageRegistry) {
+ HashMap map;
+
+ map = new HashMap();
+
+ try {
+ // Local toolbars
+ map.put(ImageConsts.IMAGE_NEW_TERMINAL, "newterminal.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_CLCL_CONNECT, "connect_co.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_CLCL_DISCONNECT, "disconnect_co.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_CLCL_SETTINGS, "properties_tsk.gif"); //$NON-NLS-1$
+
+ loadImageRegistry(imageRegistry, ImageConsts.IMAGE_DIR_LOCALTOOL, map);
+
+ map.clear();
+
+ // Enabled local toolbars
+ map.put(ImageConsts.IMAGE_NEW_TERMINAL, "newterminal.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_ELCL_CONNECT, "connect_co.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_ELCL_DISCONNECT, "disconnect_co.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_ELCL_SETTINGS, "properties_tsk.gif"); //$NON-NLS-1$
+
+ loadImageRegistry(imageRegistry, ImageConsts.IMAGE_DIR_ELCL, map);
+
+ map.clear();
+
+ // Disabled local toolbars
+ map.put(ImageConsts.IMAGE_NEW_TERMINAL, "newterminal.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_DLCL_CONNECT, "connect_co.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_DLCL_DISCONNECT, "disconnect_co.gif"); //$NON-NLS-1$
+ map.put(ImageConsts.IMAGE_DLCL_SETTINGS, "properties_tsk.gif"); //$NON-NLS-1$
+
+ loadImageRegistry(imageRegistry, ImageConsts.IMAGE_DIR_DLCL, map);
+
+ map.clear();
+ } catch (MalformedURLException malformedURLException) {
+ malformedURLException.printStackTrace();
+ }
+ }
+ protected void initializeDefaultPreferences(IPreferenceStore store) {
+ store.setDefault(TerminalPreferencePage.PREF_LIMITOUTPUT,
+ TerminalPreferencePage.DEFAULT_LIMITOUTPUT);
+ store.setDefault(TerminalPreferencePage.PREF_BUFFERLINES,
+ TerminalPreferencePage.DEFAULT_BUFFERLINES);
+ store.setDefault(TerminalPreferencePage.PREF_TIMEOUT_SERIAL,
+ TerminalPreferencePage.DEFAULT_TIMEOUT_SERIAL);
+ store.setDefault(TerminalPreferencePage.PREF_TIMEOUT_NETWORK,
+ TerminalPreferencePage.DEFAULT_TIMEOUT_NETWORK);
+ }
+ /**
+ * Returns the shared instance.
+ */
+ public static TerminalViewPlugin getDefault() {
+ return fDefault;
+ }
+
+ public static boolean isLogInfoEnabled() {
+ return isOptionEnabled(Logger.TRACE_DEBUG_LOG_INFO);
+ }
+ public static boolean isLogErrorEnabled() {
+ return isOptionEnabled(Logger.TRACE_DEBUG_LOG_ERROR);
+ }
+ public static boolean isLogEnabled() {
+ return isOptionEnabled(Logger.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;
+ }
+ 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 = TerminalViewPlugin.getDefault().getBundle().getEntry(
+ ImageConsts.IMAGE_DIR_ROOT + strDir + strFile);
+ imageDescriptor = ImageDescriptor.createFromURL(url);
+ imageRegistry.put(strKey, imageDescriptor);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/ViewMessages.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/ViewMessages.java
new file mode 100644
index 00000000000..425c31539d6
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/ViewMessages.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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.internal.view;
+
+import org.eclipse.osgi.util.NLS;
+public class ViewMessages extends NLS {
+ static {
+ NLS.initializeMessages(ViewMessages.class.getName(), ViewMessages.class);
+ }
+ public static String PROP_TITLE;
+ public static String SETTINGS;
+
+ public static String TERMINALSETTINGS;
+ public static String CONNECTIONTYPE;
+
+ public static String LIMITOUTPUT;
+ public static String BUFFERLINES;
+ public static String SERIALTIMEOUT;
+ public static String NETWORKTIMEOUT;
+
+ public static String STATE_CONNECTED;
+ public static String STATE_CONNECTING;
+ public static String STATE_OPENED;
+ public static String STATE_CLOSED;
+
+}
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/ViewMessages.properties b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/ViewMessages.properties
new file mode 100644
index 00000000000..5d9d0c549ed
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/internal/view/ViewMessages.properties
@@ -0,0 +1,27 @@
+###############################################################################
+# 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
+#
+###############################################################################
+PROP_TITLE = Terminal
+SETTINGS = Settings
+
+
+TERMINALSETTINGS = Terminal Settings
+CONNECTIONTYPE = Connection Type
+
+LIMITOUTPUT = Limit terminal output
+BUFFERLINES = Terminal buffer lines:
+SERIALTIMEOUT = Serial timeout (seconds):
+NETWORKTIMEOUT = Network timeout (seconds):
+
+STATE_CONNECTED = CONNECTED
+STATE_CONNECTING = CONNECTING...
+STATE_OPENED = OPENED
+STATE_CLOSED = CLOSED
diff --git a/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/view/Activator.java b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/view/Activator.java
new file mode 100644
index 00000000000..f93c2d7e379
--- /dev/null
+++ b/org.eclipse.tm.terminal.view/src/org/eclipse/tm/terminal/view/Activator.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.view;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.tm.terminal.internal.view";
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+}
diff --git a/org.eclipse.tm.terminal/.classpath b/org.eclipse.tm.terminal/.classpath
index 899ab4bb39a..52165ecfb58 100644
--- a/org.eclipse.tm.terminal/.classpath
+++ b/org.eclipse.tm.terminal/.classpath
@@ -1,7 +1,7 @@
Not to be implemented.
+ * @author Michael Scharf
+ *
+ */
+public interface ISettingsStore {
+ /**
+ * @param key
+ * @return value
+ */
+ String get(String key);
+ /**
+ * @param key
+ * @param defaultValue
+ * @return the value or the fecaault
+ */
+ String get(String key, String defaultValue);
+ /**
+ * Save a string value
+ * @param key
+ * @param value
+ */
+ void put(String key, String value);
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/ITerminalConnector.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/ITerminalConnector.java
new file mode 100644
index 00000000000..9dd88afb6fd
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/ITerminalConnector.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * 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.InputStream;
+import java.io.OutputStream;
+
+
+/**
+ * Manage a single connection. Implementations of this class are contributed
+ * via org.eclipse.tm.terminal.terminalConnector
extension point.
+ *
+ * @author Michael Scharf
+ *
+ */
+public interface ITerminalConnector {
+ /**
+ * @return an ID of this connector. Typically getClass().getName()
+ */
+ String getId();
+ /**
+ * @return true if the contribution is functioning (e.g. all external libraries are
+ * installed). This was added for the serial support, because it requires the java comm
+ * library, which is installed in the lib/ext directory of the
+ */
+ boolean isInstalled();
+ /**
+ * Connect using the current state of the settings.
+ * @param control Used to inform the UI about state changes and messages from the connection.
+ */
+ void connect(ITerminalControl control);
+ /**
+ * Disconnect if connected. Else do nothing.
+ */
+ void disconnect();
+
+ /**
+ * @return true if a local echo is needed.
+ * TODO:Michael Scharf: this should be handed within the connection....
+ */
+ boolean isLocalEcho();
+
+ /**
+ * Notify the remote site that the size of the terminal has changed.
+ * @param newWidth
+ * @param newHeight
+ */
+ void setTerminalSize(int newWidth, int newHeight);
+
+ /**
+ * @return a stream with data coming from the remote site.
+ */
+ OutputStream getOutputStream();
+ /**
+ * @return a stream to write to the remote site.
+ */
+ InputStream getInputStream();
+
+ /**
+ * Load the state of this connection. Is typically called before
+ * {@link #connect(ITerminalControl)}.
+ *
+ * @param store a string based data store. Short keys like "foo" can be used to
+ * store the state of the connectio.
+ */
+ void load(ISettingsStore store);
+
+ /**
+ * When the view or dialog containing the terminal is closed,
+ * the state of the connection is saved into the settings store store
+ * @param store
+ */
+ void save(ISettingsStore store);
+
+ /**
+ * @return a new page that can be used in a dialog to setup this connection.
+ *
+ */
+ ISettingsPage makeSettingsPage();
+
+ /**
+ * @param connectedLabel a String with the connected state {@link TerminalState}.
+ * Like "CONNECTED", "CLOSED". Can be used to build up the status string.
+ * @return A string that represents the state of the connection.
+ * TODO: Michael Scharf:
+ */
+ String getStatusString(String connectedLabel);
+
+}
\ No newline at end of file
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/ITerminalControl.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/ITerminalControl.java
new file mode 100644
index 00000000000..434008acc6e
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/ITerminalControl.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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.swt.widgets.Shell;
+
+/**
+ * Represents the terminal view as seen by a terminal connection.
+ *
+ *
Not to be implemented by clients. + * @author Michael Scharf + * + */ +public interface ITerminalControl { + + /** + * @return the current state of the connection + */ + TerminalState getState(); + /** + * @param state + */ + void setState(TerminalState state); + + /** + * A shell to show dialogs. + * @return the shell in which the terminal is shown. + * TODO: Michael Scharf: it's not clear to me what the meaning of the open state is + */ + Shell getShell(); + + /** + * Show a text in the terminal. If pots newlines at the beginning and the end. + * @param text + * TODO: Michael Scharf: Is this really needed? (use {@link #displayTextInTerminal(String)} + */ + void displayTextInTerminal(String text); + /** + * Write a string directly to the terminal. + * @param txt + */ + void writeToTerminal(String txt); + /** + * Set the title of the terminal view. + * @param title + */ + void setTerminalTitle(String title); + + /** + * Show an error message during connect. + * @param msg + * TODO: Michael Scharf: Should be replaced by a better error notification mechansim! + */ + void setMsg(String msg); + +} \ 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 index 1c319e18fa1..7a2a0e061c6 100644 --- a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Logger.java +++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/Logger.java @@ -16,102 +16,98 @@ 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:
- * + * 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); + * Logger.log("something has happened"); + * Logger.log("counter is " + counter); *- * + * * @author Fran Litterio
- * - * 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).
- //
- //
- * - * 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 deleted file mode 100644 index 60712d6eeef..00000000000 --- a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetOption.java +++ /dev/null @@ -1,756 +0,0 @@ -/******************************************************************************* - * 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
- *
- * 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
deleted file mode 100644
index e1b786d4867..00000000000
--- a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalAction.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*******************************************************************************
- * 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/TerminalConnectorExtension.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalConnectorExtension.java
new file mode 100644
index 00000000000..d73bc2b4f12
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalConnectorExtension.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.RegistryFactory;
+
+/**
+ * A factory to get {@link ITerminalConnector} instances.
+ *
+ * @author Michael Scharf
+ *
+ */
+public class TerminalConnectorExtension {
+ /**
+ * @return a new list of ITerminalConnectors.
+ */
+ public static ITerminalConnector[] getTerminalConnectors() {
+ IConfigurationElement[] config=RegistryFactory.getRegistry().getConfigurationElementsFor("org.eclipse.tm.terminal.terminalConnector"); //$NON-NLS-1$
+ List result=new ArrayList();
+ for (int i = 0; i < config.length; i++) {
+ try {
+ Object obj=config[i].createExecutableExtension("class"); //$NON-NLS-1$
+ if(obj instanceof ITerminalConnector) {
+ ITerminalConnector conn=(ITerminalConnector) obj;
+ if(conn.isInstalled())
+ result.add(conn);
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ return (ITerminalConnector[]) result.toArray(new ITerminalConnector[result.size()]);
+ }
+}
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
deleted file mode 100644
index f8968d42572..00000000000
--- a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalConsts.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*******************************************************************************
- * 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
deleted file mode 100644
index 0423376d1f1..00000000000
--- a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalCtrl.java
+++ /dev/null
@@ -1,1573 +0,0 @@
-/*******************************************************************************
- * 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
- *
- * 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
deleted file mode 100644
index b5117f7f294..00000000000
--- a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TerminalView.java
+++ /dev/null
@@ -1,1047 +0,0 @@
-/*******************************************************************************
- * 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
- {
- }
- }
- }
-}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/ITerminalListener.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/ITerminalListener.java
new file mode 100644
index 00000000000..972c2a7068c
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/ITerminalListener.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.control;
+
+import org.eclipse.tm.terminal.TerminalState;
+
+
+/**
+ * Provided by a view implementation.
+ * @author Michael Scharf
+ *
+ */
+public interface ITerminalListener {
+ /**
+ * Called when the state of the connection has changed.
+ * @param state
+ */
+ void setState(TerminalState state);
+ /**
+ * Set the title of the terminal.
+ * @param title
+ */
+ void setTerminalTitle(String title);
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/ITerminalViewControl.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/ITerminalViewControl.java
new file mode 100644
index 00000000000..5a8b4f6e5e8
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/ITerminalViewControl.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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.control;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.tm.terminal.TerminalState;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.tm.terminal.ITerminalConnector;
+
+/**
+ * @author Michael Scharf
+ *
+ */
+public interface ITerminalViewControl {
+ boolean isEmpty();
+ void onFontChanged();
+ StyledText getCtlText();
+ boolean isDisposed();
+ void selectAll();
+ void clearTerminal();
+ void copy();
+ void paste();
+ String getSelection();
+ TerminalState getState();
+ Clipboard getClipboard();
+ void disconnectTerminal();
+ void disposeTerminal();
+ String getStatusString(String status);
+ ITerminalConnector[] getConnectors();
+ void setFocus();
+ ITerminalConnector getTerminalConnection();
+ void setConnector(ITerminalConnector connector);
+ void connectTerminal();
+ void sendKey(char arg0);
+ boolean isConnected();
+
+}
\ No newline at end of file
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/TerminalViewControlFactory.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/TerminalViewControlFactory.java
new file mode 100644
index 00000000000..808ce9e9381
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/control/TerminalViewControlFactory.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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.control;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tm.terminal.ITerminalConnector;
+import org.eclipse.tm.terminal.internal.control.TerminalControl;
+
+public class TerminalViewControlFactory {
+ public static ITerminalViewControl makeControl(ITerminalListener target, Composite wndParent, ITerminalConnector[] connectors) {
+ return new TerminalControl(target, wndParent, connectors);
+ }
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/control/ITerminalControlForText.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/control/ITerminalControlForText.java
new file mode 100644
index 00000000000..36f542cd92e
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/control/ITerminalControlForText.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.internal.control;
+
+import java.io.OutputStream;
+
+import org.eclipse.tm.terminal.ITerminalConnector;
+import org.eclipse.tm.terminal.TerminalState;
+
+/**
+ * need a better name!
+ * @author Michael Scharf
+ *
+ */
+public interface ITerminalControlForText {
+
+ TerminalState getState();
+ void setState(TerminalState state);
+ void setTerminalTitle(String title);
+
+ ITerminalConnector getTerminalConnection();
+
+ void disconnectTerminal();
+
+ OutputStream getOutputStream();
+
+}
\ No newline at end of file
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/control/TerminalControl.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/control/TerminalControl.java
new file mode 100644
index 00000000000..99c34f68402
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/control/TerminalControl.java
@@ -0,0 +1,807 @@
+/*******************************************************************************
+ * 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.internal.control;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketException;
+
+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.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+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.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.tm.terminal.ITerminalConnector;
+import org.eclipse.tm.terminal.ITerminalControl;
+import org.eclipse.tm.terminal.Logger;
+import org.eclipse.tm.terminal.TerminalState;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.contexts.IContextActivation;
+import org.eclipse.ui.contexts.IContextService;
+import org.eclipse.ui.keys.IBindingService;
+import org.eclipse.tm.terminal.control.ITerminalListener;
+import org.eclipse.tm.terminal.control.ITerminalViewControl;
+
+/**
+ *
+ * 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
+ *
+ * @author Fran Litterio
+ *
+ * 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);
+
+ protected boolean fLimitOutput;
+ protected int fBufferLineLimit;
+ /**
+ * The constructor.
+ */
+ public TerminalText(ITerminalControlForText terminal) {
+ super();
+
+ Logger.log("entered"); //$NON-NLS-1$
+
+ this.terminal = terminal;
+
+ for (int i = 0; i < ansiParameters.length; ++i) {
+ ansiParameters[i] = new StringBuffer();
+ }
+ }
+
+ /**
+ * @param ctlText Sets the styled text.
+ *
+ * Note:This method can only be called once.
+ */
+ public void setStyledText(StyledText ctlText) {
+ if(text!=null)
+ throw new java.lang.IllegalStateException("Text can be set only once"); //$NON-NLS-1$
+ text=ctlText;
+
+ // 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 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
+ * SerialPortHandler}. 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 {
+ // 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.getState()==TerminalState.OPENED) {
+ // TODO Why????
+ terminal.setState(TerminalState.CONNECTED);
+ }
+
+ // 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 (Control-G)
+ break;
+
+ case '\b':
+ processBackspace(); // Backspace
+ break;
+
+ case '\t':
+ processTab(); // Tab.
+ break;
+
+ case '\n':
+ processNewline(); // Newline (Control-J)
+ break;
+
+ case '\r':
+ processCarriageReturn(); // Carriage Return (Control-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;
+ 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.setTerminalTitle(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$
+ 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$
+ 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$
+ }
+ }
+
+ /**
+ * 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.
+ 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 (Control-G) character.
+ */
+ protected void processBEL() {
+ // ISSUE: Is there a better way to make a sound? This is not guaranteed to
+ // work on all platforms.
+ // TODO
+ java.awt.Toolkit.getDefaultToolkit().beep();
+ }
+
+ /**
+ * Process a backspace (Control-H) character.
+ */
+ protected void processBackspace() {
+ moveCursorBackward(1);
+ }
+
+ /**
+ * Process a tab (Control-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$
+ }
+ }
+
+ /**
+ * Process a Carriage Return (Control-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.
+
+ ITerminalConnector telnetConnection = terminal.getTerminalConnection();
+
+ if (telnetConnection != null && 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() {
+ if (!fLimitOutput)
+ 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 = fBufferLineLimit;
+
+ // 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;
+ }
+
+ protected int getBufferLineLimit() {
+ return fBufferLineLimit;
+ }
+
+ protected void setBufferLineLimit(int bufferLineLimit) {
+ fBufferLineLimit = bufferLineLimit;
+ }
+
+ protected boolean isLimitOutput() {
+ return fLimitOutput;
+ }
+
+ protected void setLimitOutput(boolean limitOutput) {
+ fLimitOutput = limitOutput;
+ }
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/ITelnetSettings.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/ITelnetSettings.java
new file mode 100644
index 00000000000..61d3c9a8eb3
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/ITelnetSettings.java
@@ -0,0 +1,12 @@
+package org.eclipse.tm.terminal.internal.telnet;
+
+import org.eclipse.tm.terminal.ISettingsStore;
+
+public interface ITelnetSettings {
+ String getHost();
+ int getNetworkPort();
+ int getTimeout();
+ String getStatusString(String strConnected);
+ void load(ISettingsStore store);
+ void save(ISettingsStore store);
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/NetworkPortMap.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/NetworkPortMap.java
new file mode 100644
index 00000000000..f864c802031
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/NetworkPortMap.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * 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.internal.telnet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NetworkPortMap {
+ public static final String PROP_NAMETGTCONST = "tgtcons"; //$NON-NLS-1$
+ public static final String PROP_NAMETELNET = "telnet"; //$NON-NLS-1$
+ public static final String PROP_VALUENET = "1233"; //$NON-NLS-1$
+ public static final String PROP_VALUETGTCONST = "1232"; //$NON-NLS-1$
+ public static final String PROP_VALUETELNET = "23"; //$NON-NLS-1$
+ String[][] fPortMap=new String[][] {
+ // portName, port
+ {PROP_NAMETGTCONST, PROP_VALUETGTCONST},
+ {PROP_NAMETELNET, PROP_VALUETELNET}
+ };
+ public String getDefaultNetworkPort() {
+ return PROP_VALUETELNET;
+ }
+ public String findPortName(String strPort) {
+ for (int i = 0; i < fPortMap.length; i++) {
+ if(fPortMap[i][1].equals(strPort))
+ return fPortMap[i][0];
+ }
+ return null;
+ }
+ public String findPort(String strPortName) {
+ for (int i = 0; i < fPortMap.length; i++) {
+ if(fPortMap[i][0].equals(strPortName))
+ return fPortMap[i][1];
+ }
+ return null;
+ }
+ public List getNameTable() {
+ List names=new ArrayList();
+ for (int i = 0; i < fPortMap.length; i++) {
+ names.add(fPortMap[i][0]);
+ }
+ return names;
+ }
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetCodes.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetCodes.java
similarity index 98%
rename from org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetCodes.java
rename to org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetCodes.java
index c696786c753..da29a14ef35 100644
--- a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/TelnetCodes.java
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetCodes.java
@@ -10,7 +10,7 @@
*
*******************************************************************************/
-package org.eclipse.tm.terminal;
+package org.eclipse.tm.terminal.internal.telnet;
/**
* This interface defines symbolic constants for numeric TELNET protocol command and
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnectWorker.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnectWorker.java
new file mode 100644
index 00000000000..e4b967926ba
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnectWorker.java
@@ -0,0 +1,69 @@
+package org.eclipse.tm.terminal.internal.telnet;
+
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+
+import org.eclipse.tm.terminal.ITerminalControl;
+import org.eclipse.tm.terminal.Logger;
+import org.eclipse.tm.terminal.TerminalState;
+
+class TelnetConnectWorker extends Thread {
+ private final ITerminalControl fControl;
+ private final TelnetConnector fConn;
+ protected TelnetConnectWorker(TelnetConnector conn,ITerminalControl control) {
+ fControl = control;
+ fConn = conn;
+ fControl.setState(TerminalState.CONNECTING);
+ }
+ public void run() {
+ try {
+ int nTimeout = fConn.getTelnetSettings().getTimeout() * 1000;
+ String strHost = fConn.getTelnetSettings().getHost();
+ int nPort = fConn.getTelnetSettings().getNetworkPort();
+ InetSocketAddress address = new InetSocketAddress(strHost, nPort);
+ Socket socket=new Socket();
+
+ socket.connect(address, nTimeout);
+
+ // This next call causes reads on the socket to see TCP urgent data
+ // inline with the rest of the non-urgent data. Without this call, TCP
+ // urgent data is silently dropped by Java. This is required for
+ // TELNET support, because when the TELNET server sends "IAC DM", the
+ // IAC byte is TCP urgent data. If urgent data is silently dropped, we
+ // only see the DM, which looks like an ISO Latin-1 '�' character.
+
+ socket.setOOBInline(true);
+
+ fConn.setSocket(socket);
+
+ TelnetConnection connection=new TelnetConnection(fConn, socket);
+ socket.setKeepAlive(true);
+ fConn.setTelnetConnection(connection);
+ connection.start();
+ fControl.setState(TerminalState.CONNECTED);
+
+ } catch (UnknownHostException ex) {
+ String txt="Unknown host: " + ex.getMessage(); //$NON-NLS-1$
+ connectFailed(txt,"Unknown host: " + ex.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ } catch (SocketTimeoutException socketTimeoutException) {
+ connectFailed(socketTimeoutException.getMessage(), "Connection Error!\n" + socketTimeoutException.getMessage()); //$NON-NLS-1$
+ } catch (ConnectException connectException) {
+ connectFailed(connectException.getMessage(),"Connection refused!"); //$NON-NLS-1$
+ } catch (Exception exception) {
+ Logger.logException(exception);
+
+ connectFailed(exception.getMessage(),""); //$NON-NLS-1$
+ }
+ }
+
+ private void connectFailed(String terminalText, String msg) {
+ Logger.log(terminalText);
+ fControl.displayTextInTerminal(terminalText);
+ fConn.cleanSocket();
+ fControl.setState(TerminalState.CLOSED);
+ fControl.setMsg(msg);
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnection.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnection.java
new file mode 100644
index 00000000000..ec5d3082a35
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnection.java
@@ -0,0 +1,684 @@
+/*******************************************************************************
+ * 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.internal.telnet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+
+import org.eclipse.tm.terminal.ITerminalControl;
+import org.eclipse.tm.terminal.Logger;
+import org.eclipse.tm.terminal.TerminalState;
+
+/**
+ * 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
+ */
+public class TelnetConnection extends Thread implements TelnetCodes {
+ /**
+ * 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 ITerminalControl} singleton.
+ */
+ protected TelnetConnector terminalControl;
+
+ /**
+ * This method holds the Socket object for the TELNET connection.
+ */
+ protected Socket socket;
+
+ /**
+ * 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;
+
+ /**
+ * UNDER CONSTRUCTION
+ */
+ protected boolean localEcho = true;
+
+ /**
+ * This constructor just initializes some internal object state from its
+ * arguments.
+ */
+ public TelnetConnection(TelnetConnector terminalControl, Socket socket) throws IOException {
+ super();
+
+ Logger.log("entered"); //$NON-NLS-1$
+
+ this.terminalControl = terminalControl;
+ this.socket = socket;
+
+ inputStream = socket.getInputStream();
+ outputStream = socket.getOutputStream();
+
+ 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$
+ if (!isConnected() || !isRemoteTelnetServer())
+ return;
+ 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;
+ }
+
+ private void writeToTerminal(String string) {
+ terminalControl.writeToTerminal(string);
+ }
+
+ /**
+ * 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.
+
+ writeToTerminal("\r"+TelnetMessages.CONNECTION_CLOSED_BY_FOREIGN_HOST+"\r\n"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Tell the ITerminalControl object that the connection is
+ // closed.
+ terminalControl.setState(TerminalState.CLOSED);
+ 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) {
+ writeToTerminal(new String(processedBytes, 0, nProcessedBytes));
+ }
+ }
+ }
+ } 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;
+ 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;
+ 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/internal/telnet/TelnetConnector.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnector.java
new file mode 100644
index 00000000000..dbbeb176736
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetConnector.java
@@ -0,0 +1,144 @@
+/**
+ *
+ */
+package org.eclipse.tm.terminal.internal.telnet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import org.eclipse.tm.terminal.ISettingsPage;
+import org.eclipse.tm.terminal.ISettingsStore;
+import org.eclipse.tm.terminal.ITerminalConnector;
+import org.eclipse.tm.terminal.ITerminalControl;
+import org.eclipse.tm.terminal.Logger;
+import org.eclipse.tm.terminal.TerminalState;
+
+public class TelnetConnector implements ITerminalConnector {
+ private OutputStream fOutputStream;
+ private InputStream fInputStream;
+ private Socket fSocket;
+ private ITerminalControl fControl;
+ private TelnetConnection fTelnetConnection;
+ private final TelnetSettings fSettings;
+ public TelnetConnector() {
+ this(new TelnetSettings());
+ }
+ public String getId() {
+ return getClass().getName();
+ }
+ public TelnetConnector(TelnetSettings settings) {
+ fSettings=settings;
+ }
+ public void connect(ITerminalControl control) {
+ Logger.log("entered."); //$NON-NLS-1$
+ fControl=control;
+ TelnetConnectWorker worker = new TelnetConnectWorker(this,control);
+ worker.start();
+ }
+ public void disconnect() {
+ Logger.log("entered."); //$NON-NLS-1$
+
+ if (getSocket() != null) {
+ try {
+ getSocket().close();
+ } catch (Exception exception) {
+ Logger.logException(exception);
+ }
+ }
+
+ if (getInputStream() != null) {
+ try {
+ getInputStream().close();
+ } catch (Exception exception) {
+ Logger.logException(exception);
+ }
+ }
+
+ if (getOutputStream() != null) {
+ try {
+ getOutputStream().close();
+ } catch (Exception exception) {
+ Logger.logException(exception);
+ }
+ }
+ cleanSocket();
+ setState(TerminalState.CLOSED);
+ }
+ public boolean isLocalEcho() {
+ if(fTelnetConnection!=null)
+ return false;
+ return fTelnetConnection.localEcho();
+ }
+ public void setTerminalSize(int newWidth, int newHeight) {
+ if(fTelnetConnection!=null)
+ fTelnetConnection.setTerminalSize(newWidth, newHeight);
+
+ }
+ public InputStream getInputStream() {
+ return fInputStream;
+ }
+ public OutputStream getOutputStream() {
+ return fOutputStream;
+ }
+ private void setInputStream(InputStream inputStream) {
+ fInputStream = inputStream;
+ }
+ private void setOutputStream(OutputStream outputStream) {
+ fOutputStream = outputStream;
+ }
+ Socket getSocket() {
+ return fSocket;
+ }
+
+ /**
+ * sets the socket to null
+ */
+ void cleanSocket() {
+ fSocket=null;
+ setInputStream(null);
+ setOutputStream(null);
+ }
+
+ void setSocket(Socket socket) throws IOException {
+ if(socket==null) {
+ cleanSocket();
+ } else {
+ fSocket = socket;
+ setInputStream(socket.getInputStream());
+ setOutputStream(socket.getOutputStream());
+ }
+
+ }
+ public void setTelnetConnection(TelnetConnection connection) {
+ fTelnetConnection=connection;
+ }
+ public void writeToTerminal(String txt) {
+ fControl.writeToTerminal(txt);
+
+ }
+ public void setState(TerminalState state) {
+ fControl.setState(state);
+
+ }
+ public ITelnetSettings getTelnetSettings() {
+ return fSettings;
+ }
+ public ISettingsPage makeSettingsPage() {
+ return new TelnetSettingsPage(fSettings);
+ }
+ public String getStatusString(String strConnected) {
+ return fSettings.getStatusString(strConnected);
+ }
+ public void load(ISettingsStore store) {
+ fSettings.load(store);
+
+ }
+ public void save(ISettingsStore store) {
+ fSettings.save(store);
+ }
+ public boolean isInstalled() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetMessages.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetMessages.java
new file mode 100644
index 00000000000..793ec7e7202
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetMessages.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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.internal.telnet;
+
+import org.eclipse.osgi.util.NLS;
+
+public class TelnetMessages extends NLS {
+ static {
+ NLS.initializeMessages(TelnetMessages.class.getName(), TelnetMessages.class);
+ }
+ public static String CONNTYPE_NETWORK;
+ public static String PORT;
+ public static String HOST;
+ public static String CONNECTION_CLOSED_BY_FOREIGN_HOST;
+ public static String TIMEOUT;
+
+ }
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetMessages.properties b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetMessages.properties
new file mode 100644
index 00000000000..1ad647e083e
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetMessages.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# 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
+#
+###############################################################################
+CONNTYPE_NETWORK = Network
+PORT = Port
+HOST = Host
+CONNECTION_CLOSED_BY_FOREIGN_HOST= Connection closed by foreign host.
+TIMEOUT = Timeout
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetOption.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetOption.java
new file mode 100644
index 00000000000..5e6ebb4eeeb
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetOption.java
@@ -0,0 +1,698 @@
+/*******************************************************************************
+ * 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.internal.telnet;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+
+import org.eclipse.tm.terminal.Logger;
+
+/**
+ * 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):
+ *
+ *
+ *
+ * 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$
+ 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
+ *
+ * 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/internal/telnet/TelnetProperties.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetProperties.java
new file mode 100644
index 00000000000..2903698a223
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetProperties.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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.internal.telnet;
+
+
+
+public class TelnetProperties {
+ private final NetworkPortMap fNetworkPortMap;
+ private final String fDefaultHost;
+ private final String fDefaultNetworkPort;
+
+ public TelnetProperties() {
+ fNetworkPortMap = new NetworkPortMap();
+ fDefaultNetworkPort = fNetworkPortMap.getDefaultNetworkPort();
+ fDefaultHost = ""; //$NON-NLS-1$
+
+ }
+
+ public String getDefaultHost() {
+ return fDefaultHost;
+ }
+
+ public String getDefaultNetworkPort() {
+ return fDefaultNetworkPort;
+ }
+
+ public NetworkPortMap getNetworkPortMap() {
+ // TODO Auto-generated method stub
+ return fNetworkPortMap;
+ }
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetSettings.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetSettings.java
new file mode 100644
index 00000000000..9fa7dbc8758
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetSettings.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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.internal.telnet;
+
+import org.eclipse.tm.terminal.ISettingsStore;
+import org.eclipse.tm.terminal.ITerminalConnector;
+
+public class TelnetSettings implements ITelnetSettings {
+ protected String fHost;
+ protected String fNetworkPort;
+ protected String fTimeout;
+ private final TelnetProperties fProperties=new TelnetProperties();
+ public String getHost() {
+ return fHost;
+ }
+
+ public void setHost(String strHost) {
+ fHost = strHost;
+ }
+
+ public String getNetworkPortString() {
+ return fNetworkPort;
+ }
+
+ public int getNetworkPort() {
+ try {
+ return Integer.parseInt(fNetworkPort);
+ } catch (NumberFormatException numberFormatException) {
+ return 1313;
+ }
+ }
+
+ public void setNetworkPort(String strNetworkPort) {
+ fNetworkPort = strNetworkPort;
+ }
+
+ public String getStatusString(String strConnected) {
+ return " (" + //$NON-NLS-1$
+ getHost() + ":" + //$NON-NLS-1$
+ getNetworkPortString() + " - " + //$NON-NLS-1$
+ strConnected + ")"; //$NON-NLS-1$
+ }
+
+
+ public ITerminalConnector makeConnector() {
+ return new TelnetConnector(this);
+ }
+
+
+ public void load(ISettingsStore store) {
+ fHost = store.get("Host", fProperties.getDefaultHost());//$NON-NLS-1$
+ fNetworkPort = store.get("NetworkPort", fProperties.getDefaultNetworkPort());//$NON-NLS-1$
+ }
+
+
+ public void save(ISettingsStore store) {
+ store.put("Host", fHost);//$NON-NLS-1$
+ store.put("NetworkPort", fNetworkPort);//$NON-NLS-1$
+ }
+
+
+ public TelnetProperties getProperties() {
+ return fProperties;
+ }
+ public int getTimeout() {
+ try {
+ return Integer.parseInt(fTimeout);
+ } catch (NumberFormatException numberFormatException) {
+ return 10;
+ }
+ }
+ public String getTimeoutString() {
+ return fTimeout;
+ }
+
+ public void setTimeout(String timeout) {
+ fTimeout = timeout;
+ }
+}
diff --git a/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetSettingsPage.java b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetSettingsPage.java
new file mode 100644
index 00000000000..203e851c82a
--- /dev/null
+++ b/org.eclipse.tm.terminal/src/org/eclipse/tm/terminal/internal/telnet/TelnetSettingsPage.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * 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.internal.telnet;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+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.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.tm.terminal.ISettingsPage;
+
+public class TelnetSettingsPage implements ISettingsPage {
+ private Text fHostText;
+ private Combo fNetworkPortCombo;
+ private Text fTimeout;
+ private final TelnetSettings fTerminalSettings;
+
+ public TelnetSettingsPage(TelnetSettings settings) {
+ fTerminalSettings=settings;
+ }
+ public void saveSettings() {
+ fTerminalSettings.setHost(fHostText.getText());
+ fTerminalSettings.setTimeout(fTimeout.getText());
+ fTerminalSettings.setNetworkPort(getNetworkPort());
+ }
+
+ public void loadSettings() {
+ if(fTerminalSettings!=null) {
+ setHost(fTerminalSettings.getHost());
+ setTimeout(fTerminalSettings.getTimeoutString());
+ setNetworkPort(fTerminalSettings.getNetworkPortString());
+ }
+ }
+ private void setHost(String strHost) {
+ if(strHost==null)
+ strHost=""; //$NON-NLS-1$
+ fHostText.setText(strHost);
+
+ }
+ private void setTimeout(String timeout) {
+ if(timeout==null || timeout.length()==0)
+ timeout="5"; //$NON-NLS-1$
+ fTimeout.setText(timeout);
+
+ }
+ private void setNetworkPort(String strNetworkPort) {
+ String strPortName = getNetworkPortMap().findPortName(strNetworkPort);
+ if(strPortName==null)
+ strPortName=""; //$NON-NLS-1$
+ int nIndex = fNetworkPortCombo.indexOf(strPortName);
+
+ if (nIndex == -1) {
+ fNetworkPortCombo.setText(strNetworkPort);
+ } else {
+ fNetworkPortCombo.select(nIndex);
+ }
+ }
+ private String getNetworkPort() {
+ return getNetworkPortMap().findPort(fNetworkPortCombo.getText());
+ }
+ private NetworkPortMap getNetworkPortMap() {
+ return fTerminalSettings.getProperties().getNetworkPortMap();
+ }
+
+ public boolean validateSettings() {
+ return true;
+ }
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout gridLayout = new GridLayout(2, false);
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+
+ composite.setLayout(gridLayout);
+ composite.setLayoutData(gridData);
+
+ // Add label
+ Label ctlLabel = new Label(composite, SWT.RIGHT);
+ ctlLabel.setText(TelnetMessages.HOST + ":"); //$NON-NLS-1$
+
+ // Add control
+ gridData = new GridData(GridData.FILL_HORIZONTAL);
+ fHostText = new Text(composite, SWT.BORDER);
+ fHostText.setLayoutData(gridData);
+
+ // Add label
+ ctlLabel = new Label(composite, SWT.RIGHT);
+ ctlLabel.setText(TelnetMessages.PORT + ":"); //$NON-NLS-1$
+
+ // Add control
+ gridData = new GridData(GridData.FILL_HORIZONTAL);
+ fNetworkPortCombo = new Combo(composite, SWT.DROP_DOWN);
+
+ fNetworkPortCombo.setLayoutData(gridData);
+
+ List table = getNetworkPortMap().getNameTable();
+ Collections.sort(table);
+ loadCombo(fNetworkPortCombo, table);
+
+ new Label(composite, SWT.RIGHT).setText(TelnetMessages.TIMEOUT + ":"); //$NON-NLS-1$
+ fTimeout = new Text(composite, SWT.BORDER);
+ fTimeout.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ loadSettings();
+ }
+ private void loadCombo(Combo ctlCombo, List table) {
+ for (Iterator iter = table.iterator(); iter.hasNext();) {
+ String label = (String) iter.next();
+ ctlCombo.add(label);
+ }
+ }
+
+ public String getName() {
+ return TelnetMessages.CONNTYPE_NETWORK;
+ }
+
+}
+ * NEGOTIATION_NOT_STARTED -----> {@link #NEGOTIATION_IN_PROGRESS}
+ * | (A) | ^
+ * (C)| (B)| |(D)
+ * | V |
+ * +--------> {@link #NEGOTIATION_DONE}
+ *