blob: a31ab2a949e3e35f5b85a91e19cb5502ce94a19f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2011 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
* Anton Leherbauer (Wind River) - [206329] Changing terminal size right after connect does not scroll properly
*******************************************************************************/
package org.eclipse.tm.internal.terminal.emulator;
import static java.util.Collections.emptyList;
import java.util.*;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.tm.terminal.model.*;
import com.google.eclipse.terminal.ui.hyperlink.*;
/**
*
*/
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;
private final IHyperlinkFactory urlHyperlinkFactory = new HttpHyperlinkFactory();
private final Map<Integer, List<IHyperlink>> hyperlinks = new HashMap<Integer, List<IHyperlink>>();
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
}
// relative cursor line
int cl=getCursorLine();
int cc=getCursorColumn();
int height=fTerminal.getHeight();
// absolute cursor line
int acl=cl+height-fLines;
int newLines=Math.max(lines,height);
if(lines<fLines) {
if(height==fLines) {
// if the terminal has no history, then resize by
// setting the size to the new size
// TODO We are assuming that cursor line points at end of text
newLines=Math.max(lines, cl+1);
}
}
fLines=lines;
fColumns=cols;
// make the terminal at least as high as we need lines
fTerminal.setDimensions(newLines, fColumns);
// compute relative cursor line
cl=acl-(newLines-fLines);
setCursor(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()
*/
@Override
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)
*/
@Override
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)
*/
@Override
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 originalLine = line;
List<IHyperlink> found = emptyList();
if (buffer != null) {
found = urlHyperlinkFactory.hyperlinksIn(fCursorColumn, buffer);
hyperlinks.put(new Integer(line), found);
}
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);
}
}
drawHyperlinks(found, originalLine);
}
}
private void drawHyperlinks(List<IHyperlink> hyperlinks, int line) {
for (IHyperlink hyperlink : hyperlinks) {
IRegion region = hyperlink.getHyperlinkRegion();
int start = region.getOffset();
int end = start + region.getLength();
for (int column = start; column < end; column++) {
Style style = fTerminal.getStyle(line, column);
if (style != null) {
style = style.setUnderline(true);
fTerminal.setChar(line, column, fTerminal.getChar(line, column), style);
}
}
}
}
/**
* 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;
}
}
public List<IHyperlink> hyperlinksAt(int line) {
List<IHyperlink> found = hyperlinks.get(new Integer(line));
if (found == null) {
return emptyList();
}
return found;
}
}