blob: 519fad0a3e160f579abd8ccd43e19295acb6540c [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 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 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;
}
@Override
public void addLine() {
if (getHeight() < fMaxHeight) {
setDimensions(getHeight() + 1, getWidth());
} else {
scroll(0, getHeight(), -1);
}
}
@Override
public void cleanLine(int line) {
fData.cleanLine(getPositionOfLine(line));
}
@Override
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));
}
}
@Override
public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
fData.copyLine(source, sourceLine, getPositionOfLine(destLine));
}
@Override
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));
}
}
@Override
public char getChar(int line, int column) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
return fData.getChar(getPositionOfLine(line), column);
}
@Override
public char[] getChars(int line) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
return fData.getChars(getPositionOfLine(line));
}
@Override
public int getHeight() {
return fHeight;
}
@Override
public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
return fData.getLineSegments(getPositionOfLine(line), startCol, numberOfCols);
}
@Override
public int getMaxHeight() {
return fMaxHeight;
}
@Override
public TerminalStyle getStyle(int line, int column) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
return fData.getStyle(getPositionOfLine(line), column);
}
@Override
public TerminalStyle[] getStyles(int line) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
return fData.getStyles(getPositionOfLine(line));
}
@Override
public int getWidth() {
return fData.getWidth();
}
@Override
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));
}
}
@Override
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));
}
}
}
@Override
public void setChar(int line, int column, char c, TerminalStyle style) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
fData.setChar(getPositionOfLine(line), column, c, style);
}
@Override
public void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
fData.setChars(getPositionOfLine(line), column, chars, start, len, style);
}
@Override
public void setChars(int line, int column, char[] chars, TerminalStyle style) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
fData.setChars(getPositionOfLine(line), column, chars, style);
}
@Override
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);
}
@Override
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;
}
@Override
public int getCursorColumn() {
throw new UnsupportedOperationException();
}
@Override
public int getCursorLine() {
throw new UnsupportedOperationException();
}
@Override
public void setCursorColumn(int column) {
throw new UnsupportedOperationException();
}
@Override
public void setCursorLine(int line) {
throw new UnsupportedOperationException();
}
@Override
public boolean isWrappedLine(int line) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
return fData.isWrappedLine(getPositionOfLine(line));
}
@Override
public void setWrappedLine(int line) {
assert (line >= 0 && line < fHeight) || throwRuntimeException();
fData.setWrappedLine(getPositionOfLine(line));
}
}