Commit | Line | Data |
---|---|---|
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 | */ | |
19 | package com.googlecode.lanterna.graphics; | |
20 | ||
21 | import com.googlecode.lanterna.*; | |
22 | import com.googlecode.lanterna.screen.TabBehaviour; | |
23 | ||
24 | import java.util.Arrays; | |
25 | import java.util.Collection; | |
26 | import java.util.EnumSet; | |
27 | ||
28 | /** | |
29 | * This class hold the default logic for drawing the basic text graphic as exposed by TextGraphic. All implementations | |
30 | * rely on a setCharacter method being implemented in subclasses. | |
31 | * @author Martin | |
32 | */ | |
33 | public abstract class AbstractTextGraphics implements TextGraphics { | |
34 | protected TextColor foregroundColor; | |
35 | protected TextColor backgroundColor; | |
36 | protected TabBehaviour tabBehaviour; | |
37 | protected final EnumSet<SGR> activeModifiers; | |
38 | private final ShapeRenderer shapeRenderer; | |
39 | ||
40 | protected AbstractTextGraphics() { | |
41 | this.activeModifiers = EnumSet.noneOf(SGR.class); | |
42 | this.tabBehaviour = TabBehaviour.ALIGN_TO_COLUMN_4; | |
43 | this.foregroundColor = TextColor.ANSI.DEFAULT; | |
44 | this.backgroundColor = TextColor.ANSI.DEFAULT; | |
45 | this.shapeRenderer = new DefaultShapeRenderer(new DefaultShapeRenderer.Callback() { | |
46 | @Override | |
47 | public void onPoint(int column, int row, TextCharacter character) { | |
48 | setCharacter(column, row, character); | |
49 | } | |
50 | }); | |
51 | } | |
52 | ||
53 | @Override | |
54 | public TextColor getBackgroundColor() { | |
55 | return backgroundColor; | |
56 | } | |
57 | ||
58 | @Override | |
59 | public TextGraphics setBackgroundColor(final TextColor backgroundColor) { | |
60 | this.backgroundColor = backgroundColor; | |
61 | return this; | |
62 | } | |
63 | ||
64 | @Override | |
65 | public TextColor getForegroundColor() { | |
66 | return foregroundColor; | |
67 | } | |
68 | ||
69 | @Override | |
70 | public TextGraphics setForegroundColor(final TextColor foregroundColor) { | |
71 | this.foregroundColor = foregroundColor; | |
72 | return this; | |
73 | } | |
74 | ||
75 | @Override | |
76 | public TextGraphics enableModifiers(SGR... modifiers) { | |
77 | enableModifiers(Arrays.asList(modifiers)); | |
78 | return this; | |
79 | } | |
80 | ||
81 | private void enableModifiers(Collection<SGR> modifiers) { | |
82 | this.activeModifiers.addAll(modifiers); | |
83 | } | |
84 | ||
85 | @Override | |
86 | public TextGraphics disableModifiers(SGR... modifiers) { | |
87 | disableModifiers(Arrays.asList(modifiers)); | |
88 | return this; | |
89 | } | |
90 | ||
91 | private void disableModifiers(Collection<SGR> modifiers) { | |
92 | this.activeModifiers.removeAll(modifiers); | |
93 | } | |
94 | ||
95 | @Override | |
96 | public synchronized TextGraphics setModifiers(EnumSet<SGR> modifiers) { | |
97 | activeModifiers.clear(); | |
98 | activeModifiers.addAll(modifiers); | |
99 | return this; | |
100 | } | |
101 | ||
102 | @Override | |
103 | public TextGraphics clearModifiers() { | |
104 | this.activeModifiers.clear(); | |
105 | return this; | |
106 | } | |
107 | ||
108 | @Override | |
109 | public EnumSet<SGR> getActiveModifiers() { | |
110 | return activeModifiers; | |
111 | } | |
112 | ||
113 | @Override | |
114 | public TabBehaviour getTabBehaviour() { | |
115 | return tabBehaviour; | |
116 | } | |
117 | ||
118 | @Override | |
119 | public TextGraphics setTabBehaviour(TabBehaviour tabBehaviour) { | |
120 | if(tabBehaviour != null) { | |
121 | this.tabBehaviour = tabBehaviour; | |
122 | } | |
123 | return this; | |
124 | } | |
125 | ||
126 | @Override | |
127 | public TextGraphics fill(char c) { | |
128 | fillRectangle(TerminalPosition.TOP_LEFT_CORNER, getSize(), c); | |
129 | return this; | |
130 | } | |
131 | ||
132 | @Override | |
133 | public TextGraphics setCharacter(int column, int row, char character) { | |
134 | return setCharacter(column, row, newTextCharacter(character)); | |
135 | } | |
136 | ||
137 | @Override | |
138 | public TextGraphics setCharacter(TerminalPosition position, TextCharacter textCharacter) { | |
139 | setCharacter(position.getColumn(), position.getRow(), textCharacter); | |
140 | return this; | |
141 | } | |
142 | ||
143 | @Override | |
144 | public TextGraphics setCharacter(TerminalPosition position, char character) { | |
145 | return setCharacter(position.getColumn(), position.getRow(), character); | |
146 | } | |
147 | ||
148 | @Override | |
149 | public TextGraphics drawLine(TerminalPosition fromPosition, TerminalPosition toPoint, char character) { | |
150 | return drawLine(fromPosition, toPoint, newTextCharacter(character)); | |
151 | } | |
152 | ||
153 | @Override | |
154 | public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, TextCharacter character) { | |
155 | shapeRenderer.drawLine(fromPoint, toPoint, character); | |
156 | return this; | |
157 | } | |
158 | ||
159 | @Override | |
160 | public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, char character) { | |
161 | return drawLine(fromX, fromY, toX, toY, newTextCharacter(character)); | |
162 | } | |
163 | ||
164 | @Override | |
165 | public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, TextCharacter character) { | |
166 | return drawLine(new TerminalPosition(fromX, fromY), new TerminalPosition(toX, toY), character); | |
167 | } | |
168 | ||
169 | @Override | |
170 | public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) { | |
171 | return drawTriangle(p1, p2, p3, newTextCharacter(character)); | |
172 | } | |
173 | ||
174 | @Override | |
175 | public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) { | |
176 | shapeRenderer.drawTriangle(p1, p2, p3, character); | |
177 | return this; | |
178 | } | |
179 | ||
180 | @Override | |
181 | public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) { | |
182 | return fillTriangle(p1, p2, p3, newTextCharacter(character)); | |
183 | } | |
184 | ||
185 | @Override | |
186 | public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) { | |
187 | shapeRenderer.fillTriangle(p1, p2, p3, character); | |
188 | return this; | |
189 | } | |
190 | ||
191 | @Override | |
192 | public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character) { | |
193 | return drawRectangle(topLeft, size, newTextCharacter(character)); | |
194 | } | |
195 | ||
196 | @Override | |
197 | public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) { | |
198 | shapeRenderer.drawRectangle(topLeft, size, character); | |
199 | return this; | |
200 | } | |
201 | ||
202 | @Override | |
203 | public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character) { | |
204 | return fillRectangle(topLeft, size, newTextCharacter(character)); | |
205 | } | |
206 | ||
207 | @Override | |
208 | public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) { | |
209 | shapeRenderer.fillRectangle(topLeft, size, character); | |
210 | return this; | |
211 | } | |
212 | ||
213 | @Override | |
214 | public TextGraphics drawImage(TerminalPosition topLeft, TextImage image) { | |
215 | return drawImage(topLeft, image, TerminalPosition.TOP_LEFT_CORNER, image.getSize()); | |
216 | } | |
217 | ||
218 | @Override | |
219 | public TextGraphics drawImage( | |
220 | TerminalPosition topLeft, | |
221 | TextImage image, | |
222 | TerminalPosition sourceImageTopLeft, | |
223 | TerminalSize sourceImageSize) { | |
224 | ||
225 | // If the source image position is negative, offset the whole image | |
226 | if(sourceImageTopLeft.getColumn() < 0) { | |
227 | topLeft = topLeft.withRelativeColumn(-sourceImageTopLeft.getColumn()); | |
228 | sourceImageSize = sourceImageSize.withRelativeColumns(sourceImageTopLeft.getColumn()); | |
229 | sourceImageTopLeft = sourceImageTopLeft.withColumn(0); | |
230 | } | |
231 | if(sourceImageTopLeft.getRow() < 0) { | |
232 | topLeft = topLeft.withRelativeRow(-sourceImageTopLeft.getRow()); | |
233 | sourceImageSize = sourceImageSize.withRelativeRows(sourceImageTopLeft.getRow()); | |
234 | sourceImageTopLeft = sourceImageTopLeft.withRow(0); | |
235 | } | |
236 | ||
237 | // cropping specified image-subrectangle to the image itself: | |
238 | int fromRow = Math.max(sourceImageTopLeft.getRow(), 0); | |
239 | int untilRow = Math.min(sourceImageTopLeft.getRow() + sourceImageSize.getRows(), image.getSize().getRows()); | |
240 | int fromColumn = Math.max(sourceImageTopLeft.getColumn(), 0); | |
241 | int untilColumn = Math.min(sourceImageTopLeft.getColumn() + sourceImageSize.getColumns(), image.getSize().getColumns()); | |
242 | ||
243 | // difference between position in image and position on target: | |
244 | int diffRow = topLeft.getRow() - sourceImageTopLeft.getRow(); | |
245 | int diffColumn = topLeft.getColumn() - sourceImageTopLeft.getColumn(); | |
246 | ||
247 | // top/left-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative coordinate) | |
248 | fromRow = Math.max(fromRow, -diffRow); | |
249 | fromColumn = Math.max(fromColumn, -diffColumn); | |
250 | ||
251 | // bot/right-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative coordinate) | |
252 | untilRow = Math.min(untilRow, getSize().getRows() - diffRow); | |
253 | untilColumn = Math.min(untilColumn, getSize().getColumns() - diffColumn); | |
254 | ||
255 | if (fromRow >= untilRow || fromColumn >= untilColumn) { | |
256 | return this; | |
257 | } | |
258 | for (int row = fromRow; row < untilRow; row++) { | |
259 | for (int column = fromColumn; column < untilColumn; column++) { | |
260 | setCharacter(column + diffColumn, row + diffRow, image.getCharacterAt(column, row)); | |
261 | } | |
262 | } | |
263 | return this; | |
264 | } | |
265 | ||
266 | @Override | |
267 | public TextGraphics putString(int column, int row, String string) { | |
268 | if(string.contains("\n")) { | |
269 | string = string.substring(0, string.indexOf("\n")); | |
270 | } | |
271 | if(string.contains("\r")) { | |
272 | string = string.substring(0, string.indexOf("\r")); | |
273 | } | |
274 | string = tabBehaviour.replaceTabs(string, column); | |
275 | int offset = 0; | |
276 | for(int i = 0; i < string.length(); i++) { | |
277 | char character = string.charAt(i); | |
278 | setCharacter( | |
279 | column + offset, | |
280 | row, | |
281 | new TextCharacter( | |
282 | character, | |
283 | foregroundColor, | |
284 | backgroundColor, | |
285 | activeModifiers.clone())); | |
286 | ||
287 | if(TerminalTextUtils.isCharCJK(character)) { | |
288 | //CJK characters are twice the normal characters in width, so next character position is two columns forward | |
289 | offset += 2; | |
290 | } | |
291 | else { | |
292 | //For "normal" characters we advance to the next column | |
293 | offset += 1; | |
294 | } | |
295 | } | |
296 | return this; | |
297 | } | |
298 | ||
299 | @Override | |
300 | public TextGraphics putString(TerminalPosition position, String string) { | |
301 | putString(position.getColumn(), position.getRow(), string); | |
302 | return this; | |
303 | } | |
304 | ||
305 | @Override | |
306 | public TextGraphics putString(int column, int row, String string, SGR extraModifier, SGR... optionalExtraModifiers) {clearModifiers(); | |
307 | return putString(column, row, string, EnumSet.of(extraModifier, optionalExtraModifiers)); | |
308 | } | |
309 | ||
310 | @Override | |
311 | public TextGraphics putString(int column, int row, String string, Collection<SGR> extraModifiers) { | |
312 | extraModifiers.removeAll(activeModifiers); | |
313 | enableModifiers(extraModifiers); | |
314 | putString(column, row, string); | |
315 | disableModifiers(extraModifiers); | |
316 | return this; | |
317 | } | |
318 | ||
319 | @Override | |
320 | public TextGraphics putString(TerminalPosition position, String string, SGR extraModifier, SGR... optionalExtraModifiers) { | |
321 | putString(position.getColumn(), position.getRow(), string, extraModifier, optionalExtraModifiers); | |
322 | return this; | |
323 | } | |
324 | ||
325 | @Override | |
326 | public TextCharacter getCharacter(TerminalPosition position) { | |
327 | return getCharacter(position.getColumn(), position.getRow()); | |
328 | } | |
329 | ||
330 | @Override | |
331 | public TextGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException { | |
332 | TerminalSize writableArea = getSize(); | |
333 | if(topLeftCorner.getColumn() + size.getColumns() <= 0 || | |
334 | topLeftCorner.getColumn() >= writableArea.getColumns() || | |
335 | topLeftCorner.getRow() + size.getRows() <= 0 || | |
336 | topLeftCorner.getRow() >= writableArea.getRows()) { | |
337 | //The area selected is completely outside of this TextGraphics, so we can return a "null" object that doesn't | |
338 | //do anything because it is impossible to change anything anyway | |
339 | return new NullTextGraphics(size); | |
340 | } | |
341 | return new SubTextGraphics(this, topLeftCorner, size); | |
342 | } | |
343 | ||
344 | private TextCharacter newTextCharacter(char character) { | |
345 | return new TextCharacter(character, foregroundColor, backgroundColor, activeModifiers); | |
346 | } | |
347 | } |