Change build scripts
[jvcard.git] / src / com / googlecode / lanterna / terminal / swing / VirtualTerminal.java
CommitLineData
a3b510ab
NR
1/*
2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
3 *
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Copyright (C) 2010-2015 Martin
18 */
19package com.googlecode.lanterna.terminal.swing;
20
21import com.googlecode.lanterna.TerminalTextUtils;
22import com.googlecode.lanterna.TextCharacter;
23import com.googlecode.lanterna.screen.TabBehaviour;
24import com.googlecode.lanterna.TerminalPosition;
25import com.googlecode.lanterna.TerminalSize;
26import java.util.ArrayList;
27import java.util.List;
28
29/**
30 * Contains the internal state of the Swing terminal
31 * @author martin
32 */
33class VirtualTerminal {
34
35 private final TextBuffer mainTextBuffer;
36 private final TextBuffer privateModeTextBuffer;
37 private final TerminalScrollController terminalScrollController;
38
39 private TextBuffer currentBuffer;
40 private TerminalSize size;
41 private TerminalPosition cursorPosition;
42
43 //To avoid adding more synchronization and locking, we'll store a copy of all visible lines in this list. This is
44 //also the list we return (as an iterable) so it may not be reliable as each call to getLines will change it. This
45 //isn't 100% safe but hopefully a good trade-off
46 private final List<List<TextCharacter>> visibleLinesBuffer;
47
48 VirtualTerminal(
49 int backlog,
50 TerminalSize initialSize,
51 TerminalScrollController scrollController) {
52
53 this.mainTextBuffer = new TextBuffer(backlog);
54 this.privateModeTextBuffer = new TextBuffer(0);
55 this.terminalScrollController = scrollController;
56
57 this.currentBuffer = mainTextBuffer;
58 this.size = initialSize;
59 this.cursorPosition = TerminalPosition.TOP_LEFT_CORNER;
60
61 this.visibleLinesBuffer = new ArrayList<List<TextCharacter>>(120);
62 }
63
64 void resize(TerminalSize newSize) {
65 if(size.getRows() < newSize.getRows()) {
66 cursorPosition = cursorPosition.withRelativeRow(newSize.getRows() - size.getRows());
67 }
68 this.size = newSize;
69 updateScrollingController();
70 correctCursor();
71 }
72
73 private void updateScrollingController() {
74 int totalSize = Math.max(currentBuffer.getNumberOfLines(), size.getRows());
75 int visibleSize = size.getRows();
76 this.terminalScrollController.updateModel(totalSize, visibleSize);
77 }
78
79 TerminalSize getSize() {
80 return size;
81 }
82
83 synchronized void setCursorPosition(TerminalPosition cursorPosition) {
84 //Make sure the cursor position is within the bounds
85 cursorPosition = cursorPosition.withColumn(
86 Math.min(Math.max(cursorPosition.getColumn(), 0), size.getColumns() - 1));
87 cursorPosition = cursorPosition.withRow(
88 Math.min(Math.max(cursorPosition.getRow(), 0), size.getRows() - 1));
89
90 currentBuffer.ensurePosition(size, cursorPosition);
91 this.cursorPosition = cursorPosition;
92 correctCursor();
93 }
94
95 TerminalPosition getTranslatedCursorPosition() {
96 return cursorPosition.withRelativeRow(terminalScrollController.getScrollingOffset());
97 }
98
99 private void correctCursor() {
100 this.cursorPosition =
101 new TerminalPosition(
102 Math.min(cursorPosition.getColumn(), size.getColumns() - 1),
103 Math.min(cursorPosition.getRow(), size.getRows() - 1));
104 this.cursorPosition =
105 new TerminalPosition(
106 Math.max(cursorPosition.getColumn(), 0),
107 Math.max(cursorPosition.getRow(), 0));
108 }
109
110 synchronized TextCharacter getCharacter(TerminalPosition position) {
111 return currentBuffer.getCharacter(size, position);
112 }
113
114 synchronized void putCharacter(TextCharacter terminalCharacter) {
115 if(terminalCharacter.getCharacter() == '\n') {
116 moveCursorToNextLine();
117 }
118 else if(terminalCharacter.getCharacter() == '\t') {
119 int nrOfSpaces = TabBehaviour.ALIGN_TO_COLUMN_4.getTabReplacement(cursorPosition.getColumn()).length();
120 for(int i = 0; i < nrOfSpaces && cursorPosition.getColumn() < size.getColumns() - 1; i++) {
121 putCharacter(terminalCharacter.withCharacter(' '));
122 }
123 }
124 else {
125 currentBuffer.setCharacter(size, cursorPosition, terminalCharacter);
126
127 //Advance cursor
128 cursorPosition = cursorPosition.withRelativeColumn(TerminalTextUtils.isCharCJK(terminalCharacter.getCharacter()) ? 2 : 1);
129 if(cursorPosition.getColumn() >= size.getColumns()) {
130 moveCursorToNextLine();
131 }
132 currentBuffer.ensurePosition(size, cursorPosition);
133 }
134 }
135
136 /**
137 * Method that updates the cursor position and puts a character atomically. This method is here for thread safety.
138 * The cursor position after this call will be the following position after the one specified.
139 * @param cursorPosition Position to put the character at
140 * @param terminalCharacter Character to put
141 */
142 synchronized void setCursorAndPutCharacter(TerminalPosition cursorPosition, TextCharacter terminalCharacter) {
143 setCursorPosition(cursorPosition);
144 putCharacter(terminalCharacter);
145 }
146
147 private void moveCursorToNextLine() {
148 cursorPosition = cursorPosition.withColumn(0).withRelativeRow(1);
149 if(cursorPosition.getRow() >= size.getRows()) {
150 cursorPosition = cursorPosition.withRelativeRow(-1);
151 if(currentBuffer == mainTextBuffer) {
152 currentBuffer.newLine();
153 currentBuffer.trimBacklog(size.getRows());
154 updateScrollingController();
155 }
156 }
157 currentBuffer.ensurePosition(size, cursorPosition);
158 }
159
160 void switchToPrivateMode() {
161 currentBuffer = privateModeTextBuffer;
162 }
163
164 void switchToNormalMode() {
165 currentBuffer = mainTextBuffer;
166 }
167
168 void clear() {
169 currentBuffer.clear();
170 setCursorPosition(TerminalPosition.TOP_LEFT_CORNER);
171 }
172
173 synchronized Iterable<List<TextCharacter>> getLines() {
174 int scrollingOffset = terminalScrollController.getScrollingOffset();
175 int visibleRows = size.getRows();
176 //Make sure scrolling isn't too far off (can be sometimes when the terminal is being resized and the scrollbar
177 //hasn't adjusted itself yet)
178 if(currentBuffer.getNumberOfLines() > visibleRows &&
179 scrollingOffset + visibleRows > currentBuffer.getNumberOfLines()) {
180 scrollingOffset = currentBuffer.getNumberOfLines() - visibleRows;
181 }
182
183 visibleLinesBuffer.clear();
184 for(List<TextCharacter> line: currentBuffer.getVisibleLines(visibleRows, scrollingOffset)) {
185 visibleLinesBuffer.add(line);
186 }
187 return visibleLinesBuffer;
188 }
189}