1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-01 06:05:24 +02:00

Merge R2_0_terminal_performance changes

This commit is contained in:
Martin Oberhuber 2007-09-19 02:11:40 +00:00
parent e681ca63ff
commit 89a03bcb5a
46 changed files with 7139 additions and 29 deletions

View file

@ -1,9 +1,9 @@
feature@org.eclipse.tm.terminal=v20070913,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal-feature
feature@org.eclipse.tm.terminal=v20070918,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal-feature
feature@org.eclipse.tm.terminal.sdk=v20070808,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.sdk-feature
feature@org.eclipse.tm.terminal.serial=v20070609,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.serial-feature
feature@org.eclipse.tm.terminal.ssh=v20070808,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.ssh-feature
feature@org.eclipse.tm.terminal.telnet=v20070609,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.telnet-feature
feature@org.eclipse.tm.terminal.view=v20070913,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.view-feature
feature@org.eclipse.tm.terminal.view=v20070918,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.view-feature
plugin@org.eclipse.tm.terminal=v20070918,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal
plugin@org.eclipse.tm.terminal.serial=v20070605,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.serial
plugin@org.eclipse.tm.terminal.ssh=v20070909,:pserver:anonymous:none@dev.eclipse.org:/cvsroot/dsdp,,org.eclipse.tm.core/terminal/org.eclipse.tm.terminal.ssh

View file

@ -2,7 +2,7 @@
<feature
id="org.eclipse.tm.terminal"
label="%featureName"
version="1.0.0.qualifier"
version="1.0.1.qualifier"
provider-name="%providerName">
<description>

View file

@ -2,7 +2,7 @@
<feature
id="org.eclipse.tm.terminal.view"
label="%featureName"
version="1.0.0.qualifier"
version="1.0.1.qualifier"
provider-name="%providerName">
<description>

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.tm.terminal.view;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Version: 1.0.1.qualifier
Bundle-Activator: org.eclipse.tm.internal.terminal.view.TerminalViewPlugin
Bundle-Localization: plugin
Require-Bundle: org.eclipse.ui,

View file

@ -28,11 +28,11 @@ import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.MenuListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.tm.internal.terminal.actions.TerminalAction;
@ -522,12 +522,12 @@ public class TerminalView extends ViewPart implements ITerminalView, ITerminalLi
}
protected void setupContextMenus() {
StyledText ctlText;
Control ctlText;
MenuManager menuMgr;
Menu menu;
TerminalContextMenuHandler contextMenuHandler;
ctlText = fCtlTerminal.getCtlText();
ctlText = fCtlTerminal.getControl();
menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
menu = menuMgr.createContextMenu(ctlText);
contextMenuHandler = new TerminalContextMenuHandler();
@ -675,6 +675,7 @@ public class TerminalView extends ViewPart implements ITerminalView, ITerminalLi
fMenuAboutToShow = true;
updateEditCopy();
updateEditCut();
updateEditSelectAll();
updateEditPaste();
updateEditClearAll();

View file

@ -1,8 +1,8 @@
org.eclipse.tm.terminal/debug = true
org.eclipse.tm.terminal/debug/flag = true
org.eclipse.tm.terminal/debug/filter = *
org.eclipse.tm.terminal/debug/log/directory = /tmp/
org.eclipse.tm.terminal/debug/log = true
org.eclipse.tm.terminal/debug/log/error = true
org.eclipse.tm.terminal/debug/log/info = false
org.eclipse.tm.terminal/debug/log/char = false
org.eclipse.tm.terminal/debug/log/buffer/size = false
org.eclipse.tm.terminal/debug/log/VT100Backend = false
org.eclipse.tm.terminal/debug/use_old_implementation = false

View file

@ -1,7 +1,8 @@
#Tue Jan 30 22:33:44 CET 2007
#Thu Aug 09 03:12:08 CEST 2007
eclipse.preferences.version=1
instance/org.eclipse.core.net/org.eclipse.core.net.hasMigrated=true
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4
org.eclipse.jdt.core.compiler.compliance=1.4
org.eclipse.jdt.core.compiler.doc.comment.support=enabled
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
@ -67,4 +68,4 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=di
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.3
org.eclipse.jdt.core.compiler.source=1.4

View file

@ -1,3 +0,0 @@
#Mon Jul 31 14:55:17 CEST 2006
eclipse.preferences.version=1
internal.default.compliance=user

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.tm.terminal; singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Version: 1.0.1.qualifier
Bundle-Activator: org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin
@ -13,4 +13,8 @@ Bundle-RequiredExecutionEnvironment: J2SE-1.4
Bundle-ClassPath: .
Export-Package: org.eclipse.tm.internal.terminal.control;x-friends:="org.eclipse.tm.terminal.view",
org.eclipse.tm.internal.terminal.control.impl;x-internal:=true,
org.eclipse.tm.internal.terminal.provisional.api;x-friends:="org.eclipse.tm.terminal.serial,org.eclipse.tm.terminal.ssh,org.eclipse.tm.terminal.telnet,org.eclipse.tm.terminal.view"
org.eclipse.tm.internal.terminal.emulator;x-friends:="org.eclipse.tm.terminal.test",
org.eclipse.tm.internal.terminal.model;x-friends:="org.eclipse.tm.terminal.test",
org.eclipse.tm.internal.terminal.provisional.api;x-friends:="org.eclipse.tm.terminal.serial,org.eclipse.tm.terminal.ssh,org.eclipse.tm.terminal.telnet,org.eclipse.tm.terminal.view",
org.eclipse.tm.internal.terminal.textcanvas;x-friends:="org.eclipse.tm.terminal.test",
org.eclipse.tm.terminal.model

View file

@ -28,4 +28,6 @@ output.. = bin/
src.includes = schema/,\
README.txt,\
about.html
javacSource=1.4
javacTarget=1.4

View file

@ -149,8 +149,8 @@ public class CommandInputFieldWithHistory implements ICommandInputField {
}
public void createControl(Composite parent,final ITerminalViewControl terminal) {
fInputField=new Text(parent, SWT.SINGLE|SWT.BORDER);
fInputField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
fInputField.setFont(terminal.getCtlText().getFont());
fInputField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
fInputField.setFont(terminal.getFont());
fInputField.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e) {
if(e.keyCode=='\n' || e.keyCode=='\r') {

View file

@ -11,9 +11,9 @@
*******************************************************************************/
package org.eclipse.tm.internal.terminal.control;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Control;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnectorInfo;
import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
@ -24,7 +24,8 @@ import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
public interface ITerminalViewControl {
boolean isEmpty();
void setFont(Font font);
StyledText getCtlText();
Font getFont();
Control getControl();
boolean isDisposed();
void selectAll();
void clearTerminal();
@ -72,5 +73,4 @@ public interface ITerminalViewControl {
* in the terminal view. -1 means unlimited.
*/
public void setBufferLineLimit(int bufferLineLimit);
}

View file

@ -13,10 +13,15 @@ package org.eclipse.tm.internal.terminal.control;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tm.internal.terminal.control.impl.TerminalControl;
import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin;
import org.eclipse.tm.internal.terminal.emulator.VT100TerminalControl;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnectorInfo;
public class TerminalViewControlFactory {
public static ITerminalViewControl makeControl(ITerminalListener target, Composite wndParent, ITerminalConnectorInfo[] connectors) {
return new TerminalControl(target, wndParent, connectors);
if(TerminalPlugin.isOptionEnabled("org.eclipse.tm.terminal/debug/use_old_implementation")) //$NON-NLS-1$
return new TerminalControl(target, wndParent, connectors);
else
return new VT100TerminalControl(target, wndParent, connectors);
}
}

View file

@ -406,7 +406,12 @@ public class TerminalControl implements ITerminalControlForText, ITerminalContro
getTerminalText().fontChanged();
}
public Font getFont() {
return getCtlText().getFont();
}
public Control getControl() {
return fCtlText;
}
protected void setupControls(Composite parent) {
// The Terminal view now aims to be an ANSI-conforming terminal emulator, so it
// can't have a horizontal scroll bar (but a vertical one is ok). Also, do
@ -423,8 +428,8 @@ public class TerminalControl implements ITerminalControlForText, ITerminalContro
fWndParent.setLayout(layout);
setCtlText(new StyledText(fWndParent, SWT.V_SCROLL));
fCtlText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
//fCtlText.setWordWrap(false);
fDisplay = getCtlText().getDisplay();
fClipboard = new Clipboard(fDisplay);
// fViewer.setDocument(new TerminalDocument());
@ -515,7 +520,7 @@ public class TerminalControl implements ITerminalControlForText, ITerminalContro
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#getCtlText()
*/
public StyledText getCtlText() {
protected StyledText getCtlText() {
return fCtlText;
}
@ -856,4 +861,5 @@ public class TerminalControl implements ITerminalControlForText, ITerminalContro
getTerminalText().setBufferLineLimit(bufferLineLimit);
}
}

View file

@ -0,0 +1,172 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.emulator;
import org.eclipse.tm.terminal.model.Style;
public interface IVT100EmulatorBackend {
/**
* This method erases all text from the Terminal view. Including the history
*/
void clearAll();
/**
* Sets the Dimensions of the addressable scroll space of the screen....
* Keeps the cursor position relative to the bottom of the screen!
* @param lines
* @param cols
*/
void setDimensions(int lines, int cols);
/**
* 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 the line.
* 01234
* 0 123
*/
void insertCharacters(int charactersToInsert);
/**
* Erases from cursor to end of screen, including cursor position. Cursor does not move.
*/
void eraseToEndOfScreen();
/**
* Erases from beginning of screen to cursor, including cursor position. Cursor does not move.
*/
void eraseToCursor();
/**
* Erases complete display. All lines are erased and changed to single-width. Cursor does not move.
*/
void eraseAll();
/**
* Erases complete line.
*/
void eraseLine();
/**
* Erases from cursor to end of line, including cursor position.
*/
void eraseLineToEnd();
/**
* Erases from beginning of line to cursor, including cursor position.
*/
void eraseLineToCursor();
/**
* Inserts n lines at line with cursor. Lines displayed below cursor move down.
* Lines moved past the bottom margin are lost. This sequence is ignored when
* cursor is outside scrolling region.
* @param n the number of lines to insert
*/
void insertLines(int n);
/**
* Deletes n characters, starting with the character at cursor position.
* When a character is deleted, all characters to the right of cursor move
* left. This creates a space character at right margin. This character
* has same character attribute as the last character moved left.
* @param n
* 012345
* 0145xx
*/
void deleteCharacters(int n);
/**
* Deletes n lines, starting at line with cursor. As lines are deleted,
* lines displayed below cursor move up. Lines added to bottom of screen
* have spaces with same character attributes as last line moved up. This
* sequence is ignored when cursor is outside scrolling region.
* @param n the number of lines to delete
*/
void deleteLines(int n);
Style getDefaultStyle();
void setDefaultStyle(Style defaultStyle);
Style getStyle();
/**
* Sets the style to be used from now on
* @param style
*/
void setStyle(Style 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.
* <p>
*
* 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).
* <p>
*/
void appendString(String buffer);
/**
* 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.
* <p>
*
* 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".
* <p>
*/
void processNewline();
/**
* This method returns the relative line number of the line containing 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.
*/
int getCursorLine();
int getCursorColumn();
/**
* This method moves the cursor to the specified line and column. Parameter
* <i>targetLine</i> 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.
*/
void setCursor(int targetLine, int targetColumn);
void setCursorColumn(int targetColumn);
void setCursorLine(int targetLine);
int getLines();
int getColumns();
}

View file

@ -0,0 +1,144 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.emulator;
import java.io.PrintStream;
import org.eclipse.tm.terminal.model.Style;
public class VT100BackendTraceDecorator implements IVT100EmulatorBackend {
final IVT100EmulatorBackend fBackend;
final PrintStream fWriter;
public VT100BackendTraceDecorator(IVT100EmulatorBackend backend, PrintStream out) {
fBackend = backend;
fWriter=out;
}
public void appendString(String buffer) {
fWriter.println("appendString(\""+buffer+"\")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.appendString(buffer);
}
public void clearAll() {
fWriter.println("clearAll()"); //$NON-NLS-1$
fBackend.clearAll();
}
public void deleteCharacters(int n) {
fWriter.println("deleteCharacters("+n+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.deleteCharacters(n);
}
public void deleteLines(int n) {
fWriter.println("deleteLines("+n+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.deleteLines(n);
}
public void eraseAll() {
fWriter.println("eraseAll()"); //$NON-NLS-1$
fBackend.eraseAll();
}
public void eraseLine() {
fWriter.println("eraseLine()"); //$NON-NLS-1$
fBackend.eraseLine();
}
public void eraseLineToCursor() {
fWriter.println("eraseLineToCursor()"); //$NON-NLS-1$
fBackend.eraseLineToCursor();
}
public void eraseLineToEnd() {
fWriter.println("eraseLineToEnd()"); //$NON-NLS-1$
fBackend.eraseLineToEnd();
}
public void eraseToCursor() {
fWriter.println("eraseToCursor()"); //$NON-NLS-1$
fBackend.eraseToCursor();
}
public void eraseToEndOfScreen() {
fWriter.println("eraseToEndOfScreen()"); //$NON-NLS-1$
fBackend.eraseToEndOfScreen();
}
public int getColumns() {
return fBackend.getColumns();
}
public int getCursorColumn() {
return fBackend.getCursorColumn();
}
public int getCursorLine() {
return fBackend.getCursorLine();
}
public Style getDefaultStyle() {
return fBackend.getDefaultStyle();
}
public int getLines() {
return fBackend.getLines();
}
public Style getStyle() {
return fBackend.getStyle();
}
public void insertCharacters(int charactersToInsert) {
fWriter.println("insertCharacters("+charactersToInsert+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.insertCharacters(charactersToInsert);
}
public void insertLines(int n) {
fWriter.println("insertLines("+n+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.insertLines(n);
}
public void processNewline() {
fWriter.println("processNewline()"); //$NON-NLS-1$
fBackend.processNewline();
}
public void setCursor(int targetLine, int targetColumn) {
fWriter.println("setCursor("+targetLine+", "+targetColumn+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
fBackend.setCursor(targetLine, targetColumn);
}
public void setCursorColumn(int targetColumn) {
fWriter.println("setCursorColumn("+targetColumn+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.setCursorColumn(targetColumn);
}
public void setCursorLine(int targetLine) {
fWriter.println("setCursorLine("+targetLine+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.setCursorLine(targetLine);
}
public void setDefaultStyle(Style defaultStyle) {
fWriter.println("setDefaultStyle("+defaultStyle+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.setDefaultStyle(defaultStyle);
}
public void setDimensions(int lines, int cols) {
fWriter.println("setDimensions("+lines+","+cols+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
fBackend.setDimensions(lines, cols);
}
public void setStyle(Style style) {
fWriter.println("setStyle("+style+")"); //$NON-NLS-1$ //$NON-NLS-2$
fBackend.setStyle(style);
}
}

View file

@ -0,0 +1,387 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.emulator;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.Style;
/**
*
*/
public class VT100EmulatorBackend implements IVT100EmulatorBackend {
/**
* This field holds the number of the column in which the cursor is
* logically positioned. The leftmost column on the screen is column 0, and
* column numbers increase to the right. The maximum value of this field is
* {@link #widthInColumns} - 1. We track the cursor column using this field
* to avoid having to recompute it repeatly using StyledText method calls.
* <p>
*
* 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.
* <p>
*
* When fCursorColumn 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.
* <p>
*/
private int fCursorColumn;
private int fCursorLine;
private Style fDefaultStyle;
private Style fStyle;
int fLines;
int fColumns;
final private ITerminalTextData fTerminal;
public VT100EmulatorBackend(ITerminalTextData terminal) {
fTerminal=terminal;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#clearAll()
*/
public void clearAll() {
synchronized (fTerminal) {
// clear the history
int n=fTerminal.getHeight();
for (int line = 0; line < n; line++) {
fTerminal.cleanLine(line);
}
fTerminal.setDimensions(fLines, fTerminal.getWidth());
setStyle(getDefaultStyle());
setCursor(0, 0);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#setDimensions(int, int)
*/
public void setDimensions(int lines, int cols) {
synchronized (fTerminal) {
if(lines==fLines && cols==fColumns)
return; // nothing to do
// cursor line from the bottom
int cl=fLines-getCursorLine();
int cc=getCursorColumn();
fLines=lines;
fColumns=cols;
// make the terminal at least as high as we need lines
fTerminal.setDimensions(Math.max(fLines,fTerminal.getHeight()), fColumns);
setCursor(fLines-cl, cc);
}
}
int toAbsoluteLine(int line) {
synchronized (fTerminal) {
return fTerminal.getHeight()-fLines+line;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#insertCharacters(int)
*/
public void insertCharacters(int charactersToInsert) {
synchronized (fTerminal) {
int line=toAbsoluteLine(fCursorLine);
int n=charactersToInsert;
for (int col = fColumns-1; col >=fCursorColumn+n; col--) {
char c=fTerminal.getChar(line, col-n);
Style style=fTerminal.getStyle(line, col-n);
fTerminal.setChar(line, col,c, style);
}
int last=Math.min(fCursorColumn+n, fColumns);
for (int col = fCursorColumn; col <last; col++) {
fTerminal.setChar(line, col,'\000', null);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#eraseToEndOfScreen()
*/
public void eraseToEndOfScreen() {
synchronized (fTerminal) {
eraseLineToEnd();
for (int line = toAbsoluteLine(fCursorLine+1); line < toAbsoluteLine(fLines); line++) {
fTerminal.cleanLine(line);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#eraseToCursor()
*/
public void eraseToCursor() {
synchronized (fTerminal) {
for (int line = toAbsoluteLine(0); line < toAbsoluteLine(fCursorLine); line++) {
fTerminal.cleanLine(line);
}
eraseLineToCursor();
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#eraseAll()
*/
public void eraseAll() {
synchronized (fTerminal) {
for (int line = toAbsoluteLine(0); line < toAbsoluteLine(fLines); line++) {
fTerminal.cleanLine(line);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#eraseLine()
*/
public void eraseLine() {
synchronized (fTerminal) {
fTerminal.cleanLine(toAbsoluteLine(fCursorLine));
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#eraseLineToEnd()
*/
public void eraseLineToEnd() {
synchronized (fTerminal) {
int line=toAbsoluteLine(fCursorLine);
for (int col = fCursorColumn; col < fColumns; col++) {
fTerminal.setChar(line, col, '\000', null);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#eraseLineToCursor()
*/
public void eraseLineToCursor() {
synchronized (fTerminal) {
int line=toAbsoluteLine(fCursorLine);
for (int col = 0; col <= fCursorColumn; col++) {
fTerminal.setChar(line, col, '\000', null);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#insertLines(int)
*/
public void insertLines(int n) {
synchronized (fTerminal) {
if(!isCusorInScrollingRegion())
return;
assert n>0;
int line=toAbsoluteLine(fCursorLine);
int nLines=fTerminal.getHeight()-line;
fTerminal.scroll(line, nLines, n);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#deleteCharacters(int)
*/
public void deleteCharacters(int n) {
synchronized (fTerminal) {
int line=toAbsoluteLine(fCursorLine);
for (int col = fCursorColumn+n; col < fColumns; col++) {
char c=fTerminal.getChar(line, col);
Style style=fTerminal.getStyle(line, col);
fTerminal.setChar(line, col-n,c, style);
}
int first=Math.max(fCursorColumn, fColumns-n);
for (int col = first; col <fColumns; col++) {
fTerminal.setChar(line, col,'\000', null);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#deleteLines(int)
*/
public void deleteLines(int n) {
synchronized (fTerminal) {
if(!isCusorInScrollingRegion())
return;
assert n>0;
int line=toAbsoluteLine(fCursorLine);
int nLines=fTerminal.getHeight()-line;
fTerminal.scroll(line, nLines, -n);
}
}
private boolean isCusorInScrollingRegion() {
// TODO Auto-generated method stub
return true;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#getDefaultStyle()
*/
public Style getDefaultStyle() {
synchronized (fTerminal) {
return fDefaultStyle;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#setDefaultStyle(org.eclipse.tm.terminal.model.Style)
*/
public void setDefaultStyle(Style defaultStyle) {
synchronized (fTerminal) {
fDefaultStyle = defaultStyle;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#getStyle()
*/
public Style getStyle() {
synchronized (fTerminal) {
if(fStyle==null)
return fDefaultStyle;
return fStyle;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#setStyle(org.eclipse.tm.terminal.model.Style)
*/
public void setStyle(Style style) {
synchronized (fTerminal) {
fStyle=style;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#appendString(java.lang.String)
*/
public void appendString(String buffer) {
synchronized (fTerminal) {
char[] chars=buffer.toCharArray();
int line=toAbsoluteLine(fCursorLine);
int i=0;
while (i < chars.length) {
int n=Math.min(fColumns-fCursorColumn,chars.length-i);
fTerminal.setChars(line, fCursorColumn, chars, i, n, fStyle);
int col=fCursorColumn+n;
i+=n;
// wrap needed?
if(col>=fColumns) {
doNewline();
line=toAbsoluteLine(fCursorLine);
setCursorColumn(0);
} else {
setCursorColumn(col);
}
}
}
}
/**
* MUST be called from a synchronized block!
*/
private void doNewline() {
if(fCursorLine+1>=fLines) {
int h=fTerminal.getHeight();
fTerminal.addLine();
if(h!=fTerminal.getHeight())
setCursorLine(fCursorLine+1);
} else {
setCursorLine(fCursorLine+1);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#processNewline()
*/
public void processNewline() {
synchronized (fTerminal) {
doNewline();
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#getCursorLine()
*/
public int getCursorLine() {
synchronized (fTerminal) {
return fCursorLine;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#getCursorColumn()
*/
public int getCursorColumn() {
synchronized (fTerminal) {
return fCursorColumn;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#setCursor(int, int)
*/
public void setCursor(int targetLine, int targetColumn) {
synchronized (fTerminal) {
setCursorLine(targetLine);
setCursorColumn(targetColumn);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#setCursorColumn(int)
*/
public void setCursorColumn(int targetColumn) {
synchronized (fTerminal) {
if(targetColumn<0)
targetColumn=0;
else if(targetColumn>=fColumns)
targetColumn=fColumns-1;
fCursorColumn=targetColumn;
// We make the assumption that nobody is changing the
// terminal cursor except this class!
// This assumption gives a huge performance improvement
fTerminal.setCursorColumn(targetColumn);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#setCursorLine(int)
*/
public void setCursorLine(int targetLine) {
synchronized (fTerminal) {
if(targetLine<0)
targetLine=0;
else if(targetLine>=fLines)
targetLine=fLines-1;
fCursorLine=targetLine;
// We make the assumption that nobody is changing the
// terminal cursor except this class!
// This assumption gives a huge performance improvement
fTerminal.setCursorLine(toAbsoluteLine(targetLine));
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#getLines()
*/
public int getLines() {
synchronized (fTerminal) {
return fLines;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.emulator.IVT100EmulatorBackend#getColumns()
*/
public int getColumns() {
synchronized (fTerminal) {
return fColumns;
}
}
}

View file

@ -0,0 +1,906 @@
/*******************************************************************************
* Copyright (c) 2003, 2007 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
*
* Initial Contributors:
* The following Wind River employees contributed to the Terminal component
* that contains this file: Chris Thew, Fran Litterio, Stephen Lamb,
* Helmut Haigermoser and Ted Williams.
*
* Contributors:
* Michael Scharf (Wind River) - split into core, view and connector plugins
* Martin Oberhuber (Wind River) - fixed copyright headers and beautified
*******************************************************************************/
package org.eclipse.tm.internal.terminal.emulator;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
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.graphics.Font;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tm.internal.terminal.control.ICommandInputField;
import org.eclipse.tm.internal.terminal.control.ITerminalListener;
import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
import org.eclipse.tm.internal.terminal.control.impl.ITerminalControlForText;
import org.eclipse.tm.internal.terminal.control.impl.TerminalMessages;
import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnectorInfo;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
import org.eclipse.tm.internal.terminal.provisional.api.Logger;
import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
import org.eclipse.tm.internal.terminal.textcanvas.ITextCanvasModel;
import org.eclipse.tm.internal.terminal.textcanvas.PipedInputStream;
import org.eclipse.tm.internal.terminal.textcanvas.PollingTextCanvasModel;
import org.eclipse.tm.internal.terminal.textcanvas.TextCanvas;
import org.eclipse.tm.internal.terminal.textcanvas.TextLineRenderer;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.TerminalTextDataFactory;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.keys.IBindingService;
/**
*
* 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 <chris.thew@windriver.com>
*/
public class VT100TerminalControl implements ITerminalControlForText, ITerminalControl, ITerminalViewControl
{
protected final static String[] LINE_DELIMITERS = { "\n" }; //$NON-NLS-1$
/**
* This field holds a reference to a TerminalText object that performs all ANSI
* text processing on data received from the remote host and controls how text is
* displayed using the view's StyledText widget.
*/
private VT100Emulator fTerminalText;
private Display fDisplay;
private TextCanvas fCtlText;
private Composite fWndParent;
private Clipboard fClipboard;
private KeyListener fKeyHandler;
private ITerminalListener fTerminalListener;
private String fMsg = ""; //$NON-NLS-1$
private FocusListener fFocusListener;
private ITerminalConnectorInfo fConnectorInfo;
private final ITerminalConnectorInfo[] fConnectors;
PipedInputStream fInputStream;
private ICommandInputField fCommandInputField;
private volatile TerminalState fState;
private ITerminalTextData fTerminalModel;
volatile private Job fJob;
public VT100TerminalControl(ITerminalListener target, Composite wndParent, ITerminalConnectorInfo[] connectors) {
fConnectors=connectors;
fTerminalListener=target;
fTerminalModel=TerminalTextDataFactory.makeTerminalTextData();
fTerminalModel.setDimensions(24, 80);
fTerminalModel.setMaxHeight(1000);
fInputStream=new PipedInputStream(8*1024);
fTerminalText=new VT100Emulator(fTerminalModel,this,fInputStream);
setupTerminal(wndParent);
}
public ITerminalConnectorInfo[] getConnectors() {
return fConnectors;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#copy()
*/
public void copy() {
getCtlText().copy();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#paste()
*/
public void paste() {
TextTransfer textTransfer = TextTransfer.getInstance();
String strText = (String) fClipboard.getContents(textTransfer);
pasteString(strText);
// TODO paste in another thread.... to avoid blocking
// new Thread() {
// public void run() {
// for (int i = 0; i < strText.length(); i++) {
// sendChar(strText.charAt(i), false);
// }
//
// }
// }.start();
}
/**
* @param strText
*/
public boolean pasteString(String strText) {
if(!isConnected())
return false;
if (strText == null)
return false;
for (int i = 0; i < strText.length(); i++) {
sendChar(strText.charAt(i), false);
}
return true;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#selectAll()
*/
public void selectAll() {
getCtlText().selectAll();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#sendKey(char)
*/
public void sendKey(char character) {
Event event;
KeyEvent keyEvent;
event = new Event();
event.widget = getCtlText();
event.character = character;
event.keyCode = 0;
event.stateMask = 0;
event.doit = true;
keyEvent = new KeyEvent(event);
fKeyHandler.keyPressed(keyEvent);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#clearTerminal()
*/
public void clearTerminal() {
// The TerminalText object does all text manipulation.
getTerminalText().clearTerminal();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#getClipboard()
*/
public Clipboard getClipboard() {
return fClipboard;
}
/**
* @return non null selection
*/
public String getSelection() {
String txt= fCtlText.getSelectionText();
if(txt==null)
txt=""; //$NON-NLS-1$
return txt;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#setFocus()
*/
public void setFocus() {
getCtlText().setFocus();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#isEmpty()
*/
public boolean isEmpty() {
return getCtlText().isEmpty();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#isDisposed()
*/
public boolean isDisposed() {
return getCtlText().isDisposed();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#isConnected()
*/
public boolean isConnected() {
return fState==TerminalState.CONNECTED;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#disposeTerminal()
*/
public void disposeTerminal() {
Logger.log("entered."); //$NON-NLS-1$
disconnectTerminal();
fClipboard.dispose();
getTerminalText().dispose();
}
public void connectTerminal() {
Logger.log("entered."); //$NON-NLS-1$
if(getTerminalConnector()==null)
return;
fTerminalText.resetState();
if(fConnectorInfo.getInitializationErrorMessage()!=null) {
showErrorMessage(NLS.bind(
TerminalMessages.CannotConnectTo,
fConnectorInfo.getName(),
fConnectorInfo.getInitializationErrorMessage()));
// we cannot connect because the connector was not initialized
return;
}
getTerminalConnector().connect(this);
// clean the error message
setMsg(""); //$NON-NLS-1$
waitForConnect();
}
private ITerminalConnector getTerminalConnector() {
if(fConnectorInfo==null)
return null;
return fConnectorInfo.getConnector();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#disconnectTerminal()
*/
public void disconnectTerminal() {
Logger.log("entered."); //$NON-NLS-1$
if (getState()==TerminalState.CLOSED) {
return;
}
if(getTerminalConnector()!=null) {
getTerminalConnector().disconnect();
}
}
// TODO
private void waitForConnect() {
Logger.log("entered."); //$NON-NLS-1$
// TODO
// Eliminate this code
while (getState()==TerminalState.CONNECTING) {
if (fDisplay.readAndDispatch())
continue;
fDisplay.sleep();
}
if (!getMsg().equals("")) //$NON-NLS-1$
{
showErrorMessage(getMsg());
disconnectTerminal();
return;
}
getCtlText().setFocus();
startReaderJob();
}
private void startReaderJob() {
if(fJob==null) {
fJob=new Job("Terminal data reader") { //$NON-NLS-1$
protected IStatus run(IProgressMonitor monitor) {
IStatus status=Status.OK_STATUS;
while(true) {
while(fInputStream.available()==0 && !monitor.isCanceled()) {
try {
fInputStream.waitForAvailable(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if(monitor.isCanceled()) {
disconnectTerminal();
status=Status.CANCEL_STATUS;
break;
}
try {
// TODO: should block when no text is available!
fTerminalText.processText();
} catch (Exception e) {
disconnectTerminal();
status=new Status(IStatus.ERROR,TerminalPlugin.PLUGIN_ID,e.getLocalizedMessage(),e);
break;
}
}
// clean the job: start a new one when the connection getst restarted
fJob=null;
return status;
}
};
fJob.setSystem(true);
fJob.schedule();
}
}
private void showErrorMessage(String message) {
String strTitle = TerminalMessages.TerminalError;
MessageDialog.openError( getShell(), strTitle, message);
}
protected void sendString(String string) {
try {
// Send the string after converting it to an array of bytes using the
// platform's default character encoding.
//
// TODO: Find a way to force this to use the ISO Latin-1 encoding.
getOutputStream().write(string.getBytes());
getOutputStream().flush();
} catch (SocketException socketException) {
displayTextInTerminal(socketException.getMessage());
String strMsg = TerminalMessages.SocketError
+ "!\n" + socketException.getMessage(); //$NON-NLS-1$
showErrorMessage(strMsg);
Logger.logException(socketException);
disconnectTerminal();
} catch (IOException ioException) {
showErrorMessage(TerminalMessages.IOError + "!\n" + ioException.getMessage());//$NON-NLS-1$
Logger.logException(ioException);
disconnectTerminal();
}
}
public Shell getShell() {
return getCtlText().getShell();
}
protected void sendChar(char chKey, boolean altKeyPressed) {
try {
int byteToSend = chKey;
if (altKeyPressed) {
// When the ALT key is pressed at the same time that a character is
// typed, translate it into an ESCAPE followed by the character. The
// alternative in this case is to set the high bit of the character
// being transmitted, but that will cause input such as ALT-f to be
// seen as the ISO Latin-1 character '�', which can be confusing to
// European users running Emacs, for whom Alt-f should move forward a
// word instead of inserting the '�' character.
//
// TODO: Make the ESCAPE-vs-highbit behavior user configurable.
Logger.log("sending ESC + '" + byteToSend + "'"); //$NON-NLS-1$ //$NON-NLS-2$
getOutputStream().write('\u001b');
getOutputStream().write(byteToSend);
} else {
Logger.log("sending '" + byteToSend + "'"); //$NON-NLS-1$ //$NON-NLS-2$
getOutputStream().write(byteToSend);
}
getOutputStream().flush();
} catch (SocketException socketException) {
Logger.logException(socketException);
displayTextInTerminal(socketException.getMessage());
String strMsg = TerminalMessages.SocketError
+ "!\n" + socketException.getMessage(); //$NON-NLS-1$
showErrorMessage(strMsg);
Logger.logException(socketException);
disconnectTerminal();
} catch (IOException ioException) {
Logger.logException(ioException);
displayTextInTerminal(ioException.getMessage());
String strMsg = TerminalMessages.IOError + "!\n" + ioException.getMessage(); //$NON-NLS-1$
showErrorMessage(strMsg);
Logger.logException(ioException);
disconnectTerminal();
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#setupTerminal()
*/
public void setupTerminal(Composite parent) {
fState=TerminalState.CLOSED;
setupControls(parent);
setupListeners();
setupHelp(fWndParent, TerminalPlugin.HELP_VIEW);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#onFontChanged()
*/
public void setFont(Font font) {
getCtlText().setFont(font);
if(fCommandInputField!=null) {
fCommandInputField.setFont(font);
}
// Tell the TerminalControl singleton that the font has changed.
getTerminalText().fontChanged();
}
public Font getFont() {
return getCtlText().getFont();
}
public Control getControl() {
return fCtlText;
}
protected void setupControls(Composite parent) {
// The Terminal view now aims to be an ANSI-conforming terminal emulator, so it
// can't have a horizontal scroll bar (but a vertical one is ok). Also, do
// _not_ make the TextViewer read-only, because that prevents it from seeing a
// TAB character when the user presses TAB (instead, the TAB causes focus to
// switch to another Workbench control). We prevent local keyboard input from
// modifying the text in method TerminalVerifyKeyListener.verifyKey().
fWndParent=new Composite(parent,SWT.NONE);
GridLayout layout=new GridLayout();
layout.marginWidth=0;
layout.marginHeight=0;
fWndParent.setLayout(layout);
ITerminalTextDataSnapshot snapshot=fTerminalModel.makeSnapshot();
// TODO how to get the initial size correctly!
snapshot.updateSnapshot(false);
ITextCanvasModel canvasModel=new PollingTextCanvasModel(snapshot);
fCtlText=new TextCanvas(fWndParent,canvasModel,SWT.NONE);
fCtlText.setCellRenderer(new TextLineRenderer(fCtlText,canvasModel));
fCtlText.setLayoutData(new GridData(GridData.FILL_BOTH));
fCtlText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
fCtlText.addListener(SWT.Resize, new Listener() {
public void handleEvent(Event e) {
Rectangle bonds=fCtlText.getClientArea();
int lines=bonds.height/fCtlText.getCellHeight();
int columns=bonds.width/fCtlText.getCellWidth();
fTerminalText.setDimensions(lines, columns);
}
});
fDisplay = getCtlText().getDisplay();
fClipboard = new Clipboard(fDisplay);
// fViewer.setDocument(new TerminalDocument());
setFont(JFaceResources.getTextFont());
}
protected void setupListeners() {
fKeyHandler = new TerminalKeyHandler();
fFocusListener = new TerminalFocusListener();
getCtlText().addKeyListener(fKeyHandler);
getCtlText().addFocusListener(fFocusListener);
}
/**
* Setup all the help contexts for the controls.
*/
protected void setupHelp(Composite parent, String id) {
Control[] children = parent.getChildren();
for (int nIndex = 0; nIndex < children.length; nIndex++) {
if (children[nIndex] instanceof Composite) {
setupHelp((Composite) children[nIndex], id);
}
}
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, id);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#displayTextInTerminal(java.lang.String)
*/
public void displayTextInTerminal(String text) {
writeToTerminal("\r\n"+text+"\r\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
private void writeToTerminal(String text) {
try {
getRemoteToTerminalOutputStream().write(text.getBytes("ISO-8859-1")); //$NON-NLS-1$
} catch (UnsupportedEncodingException e) {
// should never happen!
e.printStackTrace();
} catch (IOException e) {
// should never happen!
e.printStackTrace();
}
}
public OutputStream getRemoteToTerminalOutputStream() {
return fInputStream.getOutputStream();
}
protected boolean isLogCharEnabled() {
return TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_CHAR);
}
protected boolean isLogBufferSizeEnabled() {
return TerminalPlugin
.isOptionEnabled(Logger.TRACE_DEBUG_LOG_BUFFER_SIZE);
}
public OutputStream getOutputStream() {
if(getTerminalConnector()!=null)
return getTerminalConnector().getOutputStream();
return null;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#setMsg(java.lang.String)
*/
public void setMsg(String msg) {
fMsg = msg;
}
public String getMsg() {
return fMsg;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#getCtlText()
*/
protected TextCanvas getCtlText() {
return fCtlText;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl#getTerminalText()
*/
public VT100Emulator getTerminalText() {
return fTerminalText;
}
/**
*/
public ITerminalConnectorInfo getTerminalConnectorInfo() {
return fConnectorInfo;
}
protected class TerminalFocusListener implements FocusListener {
private IContextActivation contextActivation = null;
protected TerminalFocusListener() {
super();
}
public void focusGained(FocusEvent event) {
// Disable all keyboard accelerators (e.g., Control-B) so the Terminal view
// can see every keystroke. Without this, Emacs, vi, and Bash are unusable
// in the Terminal view.
IBindingService bindingService = (IBindingService) PlatformUI
.getWorkbench().getAdapter(IBindingService.class);
bindingService.setKeyFilterEnabled(false);
// The above code fails to cause Eclipse to disable menu-activation
// accelerators (e.g., Alt-F for the File menu), so we set the command
// context to be the Terminal view's command context. This enables us to
// override menu-activation accelerators with no-op commands in our
// plugin.xml file, which enables the Terminal view to see absolutly _all_
// key-presses.
IContextService contextService = (IContextService) PlatformUI
.getWorkbench().getAdapter(IContextService.class);
contextActivation = contextService
.activateContext("org.eclipse.tm.terminal.TerminalContext"); //$NON-NLS-1$
}
public void focusLost(FocusEvent event) {
// Enable all keybindings.
IBindingService bindingService = (IBindingService) PlatformUI
.getWorkbench().getAdapter(IBindingService.class);
bindingService.setKeyFilterEnabled(true);
// Restore the command context to its previous value.
IContextService contextService = (IContextService) PlatformUI
.getWorkbench().getAdapter(IContextService.class);
contextService.deactivateContext(contextActivation);
}
}
protected class TerminalKeyHandler extends KeyAdapter {
public void keyPressed(KeyEvent event) {
if (getState()==TerminalState.CONNECTING)
return;
// We set the event.doit to false to prevent any further processing of this
// key event. The only reason this is here is because I was seeing the F10
// key both send an escape sequence (due to this method) and switch focus
// to the Workbench File menu (forcing the user to click in the Terminal
// view again to continue entering text). This fixes that.
event.doit = false;
char character = event.character;
if (!isConnected()) {
// Pressing ENTER while not connected causes us to connect.
if (character == '\r') {
connectTerminal();
return;
}
// Ignore all other keyboard input when not connected.
return;
}
// If the event character is NUL ('\u0000'), then a special key was pressed
// (e.g., PageUp, PageDown, an arrow key, a function key, Shift, Alt,
// Control, etc.). The one exception is when the user presses Control-@,
// which sends a NUL character, in which case we must send the NUL to the
// remote endpoint. This is necessary so that Emacs will work correctly,
// because Control-@ (i.e., NUL) invokes Emacs' set-mark-command when Emacs
// is running on a terminal. When the user presses Control-@, the keyCode
// is 50.
if (character == '\u0000' && event.keyCode != 50) {
// A special key was pressed. Figure out which one it was and send the
// appropriate ANSI escape sequence.
//
// IMPORTANT: Control will not enter this method for these special keys
// unless certain <keybinding> tags are present in the plugin.xml file
// for the Terminal view. Do not delete those tags.
switch (event.keyCode) {
case 0x1000001: // Up arrow.
sendString("\u001b[A"); //$NON-NLS-1$
break;
case 0x1000002: // Down arrow.
sendString("\u001b[B"); //$NON-NLS-1$
break;
case 0x1000003: // Left arrow.
sendString("\u001b[D"); //$NON-NLS-1$
break;
case 0x1000004: // Right arrow.
sendString("\u001b[C"); //$NON-NLS-1$
break;
case 0x1000005: // PgUp key.
sendString("\u001b[I"); //$NON-NLS-1$
break;
case 0x1000006: // PgDn key.
sendString("\u001b[G"); //$NON-NLS-1$
break;
case 0x1000007: // Home key.
sendString("\u001b[H"); //$NON-NLS-1$
break;
case 0x1000008: // End key.
sendString("\u001b[F"); //$NON-NLS-1$
break;
case 0x100000a: // F1 key.
sendString("\u001b[M"); //$NON-NLS-1$
break;
case 0x100000b: // F2 key.
sendString("\u001b[N"); //$NON-NLS-1$
break;
case 0x100000c: // F3 key.
sendString("\u001b[O"); //$NON-NLS-1$
break;
case 0x100000d: // F4 key.
sendString("\u001b[P"); //$NON-NLS-1$
break;
case 0x100000e: // F5 key.
sendString("\u001b[Q"); //$NON-NLS-1$
break;
case 0x100000f: // F6 key.
sendString("\u001b[R"); //$NON-NLS-1$
break;
case 0x1000010: // F7 key.
sendString("\u001b[S"); //$NON-NLS-1$
break;
case 0x1000011: // F8 key.
sendString("\u001b[T"); //$NON-NLS-1$
break;
case 0x1000012: // F9 key.
sendString("\u001b[U"); //$NON-NLS-1$
break;
case 0x1000013: // F10 key.
sendString("\u001b[V"); //$NON-NLS-1$
break;
case 0x1000014: // F11 key.
sendString("\u001b[W"); //$NON-NLS-1$
break;
case 0x1000015: // F12 key.
sendString("\u001b[X"); //$NON-NLS-1$
break;
default:
// Ignore other special keys. Control flows through this case when
// the user presses SHIFT, CONTROL, ALT, and any other key not
// handled by the above cases.
break;
}
// It's ok to return here, because we never locally echo special keys.
return;
}
// To fix SPR 110341, we consider the Alt key to be pressed only when the
// Control key is _not_ also pressed. This works around a bug in SWT where,
// on European keyboards, the AltGr key being pressed appears to us as Control
// + Alt being pressed simultaneously.
Logger.log("stateMask = " + event.stateMask); //$NON-NLS-1$
boolean altKeyPressed = (((event.stateMask & SWT.ALT) != 0) && ((event.stateMask & SWT.CTRL) == 0));
if (!altKeyPressed && (event.stateMask & SWT.CTRL) != 0
&& character == ' ') {
// Send a NUL character -- many terminal emulators send NUL when
// Control-Space is pressed. This is used to set the mark in Emacs.
character = '\u0000';
}
sendChar(character, altKeyPressed);
// Special case: When we are in a TCP connection and echoing characters
// locally, send a LF after sending a CR.
// ISSUE: Is this absolutely required?
if (character == '\r' && getTerminalConnectorInfo() != null
&& isConnected()
&& getTerminalConnectorInfo().getConnector().isLocalEcho()) {
sendChar('\n', false);
}
// Now decide if we should locally echo the character we just sent. We do
// _not_ locally echo the character if any of these conditions are true:
//
// o This is a serial connection.
//
// o This is a TCP connection (i.e., m_telnetConnection is not null) and
// the remote endpoint is not a TELNET server.
//
// o The ALT (or META) key is pressed.
//
// o The character is any of the first 32 ISO Latin-1 characters except
// Control-I or Control-M.
//
// o The character is the DELETE character.
if (getTerminalConnectorInfo() == null
|| getTerminalConnectorInfo().getConnector().isLocalEcho() == false || altKeyPressed
|| (character >= '\u0001' && character < '\t')
|| (character > '\t' && character < '\r')
|| (character > '\r' && character <= '\u001f')
|| character == '\u007f') {
// No local echoing.
return;
}
// Locally echo the character.
StringBuffer charBuffer = new StringBuffer();
charBuffer.append(character);
// If the character is a carriage return, we locally echo it as a CR + LF
// combination.
if (character == '\r')
charBuffer.append('\n');
writeToTerminal(charBuffer.toString());
}
}
public void setTerminalTitle(String title) {
fTerminalListener.setTerminalTitle(title);
}
public TerminalState getState() {
return fState;
}
public void setState(TerminalState state) {
fState=state;
fTerminalListener.setState(state);
}
public String getSettingsSummary() {
if(getTerminalConnector()!=null)
return getTerminalConnector().getSettingsSummary();
return ""; //$NON-NLS-1$
}
public void setConnector(ITerminalConnectorInfo connector) {
fConnectorInfo=connector;
}
public ICommandInputField getCommandInputField() {
return fCommandInputField;
}
public void setCommandInputField(ICommandInputField inputField) {
if(fCommandInputField!=null)
fCommandInputField.dispose();
fCommandInputField=inputField;
if(fCommandInputField!=null)
fCommandInputField.createControl(fWndParent, this);
fWndParent.layout(true);
}
public int getBufferLineLimit() {
return fTerminalModel.getMaxHeight();
}
public void setBufferLineLimit(int bufferLineLimit) {
if(bufferLineLimit<=0)
return;
synchronized (fTerminalModel) {
if(fTerminalModel.getHeight()>bufferLineLimit)
fTerminalModel.setDimensions(bufferLineLimit, fTerminalModel.getWidth());
fTerminalModel.setMaxHeight(bufferLineLimit);
}
}
}

View file

@ -0,0 +1,80 @@
package org.eclipse.tm.internal.terminal.model;
import org.eclipse.tm.terminal.model.ITerminalTextData;
public interface ISnapshotChanges {
/**
* @param line might bigger than the number of lines....
*/
void markLineChanged(int line);
/**
* Marks all lines in the range as changed
* @param line >=0
* @param n might be out of range
*/
void markLinesChanged(int line, int n);
/**
* Marks all lines within the scrolling region
* changed and resets the scrolling information
*/
void convertScrollingIntoChanges();
/**
* @return true if something has changed
*/
boolean hasChanged();
/**
* @param startLine
* @param size
* @param shift
*/
void scroll(int startLine, int size, int shift);
/**
* Mark all lines changed
* @param height if no window is set this is the number of
* lines that are marked as changed
*/
void setAllChanged(int height);
int getFirstChangedLine();
int getLastChangedLine();
int getScrollWindowStartLine();
int getScrollWindowSize();
int getScrollWindowShift();
boolean hasLineChanged(int line);
void markDimensionsChanged();
boolean hasDimensionsChanged();
void markCursorChanged();
/**
* @return true if the terminal data has changed
*/
boolean hasTerminalChanged();
/**
* mark the terminal as changed
*/
void setTerminalChanged();
void copyChangedLines(ITerminalTextData dest, ITerminalTextData source);
/**
* @param startLine -1 means follow the end of the data
* @param size number of lines to follow
*/
void setInterestWindow(int startLine, int size);
int getInterestWindowStartLine();
int getInterestWindowSize();
}

View file

@ -0,0 +1,381 @@
package org.eclipse.tm.internal.terminal.model;
import org.eclipse.tm.terminal.model.ITerminalTextData;
/**
* Collects the changes of the {@link ITerminalTextData}
*
*/
public class SnapshotChanges implements ISnapshotChanges {
/**
* The first line changed
*/
private int fFirstChangedLine;
/**
* The last line changed
*/
private int fLastChangedLine;
private int fScrollWindowStartLine;
private int fScrollWindowSize;
private int fScrollWindowShift;
/**
* true, if scrolling should not tracked anymore
*/
private boolean fScrollDontTrack;
/**
* The lines that need to be copied
* into the snapshot (lines that have
* not changed don't have to be copied)
*/
private boolean[] fChangedLines;
private int fInterestWindowSize;
private int fInterestWindowStartLine;
private boolean fDimensionsChanged;
private boolean fTerminalHasChanged;
private boolean fCursorHasChanged;
public SnapshotChanges(int nLines) {
setChangedLinesLength(nLines);
fFirstChangedLine=Integer.MAX_VALUE;
fLastChangedLine=-1;
}
public SnapshotChanges(int windowStart, int windowSize) {
setChangedLinesLength(windowStart+windowSize);
fFirstChangedLine=Integer.MAX_VALUE;
fLastChangedLine=-1;
fInterestWindowStartLine=windowStart;
fInterestWindowSize=windowSize;
}
/**
* This is used in asserts to throw an {@link RuntimeException}.
* This is useful for tests.
* @return never -- throws an exception
*/
private boolean throwRuntimeException() {
throw new RuntimeException();
}
/**
* @param line
* @param size
* @return true if the range overlaps with the interest window
*/
boolean isInInterestWindow(int line, int size) {
if(fInterestWindowSize<=0)
return true;
if(line+size<=fInterestWindowStartLine || line>=fInterestWindowStartLine+fInterestWindowSize)
return false;
return true;
}
/**
* @param line
* @return true if the line is within the interest window
*/
boolean isInInterestWindow(int line) {
if(fInterestWindowSize<=0)
return true;
if(line<fInterestWindowStartLine || line>=fInterestWindowStartLine+fInterestWindowSize)
return false;
return true;
}
/**
* @param line
* @return the line within the window
*/
int fitLineToWindow(int line) {
if(fInterestWindowSize<=0)
return line;
if(line<fInterestWindowStartLine)
return fInterestWindowStartLine;
return line;
}
/**
* The result is only defined if {@link #isInInterestWindow(int, int)} returns true!
* @param line the line <b>before</b> {@link #fitLineToWindow(int)} has been called!
* @param size
* @return the adjusted size.
* <p>Note:</p> {@link #fitLineToWindow(int)} has to be called on the line to
* move the window correctly!
*/
int fitSizeToWindow(int line, int size) {
if(fInterestWindowSize<=0)
return size;
if(line<fInterestWindowStartLine) {
size-=fInterestWindowStartLine-line;
line=fInterestWindowStartLine;
}
if(line+size>fInterestWindowStartLine+fInterestWindowSize)
size=fInterestWindowStartLine+fInterestWindowSize-line;
return size;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#markLineChanged(int)
*/
public void markLineChanged(int line) {
if(!isInInterestWindow(line))
return;
line=fitLineToWindow(line);
if(line<fFirstChangedLine)
fFirstChangedLine=line;
if(line>fLastChangedLine)
fLastChangedLine=line;
// in case the terminal got resized we expand
// don't remember the changed line because
// there is nothing to copy
if(line<getChangedLineLength()) {
setChangedLine(line,true);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#markLinesChanged(int, int)
*/
public void markLinesChanged(int line, int n) {
if(n<=0 || !isInInterestWindow(line,n))
return;
// do not exceed the bounds of fChangedLines
// the terminal might have been resized and
// we can only keep changes for the size of the
// previous terminal
n=fitSizeToWindow(line, n);
line=fitLineToWindow(line);
int m=Math.min(line+n, getChangedLineLength());
for (int i = line; i < m; i++) {
setChangedLine(i,true);
}
// this sets fFirstChangedLine as well
markLineChanged(line);
// this sets fLastChangedLine as well
markLineChanged(line+n-1);
}
public void markCursorChanged() {
fCursorHasChanged=true;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#convertScrollingIntoChanges()
*/
public void convertScrollingIntoChanges() {
markLinesChanged(fScrollWindowStartLine,fScrollWindowSize);
fScrollWindowStartLine=0;
fScrollWindowSize=0;
fScrollWindowShift=0;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#hasChanged()
*/
public boolean hasChanged() {
if(fFirstChangedLine!=Integer.MAX_VALUE || fLastChangedLine>0 || fScrollWindowShift!=0 ||fDimensionsChanged || fCursorHasChanged)
return true;
return false;
}
public void markDimensionsChanged() {
fDimensionsChanged=true;
}
public boolean hasDimensionsChanged() {
return fDimensionsChanged;
}
public boolean hasTerminalChanged() {
return fTerminalHasChanged;
}
public void setTerminalChanged() {
fTerminalHasChanged=true;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#scroll(int, int, int)
*/
public void scroll(int startLine, int size, int shift) {
size=fitSizeToWindow(startLine, size);
startLine=fitLineToWindow(startLine);
// let's track only negative shifts
if(fScrollDontTrack) {
// we are in a state where we cannot track scrolling
// so let's simply mark the scrolled lines as changed
markLinesChanged(startLine, size);
} else if(shift>=0) {
// we cannot handle positive scroll
// forget about clever caching of scroll events
doNotTrackScrollingAnymore();
// mark all lines inside the scroll region as changed
markLinesChanged(startLine, size);
} else {
// we have already scrolled
if(fScrollWindowShift<0) {
// we have already scrolled
if(fScrollWindowStartLine==startLine && fScrollWindowSize==size) {
// we are scrolling the same region again?
fScrollWindowShift+=shift;
scrollChangesLinesWithNegativeShift(startLine,size,shift);
} else {
// mark all lines in the old scroll region as changed
doNotTrackScrollingAnymore();
// mark all lines changed, because
markLinesChanged(startLine, size);
}
} else {
// first scroll in this change -- we just notify it
fScrollWindowStartLine=startLine;
fScrollWindowSize=size;
fScrollWindowShift=shift;
scrollChangesLinesWithNegativeShift(startLine,size,shift);
}
}
}
/**
* Some incompatible scrolling occurred. We cannot do the
* scroll optimization anymore...
*/
private void doNotTrackScrollingAnymore() {
if(fScrollWindowSize>0) {
// convert the current scrolling into changes
markLinesChanged(fScrollWindowStartLine, fScrollWindowSize);
fScrollWindowStartLine=0;
fScrollWindowSize=0;
fScrollWindowShift=0;
}
// don't be clever on scrolling anymore
fScrollDontTrack=true;
}
/**
* Scrolls the changed lines data
*
* @param line
* @param n
* @param shift must be negative!
*/
private void scrollChangesLinesWithNegativeShift(int line, int n, int shift) {
assert shift <0 || throwRuntimeException();
// scroll the region
// don't run out of bounds!
int m=Math.min(line+n+shift,getChangedLineLength()+shift);
for (int i = line; i < m; i++) {
setChangedLine(i, hasLineChanged(i-shift));
// move the first changed line up.
// We don't have to move the maximum down,
// because with a shift scroll, the max is moved
// my the next loop in this method
if(i<fFirstChangedLine && hasLineChanged(i)) {
fFirstChangedLine=i;
}
}
// mark the "opened" lines as changed
for (int i = Math.max(0,line+n+shift); i < line+n; i++) {
markLineChanged(i);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#setAllChanged(int)
*/
public void setAllChanged(int height) {
fScrollWindowStartLine=0;
fScrollWindowSize=0;
fScrollWindowShift=0;
fFirstChangedLine=fitLineToWindow(0);
fLastChangedLine=fFirstChangedLine+fitSizeToWindow(0, height)-1;
// no need to keep an array of changes anymore
setChangedLinesLength(0);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#getFirstChangedLine()
*/
public int getFirstChangedLine() {
return fFirstChangedLine;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#getLastChangedLine()
*/
public int getLastChangedLine() {
return fLastChangedLine;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#getScrollWindowStartLine()
*/
public int getScrollWindowStartLine() {
return fScrollWindowStartLine;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#getScrollWindowSize()
*/
public int getScrollWindowSize() {
return fScrollWindowSize;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#getScrollWindowShift()
*/
public int getScrollWindowShift() {
return fScrollWindowShift;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#copyChangedLines(org.eclipse.tm.terminal.model.ITerminalTextData, org.eclipse.tm.terminal.model.ITerminalTextData)
*/
public void copyChangedLines(ITerminalTextData dest, ITerminalTextData source) {
int n=Math.min(fLastChangedLine+1,source.getHeight());
for (int i = fFirstChangedLine; i < n ; i++) {
if(hasLineChanged(i))
dest.copyLine(source,i,i);
}
}
public int getInterestWindowSize() {
return fInterestWindowSize;
}
public int getInterestWindowStartLine() {
return fInterestWindowStartLine;
}
public void setInterestWindow(int startLine, int size) {
int oldStartLine=fInterestWindowStartLine;
int oldSize=fInterestWindowSize;
fInterestWindowStartLine=startLine;
fInterestWindowSize=size;
if(oldSize>0) {
int shift=oldStartLine-startLine;
if(shift==0) {
if(size>oldSize) {
// add lines to the end
markLinesChanged(oldStartLine+oldSize, size-oldSize);
}
// else no lines within the window have changed
} else if(Math.abs(shift)<size) {
if(shift<0) {
// we can scroll
scroll(startLine, oldSize, shift);
// mark the lines at the end as new
for (int i = oldStartLine+oldSize; i < startLine+size; i++) {
markLineChanged(i);
}
} else {
// we cannot shift positive -- mark all changed
markLinesChanged(startLine, size);
}
} else {
// no scrolling possible
markLinesChanged(startLine, size);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ISnapshotChanges#hasLineChanged(int)
*/
public boolean hasLineChanged(int line) {
if(line<fChangedLines.length)
return fChangedLines[line];
// since the height of the terminal could
// have changed but we have tracked only changes
// of the previous terminal height, any line outside
// the the range of the previous height has changed
return isInInterestWindow(line);
}
int getChangedLineLength() {
return fChangedLines.length;
}
void setChangedLine(int line,boolean changed){
fChangedLines[line]=changed;
}
void setChangedLinesLength(int length) {
fChangedLines=new boolean[length];
}
}

View file

@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse synchronized public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
* This is a decorator to make all access to
* ITerminalTextData synchronized
*
*/
public class SynchronizedTerminalTextData implements ITerminalTextData {
final ITerminalTextData fData;
public SynchronizedTerminalTextData(ITerminalTextData data) {
fData=data;
}
synchronized public void addLine() {
fData.addLine();
}
synchronized public void cleanLine(int line) {
fData.cleanLine(line);
}
synchronized public void copy(ITerminalTextData source) {
fData.copy(source);
}
synchronized public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
fData.copyLine(source, sourceLine, destLine);
}
synchronized public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) {
fData.copyRange(source, sourceStartLine, destStartLine, length);
}
synchronized public char getChar(int line, int column) {
return fData.getChar(line, column);
}
synchronized public char[] getChars(int line) {
return fData.getChars(line);
}
synchronized public int getCursorColumn() {
return fData.getCursorColumn();
}
synchronized public int getCursorLine() {
return fData.getCursorLine();
}
synchronized public int getHeight() {
return fData.getHeight();
}
synchronized public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) {
return fData.getLineSegments(line, startCol, numberOfCols);
}
synchronized public int getMaxHeight() {
return fData.getMaxHeight();
}
synchronized public Style getStyle(int line, int column) {
return fData.getStyle(line, column);
}
synchronized public Style[] getStyles(int line) {
return fData.getStyles(line);
}
synchronized public int getWidth() {
return fData.getWidth();
}
synchronized public ITerminalTextDataSnapshot makeSnapshot() {
return fData.makeSnapshot();
}
synchronized public void scroll(int startLine, int size, int shift) {
fData.scroll(startLine, size, shift);
}
synchronized public void setChar(int line, int column, char c, Style style) {
fData.setChar(line, column, c, style);
}
synchronized public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
fData.setChars(line, column, chars, start, len, style);
}
synchronized public void setChars(int line, int column, char[] chars, Style style) {
fData.setChars(line, column, chars, style);
}
synchronized public void setCursorColumn(int column) {
fData.setCursorColumn(column);
}
synchronized public void setCursorLine(int line) {
fData.setCursorLine(line);
}
synchronized public void setDimensions(int height, int width) {
fData.setDimensions(height, width);
}
synchronized public void setMaxHeight(int height) {
fData.setMaxHeight(height);
}
}

View file

@ -0,0 +1,220 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
* This class is thread safe.
*
*/
public class TerminalTextData implements ITerminalTextData {
final ITerminalTextData fData;
/**
* A list of active snapshots
*/
public TerminalTextDataSnapshot[] fSnapshots=new TerminalTextDataSnapshot[0];
private int fCursorColumn;
private int fCursorLine;
public TerminalTextData() {
this(new TerminalTextDataFastScroll());
// this(new TerminalTextDataStore());
}
public TerminalTextData(ITerminalTextData data) {
fData=data;
}
public int getWidth() {
return fData.getWidth();
}
public int getHeight() {
// no need for an extra variable
return fData.getHeight();
}
public void setDimensions(int height, int width) {
int h=getHeight();
int w=getWidth();
if(w==width && h==height)
return;
fData.setDimensions(height, width);
sendDimensionsChanged(h, w, height, width);
}
private void sendDimensionsChanged(int oldHeight, int oldWidth, int newHeight, int newWidth) {
// determine what has changed
if(oldWidth==newWidth) {
if(oldHeight<newHeight)
sendLinesChangedToSnapshot(oldHeight, newHeight-oldHeight);
else
sendLinesChangedToSnapshot(newHeight,oldHeight-newHeight);
} else {
sendLinesChangedToSnapshot(0, oldHeight);
}
sendDimensionsChanged();
}
public LineSegment[] getLineSegments(int line, int column, int len) {
return fData.getLineSegments(line, column, len);
}
public char getChar(int line, int column) {
return fData.getChar(line, column);
}
public Style getStyle(int line, int column) {
return fData.getStyle(line, column);
}
public void setChar(int line, int column, char c, Style style) {
fData.setChar(line, column, c, style);
sendLineChangedToSnapshots(line);
}
public void setChars(int line, int column, char[] chars, Style style) {
fData.setChars(line, column, chars, style);
sendLineChangedToSnapshots(line);
}
public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
fData.setChars(line, column, chars, start, len, style);
sendLineChangedToSnapshots(line);
}
public void scroll(int startLine, int size, int shift) {
fData.scroll(startLine, size, shift);
sendScrolledToSnapshots(startLine, size, shift);
}
public String toString() {
return fData.toString();
}
private void sendDimensionsChanged() {
for (int i = 0; i < fSnapshots.length; i++) {
fSnapshots[i].markDimensionsChanged();
}
}
/**
* @param line notifies snapshots that line line has changed
*/
protected void sendLineChangedToSnapshots(int line) {
for (int i = 0; i < fSnapshots.length; i++) {
fSnapshots[i].markLineChanged(line);
}
}
/**
* Notify snapshots that multiple lines have changed
* @param line changed line
* @param n number of changed lines
*/
protected void sendLinesChangedToSnapshot(int line,int n) {
for (int i = 0; i < fSnapshots.length; i++) {
fSnapshots[i].markLinesChanged(line, n);
}
}
/**
* Notify snapshot that a region was scrolled
* @param startLine
* @param size
* @param shift
*/
protected void sendScrolledToSnapshots(int startLine,int size, int shift) {
for (int i = 0; i < fSnapshots.length; i++) {
fSnapshots[i].scroll(startLine, size, shift);
}
}
protected void sendCursorChanged() {
for (int i = 0; i < fSnapshots.length; i++) {
fSnapshots[i].markCursorChanged();
}
}
/**
* Removes the snapshot from the @observer@ list
* @param snapshot
*/
protected void removeSnapshot(TerminalTextDataSnapshot snapshot) {
// poor mans approach to modify the array
List list=new ArrayList();
list.addAll(Arrays.asList(fSnapshots));
list.remove(snapshot);
fSnapshots=(TerminalTextDataSnapshot[]) list.toArray(new TerminalTextDataSnapshot[list.size()]);
}
public ITerminalTextDataSnapshot makeSnapshot() {
// poor mans approach to modify the array
TerminalTextDataSnapshot snapshot=new TerminalTextDataSnapshot(this);
snapshot.markDimensionsChanged();
List list=new ArrayList();
list.addAll(Arrays.asList(fSnapshots));
list.add(snapshot);
fSnapshots=(TerminalTextDataSnapshot[]) list.toArray(new TerminalTextDataSnapshot[list.size()]);
return snapshot;
}
public void addLine() {
int oldHeight=getHeight();
fData.addLine();
// was is an append or a scroll?
int newHeight=getHeight();
if(newHeight>oldHeight) {
//the line was appended
sendLinesChangedToSnapshot(oldHeight, 1);
int width=getWidth();
sendDimensionsChanged(oldHeight, width, newHeight, width);
} else {
// the line was scrolled
sendScrolledToSnapshots(0, oldHeight, -1);
}
}
public void copy(ITerminalTextData source) {
fData.copy(source);
fCursorLine=source.getCursorLine();
fCursorColumn=source.getCursorColumn();
}
public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
fData.copyLine(source, sourceLine, destLine);
}
public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) {
fData.copyRange(source, sourceStartLine, destStartLine, length);
}
public char[] getChars(int line) {
return fData.getChars(line);
}
public Style[] getStyles(int line) {
return fData.getStyles(line);
}
public int getMaxHeight() {
return fData.getMaxHeight();
}
public void setMaxHeight(int height) {
fData.setMaxHeight(height);
}
public void cleanLine(int line) {
fData.cleanLine(line);
sendLineChangedToSnapshots(line);
}
public int getCursorColumn() {
return fCursorColumn;
}
public int getCursorLine() {
return fCursorLine;
}
public void setCursorColumn(int column) {
fCursorColumn=column;
sendCursorChanged();
}
public void setCursorLine(int line) {
fCursorLine=line;
sendCursorChanged();
}
}

View file

@ -0,0 +1,250 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
* This class is optimized for scrolling the entire {@link #getHeight()}.
* The scrolling is done by moving an offset into the data and using
* the modulo operator.
*
*/
public class TerminalTextDataFastScroll implements ITerminalTextData {
final ITerminalTextData fData;
private int fHeight;
private int fMaxHeight;
/**
* The offset into the array.
*/
int fOffset;
public TerminalTextDataFastScroll(ITerminalTextData data,int maxHeight) {
fMaxHeight=maxHeight;
fData=data;
fData.setDimensions(maxHeight, fData.getWidth());
if(maxHeight>2)
assert shiftOffset(-2) || throwRuntimeException();
}
public TerminalTextDataFastScroll(int maxHeight) {
this(new TerminalTextDataStore(),maxHeight);
}
public TerminalTextDataFastScroll() {
this(new TerminalTextDataStore(),1);
}
/**
* This is used in asserts to throw an {@link RuntimeException}.
* This is useful for tests.
* @return never -- throws an exception
*/
private boolean throwRuntimeException() {
throw new RuntimeException();
}
/**
*
* @param line
* @return the actual line number in {@link #fData}
*/
int getPositionOfLine(int line) {
return (line+fOffset)%fMaxHeight;
}
/**
* Moves offset by delta. This does <b>not</b> move the data!
* @param delta
*/
void moveOffset(int delta) {
assert Math.abs(delta)<fMaxHeight || throwRuntimeException();
fOffset=(fMaxHeight+fOffset+delta)%fMaxHeight;
}
/**
* Test method to shift the offset for testing (if assert ==true)
* @param shift TODO
* @return true
*/
private boolean shiftOffset(int shift) {
moveOffset(shift);
return true;
}
public void addLine() {
if(getHeight()<fMaxHeight) {
setDimensions(getHeight()+1, getWidth());
} else {
scroll(0,getHeight(),-1);
}
}
public void cleanLine(int line) {
fData.cleanLine(getPositionOfLine(line));
}
public void copy(ITerminalTextData source) {
int n=source.getHeight();
setDimensions(source.getHeight(),source.getWidth());
for (int i = 0; i < n; i++) {
fData.copyLine(source, i, getPositionOfLine(i));
}
}
public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
fData.copyLine(source, sourceLine, getPositionOfLine(destLine));
}
public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) {
assert (destStartLine>=0 && destStartLine+length<=fHeight) || throwRuntimeException();
for (int i = 0; i < length; i++) {
fData.copyLine(source, i+sourceStartLine, getPositionOfLine(i+destStartLine));
}
}
public char getChar(int line, int column) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
return fData.getChar(getPositionOfLine(line), column);
}
public char[] getChars(int line) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
return fData.getChars(getPositionOfLine(line));
}
public int getHeight() {
return fHeight;
}
public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
return fData.getLineSegments(getPositionOfLine(line), startCol, numberOfCols);
}
public int getMaxHeight() {
return fMaxHeight;
}
public Style getStyle(int line, int column) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
return fData.getStyle(getPositionOfLine(line), column);
}
public Style[] getStyles(int line) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
return fData.getStyles(getPositionOfLine(line));
}
public int getWidth() {
return fData.getWidth();
}
public ITerminalTextDataSnapshot makeSnapshot() {
return fData.makeSnapshot();
}
private void cleanLines(int line, int len) {
for (int i = line; i < line+len; i++) {
fData.cleanLine(getPositionOfLine(i));
}
}
public void scroll(int startLine, int size, int shift) {
assert (startLine>=0 && startLine+size<=fHeight) || throwRuntimeException();
if(shift>=fMaxHeight || -shift>=fMaxHeight) {
cleanLines(startLine, fMaxHeight-startLine);
return;
}
if(size==fHeight) {
// This is the case this class is optimized for!
moveOffset(-shift);
// we only have to clean the lines that appear by the move
if(shift<0) {
cleanLines(Math.max(startLine, startLine+size+shift),Math.min(-shift, getHeight()-startLine));
} else {
cleanLines(startLine, Math.min(shift, getHeight()-startLine));
}
} else {
// we have to copy the lines.
if(shift<0) {
// move the region up
// shift is negative!!
for (int i = startLine; i < startLine+size+shift; i++) {
fData.copyLine(fData, getPositionOfLine(i-shift), getPositionOfLine(i));
}
// then clean the opened lines
cleanLines(Math.max(0, startLine+size+shift),Math.min(-shift, getHeight()-startLine));
} else {
for (int i = startLine+size-1; i >=startLine && i-shift>=0; i--) {
fData.copyLine(fData, getPositionOfLine(i-shift), getPositionOfLine(i));
}
cleanLines(startLine, Math.min(shift, getHeight()-startLine));
}
}
}
public void setChar(int line, int column, char c, Style style) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
fData.setChar(getPositionOfLine(line), column, c, style);
}
public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
fData.setChars(getPositionOfLine(line), column, chars, start, len, style);
}
public void setChars(int line, int column, char[] chars, Style style) {
assert (line>=0 && line<fHeight) || throwRuntimeException();
fData.setChars(getPositionOfLine(line), column, chars, style);
}
public void setDimensions(int height, int width) {
assert height>=0 || throwRuntimeException();
assert width>=0 || throwRuntimeException();
if(height > fMaxHeight)
setMaxHeight(height);
fHeight=height;
if(width!=fData.getWidth())
fData.setDimensions(fMaxHeight, width);
}
public void setMaxHeight(int maxHeight) {
assert maxHeight>=fHeight || throwRuntimeException();
// move everything to offset0
int start=getPositionOfLine(0);
if(start!=0) {
// invent a more efficient algorithm....
ITerminalTextData buffer=new TerminalTextDataStore();
// create a buffer with the expected height
buffer.setDimensions(maxHeight, getWidth());
int n=Math.min(fMaxHeight-start,maxHeight);
// copy the first part
buffer.copyRange(fData, start, 0, n);
// copy the second part
if(n<maxHeight)
buffer.copyRange(fData, 0, n, Math.min(fMaxHeight-n,maxHeight-n));
// copy the buffer back to our data
fData.copy(buffer);
shiftOffset(-start);
} else {
fData.setDimensions(maxHeight, fData.getWidth());
}
fMaxHeight=maxHeight;
}
public int getCursorColumn() {
throw new UnsupportedOperationException();
}
public int getCursorLine() {
throw new UnsupportedOperationException();
}
public void setCursorColumn(int column) {
throw new UnsupportedOperationException();
}
public void setCursorLine(int line) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,299 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
* The public methods of this class have to be called from one thread!
*
* Threading considerations:
* This class is <b>not threadsafe</b>!
*/
class TerminalTextDataSnapshot implements ITerminalTextDataSnapshot {
/**
* The changes of the current snapshot relative to the
* previous snapshot
*/
volatile ISnapshotChanges fCurrentChanges;
/**
* Keeps track of changes that happened since the current
* snapshot has been made.
*/
ISnapshotChanges fFutureChanges;
/**
* Is used as lock and is the reference to the terminal we take snapshots from.
*/
final TerminalTextData fTerminal;
/**
* A snapshot copy of of fTerminal
*/
// snapshot does not need internal synchronisation
final TerminalTextDataWindow fSnapshot;
// this variable is synchronized on fTerminal!
private SnapshotOutOfDateListener[] fListener=new SnapshotOutOfDateListener[0];
// this variable is synchronized on fTerminal!
private boolean fListenersNeedNotify;
private int fInterestWindowSize;
private int fInterestWindowStartLine;
TerminalTextDataSnapshot(TerminalTextData terminal) {
fSnapshot = new TerminalTextDataWindow();
fTerminal = terminal;
fCurrentChanges = new SnapshotChanges(fTerminal.getHeight());
fCurrentChanges.setTerminalChanged();
fFutureChanges = new SnapshotChanges(fTerminal.getHeight());
fFutureChanges.markLinesChanged(0, fTerminal.getHeight());
fListenersNeedNotify=true;
fInterestWindowSize=-1;
}
/**
* This is used in asserts to throw an {@link RuntimeException}.
* This is useful for tests.
* @return never -- throws an exception
*/
private boolean throwRuntimeException() {
throw new RuntimeException();
}
public void detach() {
fTerminal.removeSnapshot(this);
}
public boolean isOutOfDate() {
// this is called from fTerminal, therefore we lock on fTerminal
synchronized (fTerminal) {
return fFutureChanges.hasChanged();
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ITerminalTextDataSnapshot#snapshot()
*/
public void updateSnapshot(boolean detectScrolling) {
// make sure terminal does not change while we make the snapshot
synchronized (fTerminal) {
// let's make the future changes current
fCurrentChanges=fFutureChanges;
fFutureChanges=new SnapshotChanges(fTerminal.getHeight());
fFutureChanges.setInterestWindow(fInterestWindowStartLine, fInterestWindowSize);
// and update the snapshot
if(fSnapshot.getHeight()!=fTerminal.getHeight()||fSnapshot.getWidth()!=fTerminal.getWidth()) {
if(fInterestWindowSize==-1)
fSnapshot.setWindow(0, fTerminal.getHeight());
// if the dimensions have changed, we need a full copy
fSnapshot.copy(fTerminal);
// and we mark all lines as changed
fCurrentChanges.setAllChanged(fTerminal.getHeight());
} else {
// first we do the scroll on the copy
int start=fCurrentChanges.getScrollWindowStartLine();
int lines=Math.min(fCurrentChanges.getScrollWindowSize(), fSnapshot.getHeight()-start);
fSnapshot.scroll(start, lines, fCurrentChanges.getScrollWindowShift());
// and then create the snapshot of the changed lines
fCurrentChanges.copyChangedLines(fSnapshot, fTerminal);
}
fListenersNeedNotify=true;
fSnapshot.setCursorLine(fTerminal.getCursorLine());
fSnapshot.setCursorColumn(fTerminal.getCursorColumn());
}
if(!detectScrolling) {
// let's pretend there was no scrolling and
// convert the scrolling into line changes
fCurrentChanges.convertScrollingIntoChanges();
}
}
public char getChar(int line, int column) {
return fSnapshot.getChar(line, column);
}
public int getHeight() {
return fSnapshot.getHeight();
}
public LineSegment[] getLineSegments(int line, int column, int len) {
return fSnapshot.getLineSegments(line, column, len);
}
public Style getStyle(int line, int column) {
return fSnapshot.getStyle(line, column);
}
public int getWidth() {
return fSnapshot.getWidth();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ITerminalTextDataSnapshot#getFirstChangedLine()
*/
public int getFirstChangedLine() {
return fCurrentChanges.getFirstChangedLine();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ITerminalTextDataSnapshot#getLastChangedLine()
*/
public int getLastChangedLine() {
return fCurrentChanges.getLastChangedLine();
}
public boolean hasLineChanged(int line) {
return fCurrentChanges.hasLineChanged(line);
}
public boolean hasDimensionsChanged() {
return fCurrentChanges.hasDimensionsChanged();
}
public boolean hasTerminalChanged() {
return fCurrentChanges.hasTerminalChanged();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ITerminalTextDataSnapshot#getScrollChangeY()
*/
public int getScrollWindowStartLine() {
return fCurrentChanges.getScrollWindowStartLine();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ITerminalTextDataSnapshot#getScrollChangeN()
*/
public int getScrollWindowSize() {
return fCurrentChanges.getScrollWindowSize();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.model.ITerminalTextDataSnapshot#getScrollChangeShift()
*/
public int getScrollWindowShift() {
return fCurrentChanges.getScrollWindowShift();
}
/**
* Announces a change in line line
* @param line
*/
void markLineChanged(int line) {
// threading
fFutureChanges.markLineChanged(line);
fFutureChanges.setTerminalChanged();
notifyListers();
}
/**
* Announces a change of n lines beginning with line line
* @param line
* @param n
*/
void markLinesChanged(int line,int n) {
fFutureChanges.markLinesChanged(line,n);
fFutureChanges.setTerminalChanged();
notifyListers();
}
void markDimensionsChanged() {
fFutureChanges.markDimensionsChanged();
fFutureChanges.setTerminalChanged();
notifyListers();
}
void markCursorChanged() {
fFutureChanges.markCursorChanged();
fFutureChanges.setTerminalChanged();
notifyListers();
}
/**
* @param startLine
* @param size
* @param shift
*/
void scroll(int startLine, int size, int shift) {
fFutureChanges.scroll(startLine,size,shift);
fFutureChanges.setTerminalChanged();
notifyListers();
}
/**
* Notifies listeners about the change
*/
private void notifyListers() {
// this code has to be called from a block synchronized on fTerminal
synchronized (fTerminal) {
if(fListenersNeedNotify) {
for (int i = 0; i < fListener.length; i++) {
fListener[i].snapshotOutOfDate(this);
}
fListenersNeedNotify=false;
}
}
}
public ITerminalTextDataSnapshot makeSnapshot() {
return fSnapshot.makeSnapshot();
}
synchronized public void addListener(SnapshotOutOfDateListener listener) {
List list=new ArrayList();
list.addAll(Arrays.asList(fListener));
list.add(listener);
fListener=(SnapshotOutOfDateListener[]) list.toArray(new SnapshotOutOfDateListener[list.size()]);
}
synchronized public void removeListener(SnapshotOutOfDateListener listener) {
List list=new ArrayList();
list.addAll(Arrays.asList(fListener));
list.remove(listener);
fListener=(SnapshotOutOfDateListener[]) list.toArray(new SnapshotOutOfDateListener[list.size()]);
}
public String toString() {
return fSnapshot.toString();
}
public int getInterestWindowSize() {
return fInterestWindowSize;
}
public int getInterestWindowStartLine() {
return fInterestWindowStartLine;
}
public void setInterestWindow(int startLine, int size) {
assert startLine>=0 || throwRuntimeException();
assert size>=0 || throwRuntimeException();
fInterestWindowStartLine=startLine;
fInterestWindowSize=size;
fSnapshot.setWindow(startLine, size);
fFutureChanges.setInterestWindow(startLine, size);
notifyListers();
}
public char[] getChars(int line) {
return fSnapshot.getChars(line);
}
public Style[] getStyles(int line) {
return fSnapshot.getStyles(line);
}
public int getCursorColumn() {
return fSnapshot.getCursorColumn();
}
public int getCursorLine() {
return fSnapshot.getCursorLine();
}
public ITerminalTextData getTerminalTextData() {
return fTerminal;
}
}

View file

@ -0,0 +1,333 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
* This class is thread safe.
*
*/
public class TerminalTextDataStore implements ITerminalTextData {
private char[][] fChars;
private Style[][] fStyle;
private int fWidth;
private int fHeight;
private int fMaxHeight;
private int fCursorColumn;
private int fCursorLine;
public TerminalTextDataStore() {
fChars=new char[0][];
fStyle=new Style[0][];
fWidth=0;
}
/**
* This is used in asserts to throw an {@link RuntimeException}.
* This is useful for tests.
* @return never -- throws an exception
*/
private boolean throwRuntimeException() {
throw new RuntimeException();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getWidth()
*/
public int getWidth() {
return fWidth;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getHeight()
*/
public int getHeight() {
return fHeight;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setDimensions(int, int)
*/
public void setDimensions(int height, int width) {
assert height>=0 || throwRuntimeException();
assert width>=0 || throwRuntimeException();
// just extend the region
if(height>fChars.length) {
int h=4*height/3;
if(fMaxHeight>0 && h>fMaxHeight)
h=fMaxHeight;
fStyle=(Style[][]) resizeArray(fStyle, height);
fChars=(char[][]) resizeArray(fChars, height);
}
// clean the new lines
if(height>fHeight) {
for (int i = fHeight; i < height; i++) {
fStyle[i]=null;
fChars[i]=null;
}
}
// set dimensions after successful resize!
fWidth=width;
fHeight=height;
}
/**
* Reallocates an array with a new size, and copies the contents of the old
* array to the new array.
*
* @param origArray the old array, to be reallocated.
* @param newSize the new array size.
* @return A new array with the same contents (chopped off if needed or filled with 0 or null).
*/
private Object resizeArray(Object origArray, int newSize) {
int oldSize = Array.getLength(origArray);
if(oldSize==newSize)
return origArray;
Class elementType = origArray.getClass().getComponentType();
Object newArray = Array.newInstance(elementType, newSize);
int preserveLength = Math.min(oldSize, newSize);
if (preserveLength > 0)
System.arraycopy(origArray, 0, newArray, 0, preserveLength);
return newArray;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getLineSegments(int, int, int)
*/
public LineSegment[] getLineSegments(int line, int column, int len) {
// get the styles and chars for this line
Style[] styles=fStyle[line];
char[] chars=fChars[line];
int col=column;
int n=column+len;
// expand the line if needed....
if(styles==null)
styles=new Style[n];
else if(styles.length<n)
styles=(Style[]) resizeArray(styles, n);
if(chars==null)
chars=new char[n];
else if(chars.length<n)
chars=(char[]) resizeArray(chars, n);
// and create the line segments
Style style=styles[column];
List segments=new ArrayList();
for (int i = column; i < n; i++) {
if(styles[i]!=style) {
segments.add(new LineSegment(col,new String(chars,col,i-col),style));
style=styles[i];
col=i;
}
}
if(col < n) {
segments.add(new LineSegment(col,new String(chars,col,n-col),style));
}
return (LineSegment[]) segments.toArray(new LineSegment[segments.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getChar(int, int)
*/
public char getChar(int line, int column) {
assert column<fWidth || throwRuntimeException();
if(fChars[line]==null||column>=fChars[line].length)
return 0;
return fChars[line][column];
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getStyle(int, int)
*/
public Style getStyle(int line, int column) {
assert column<fWidth || throwRuntimeException();
if(fStyle[line]==null || column>=fStyle[line].length)
return null;
return fStyle[line][column];
}
void ensureLineLength(int iLine, int length) {
if(length>fWidth)
throw new RuntimeException();
if(fChars[iLine]==null) {
fChars[iLine]=new char[length];
} else if(fChars[iLine].length<length) {
fChars[iLine]=(char[]) resizeArray(fChars[iLine],length);
}
if(fStyle[iLine]==null) {
fStyle[iLine]=new Style[length];
} else if(fStyle[iLine].length<length) {
fStyle[iLine]=(Style[]) resizeArray(fStyle[iLine],length);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setChar(int, int, char, org.eclipse.tm.internal.terminal.text.Style)
*/
public void setChar(int line, int column, char c, Style style) {
ensureLineLength(line,column+1);
fChars[line][column]=c;
fStyle[line][column]=style;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setChars(int, int, char[], org.eclipse.tm.internal.terminal.text.Style)
*/
public void setChars(int line, int column, char[] chars, Style style) {
setChars(line,column,chars,0,chars.length,style);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setChars(int, int, char[], int, int, org.eclipse.tm.internal.terminal.text.Style)
*/
public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
ensureLineLength(line,column+len);
for (int i = 0; i < len; i++) {
fChars[line][column+i]=chars[i+start];
fStyle[line][column+i]=style;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#scroll(int, int, int)
*/
public void scroll(int startLine, int size, int shift) {
assert startLine+size <= getHeight() || throwRuntimeException();
if(shift<0) {
// move the region up
// shift is negative!!
for (int i = startLine; i < startLine+size+shift; i++) {
fChars[i]=fChars[i-shift];
fStyle[i]=fStyle[i-shift];
}
// then clean the opened lines
cleanLines(Math.max(startLine, startLine+size+shift),Math.min(-shift, getHeight()-startLine));
// cleanLines(Math.max(0, startLine+size+shift),Math.min(-shift, getHeight()-startLine));
} else {
for (int i = startLine+size-1; i >=startLine && i-shift>=0; i--) {
fChars[i]=fChars[i-shift];
fStyle[i]=fStyle[i-shift];
}
cleanLines(startLine, Math.min(shift, getHeight()-startLine));
}
}
/**
* Replaces the lines with new empty data
* @param line
* @param len
*/
private void cleanLines(int line, int len) {
for (int i = line; i < line+len; i++) {
fChars[i]=null;
fStyle[i]=null;
}
}
/*
* @return a text representation of the object.
* Lines are separated by '\n'. No style information is returned.
*/
public String toString() {
StringBuffer buff=new StringBuffer();
for (int line = 0; line < getHeight(); line++) {
if(line>0)
buff.append("\n"); //$NON-NLS-1$
for (int column = 0; column < fWidth; column++) {
buff.append(getChar(line, column));
}
}
return buff.toString();
}
public ITerminalTextDataSnapshot makeSnapshot() {
throw new UnsupportedOperationException();
}
public void addLine() {
if(fMaxHeight>0 && getHeight()<fMaxHeight) {
setDimensions(getHeight()+1, getWidth());
} else {
scroll(0,getHeight(),-1);
}
}
public void copy(ITerminalTextData source) {
fWidth=source.getWidth();
int n=source.getHeight();
if(getHeight()!=n) {
fChars=new char[n][];
fStyle=new Style[n][];
}
for (int i = 0; i < n; i++) {
fChars[i]=source.getChars(i);
fStyle[i]=source.getStyles(i);
}
fHeight=n;
fCursorLine=source.getCursorLine();
fCursorColumn=source.getCursorColumn();
}
public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine,int length) {
for (int i = 0; i < length; i++) {
fChars[i+destStartLine]=source.getChars(i+sourceStartLine);
fStyle[i+destStartLine]=source.getStyles(i+sourceStartLine);
}
}
public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
fChars[destLine]=source.getChars(sourceLine);
fStyle[destLine]=source.getStyles(sourceLine);
}
public char[] getChars(int line) {
if(fChars[line]==null)
return null;
return (char[]) fChars[line].clone();
}
public Style[] getStyles(int line) {
if(fStyle[line]==null)
return null;
return (Style[]) fStyle[line].clone();
}
public void setLine(int line, char[] chars, Style[] styles) {
fChars[line]=(char[]) chars.clone();
fStyle[line]=(Style[]) styles.clone();
}
public void setMaxHeight(int height) {
fMaxHeight=height;
}
public int getMaxHeight() {
return fMaxHeight;
}
public void cleanLine(int line) {
fChars[line]=null;
fStyle[line]=null;
}
public int getCursorColumn() {
return fCursorColumn;
}
public int getCursorLine() {
return fCursorLine;
}
public void setCursorColumn(int column) {
fCursorColumn=column;
}
public void setCursorLine(int line) {
fCursorLine=line;
}
}

View file

@ -0,0 +1,198 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
* This class stores the data only within a window {@link #setWindow(int, int)} and
* {@link #getWindowStartLine()} and {@link #getWindowSize()}. Everything outside
* the is <code>char=='\000'</code> and <code>style=null</code>.
*
*/
public class TerminalTextDataWindow implements ITerminalTextData {
final ITerminalTextData fData;
int fWindowStartLine;
int fWindowSize;
int fHeight;
int fMaxHeight;
public TerminalTextDataWindow(ITerminalTextData data) {
fData=data;
}
public TerminalTextDataWindow() {
this(new TerminalTextDataStore());
}
/**
* This is used in asserts to throw an {@link RuntimeException}.
* This is useful for tests.
* @return never -- throws an exception
*/
private boolean throwRuntimeException() {
throw new RuntimeException();
}
/**
* @param line
* @return true if the line is within the window
*/
boolean isInWindow(int line) {
return line>=fWindowStartLine && line<fWindowStartLine+fWindowSize;
}
public char getChar(int line, int column) {
if(!isInWindow(line))
return 0;
return fData.getChar(line-fWindowStartLine, column);
}
public char[] getChars(int line) {
if(!isInWindow(line))
return null;
return fData.getChars(line-fWindowStartLine);
}
public int getHeight() {
return fHeight;
}
public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) {
if(!isInWindow(line))
return new LineSegment[]{new LineSegment(startCol,new String(new char[numberOfCols]),null)};
return fData.getLineSegments(line-fWindowStartLine, startCol, numberOfCols);
}
public int getMaxHeight() {
return fMaxHeight;
}
public Style getStyle(int line, int column) {
if(!isInWindow(line))
return null;
return fData.getStyle(line-fWindowStartLine, column);
}
public Style[] getStyles(int line) {
if(!isInWindow(line))
return null;
return fData.getStyles(line-fWindowStartLine);
}
public int getWidth() {
return fData.getWidth();
}
public ITerminalTextDataSnapshot makeSnapshot() {
throw new UnsupportedOperationException();
}
public void addLine() {
if(fMaxHeight>0 && getHeight()<fMaxHeight) {
setDimensions(getHeight()+1, getWidth());
} else {
scroll(0,getHeight(),-1);
}
}
public void copy(ITerminalTextData source) {
// we inherit the dimensions of the source
setDimensions(source.getHeight(), source.getWidth());
int n=Math.min(fWindowSize, source.getHeight()-fWindowStartLine);
if(n>0)
fData.copyRange(source, fWindowStartLine, 0, n);
}
public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) {
int n=length;
int dStart=destStartLine-fWindowStartLine;
int sStart=sourceStartLine;
// if start outside our range, cut the length to copy
if(dStart<0) {
n+=dStart;
sStart-=dStart;
dStart=0;
}
// do not exceed the window size
n=Math.min(n,fWindowSize);
if(n>0)
fData.copyRange(source, sStart, dStart, n);
}
public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
if(isInWindow(destLine))
fData.copyLine(source, sourceLine, destLine-fWindowStartLine);
}
public void scroll(int startLine, int size, int shift) {
assert (startLine>=0 && startLine+size<=fHeight) || throwRuntimeException();
int n=size;
int start=startLine-fWindowStartLine;
// if start outside our range, cut the length to copy
if(start<0) {
n+=start;
start=0;
}
n=Math.min(n,fWindowSize-start);
// do not exceed the window size
if(n>0)
fData.scroll(start, n, shift);
}
public void setChar(int line, int column, char c, Style style) {
if(!isInWindow(line))
return;
fData.setChar(line-fWindowStartLine, column, c, style);
}
public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
if(!isInWindow(line))
return;
fData.setChars(line-fWindowStartLine, column, chars, start, len, style);
}
public void setChars(int line, int column, char[] chars, Style style) {
if(!isInWindow(line))
return;
fData.setChars(line-fWindowStartLine, column, chars, style);
}
public void setDimensions(int height, int width) {
assert height>=0 || throwRuntimeException();
fData.setDimensions(fWindowSize, width);
fHeight=height;
}
public void setMaxHeight(int height) {
fMaxHeight=height;
}
public void setWindow(int startLine, int size) {
// assert startLine+size<=getHeight()||throwRuntimeException();
fWindowStartLine=startLine;
fWindowSize=size;
fData.setDimensions(fWindowSize, getWidth());
}
public int getWindowStartLine() {
return fWindowStartLine;
}
public int getWindowSize() {
return fWindowSize;
}
public void setHeight(int height) {
fHeight = height;
}
public void cleanLine(int line) {
if(isInWindow(line))
fData.cleanLine(line-fWindowStartLine);
}
public int getCursorColumn() {
return fData.getCursorColumn();
}
public int getCursorLine() {
return fData.getCursorLine();
}
public void setCursorColumn(int column) {
fData.setCursorColumn(column);
}
public void setCursorLine(int line) {
fData.setCursorLine(line);
}
}

View file

@ -168,7 +168,8 @@ public final class Logger {
*/
public static final void logException(Exception ex) {
// log in eclipse error log
TerminalPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, IStatus.OK, ex.getMessage(), ex));
if(TerminalPlugin.getDefault()!=null)
TerminalPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, IStatus.OK, ex.getMessage(), ex));
// Read my own stack to get the class name, method name, and line number
// of
// where this method was called.
@ -189,6 +190,8 @@ public final class Logger {
+ "." + methodName + ":" + lineNumber + ": " + //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
"Caught exception: " + ex); //$NON-NLS-1$
ex.printStackTrace(tmpStream);
} else {
ex.printStackTrace();
}
}
}

View file

@ -0,0 +1,289 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.graphics.Point;
import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
abstract public class AbstractTextCanvasModel implements ITextCanvasModel {
protected List fListeners = new ArrayList();
private int fCursorLine;
private int fCursorColumn;
private boolean fShowCursor;
private long fCursorTime;
private boolean fCursorIsEnabled;
private final ITerminalTextDataSnapshot fSnapshot;
private int fLines;
private int fSelectionStartLine;
private int fSeletionEndLine;
private int fSelectionStartCoumn;
private int fSelectionEndColumn;
private ITerminalTextDataSnapshot fSelectionSnapshot;
public AbstractTextCanvasModel(ITerminalTextDataSnapshot snapshot) {
fSnapshot=snapshot;
fLines=fSnapshot.getHeight();
}
public void addCellCanvasModelListener(ITextCanvasModelListener listener) {
fListeners.add(listener);
}
public void removeCellCanvasModelListener(ITextCanvasModelListener listener) {
fListeners.remove(listener);
}
protected void fireCellRangeChanged(int x, int y, int width, int height) {
for (Iterator iter = fListeners.iterator(); iter.hasNext();) {
ITextCanvasModelListener listener = (ITextCanvasModelListener) iter.next();
listener.rangeChanged(x, y, width, height);
}
}
protected void fireDimensionsChanged( int width,int height) {
for (Iterator iter = fListeners.iterator(); iter.hasNext();) {
ITextCanvasModelListener listener = (ITextCanvasModelListener) iter.next();
listener.dimensionsChanged(width,height);
}
}
protected void fireTerminalDataChanged() {
for (Iterator iter = fListeners.iterator(); iter.hasNext();) {
ITextCanvasModelListener listener = (ITextCanvasModelListener) iter.next();
listener.terminalDataChanged();
}
}
public ITerminalTextDataReadOnly getTerminalText() {
return fSnapshot;
}
protected ITerminalTextDataSnapshot getSnapshot() {
return fSnapshot;
}
protected void updateSnapshot() {
if(fSnapshot.isOutOfDate()) {
fSnapshot.updateSnapshot(false);
if(fSnapshot.hasTerminalChanged())
fireTerminalDataChanged();
// TODO why does hasDimensionsChanged not work??????
// if(fSnapshot.hasDimensionsChanged())
// fireDimensionsChanged();
if(fLines!=fSnapshot.getHeight()) {
fireDimensionsChanged(fSnapshot.getWidth(),fSnapshot.getHeight());
fLines=fSnapshot.getHeight();
}
int y=fSnapshot.getFirstChangedLine();
// has any line changed?
if(y<Integer.MAX_VALUE) {
int height=fSnapshot.getLastChangedLine()-y+1;
fireCellRangeChanged(0, y, fSnapshot.getWidth(), height);
}
}
}
/**
* must be called from the UI thread
*/
public void update() {
// do the poll....
updateSnapshot();
updateSelection();
updateCursor();
}
public int getCursorColumn() {
return fCursorColumn;
}
public int getCursorLine() {
return fCursorLine;
}
public boolean isCursorOn() {
return fShowCursor && fCursorIsEnabled;
}
/**
* should be called regularly to draw an update of the
* blinking cursor
*/
protected void updateCursor() {
if(!fCursorIsEnabled)
return;
int cursorLine=getSnapshot().getCursorLine();
int cursorColumn=getSnapshot().getCursorColumn();
// if cursor at the end put it to the end of the
// last line...
if(cursorLine>=getSnapshot().getHeight()) {
cursorLine=getSnapshot().getHeight()-1;
cursorColumn=getSnapshot().getWidth()-1;
}
// has the cursor moved?
if(fCursorLine!=cursorLine || fCursorColumn!=cursorColumn) {
// hide the old cursor!
fShowCursor=false;
// clean the previous cursor
fireCellRangeChanged(fCursorColumn, fCursorLine, 1, 1);
// the cursor is shown when it moves!
fShowCursor=true;
fCursorTime=System.currentTimeMillis();
fCursorLine=cursorLine;
fCursorColumn=cursorColumn;
// and draw the new cursor
fireCellRangeChanged(fCursorColumn, fCursorLine, 1, 1);
} else {
long t=System.currentTimeMillis();
// TODO make the cursor blink time customisable
if(t-fCursorTime>500) {
fShowCursor=!fShowCursor;
fCursorTime=t;
fireCellRangeChanged(fCursorColumn, fCursorLine, 1, 1);
}
}
}
public void setVisibleRectangle(int startLine, int startCol, int height, int width) {
fSnapshot.setInterestWindow(Math.max(0,startLine), Math.max(1,Math.min(fSnapshot.getHeight(),height)));
update();
}
protected void showCursor(boolean show) {
fShowCursor=true;
}
public void setCursorEnabled(boolean visible) {
fCursorTime=System.currentTimeMillis();
fShowCursor=visible;
fCursorIsEnabled=visible;
fireCellRangeChanged(fCursorColumn, fCursorLine, 1, 1);
}
public boolean isCursorEnabled() {
return fCursorIsEnabled;
}
public Point getSelectionEnd() {
if(fSelectionStartLine<0)
return null;
else
return new Point(fSelectionEndColumn, fSeletionEndLine);
}
public Point getSelectionStart() {
if (fSelectionStartLine < 0)
return null;
else
return new Point(fSelectionStartCoumn,fSelectionStartLine);
}
public void setSelection(int startLine, int endLine, int startColumn, int endColumn) {
assert(startLine<0 || startLine<=endLine);
if(startLine>=0) {
if(fSelectionSnapshot==null) {
fSelectionSnapshot=fSnapshot.getTerminalTextData().makeSnapshot();
fSelectionSnapshot.updateSnapshot(true);
}
} else if(fSelectionSnapshot!=null) {
fSelectionSnapshot.detach();
fSelectionSnapshot=null;
}
int oldStart=fSelectionStartLine;
int oldEnd=fSeletionEndLine;
fSelectionStartLine = startLine;
fSeletionEndLine = endLine;
fSelectionStartCoumn = startColumn;
fSelectionEndColumn = endColumn;
if(fSelectionSnapshot!=null) {
fSelectionSnapshot.setInterestWindow(0, fSeletionEndLine);
}
int changedStart;
int changedEnd;
if(oldStart<0) {
changedStart=fSelectionStartLine;
changedEnd=fSeletionEndLine;
} else if(fSelectionStartLine<0) {
changedStart=oldStart;
changedEnd=oldEnd;
} else {
changedStart=Math.min(oldStart, fSelectionStartLine);
changedEnd=Math.max(oldEnd, fSeletionEndLine);
}
if(changedStart>=0) {
fireCellRangeChanged(0, changedStart, fSnapshot.getWidth(), changedEnd-changedStart+1);
}
}
public boolean hasLineSelection(int line) {
if (fSelectionStartLine < 0)
return false;
else
return line >= fSelectionStartLine && line <= fSeletionEndLine;
}
public String getSelectedText() {
if(fSelectionStartLine<0 || fSelectionSnapshot==null)
return ""; //$NON-NLS-1$
if(fSelectionStartLine<0 || fSelectionSnapshot==null)
return ""; //$NON-NLS-1$
StringBuffer buffer=new StringBuffer();
for (int line = fSelectionStartLine; line <= fSeletionEndLine; line++) {
String text;
char[] chars=fSelectionSnapshot.getChars(line);
if(chars!=null) {
text=new String(chars);
if(line==fSeletionEndLine)
text=text.substring(0, Math.min(fSelectionEndColumn,text.length()));
if(line==fSelectionStartLine)
text=text.substring(Math.min(fSelectionStartCoumn,text.length()));
// get rid of the empty space at the end of the lines
text=text.replaceAll("\000+$",""); //$NON-NLS-1$//$NON-NLS-2$
// null means space
text=text.replace('\000', ' ');
} else {
text=""; //$NON-NLS-1$
}
buffer.append(text);
if(line < fSeletionEndLine)
buffer.append('\n');
}
return buffer.toString();
}
private void updateSelection() {
if (fSelectionSnapshot != null && fSelectionSnapshot.isOutOfDate()) {
// let's see if the selection text has changed since the last snapshot
String oldSelection = getSelectedText();
fSelectionSnapshot.updateSnapshot(true);
// has the selection moved?
if (fSelectionSnapshot != null && fSelectionStartLine >= 0 && fSelectionSnapshot.getScrollWindowSize() > 0) {
int start = fSelectionStartLine + fSelectionSnapshot.getScrollWindowShift();
int end = fSeletionEndLine + fSelectionSnapshot.getScrollWindowShift();
if (start < 0)
if (end >= 0)
start = 0;
else
start = -1;
setSelection(start, end, fSelectionStartCoumn, fSelectionEndColumn);
}
// have lines inside the selection changed?
if (fSelectionSnapshot != null && fSelectionSnapshot.getFirstChangedLine() <= fSeletionEndLine &&
fSelectionSnapshot.getLastChangedLine() >= fSelectionStartLine) {
// has the selected text changed?
String newSelection = getSelectedText();
if (!oldSelection.equals(newSelection))
setSelection(-1, -1, -1, -1);
}
// update the observed window...
if (fSelectionSnapshot != null)
// todo make -1 to work!
fSelectionSnapshot.setInterestWindow(0, fSeletionEndLine);
}
}
}

View file

@ -0,0 +1,183 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
/**
* A Grid based Canvas. The canvas has rows and columns.
* CellPainting is done with the abstract method drawCell
*/
abstract public class GridCanvas extends VirtualCanvas {
/** width of a cell */
private int fCellWidth;
/** height of a cell */
private int fCellHeight;
public GridCanvas(Composite parent, int style) {
super(parent, style);
addListener(SWT.MouseWheel, new Listener() {
public void handleEvent(Event event) {
if(getVerticalBar().isVisible()) {
int delta=-fCellHeight;
if(event.count<0)
delta=-delta;
scrollYDelta(delta);
}
event.doit=false;
}
});
}
/** template method paint.
* iterates over all cells in the clipping rectangle and paints them.
*/
protected void paint(GC gc) {
Rectangle clipping=gc.getClipping();
if(clipping.width==0 || clipping.height==0)
return;
Rectangle clientArea= getScreenRectInVirtualSpace();
// Beginning coordinates
int xOffset=clientArea.x;
int yOffset=clientArea.y;
int colFirst=virtualXToCell(xOffset+clipping.x);
if(colFirst>getCols())
colFirst=getCols();
int rowFirst=virtualYToCell(yOffset+clipping.y);
// End coordinates
int colLast=virtualXToCell(xOffset+clipping.x+clipping.width+fCellWidth);
if(colLast>getCols())
colLast=getCols();
int rowLast=virtualYToCell(yOffset+clipping.y+clipping.height+fCellHeight);
if(rowLast>getRows())
rowLast=getRows();
// System.out.println(rowFirst+"->"+rowLast+" "+System.currentTimeMillis());
// draw the cells
for(int row=rowFirst;row<=rowLast;row++) {
int cx=colFirst*fCellWidth-xOffset;
int cy=row*fCellHeight-yOffset;
drawLine(gc,row,cx,cy,colFirst,colLast);
}
paintUnoccupiedSpace(gc,clipping);
}
/**
* @param gc
* @param row the line to draw
* @param x coordinate on screen
* @param y coordinate on screen
* @param colFirst first column to draw
* @param colLast last column to draw
*/
abstract void drawLine(GC gc, int row, int x, int y, int colFirst, int colLast);
abstract protected int getRows();
abstract protected int getCols();
protected void setCellWidth(int cellWidth) {
fCellWidth = cellWidth;
getHorizontalBar().setIncrement(fCellWidth);
}
public int getCellWidth() {
return fCellWidth;
}
protected void setCellHeight(int cellHeight) {
fCellHeight = cellHeight;
getVerticalBar().setIncrement(fCellHeight);
}
public int getCellHeight() {
return fCellHeight;
}
int virtualXToCell(int x) {
return x/fCellWidth;
}
int virtualYToCell(int y) {
return y/fCellHeight;
}
protected Point screenPointToCell(int x, int y) {
x=screenXtoVirtual(x)/fCellWidth;
y=screenYtoVirtual(y)/fCellHeight;
return new Point(x,y);
}
Point screenPointToCell(Point point) {
return screenPointToCell(point.x,point.y);
}
protected Point cellToOriginOnScreen(int x, int y) {
x=virtualXtoScreen(fCellWidth*x);
y=virtualYtoScreen(fCellHeight*y);
return new Point(x,y);
}
Point cellToOriginOnScreen(Point cell) {
return cellToOriginOnScreen(cell.x,cell.y);
}
Rectangle getCellScreenRect(Point cell) {
return getCellScreenRect(cell.x,cell.y);
}
Rectangle getCellScreenRect(int x, int y) {
x=fCellWidth*virtualXtoScreen(x);
y=fCellHeight*virtualYtoScreen(y);
return new Rectangle(x,y,fCellWidth,fCellHeight);
}
protected Rectangle getCellVirtualRect(Point cell) {
return getCellVirtualRect(cell.x,cell.y);
}
Rectangle getCellVirtualRect(int x, int y) {
x=fCellWidth*x;
y=fCellHeight*y;
return new Rectangle(x,y,fCellWidth,fCellHeight);
}
protected void viewRectangleChanged(int x, int y, int width, int height) {
int cellX=virtualXToCell(x);
int cellY=virtualYToCell(y);
// End coordinates
int xE=virtualXToCell(x+fCellWidth+width-1);
if(xE>getCols())
xE=getCols();
int yE=virtualYToCell(y+fCellHeight+height-1);
if(yE>getRows())
yE=getRows();
visibleCellRectangleChanged(cellX,cellY,xE-cellX,yE-cellY);
}
/**
* Called when the viewed part has changed.
* Override when you need this information....
* Is only called if the values change (well, almost)
* @param x origin of visible cells
* @param y
* @param width number of cells visible in x direction
* @param height
*/
protected void visibleCellRectangleChanged(int x, int y, int width, int height) {
}
}

View file

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import org.eclipse.swt.graphics.GC;
/**
*
*/
public interface ILinelRenderer {
int getCellWidth();
int getCellHeight();
void drawLine(ITextCanvasModel model, GC gc, int line, int x, int y, int colFirst, int colLast);
}

View file

@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import org.eclipse.swt.graphics.Point;
import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly;
public interface ITextCanvasModel {
void addCellCanvasModelListener(ITextCanvasModelListener listener);
void removeCellCanvasModelListener(ITextCanvasModelListener listener);
ITerminalTextDataReadOnly getTerminalText();
/**
* This is is
* @param startLine
* @param startCol
* @param height
* @param width
*/
void setVisibleRectangle(int startLine, int startCol, int height, int width);
/**
* @return true when the cursor is shown (used for blinking cursors)
*/
boolean isCursorOn();
/**
* Show/Hide the cursor.
* @param visible
*/
void setCursorEnabled(boolean visible);
/**
* @return true if the cursor is shown.
*/
boolean isCursorEnabled();
/**
* @return the line of the cursor
*/
int getCursorLine();
/**
* @return the column of the cursor
*/
int getCursorColumn();
/**
* @return the start of the selection or null if nothing is selected
* {@link Point#x} is the column and {@link Point#y} is the line.
*/
Point getSelectionStart();
/**
* @return the end of the selection or null if nothing is selected
* {@link Point#x} is the column and {@link Point#y} is the line.
*/
Point getSelectionEnd();
/**
* @param startLine
* @param endLine
* @param startColumn
* @param endColumn
*/
void setSelection(int startLine, int endLine, int startColumn, int endColumn);
/**
* @param line
* @return true if line is part of the selection
*/
boolean hasLineSelection(int line);
String getSelectedText();
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
/**
*/
public interface ITextCanvasModelListener {
void cellSizeChanged();
void rangeChanged(int col, int line, int width, int height);
void dimensionsChanged(int cols, int rows);
/**
* Called when any text change happened. Used to scroll to the
* end of text in auto scroll mode. This does not get fired
* when the window of interest has changed!
*/
void terminalDataChanged();
}

View file

@ -0,0 +1,305 @@
/*******************************************************************************
* Copyright (c) 1996, 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
* Douglas Lea (Addison Wesley) - [cq:1552] BoundedBufferWithStateTracking adapted to BoundedByteBuffer
* Martin Oberhuber (Wind River) - the waitForAvailable method
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* The main purpose of this class is to start a runnable in the
* display thread when data is available and to pretend no data
* is available after a given amount of time the runnable is running.
*
*/
public class PipedInputStream extends InputStream {
/**
* The output stream used by the terminal backend to write to the terminal
*/
protected final OutputStream fOutputStream;
/**
* A blocking byte queue.
*/
private final BoundedByteBuffer fQueue;
/**
* A byte bounded buffer used to synchronize the input and the output stream.
* <p>
* Adapted from BoundedBufferWithStateTracking
* http://gee.cs.oswego.edu/dl/cpj/allcode.java
* http://gee.cs.oswego.edu/dl/cpj/
* <p>
* BoundedBufferWithStateTracking is part of the examples for the book
* Concurrent Programming in Java: Design Principles and Patterns by
* Doug Lea (ISBN 0-201-31009-0). Second edition published by
* Addison-Wesley, November 1999. The code is
* Copyright(c) Douglas Lea 1996, 1999 and released to the public domain
* and may be used for any purposes whatsoever.
* <p>
* For some reasons a solution based on
* PipedOutputStream/PipedIntputStream
* does work *very* slowly:
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4404700
* <p>
*
*/
private class BoundedByteBuffer {
protected final byte[] fBuffer; // the elements
protected int fPutPos = 0; // circular indices
protected int fTakePos = 0;
protected int fUsedSlots = 0; // the count
private boolean fClosed;
public BoundedByteBuffer(int capacity) throws IllegalArgumentException {
// make sure we don't deadlock on too small capacity
if (capacity <= 0)
throw new IllegalArgumentException();
fBuffer = new byte[capacity];
}
/**
* @return the bytes available for {@link #read()}
* Must be called with a lock on this!
*/
public int available() {
return fUsedSlots;
}
/**
* Writes a single byte to the buffer. Blocks if the buffer is full.
* @param b
* @throws InterruptedException
* Must be called with a lock on this!
*/
public void write(byte b) throws InterruptedException {
while (fUsedSlots == fBuffer.length)
// wait until not full
wait();
fBuffer[fPutPos] = b;
fPutPos = (fPutPos + 1) % fBuffer.length; // cyclically increment
if (fUsedSlots++ == 0) // signal if was empty
notifyAll();
}
public int getFreeSlots() {
return fBuffer.length - fUsedSlots;
}
public void write(byte[] b, int off, int len) throws InterruptedException {
assert len<=getFreeSlots();
while (fUsedSlots == fBuffer.length)
// wait until not full
wait();
int n = Math.min(len, fBuffer.length - fPutPos);
System.arraycopy(b, off, fBuffer, fPutPos, n);
if (fPutPos + len > n)
System.arraycopy(b, off + n, fBuffer, 0, len - n);
fPutPos = (fPutPos + len) % fBuffer.length; // cyclically increment
boolean wasEmpty = fUsedSlots == 0;
fUsedSlots += len;
if (wasEmpty) // signal if was empty
notifyAll();
}
/**
* Read a single byte. Blocks until a byte is available.
* @return a byte from the buffer
* @throws InterruptedException
* Must be called with a lock on this!
*/
public int read() throws InterruptedException {
while (fUsedSlots == 0) {
if(fClosed)
return -1;
// wait until not empty
wait();
}
byte b = fBuffer[fTakePos];
fTakePos = (fTakePos + 1) % fBuffer.length;
if (fUsedSlots-- == fBuffer.length) // signal if was full
notifyAll();
return b;
}
public int read(byte[] cbuf, int off, int len) throws InterruptedException {
assert len<=available();
while (fUsedSlots == 0) {
if(fClosed)
return 0;
// wait until not empty
wait();
}
int n = Math.min(len, fBuffer.length - fTakePos);
System.arraycopy(fBuffer, fTakePos, cbuf, off, n);
if (fTakePos + len > n)
System.arraycopy(fBuffer, 0, cbuf, off + n, len - n);
fTakePos = (fTakePos + len) % fBuffer.length;
boolean wasFull = fUsedSlots == fBuffer.length;
fUsedSlots -= len;
if(wasFull)
notifyAll();
return len;
}
public void close() {
fClosed=true;
notifyAll();
}
public boolean isClosed() {
return fClosed;
}
}
/**
* An output stream that calls {@link PipedInputStream#textAvailable}
* every time data is written to the stream. The data is written to
* {@link PipedInputStream#fQueue}.
*
*/
class PipedOutputStream extends OutputStream {
public void write(byte[] b, int off, int len) throws IOException {
try {
synchronized (fQueue) {
if(fQueue.isClosed())
throw new IOException("Stream is closed!"); //$NON-NLS-1$
int written=0;
while(written<len) {
if(fQueue.getFreeSlots()==0) {
// if no slots available, write one byte and block
// until free slots are available
fQueue.write(b[off + written]);
written++;
} else {
// if slots are available, write as much as
// we can in one junk
int n=Math.min(fQueue.getFreeSlots(), len-written);
fQueue.write(b, off + written, n);
written+=n;
}
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void write(int b) throws IOException {
try {
synchronized(fQueue) {
if(fQueue.isClosed())
throw new IOException("Stream is closed!"); //$NON-NLS-1$
fQueue.write((byte)b);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void close() throws IOException {
synchronized(fQueue) {
fQueue.close();
}
}
}
/**
* @param bufferSize the size of the buffer of the output stream
*/
public PipedInputStream(int bufferSize) {
fOutputStream =new PipedOutputStream();
fQueue=new BoundedByteBuffer(bufferSize);
}
/**
* @return the output stream used by the backend to write to the terminal.
*/
public OutputStream getOutputStream() {
return fOutputStream;
}
/**
* Waits until data is available for reading.
* @param millis see {@link Object#wait(long)}
* @throws InterruptedException
*/
public void waitForAvailable(long millis) throws InterruptedException {
synchronized(fQueue) {
if(fQueue.available()==0)
fQueue.wait(millis);
}
}
/**
* Must be called in the Display Thread!
* @return true if a character is available for the terminal to show.
*/
public int available() {
synchronized(fQueue) {
return fQueue.available();
}
}
/**
* @return the next available byte. Check with {@link #available}
* if characters are available.
*/
public int read() throws IOException {
try {
synchronized (fQueue) {
return fQueue.read();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
}
}
/**
* Closing a <tt>PipedInputStream</tt> has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an <tt>IOException</tt>.
* <p>
*/
public void close() throws IOException {
}
public int read(byte[] cbuf, int off, int len) throws IOException {
int n=0;
if(len==0)
return 0;
// read as much as we can using a single synchronized statement
try {
synchronized (fQueue) {
// if nothing available, block and read one byte
if (fQueue.available() == 0) {
// block now until at least one byte is available
int c = fQueue.read();
// are we at the end of stream
if (c == -1)
return -1;
cbuf[off] = (byte) c;
n++;
}
// is there more data available?
if (n < len && fQueue.available() > 0) {
// read at most available()
int nn = Math.min(fQueue.available(), len - n);
// are we at the end of the stream?
if (nn == 0 && fQueue.isClosed()) {
// if no byte was read, return -1 to indicate end of stream
// else return the bytes we read up to now
if (n == 0)
n = -1;
return n;
}
fQueue.read(cbuf, off + n, nn);
n += nn;
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return n;
}
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
/**
* @author Michael.Scharf@scharf-software.com
*
*/
public class PollingTextCanvasModel extends AbstractTextCanvasModel {
int fPollInterval=50;
/**
*
*/
public PollingTextCanvasModel(ITerminalTextDataSnapshot snapshot) {
super(snapshot);
Display.getDefault().timerExec(fPollInterval,new Runnable(){
public void run() {
update();
Display.getDefault().timerExec(fPollInterval,this);
}});
}
public void setUpdateInterval(int t) {
fPollInterval=t;
}
}

View file

@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tm.terminal.model.Style;
import org.eclipse.tm.terminal.model.StyleColor;
public class StyleMap {
String fFontName=JFaceResources.TEXT_FONT;
Map fColorMap=new HashMap();
Map fFontMap=new HashMap();
private Point fCharSize;
private Style fDefaultStyle;
StyleMap() {
Display display=Display.getCurrent();
fColorMap.put(StyleColor.getStyleColor("white"), new Color(display,255,255,255)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("black"), new Color(display,0,0,0)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("red"), new Color(display,255,128,128)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("green"), new Color(display,128,255,128)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("blue"), new Color(display,128,128,255)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("yellow"), new Color(display,255,255,0)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("cyan"), new Color(display,0,255,255)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("magenta"), new Color(display,255,255,0)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("gray"), new Color(display,128,128,128)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("WHITE"), new Color(display,255,255,255)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("BLACK"), new Color(display,0,0,0)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("RED"), new Color(display,255,128,128)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("GREEN"), new Color(display,128,255,128)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("BLUE"), new Color(display,128,128,255)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("YELLOW"), new Color(display,255,255,0)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("CYAN"), new Color(display,0,255,255)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("MAGENTA"), new Color(display,255,255,0)); //$NON-NLS-1$
fColorMap.put(StyleColor.getStyleColor("GRAY"), new Color(display,128,128,128)); //$NON-NLS-1$
fDefaultStyle=Style.getStyle(StyleColor.getStyleColor("black"),StyleColor.getStyleColor("white")); //$NON-NLS-1$ //$NON-NLS-2$
GC gc = new GC (display);
gc.setFont(getFont());
fCharSize = gc.textExtent ("W"); //$NON-NLS-1$
gc.dispose ();
}
public Color getColor(StyleColor colorName) {
return (Color) fColorMap.get(colorName);
}
public Color getForegrondColor(Style style) {
style = defaultIfNull(style);
if(style.isReverse())
return getColor(style.getBackground());
else
return getColor(style.getForground());
}
private Style defaultIfNull(Style style) {
if(style==null)
style=fDefaultStyle;
return style;
}
public Color getBackgroundColor(Style style) {
style = defaultIfNull(style);
if(style.isReverse())
return getColor(style.getForground());
else
return getColor(style.getBackground());
}
// static Font getBoldFont(Font font) {
// FontData fontDatas[] = font.getFontData();
// FontData data = fontDatas[0];
// return new Font(Display.getCurrent(), data.getName(), data.getHeight(), data.getStyle()|SWT.BOLD);
// }
public Font getFont(Style style) {
style = defaultIfNull(style);
if(style.isBold()) {
return JFaceResources.getFontRegistry().getBold(fFontName);
} else if(style.isUnderline()) {
return JFaceResources.getFontRegistry().getItalic(fFontName);
}
return JFaceResources.getFontRegistry().get(fFontName);
}
public Font getFont() {
return JFaceResources.getFontRegistry().get(fFontName);
}
public int getFontWidth() {
return fCharSize.x;
}
public int getFontHeight() {
return fCharSize.y;
}
}

View file

@ -0,0 +1,213 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
/**
* A cell oriented Canvas. Maintains a list of "cells".
* It can either be vertically or horizontally scrolled.
* The CellRenderer is responsible for painting the cell.
*/
public class TextCanvas extends GridCanvas {
protected final ITextCanvasModel fCellCanvasModel;
/** Renders the cells */
private ILinelRenderer fCellRenderer;
private boolean fScrollLock;
private Point fDraggingStart;
private Point fDraggingEnd;
/**
* Create a new CellCanvas with the given SWT style bits.
* (SWT.H_SCROLL and SWT.V_SCROLL are automatically added).
*/
public TextCanvas(Composite parent, ITextCanvasModel model, int style) {
super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL);
fCellCanvasModel=model;
fCellCanvasModel.addCellCanvasModelListener(new ITextCanvasModelListener(){
public void cellSizeChanged() {
setCellWidth(fCellRenderer.getCellWidth());
setCellHeight(fCellRenderer.getCellHeight());
calculateGrid();
}
public void rangeChanged(int col, int line, int width, int height) {
repaintRange(col,line,width,height);
}
public void dimensionsChanged(int cols, int rows) {
calculateGrid();
}
public void terminalDataChanged() {
if(isDisposed())
return;
scrollToEnd();
}
});
addListener(SWT.Resize, new Listener() {
public void handleEvent(Event e) {
calculateGrid();
}
});
addFocusListener(new FocusListener(){
public void focusGained(FocusEvent e) {
fCellCanvasModel.setCursorEnabled(true);
}
public void focusLost(FocusEvent e) {
fCellCanvasModel.setCursorEnabled(false);
}});
addMouseListener(new MouseListener(){
public void mouseDoubleClick(MouseEvent e) {
}
public void mouseDown(MouseEvent e) {
if(e.button==1) { // left button
fDraggingStart=screenPointToCell(e.x, e.y);
fDraggingEnd=null;
}
}
public void mouseUp(MouseEvent e) {
if(e.button==1) { // left button
setSelection(screenPointToCell(e.x, e.y));
fDraggingStart=null;
}
}
});
addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
if (fDraggingStart != null) {
setSelection(screenPointToCell(e.x, e.y));
}
}
});
}
void setSelection(Point p) {
if (!p.equals(fDraggingEnd)) {
fDraggingEnd = p;
if (compare(p, fDraggingStart) < 0) {
fCellCanvasModel.setSelection(p.y, fDraggingStart.y, p.x, fDraggingStart.x);
} else {
fCellCanvasModel.setSelection(fDraggingStart.y, p.y, fDraggingStart.x, p.x);
}
}
}
int compare(Point p1, Point p2) {
if (p1.equals(p2))
return 0;
if (p1.y == p2.y) {
if (p1.x > p2.x)
return 1;
else
return -1;
}
if (p1.y > p2.y) {
return 1;
} else {
return -1;
}
}
public void setCellRenderer(ILinelRenderer cellRenderer) {
fCellRenderer = cellRenderer;
setCellWidth(fCellRenderer.getCellWidth());
setCellHeight(fCellRenderer.getCellHeight());
}
public ILinelRenderer getCellRenderer() {
return fCellRenderer;
}
private void calculateGrid() {
setVirtualExtend(getCols()*getCellWidth(),getRows()*getCellHeight());
// scroll to end
scrollToEnd();
// make sure the scroll area is correct:
scrollY(getVerticalBar());
scrollX(getHorizontalBar());
updateViewRectangle();
getParent().layout();
redraw();
}
void scrollToEnd() {
if(!fScrollLock) {
int y=-(getRows()*getCellHeight()-getClientArea().height);
Rectangle v=getViewRectangle();
if(v.y!=y) {
setVirtualOrigin(0,y);
}
}
}
/**
*
* @return true if the cursor should be shown on output....
*/
public boolean isScrollLock() {
return fScrollLock;
}
/**
* If set then if the size changes
* @param scrollLock
*/
public void setScrollLock(boolean scrollLock) {
fScrollLock=scrollLock;
}
protected void repaintRange(int col, int line, int width, int height) {
Point origin=cellToOriginOnScreen(col,line);
Rectangle r=new Rectangle(origin.x,origin.y,width*getCellWidth(),height*getCellHeight());
repaint(r);
}
protected void drawLine(GC gc, int line, int x, int y, int colFirst, int colLast) {
fCellRenderer.drawLine(fCellCanvasModel, gc,line,x,y,colFirst, colLast);
}
protected void visibleCellRectangleChanged(int x, int y, int width, int height) {
fCellCanvasModel.setVisibleRectangle(y,x,height,width);
update();
}
protected int getCols() {
return fCellCanvasModel.getTerminalText().getWidth();
}
protected int getRows() {
return fCellCanvasModel.getTerminalText().getHeight();
}
public String getSelectionText() {
// TODO -- create a hasSelectionMethod!
return fCellCanvasModel.getSelectedText();
}
public void copy() {
Clipboard clipboard = new Clipboard(getDisplay());
clipboard.setContents(new Object[] { getSelectionText() }, new Transfer[] { TextTransfer.getInstance() });
clipboard.dispose();
}
public void selectAll() {
fCellCanvasModel.setSelection(0, fCellCanvasModel.getTerminalText().getHeight(), 0, fCellCanvasModel.getTerminalText().getWidth());
}
public boolean isEmpty() {
return false;
}
}

View file

@ -0,0 +1,141 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
*
*/
public class TextLineRenderer implements ILinelRenderer {
TextCanvas fCanvas;
private final ITextCanvasModel fModel;
StyleMap fStyleMap=new StyleMap();
Color fBackgroundColor;
public TextLineRenderer(TextCanvas c, ITextCanvasModel model) {
fCanvas=c;
fModel=model;
fBackgroundColor=c.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
}
/* (non-Javadoc)
* @see com.imagicus.thumbs.view.ICellRenderer#getCellWidth()
*/
public int getCellWidth() {
return fStyleMap.getFontWidth();
}
/* (non-Javadoc)
* @see com.imagicus.thumbs.view.ICellRenderer#getCellHeight()
*/
public int getCellHeight() {
return fStyleMap.getFontHeight();
}
public void drawLine(ITextCanvasModel model, GC gc, int line, int x, int y, int colFirst, int colLast) {
if(line<0 || line>=getTerminalText().getHeight() || colFirst>=getTerminalText().getWidth() || colFirst-colLast==0) {
fillBackground(gc, x, y, getCellWidth()*(colFirst-colLast), getCellHeight());
} else {
colLast=Math.min(colLast, getTerminalText().getWidth());
LineSegment[] segments=getTerminalText().getLineSegments(line, colFirst, colLast-colFirst);
for (int i = 0; i < segments.length; i++) {
LineSegment segment=segments[i];
Style style=segment.getStyle();
setupGC(gc, style);
String text=segment.getText();
drawText(gc, x, y, colFirst, segment.getColumn(), text);
drawCursor(model, gc, line, x, y, colFirst);
}
if(fModel.hasLineSelection(line)) {
gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_SELECTION));
Point start=model.getSelectionStart();
Point end=model.getSelectionEnd();
char[] chars=model.getTerminalText().getChars(line);
if(chars==null)
return;
int offset=0;
if(start.y==line)
offset=start.x;
offset=Math.max(offset, colFirst);
int len;
if(end.y==line)
len=end.x-offset+1;
else
len=chars.length-offset+1;
len=Math.min(len,chars.length-offset);
if(len>0) {
String text=new String(chars,offset,len);
drawText(gc, x, y, colFirst, offset, text);
}
}
}
}
private void fillBackground(GC gc, int x, int y, int width, int height) {
Color bg=gc.getBackground();
gc.setBackground(getBackgroundColor());
gc.fillRectangle (x,y,width,height);
gc.setBackground(bg);
}
private Color getBackgroundColor() {
return fBackgroundColor;
}
private void drawCursor(ITextCanvasModel model, GC gc, int row, int x, int y, int colFirst) {
if(!model.isCursorOn())
return;
int cursorLine=model.getCursorLine();
if(row==cursorLine) {
int cursorColumn=model.getCursorColumn();
if(cursorColumn<getTerminalText().getWidth()) {
Style style=getTerminalText().getStyle(row, cursorColumn);
if(style==null) {
// TODO make the cursor color customizable
style=Style.getStyle("BLACK", "WHITE"); //$NON-NLS-1$//$NON-NLS-2$
}
style=style.setReverse(!style.isReverse());
setupGC(gc,style);
String text=String.valueOf(getTerminalText().getChar(row, cursorColumn));
drawText(gc, x, y, colFirst, cursorColumn, text);
}
}
}
private void drawText(GC gc, int x, int y, int colFirst, int col, String text) {
int offset=(col-colFirst)*getCellWidth();
text=text.replace('\000', ' ');
gc.drawString(text,x+offset,y,false);
}
private void setupGC(GC gc, Style style) {
Color c=fStyleMap.getForegrondColor(style);
if(c!=gc.getForeground()) {
gc.setForeground(c);
}
c=fStyleMap.getBackgroundColor(style);
if(c!=gc.getBackground()) {
gc.setBackground(c);
}
Font f=fStyleMap.getFont(style);
if(f!=gc.getFont()) {
gc.setFont(f);
}
}
ITerminalTextDataReadOnly getTerminalText() {
return fModel.getTerminalText();
}
}

View file

@ -0,0 +1,353 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.textcanvas;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
/**
* A <code>Canvas</code> showing a virtual object.
* Virtual: the extent of the total canvas.
* Screen: the visible client area in the screen.
*/
public abstract class VirtualCanvas extends Canvas {
private Rectangle fVirtualBounds = new Rectangle(0,0,0,0);
private Rectangle fClientArea;
private GC fPaintGC=null;
/**
* prevent infinite loop in {@link #updateScrollbars()}
*/
private boolean fInUpdateScrollbars;
public VirtualCanvas(Composite parent, int style) {
super(parent, style|SWT.NO_BACKGROUND|SWT.NO_REDRAW_RESIZE);
fPaintGC= new GC(this);
fClientArea=getClientArea();
addListener(SWT.Paint, new Listener() {
public void handleEvent(Event event) {
paint(event.gc);
}
});
addListener(SWT.Resize, new Listener() {
public void handleEvent(Event event) {
fClientArea=getClientArea();
updateViewRectangle();
}
});
getVerticalBar().addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
scrollY((ScrollBar)e.widget);
postScrollEventHandling(e);
}
});
getHorizontalBar().addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
scrollX((ScrollBar)e.widget);
postScrollEventHandling(e);
}
});
addDisposeListener(new DisposeListener(){
public void widgetDisposed(DisposeEvent e) {
if(fPaintGC!=null){
fPaintGC.dispose();
fPaintGC=null;
}
}
});
}
/** HACK: run an event loop if the scrollbar is dragged...*/
private void postScrollEventHandling(Event e) {
if(true&&e.detail==SWT.DRAG) {
// TODO check if this is always ok???
// used to process runnables while scrolling
// This fixes the update problems when scrolling!
// see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=47582#5
// TODO investigate:
// The alternative is to call redraw on the new visible area
// redraw(expose.x, expose.y, expose.width, expose.height, true);
while (!getDisplay().isDisposed() && getDisplay().readAndDispatch()) {
// do nothing here...
}
}
}
protected void scrollX(ScrollBar hBar) {
int hSelection = hBar.getSelection ();
int destX = -hSelection - fVirtualBounds.x;
fVirtualBounds.x = -hSelection;
scrollSmart(destX, 0);
updateViewRectangle();
}
protected void scrollXDelta(int delta) {
getHorizontalBar().setSelection(-fVirtualBounds.x+delta);
scrollX(getHorizontalBar());
}
protected void scrollY(ScrollBar vBar) {
int vSelection = vBar.getSelection ();
int destY = -vSelection - fVirtualBounds.y;
if(destY!=0) {
fVirtualBounds.y = -vSelection;
scrollSmart(0,destY);
updateViewRectangle();
}
}
protected void scrollYDelta(int delta) {
getVerticalBar().setSelection(-fVirtualBounds.y+delta);
scrollY(getVerticalBar());
}
private void scrollSmart(int deltaX, int deltaY) {
Rectangle rect = getBounds();
scroll (deltaX, deltaY, 0, 0, rect.width, rect.height, false);
}
/**
* @param rect in virtual space
*/
protected void revealRect(Rectangle rect) {
Rectangle visibleRect=getScreenRectInVirtualSpace();
// scroll the X part
int deltaX=0;
if(rect.x<visibleRect.x) {
deltaX=rect.x-visibleRect.x;
} else if(visibleRect.x+visibleRect.width<rect.x+rect.width){
deltaX=(rect.x+rect.width)-(visibleRect.x+visibleRect.width);
}
if(deltaX!=0) {
getHorizontalBar().setSelection(-fVirtualBounds.x+deltaX);
scrollX(getHorizontalBar());
}
// scroll the Y part
int deltaY=0;
if(rect.y<visibleRect.y){
deltaY=rect.y-visibleRect.y;
} else if(visibleRect.y+visibleRect.height<rect.y+rect.height){
deltaY=(rect.y+rect.height)-(visibleRect.y+visibleRect.height);
}
if(deltaY!=0) {
getVerticalBar().setSelection(-fVirtualBounds.y+deltaY);
scrollY(getVerticalBar());
}
}
protected void repaint(Rectangle r) {
if (fPaintGC!=null) {
if(inClipping(r,fClientArea)) {
fPaintGC.setClipping(r);
paint(fPaintGC);
}
}
}
/**
* @param gc
*/
abstract protected void paint(GC gc);
protected Color getBackgroundColor() {
// return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
}
protected void paintUnoccupiedSpace(GC gc, Rectangle clipping) {
int width=fVirtualBounds.width;
int height=fVirtualBounds.height;
int marginWidth = (clipping.x+clipping.width) - width;
int marginHeight = (clipping.y+clipping.height) - height;
if(marginWidth>0||marginHeight>0){
Color bg=getBackground();
gc.setBackground(getBackgroundColor());
if (marginWidth > 0) {
gc.fillRectangle (width, clipping.y, marginWidth, clipping.height);
}
if (marginHeight > 0) {
gc.fillRectangle (clipping.x, height, clipping.width, marginHeight);
}
gc.setBackground(bg);
}
}
/**
* @private
*/
protected boolean inClipping(Rectangle clipping, Rectangle r) {
// TODO check if this is OK in all cases (the <=!)
//
if(r.x+r.width<=clipping.x)
return false;
if(clipping.x+clipping.width<=r.x)
return false;
if(r.y+r.height<=clipping.y)
return false;
if(clipping.y+clipping.height<=r.y)
return false;
return true;
}
/**
* @return the screen rect in virtual space (starting with (0,0))
* of the visible screen. (x,y>=0)
*/
protected Rectangle getScreenRectInVirtualSpace() {
Rectangle r= new Rectangle(fClientArea.x-fVirtualBounds.x,fClientArea.y-fVirtualBounds.y,fClientArea.width,fClientArea.height);
return r;
}
/**
* @return the rect in virtual space (starting with (0,0))
* of the visible screen. (x,y>=0)
*/
protected Rectangle getRectInVirtualSpace(Rectangle r) {
return new Rectangle(r.x-fVirtualBounds.x,r.y-fVirtualBounds.y,r.width,r.height);
}
/**
* Sets the extend of the virtual dieplay ares
* @param width
* @param height
*/
protected void setVirtualExtend(int width, int height) {
fVirtualBounds.width=width;
fVirtualBounds.height=height;
updateScrollbars();
updateViewRectangle();
}
/**
* sets the scrolling origin. Also sets the scrollbars.
* Does NOT redraw!
* Use negative values (move the virtual origin to the top left
* to see something in the screen (which is located at (0,0))
* @param x
* @param y
*/
protected void setVirtualOrigin(int x, int y) {
fVirtualBounds.x=x;
fVirtualBounds.y=y;
getHorizontalBar().setSelection(-x);
getVerticalBar().setSelection(-y);
updateViewRectangle();
}
protected Rectangle getVirtualBounds() {
return cloneRectangle(fVirtualBounds);
}
/**
* @param x
* @return the virtual coordinate in scree space
*/
protected int virtualXtoScreen(int x) {
return x+fVirtualBounds.x;
}
protected int virtualYtoScreen(int y) {
return y+fVirtualBounds.y;
}
protected int screenXtoVirtual(int x) {
return x-fVirtualBounds.x;
}
protected int screenYtoVirtual(int y) {
return y-fVirtualBounds.y;
}
/** called when the viewed part is changing */
private Rectangle fViewRectangle=new Rectangle(0,0,0,0);
void updateViewRectangle() {
if(
fViewRectangle.x==-fVirtualBounds.x
&& fViewRectangle.y==-fVirtualBounds.y
&& fViewRectangle.width==fClientArea.width
&& fViewRectangle.height==fClientArea.height
)
return;
fViewRectangle.x=-fVirtualBounds.x;
fViewRectangle.y=-fVirtualBounds.y;
fViewRectangle.width=fClientArea.width;
fViewRectangle.height=fClientArea.height;
viewRectangleChanged(fViewRectangle.x,fViewRectangle.y,fViewRectangle.width,fViewRectangle.height);
}
protected Rectangle getViewRectangle() {
return cloneRectangle(fViewRectangle);
}
private Rectangle cloneRectangle(Rectangle r) {
return new Rectangle(r.x,r.y,r.width,r.height);
}
/**
* Called when the viewed part has changed.
* Override when you need this information....
* Is only called if the values change!
* @param x visible in virtual space
* @param y visible in virtual space
* @param width
* @param height
*/
protected void viewRectangleChanged(int x, int y, int width, int height) {
}
/**
* @private
*/
private void updateScrollbars() {
// don't get into infinite loops....
if(!fInUpdateScrollbars) {
fInUpdateScrollbars=true;
try {
doUpdateScrollbar();
} finally {
fInUpdateScrollbars=false;
}
}
}
private void doUpdateScrollbar() {
Point size= getSize();
Rectangle clientArea= getClientArea();
ScrollBar horizontal= getHorizontalBar();
if (fVirtualBounds.width <= clientArea.width) {
// TODO IMPORTANT in ScrollBar.setVisible comment out the line
// that checks 'isvisible' and returns (at the beginning)
horizontal.setVisible(false);
horizontal.setSelection(0);
} else {
horizontal.setPageIncrement(clientArea.width - horizontal.getIncrement());
int max= fVirtualBounds.width + (size.x - clientArea.width);
horizontal.setMaximum(max);
horizontal.setThumb(size.x > max ? max : size.x);
horizontal.setVisible(true);
}
ScrollBar vertical= getVerticalBar();
if (fVirtualBounds.height <= clientArea.height) {
vertical.setVisible(false);
vertical.setSelection(0);
} else {
vertical.setPageIncrement(clientArea.height - vertical.getIncrement());
int max= fVirtualBounds.height + (size.y - clientArea.height);
vertical.setMaximum(max);
vertical.setThumb(size.y > max ? max : size.y);
vertical.setVisible(true);
}
}
}

View file

@ -0,0 +1,151 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.terminal.model;
/**
* A writable matrix of characters and {@link Style}. This is intended to be the
* low level representation of the text of a Terminal. Higher layers are responsible
* to fill the text and styles into this representation.
*
* <p><b>Note: </b> Implementations of this interface has to be thread safe.
* <p><b>Note: </b> This interface is not intended to be implemented by clients.
*/
public interface ITerminalTextData extends ITerminalTextDataReadOnly {
/**
* Sets the dimensions of the data. If the dimensions are smaller than the current
* dimensions, the lines will be chopped. If the dimensions are bigger, then
* the new elements will be filled with 0 chars and null Style.
* @param height
* @param width
*/
void setDimensions(int height, int width);
void setMaxHeight(int height);
int getMaxHeight();
/**
* Set a single character and the associated {@link Style}.
* @param line line must be >=0 and < height
* @param column column must be >=0 and < width
* @param c the new character at this position
* @param style the style or null
*/
void setChar(int line, int column, char c, Style style);
/**
* Set an array of characters showing in the same {@link Style}.
* @param line line must be >=0 and < height
* @param column column must be >=0 and < width
* @param chars the new characters at this position
* @param style the style or null
*/
void setChars(int line, int column, char[] chars, Style style);
/**
* Set a subrange of an array of characters showing in the same {@link Style}.
* @param line line must be >=0 and < height
* @param column column must be >=0 and < width
* @param chars the new characters at this position
* @param start the start index in the chars array
* @param len the number of characters to insert. Characters beyond width are not inserted.
* @param style the style or null
*/
void setChars(int line, int column, char[] chars, int start, int len, Style style);
/**
* Cleans the entire line.
* @param line
*/
void cleanLine(int line);
// /**
// * @param line
// * @return true if this line belongs to the previous line but is simply
// * wrapped.
// */
// boolean isWrappedLine(int line);
//
// /**
// * Makes this line an extension to the previous line. Wrapped lines get folded back
// * when the width of the terminal changes
// * @param line
// * @param extendsPreviousLine
// */
// void setWrappedLine(int line, boolean extendsPreviousLine);
/**
* Shifts some lines up or down. The "empty" space is filled with <code>'\000'</code> chars
* and <code>null</code> {@link Style}
* <p>To illustrate shift, here is some sample data:
* <pre>
* 0 aaaa
* 1 bbbb
* 2 cccc
* 3 dddd
* 4 eeee
* </pre>
*
* Shift a region of 3 lines <b>up</b> by one line <code>shift(1,3,-1)</code>
* <pre>
* 0 aaaa
* 1 cccc
* 2 dddd
* 3
* 4 eeee
* </pre>
*
*
* Shift a region of 3 lines <b>down</b> by one line <code>shift(1,3,1)</code>
* <pre>
* 0 aaaa
* 1
* 2 bbbb
* 3 cccc
* 4 eeee
* </pre>
* @param startLine the start line of the shift
* @param size the number of lines to shift
* @param shift how much scrolling is done. New scrolled area is filled with <code>'\000</code>'.
* Negative number means scroll down, positive scroll up (see example above).
*/
void scroll(int startLine, int size, int shift);
/**Adds a new line to the terminal. If maxHeigth is reached, the entire terminal
* will be scrolled. Else a line will be added.
*/
void addLine();
/**
* Copies the entire source into this and changes the size accordingly
* @param source
*/
void copy(ITerminalTextData source);
/**
* Copy a sourceLine from source to this at destLine.
* @param source
* @param sourceLine
* @param destLine
*/
void copyLine(ITerminalTextData source,int sourceLine, int destLine);
/**
* Copy <code>length</code> lines from source starting at sourceLine into this starting at
* destLine.
* @param source
* @param sourceStartLine
* @param destStartLine
* @param length
*/
void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine,int length);
void setCursorLine(int line);
void setCursorColumn(int column);
}

View file

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.terminal.model;
public interface ITerminalTextDataReadOnly {
/**
* @return the width of the terminal
*/
int getWidth();
/**
* @return the height of the terminal
*/
int getHeight();
/**
* @param line be >=0 and < height
* @param startCol must be >=0 and < width
* @param numberOfCols must be > 0
* @return a the line segments of the specified range
*/
LineSegment[] getLineSegments(int line, int startCol, int numberOfCols);
/**
* @param line must be >=0 and < height
* @param column must be >=0 and < width
* @return the character at column,line
*/
char getChar(int line, int column);
/**
* @param line must be >=0 and < height
* @param column must be >=0 and < width
* @return style at column,line or null
*/
Style getStyle(int line, int column);
/**
* Creates a new instance of {@link ITerminalTextDataSnapshot} that
* can be used to track changes. Make sure to call {@link ITerminalTextDataSnapshot#detach()}
* if you don't need the snapshots anymore.
* <p><b>Note: </b>A new snapshot is empty and needs a call to {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} to
* get its initial values. You might want to setup the snapshot to your needs by calling
* {@link ITerminalTextDataSnapshot#setInterestWindow(int, int)}.
* </p>
* @return a new instance of {@link ITerminalTextDataSnapshot} that "listens" to changes of
* <code>this</code>.
*/
public ITerminalTextDataSnapshot makeSnapshot();
char[] getChars(int line);
Style[] getStyles(int line);
/**
* @return the line in which the cursor is at the moment
*/
int getCursorLine();
/**
* @return the column at which the cursor is at the moment
*/
int getCursorColumn();
}

View file

@ -0,0 +1,212 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.terminal.model;
/**
* This class maintains a snapshot of an instance of {@link ITerminalTextData}.
* While the {@link ITerminalTextData} continues changing, the snapshot remains
* unchanged until the next snapshot is taken by calling {@link #updateSnapshot(boolean)}.
* This is important, because the {@link ITerminalTextData} might get
* modified by another thread. Suppose you would want to draw the content of
* the {@link ITerminalTextData} using the following loop:
* <pre>
* for(int line=0;line&lt;term.getHeight();line++)
* for(int column=0; column&lt;term.getWidth();column++)
* drawCharacter(column,line,term.getChar(column,line),term.getStyle(column,line));
* </pre>
* This might fail because the background thread could change the dimensions of the
* {@link ITerminalTextData} while you iterate the loop. One solution would be to
* put a <code>synchronized(term){}</code> statement around the code. This has
* two problems: 1. you would have to know about the internals of the synchronisation
* of {@link ITerminalTextData}. 2. The other thread that changes {@link ITerminalTextData}
* is blocked while the potentially slow drawing is done.
* <p>
* <b>Solution:</b> Take a snapshot of the terminal and use the snapshot to draw
* the content. There is no danger that the data structure get changed while
* you draw. There are also methods to find out what has changed to minimize
* the number of lines that get redrawn.</p>
*
* <p><b>Drawing optimization</b>: To optimize redrawing of changed lines, this class keeps
* track of lines that have changed since the previous snapshot.</p>
*
* <pre>
* // iterate over the potentially changed lines
* for(int line=snap.getFirstChangedLine();line&lt;=snap.getLastChangedLine();line++)
* // redraw only if the line has changed
* if(snap.hasLineChanged(line))
* for(int column=0; column&lt;snap.getWidth();column++)
* drawCharacter(column,line,snap.getChar(column,line),snap.getStyle(column,line));
* </pre>
*
* <p><b>Scroll optimization:</b> Often new lines are appended at the bottom of the
* terminal and the rest of the lines are scrolled up. In this case all lines would be
* marked as changed. To optimize for this
* case, {@link #updateSnapshot(boolean)} can be called with <code>true</code> for
* the <code>detectScrolling</code> parameter. The object will keep track of scrolling.
* The UI must <b>first</b> handle the scrolling and then use the {@link #hasLineChanged(int)}
* method to determine scrolling:
* <pre>
* // scroll the visible region of the UI <b>before</b> drawing the changed lines.
* doUIScrolling(snap.getScrollChangeY(),snap.getScrollChangeN(),snap.getScrollChangeShift());
* // iterate over the potentially changed lines
* for(int line=snap.getFirstChangedLine();line&lt;=snap.getFirstChangedLine();line++)
* // redraw only if the line has changed
* if(snap.hasLineChanged(line))
* for(int column=0; column&lt;snap.getWidth();column++)
* drawCharacter(column,line,snap.getChar(column,line),snap.getStyle(column,line));
* </pre>
* </p>
*
* <p><b>Note</b>: This interface is not intended to be implemented by clients.</p>
* <p><b>Threading Note</b>: This class is not thread save! All methods have to be called by
* the a same thread, that created the instance by calling
* {@link ITerminalTextDataReadOnly#makeSnapshot()}. </p>
*/
public interface ITerminalTextDataSnapshot extends ITerminalTextDataReadOnly {
/**
* This listener gets called when the current snapshot
* is out of date. Calling {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}
* will have an effect. Once the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} method is called,
* it will not be called until {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}
* is called and a new snapshot needs to be updated again.
* <p>
* A typical terminal view would not update the snapshot immediately
* after the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} has been called. It would introduce a
* delay to update the UI (and the snapshot} 10 or 20 times per second.
*
* <p>Make sure you don't spend too much time in this method.
*/
interface SnapshotOutOfDateListener {
/**
* Gets called when the snapshot is out of date. To get the snapshot up to date,
* call {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}.
* @param snapshot The snapshot that is out of date
*/
void snapshotOutOfDate(ITerminalTextDataSnapshot snapshot);
}
void addListener(SnapshotOutOfDateListener listener);
void removeListener(SnapshotOutOfDateListener listener);
/**
* Ends the listening to the {@link ITerminalTextData}. After this
* has been called no new snapshot data is collected.
*/
void detach();
/**
* @return true if the data has changed since the previous snapshot.
*/
boolean isOutOfDate();
/**
* The window of interest is the region the snapshot should track.
* Changes outside this region are ignored. The change takes effect after
* an update!
* @param startLine -1 means track the end of the data
* @param size number of lines to track. A size of -1 means track all.
*/
void setInterestWindow(int startLine, int size);
int getInterestWindowStartLine();
int getInterestWindowSize();
/**
* Create a new snapshot of the {@link ITerminalTextData}. It will efficiently
* copy the data of the {@link ITerminalTextData} into an internal representation.
* The snapshot also keeps track of the changes since the previous snapshot.
* <p>With the methods {@link #getFirstChangedLine()}, {@link #getLastChangedLine()} and
* {@link #hasLineChanged(int)}
* you can find out what has changed in the current snapshot since the previous snapshot.
* @param detectScrolling if <code>true</code> the snapshot tries to identify scroll
* changes since the last snapshot. In this case the information about scrolling
* can be retrieved using the following methods:
* {@link #getScrollWindowStartLine()}, {@link #getScrollWindowSize()} and {@link #getScrollWindowShift()}
* <br><b>Note:</b> The method {@link #hasLineChanged(int)} returns changes <b>after</b> the
* scrolling has been applied.
*/
void updateSnapshot(boolean detectScrolling);
/**
* @return The first line changed in this snapshot compared
* to the previous snapshot.
*
* <p><b>Note:</b> If no line has changed, this
* returns {@link Integer#MAX_VALUE}
*
* <p><b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>,
* then this does not include lines that only have been scrolled. This is the
* first line that has changed <b>after</b> the scroll has been applied.
*/
int getFirstChangedLine();
/**
* @return The last line changed in this snapshot compared
* to the previous snapshot. If the height has changed since the
* last update of the snapshot, then the returned value is within
* the new dimensions.
*
* <p><b>Note:</b> If no line has changed, this returns <code>-1</code>
*
* <p><b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>,
* then this does not include lines that only have been scrolled. This is the
* last line that has changed <b>after</b> the scroll has been applied.
*
* <p>A typical for loop using this method would look like this (note the <code>&lt;=</code> in the for loop):
* <pre>
* for(int line=snap.{@link #getFirstChangedLine()}; line <b>&lt;=</b> snap.getLastChangedLine(); line++)
* if(snap.{@link #hasLineChanged(int) hasLineChanged(line)})
* doSomething(line);
* </pre>
*/
int getLastChangedLine();
/**
* @param line
* @return true if the line has changed since the previous snapshot
*/
boolean hasLineChanged(int line);
boolean hasDimensionsChanged();
/**
* @return true if the terminal has changed (and not just the
* window of interest)
*/
boolean hasTerminalChanged();
/**
* If {@link #updateSnapshot(boolean)} was called with <code>true</code>, then this method
* returns the top of the scroll region.
* @return The first line scrolled in this snapshot compared
* to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}.
*/
int getScrollWindowStartLine();
/**
* If {@link #updateSnapshot(boolean)} was called with <code>true</code>, then this method
* returns the size of the scroll region.
* @return The number of lines scrolled in this snapshot compared
* to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}
* If nothing has changed, 0 is returned.
*/
int getScrollWindowSize();
/**
* If {@link #updateSnapshot(boolean)} was called with <code>true</code>, then this method
* returns number of lines moved by the scroll region.
* @return The the scroll shift of this snapshot compared
* to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}
*/
int getScrollWindowShift();
/**
* @return The {@link ITerminalTextData} on that this instance is observing.
*/
ITerminalTextData getTerminalTextData();
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.terminal.model;
public class LineSegment {
private final String fText;
private final int fCol;
private final Style fStyle;
public LineSegment(int col, String text, Style style) {
fCol = col;
fText = text;
fStyle = style;
}
public Style getStyle() {
return fStyle;
}
public String getText() {
return fText;
}
public int getColumn() {
return fCol;
}
public String toString() {
return "LineSegment("+fCol+", \""+fText+"\","+fStyle+")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}

View file

@ -0,0 +1,154 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.terminal.model;
import java.util.HashMap;
import java.util.Map;
/**
* @author scharf
* Flyweight
* Threadsafe.
*
*/
// TODO add an Object for user data, use weak map to keep track of styles with associated
// user data
public class Style {
private final StyleColor fForground;
private final StyleColor fBackground;
private final boolean fBold;
private final boolean fBlink;
private final boolean fUnderline;
private final boolean fReverse;
private final static Map fgStyles=new HashMap();
private Style(StyleColor forground, StyleColor background, boolean bold, boolean blink, boolean underline, boolean reverse) {
fForground = forground;
fBackground = background;
fBold = bold;
fBlink = blink;
fUnderline = underline;
fReverse = reverse;
}
public static Style getStyle(StyleColor forground, StyleColor background, boolean bold, boolean blink, boolean underline, boolean reverse) {
Style style = new Style(forground,background, bold, blink,underline,reverse);
Style cached;
synchronized (fgStyles) {
cached=(Style) fgStyles.get(style);
if(cached==null) {
cached=style;
fgStyles.put(cached, cached);
}
}
return cached;
}
public static Style getStyle(String forground, String background) {
return getStyle(StyleColor.getStyleColor(forground), StyleColor.getStyleColor(background),false,false,false,false);
}
public static Style getStyle(StyleColor forground, StyleColor background) {
return getStyle(forground, background,false,false,false,false);
}
public Style setForground(StyleColor forground) {
return getStyle(forground,fBackground,fBold,fBlink,fUnderline,fReverse);
}
public Style setBackground(StyleColor background) {
return getStyle(fForground,background,fBold,fBlink,fUnderline,fReverse);
}
public Style setForground(String colorName) {
return getStyle(StyleColor.getStyleColor(colorName),fBackground,fBold,fBlink,fUnderline,fReverse);
}
public Style setBackground(String colorName) {
return getStyle(fForground,StyleColor.getStyleColor(colorName),fBold,fBlink,fUnderline,fReverse);
}
public Style setBold(boolean bold) {
return getStyle(fForground,fBackground,bold,fBlink,fUnderline,fReverse);
}
public Style setBlink(boolean blink) {
return getStyle(fForground,fBackground,fBold,blink,fUnderline,fReverse);
}
public Style setUnderline(boolean underline) {
return getStyle(fForground,fBackground,fBold,fBlink,underline,fReverse);
}
public Style setReverse(boolean reverse) {
return getStyle(fForground,fBackground,fBold,fBlink,fUnderline,reverse);
}
public StyleColor getBackground() {
return fBackground;
}
public boolean isBlink() {
return fBlink;
}
public boolean isBold() {
return fBold;
}
public StyleColor getForground() {
return fForground;
}
public boolean isReverse() {
return fReverse;
}
public boolean isUnderline() {
return fUnderline;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fBackground == null) ? 0 : fBackground.hashCode());
result = prime * result + (fBlink ? 1231 : 1237);
result = prime * result + (fBold ? 1231 : 1237);
result = prime * result + ((fForground == null) ? 0 : fForground.hashCode());
result = prime * result + (fReverse ? 1231 : 1237);
result = prime * result + (fUnderline ? 1231 : 1237);
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Style other = (Style) obj;
// background == is the same as equals
if (fBackground != other.fBackground)
return false;
if (fBlink != other.fBlink)
return false;
if (fBold != other.fBold)
return false;
if (fForground != other.fForground)
return false;
if (fReverse != other.fReverse)
return false;
if (fUnderline != other.fUnderline)
return false;
return true;
}
public String toString() {
StringBuffer result=new StringBuffer();
result.append("Style(foreground="); //$NON-NLS-1$
result.append(fForground);
result.append(", background="); //$NON-NLS-1$
result.append(fBackground);
if(fBlink)
result.append(", blink"); //$NON-NLS-1$
if(fBold)
result.append(", bold"); //$NON-NLS-1$
if(fBlink)
result.append(", blink"); //$NON-NLS-1$
if(fReverse)
result.append(", reverse"); //$NON-NLS-1$
if(fUnderline)
result.append(", underline"); //$NON-NLS-1$
result.append(")"); //$NON-NLS-1$
return result.toString();
}
}

View file

@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.terminal.model;
import java.util.HashMap;
import java.util.Map;
/**
*
* Flyweight
* Threadsafe.
*/
public class StyleColor {
private final static Map fgStyleColors=new HashMap();
final String fName;
/**
* @param name the name of the color. It is up to the UI to associate a
* named color with a visual representation
* @return a StyleColor
*/
public static StyleColor getStyleColor(String name) {
StyleColor result;
synchronized (fgStyleColors) {
result=(StyleColor) fgStyleColors.get(name);
if(result==null) {
result=new StyleColor(name);
fgStyleColors.put(name, result);
}
}
return result;
}
// nobody except the factory method is allowed to instantiate this class!
private StyleColor(String name) {
fName = name;
}
public String getName() {
return fName;
}
public String toString() {
return fName;
}
// no need to override equals and hashCode, because Object uses object identity
}

View file

@ -0,0 +1,20 @@
/*******************************************************************************
* Copyright (c) 2007 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:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.terminal.model;
import org.eclipse.tm.internal.terminal.model.SynchronizedTerminalTextData;
import org.eclipse.tm.internal.terminal.model.TerminalTextData;
public class TerminalTextDataFactory {
static public ITerminalTextData makeTerminalTextData() {
return new SynchronizedTerminalTextData(new TerminalTextData());
}
}