/* * This file is part of lanterna (http://code.google.com/p/lanterna/). * * lanterna is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Copyright (C) 2010-2017 Martin Berglund */ package com.googlecode.lanterna.terminal.virtual; import com.googlecode.lanterna.TextCharacter; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; /** * This class is used to store lines of text inside of a terminal emulator. As used by {@link DefaultVirtualTerminal}, it keeps * two {@link TextBuffer}s, one for private mode and one for normal mode and it can switch between them as needed. */ class TextBuffer { private static final TextCharacter DOUBLE_WIDTH_CHAR_PADDING = new TextCharacter(' '); private final LinkedList<List<TextCharacter>> lines; TextBuffer() { this.lines = new LinkedList<List<TextCharacter>>(); newLine(); } synchronized void newLine() { lines.add(new ArrayList<TextCharacter>(200)); } synchronized void removeTopLines(int numberOfLinesToRemove) { for(int i = 0; i < numberOfLinesToRemove; i++) { lines.removeFirst(); } } synchronized void clear() { lines.clear(); newLine(); } ListIterator<List<TextCharacter>> getLinesFrom(int rowNumber) { return lines.listIterator(rowNumber); } synchronized int getLineCount() { return lines.size(); } synchronized int setCharacter(int lineNumber, int columnIndex, TextCharacter textCharacter) { if(lineNumber < 0 || columnIndex < 0) { throw new IllegalArgumentException("Illegal argument to TextBuffer.setCharacter(..), lineNumber = " + lineNumber + ", columnIndex = " + columnIndex); } if(textCharacter == null) { textCharacter = TextCharacter.DEFAULT_CHARACTER; } while(lineNumber >= lines.size()) { newLine(); } List<TextCharacter> line = lines.get(lineNumber); while(line.size() <= columnIndex) { line.add(TextCharacter.DEFAULT_CHARACTER); } // Default int returnStyle = 0; // Check if we are overwriting a double-width character, in that case we need to reset the other half if(line.get(columnIndex).isDoubleWidth()) { line.set(columnIndex + 1, line.get(columnIndex).withCharacter(' ')); returnStyle = 1; // this character and the one to the right } else if(line.get(columnIndex) == DOUBLE_WIDTH_CHAR_PADDING) { line.set(columnIndex - 1, TextCharacter.DEFAULT_CHARACTER); returnStyle = 2; // this character and the one to the left } line.set(columnIndex, textCharacter); if(textCharacter.isDoubleWidth()) { // We don't report this column as dirty (yet), it's implied since a double-width character is reported setCharacter(lineNumber, columnIndex + 1, DOUBLE_WIDTH_CHAR_PADDING); } return returnStyle; } synchronized TextCharacter getCharacter(int lineNumber, int columnIndex) { if(lineNumber < 0 || columnIndex < 0) { throw new IllegalArgumentException("Illegal argument to TextBuffer.getCharacter(..), lineNumber = " + lineNumber + ", columnIndex = " + columnIndex); } if(lineNumber >= lines.size()) { return TextCharacter.DEFAULT_CHARACTER; } List<TextCharacter> line = lines.get(lineNumber); if(line.size() <= columnIndex) { return TextCharacter.DEFAULT_CHARACTER; } TextCharacter textCharacter = line.get(columnIndex); if(textCharacter == DOUBLE_WIDTH_CHAR_PADDING) { return line.get(columnIndex - 1); } return textCharacter; } }