blob: f0472aba92878342b52827a8da189f10b32da98b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2018 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 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Michael Scharf (Wind River) - initial API and implementation
* Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.BitSet;
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.TerminalStyle;
/**
* This class is thread safe.
*
*/
public class TerminalTextDataStore implements ITerminalTextData {
private char[][] fChars;
private TerminalStyle[][] fStyle;
private int fWidth;
private int fHeight;
private int fMaxHeight;
private int fCursorColumn;
private int fCursorLine;
final private BitSet fWrappedLines = new BitSet();
public TerminalTextDataStore() {
fChars = new char[0][];
fStyle = new TerminalStyle[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();
}
@Override
public int getWidth() {
return fWidth;
}
@Override
public int getHeight() {
return fHeight;
}
@Override
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 = (TerminalStyle[][]) resizeArray(fStyle, height);
fChars = (char[][]) resizeArray(fChars, height);
}
// clean the new lines
if (height > fHeight) {
for (int i = fHeight; i < height; i++) {
cleanLine(i);
}
}
// 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;
}
@Override
public LineSegment[] getLineSegments(int line, int column, int len) {
// get the styles and chars for this line
TerminalStyle[] styles = fStyle[line];
char[] chars = fChars[line];
int col = column;
int n = column + len;
// expand the line if needed....
if (styles == null)
styles = new TerminalStyle[n];
else if (styles.length < n)
styles = (TerminalStyle[]) 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
TerminalStyle style = styles[column];
List<LineSegment> 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 segments.toArray(new LineSegment[segments.size()]);
}
@Override
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];
}
@Override
public TerminalStyle 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 TerminalStyle[length];
} else if (fStyle[iLine].length < length) {
fStyle[iLine] = (TerminalStyle[]) resizeArray(fStyle[iLine], length);
}
}
@Override
public void setChar(int line, int column, char c, TerminalStyle style) {
ensureLineLength(line, column + 1);
fChars[line][column] = c;
fStyle[line][column] = style;
}
@Override
public void setChars(int line, int column, char[] chars, TerminalStyle style) {
setChars(line, column, chars, 0, chars.length, style);
}
@Override
public void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style) {
ensureLineLength(line, column + len);
for (int i = 0; i < len; i++) {
fChars[line][column + i] = chars[i + start];
fStyle[line][column + i] = style;
}
}
@Override
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];
fWrappedLines.set(i, fWrappedLines.get(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];
fWrappedLines.set(i, fWrappedLines.get(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++) {
cleanLine(i);
}
}
/*
* @return a text representation of the object.
* Lines are separated by '\n'. No style information is returned.
*/
@Override
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();
}
@Override
public ITerminalTextDataSnapshot makeSnapshot() {
throw new UnsupportedOperationException();
}
@Override
public void addLine() {
if (fMaxHeight > 0 && getHeight() < fMaxHeight) {
setDimensions(getHeight() + 1, getWidth());
} else {
scroll(0, getHeight(), -1);
}
}
@Override
public void copy(ITerminalTextData source) {
fWidth = source.getWidth();
int n = source.getHeight();
if (getHeight() != n) {
fChars = new char[n][];
fStyle = new TerminalStyle[n][];
}
for (int i = 0; i < n; i++) {
copyLine(source, i, i);
}
fHeight = n;
fCursorLine = source.getCursorLine();
fCursorColumn = source.getCursorColumn();
}
@Override
public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) {
for (int i = 0; i < length; i++) {
copyLine(source, i + sourceStartLine, i + destStartLine);
}
}
@Override
public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
fChars[destLine] = source.getChars(sourceLine);
fStyle[destLine] = source.getStyles(sourceLine);
fWrappedLines.set(destLine, source.isWrappedLine(sourceLine));
}
@Override
public char[] getChars(int line) {
if (fChars[line] == null)
return null;
return fChars[line].clone();
}
@Override
public TerminalStyle[] getStyles(int line) {
if (fStyle[line] == null)
return null;
return fStyle[line].clone();
}
public void setLine(int line, char[] chars, TerminalStyle[] styles) {
fChars[line] = chars.clone();
fStyle[line] = styles.clone();
fWrappedLines.clear(line);
}
@Override
public void setMaxHeight(int height) {
fMaxHeight = height;
}
@Override
public int getMaxHeight() {
return fMaxHeight;
}
@Override
public void cleanLine(int line) {
fChars[line] = null;
fStyle[line] = null;
fWrappedLines.clear(line);
}
@Override
public int getCursorColumn() {
return fCursorColumn;
}
@Override
public int getCursorLine() {
return fCursorLine;
}
@Override
public void setCursorColumn(int column) {
fCursorColumn = column;
}
@Override
public void setCursorLine(int line) {
fCursorLine = line;
}
@Override
public boolean isWrappedLine(int line) {
return fWrappedLines.get(line);
}
@Override
public void setWrappedLine(int line) {
fWrappedLines.set(line);
}
}