2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
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.
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.
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/>.
17 * Copyright (C) 2010-2015 Martin
19 package com
.googlecode
.lanterna
.graphics
;
21 import java
.util
.Arrays
;
23 import com
.googlecode
.lanterna
.TerminalPosition
;
24 import com
.googlecode
.lanterna
.TerminalSize
;
25 import com
.googlecode
.lanterna
.TextCharacter
;
26 import com
.googlecode
.lanterna
.TextColor
;
29 * Simple implementation of TextImage that keeps the content as a two-dimensional TextCharacter array. Copy operations
30 * between two BasicTextImage classes are semi-optimized by using System.arraycopy instead of iterating over each
31 * character and copying them over one by one.
34 public class BasicTextImage
implements TextImage
{
35 private final TerminalSize size
;
36 private final TextCharacter
[][] buffer
;
39 * Creates a new BasicTextImage with the specified size and fills it initially with space characters using the
40 * default foreground and background color
41 * @param columns Size of the image in number of columns
42 * @param rows Size of the image in number of rows
44 public BasicTextImage(int columns
, int rows
) {
45 this(new TerminalSize(columns
, rows
));
49 * Creates a new BasicTextImage with the specified size and fills it initially with space characters using the
50 * default foreground and background color
51 * @param size Size to make the image
53 public BasicTextImage(TerminalSize size
) {
54 this(size
, new TextCharacter(' ', TextColor
.ANSI
.DEFAULT
, TextColor
.ANSI
.DEFAULT
));
58 * Creates a new BasicTextImage with a given size and a TextCharacter to initially fill it with
59 * @param size Size of the image
60 * @param initialContent What character to set as the initial content
62 public BasicTextImage(TerminalSize size
, TextCharacter initialContent
) {
63 this(size
, new TextCharacter
[0][], initialContent
);
67 * Creates a new BasicTextImage by copying a region of a two-dimensional array of TextCharacter:s. If the area to be
68 * copied to larger than the source array, a filler character is used.
69 * @param size Size to create the new BasicTextImage as (and size to copy from the array)
70 * @param toCopy Array to copy initial data from
71 * @param initialContent Filler character to use if the source array is smaller than the requested size
73 private BasicTextImage(TerminalSize size
, TextCharacter
[][] toCopy
, TextCharacter initialContent
) {
74 if(size
== null || toCopy
== null || initialContent
== null) {
75 throw new IllegalArgumentException("Cannot create BasicTextImage with null " +
76 (size
== null ?
"size" : (toCopy
== null ?
"toCopy" : "filler")));
80 int rows
= size
.getRows();
81 int columns
= size
.getColumns();
82 buffer
= new TextCharacter
[rows
][];
83 for(int y
= 0; y
< rows
; y
++) {
84 buffer
[y
] = new TextCharacter
[columns
];
85 for(int x
= 0; x
< columns
; x
++) {
86 if(y
< toCopy
.length
&& x
< toCopy
[y
].length
) {
87 buffer
[y
][x
] = toCopy
[y
][x
];
90 buffer
[y
][x
] = initialContent
;
97 public TerminalSize
getSize() {
102 public void setAll(TextCharacter character
) {
103 if(character
== null) {
104 throw new IllegalArgumentException("Cannot call BasicTextImage.setAll(..) with null character");
106 for(TextCharacter
[] line
: buffer
) {
107 Arrays
.fill(line
, character
);
112 public BasicTextImage
resize(TerminalSize newSize
, TextCharacter filler
) {
113 if(newSize
== null || filler
== null) {
114 throw new IllegalArgumentException("Cannot resize BasicTextImage with null " +
115 (newSize
== null ?
"newSize" : "filler"));
117 if(newSize
.getRows() == buffer
.length
&&
118 (buffer
.length
== 0 || newSize
.getColumns() == buffer
[0].length
)) {
121 return new BasicTextImage(newSize
, buffer
, filler
);
125 public void setCharacterAt(TerminalPosition position
, TextCharacter character
) {
126 if(position
== null) {
127 throw new IllegalArgumentException("Cannot call BasicTextImage.setCharacterAt(..) with null position");
129 setCharacterAt(position
.getColumn(), position
.getRow(), character
);
133 public void setCharacterAt(int column
, int row
, TextCharacter character
) {
134 if(character
== null) {
135 throw new IllegalArgumentException("Cannot call BasicTextImage.setCharacterAt(..) with null character");
137 if(column
< 0 || row
< 0 || row
>= buffer
.length
|| column
>= buffer
[0].length
) {
141 buffer
[row
][column
] = character
;
145 public TextCharacter
getCharacterAt(TerminalPosition position
) {
146 if(position
== null) {
147 throw new IllegalArgumentException("Cannot call BasicTextImage.getCharacterAt(..) with null position");
149 return getCharacterAt(position
.getColumn(), position
.getRow());
153 public TextCharacter
getCharacterAt(int column
, int row
) {
154 if(column
< 0 || row
< 0 || row
>= buffer
.length
|| column
>= buffer
[0].length
) {
158 return buffer
[row
][column
];
162 public void copyTo(TextImage destination
) {
163 copyTo(destination
, 0, buffer
.length
, 0, buffer
[0].length
, 0, 0);
168 TextImage destination
,
171 int startColumnIndex
,
173 int destinationRowOffset
,
174 int destinationColumnOffset
) {
176 // If the source image position is negative, offset the whole image
177 if(startColumnIndex
< 0) {
178 destinationColumnOffset
+= -startColumnIndex
;
179 columns
+= startColumnIndex
;
180 startColumnIndex
= 0;
182 if(startRowIndex
< 0) {
183 startRowIndex
+= -startRowIndex
;
184 rows
= startRowIndex
;
188 // If the destination offset is negative, adjust the source start indexes
189 if(destinationColumnOffset
< 0) {
190 startColumnIndex
-= destinationColumnOffset
;
191 columns
+= destinationColumnOffset
;
192 destinationColumnOffset
= 0;
194 if(destinationRowOffset
< 0) {
195 startRowIndex
-= destinationRowOffset
;
196 rows
+= destinationRowOffset
;
197 destinationRowOffset
= 0;
200 //Make sure we can't copy more than is available
201 columns
= Math
.min(buffer
[0].length
- startColumnIndex
, columns
);
202 rows
= Math
.min(buffer
.length
- startRowIndex
, rows
);
204 //Adjust target lengths as well
205 columns
= Math
.min(destination
.getSize().getColumns() - destinationColumnOffset
, columns
);
206 rows
= Math
.min(destination
.getSize().getRows() - destinationRowOffset
, rows
);
208 if(columns
<= 0 || rows
<= 0) {
212 TerminalSize destinationSize
= destination
.getSize();
213 if(destination
instanceof BasicTextImage
) {
214 int targetRow
= destinationRowOffset
;
215 for(int y
= startRowIndex
; y
< startRowIndex
+ rows
&& targetRow
< destinationSize
.getRows(); y
++) {
216 System
.arraycopy(buffer
[y
], startColumnIndex
, ((BasicTextImage
)destination
).buffer
[targetRow
++], destinationColumnOffset
, columns
);
220 //Manually copy character by character
221 for(int y
= startRowIndex
; y
< startRowIndex
+ rows
; y
++) {
222 for(int x
= startColumnIndex
; x
< startColumnIndex
+ columns
; x
++) {
223 destination
.setCharacterAt(
224 x
- startColumnIndex
+ destinationColumnOffset
,
225 y
- startRowIndex
+ destinationRowOffset
,
233 public TextGraphics
newTextGraphics() {
234 return new AbstractTextGraphics() {
236 public TextGraphics
setCharacter(int columnIndex
, int rowIndex
, TextCharacter textCharacter
) {
237 BasicTextImage
.this.setCharacterAt(columnIndex
, rowIndex
, textCharacter
);
242 public TextCharacter
getCharacter(int column
, int row
) {
243 return BasicTextImage
.this.getCharacterAt(column
, row
);
247 public TerminalSize
getSize() {
253 private TextCharacter
[] newBlankLine() {
254 TextCharacter
[] line
= new TextCharacter
[size
.getColumns()];
255 Arrays
.fill(line
, TextCharacter
.DEFAULT_CHARACTER
);
260 public void scrollLines(int firstLine
, int lastLine
, int distance
) {
261 if (firstLine
< 0) { firstLine
= 0; }
262 if (lastLine
>= size
.getRows()) { lastLine
= size
.getRows() - 1; }
263 if (firstLine
< lastLine
) {
265 // scrolling up: start with first line as target:
266 int curLine
= firstLine
;
267 // copy lines from further "below":
268 for (; curLine
<= lastLine
- distance
; curLine
++) {
269 buffer
[curLine
] = buffer
[curLine
+distance
];
271 // blank out the remaining lines:
272 for (; curLine
<= lastLine
; curLine
++) {
273 buffer
[curLine
] = newBlankLine();
276 else if (distance
< 0) {
277 // scrolling down: start with last line as target:
278 int curLine
= lastLine
; distance
= -distance
;
279 // copy lines from further "above":
280 for (; curLine
>= firstLine
+ distance
; curLine
--) {
281 buffer
[curLine
] = buffer
[curLine
-distance
];
283 // blank out the remaining lines:
284 for (; curLine
>= firstLine
; curLine
--) {
285 buffer
[curLine
] = newBlankLine();
287 } /* else: distance == 0 => no-op */
292 public String
toString() {
293 StringBuilder sb
= new StringBuilder(size
.getRows()*(size
.getColumns()+1)+50);
294 sb
.append('{').append(size
.getColumns()).append('x').append(size
.getRows()).append('}').append('\n');
295 for (TextCharacter
[] line
: buffer
) {
296 for (TextCharacter tc
: line
) {
297 sb
.append(tc
.getCharacter());
301 return sb
.toString();