Finish code sweep
[fanfix.git] / src / jexer / TText.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2017 Kevin Lamonte
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer;
30
31 import java.util.LinkedList;
32 import java.util.List;
33
34 import jexer.bits.CellAttributes;
35 import jexer.event.TKeypressEvent;
36 import jexer.event.TMouseEvent;
37 import static jexer.TKeypress.kbDown;
38 import static jexer.TKeypress.kbEnd;
39 import static jexer.TKeypress.kbHome;
40 import static jexer.TKeypress.kbLeft;
41 import static jexer.TKeypress.kbPgDn;
42 import static jexer.TKeypress.kbPgUp;
43 import static jexer.TKeypress.kbRight;
44 import static jexer.TKeypress.kbUp;
45
46 /**
47 * TText implements a simple scrollable text area. It reflows automatically on
48 * resize.
49 */
50 public class TText extends TScrollableWidget {
51
52 // ------------------------------------------------------------------------
53 // Constants --------------------------------------------------------------
54 // ------------------------------------------------------------------------
55
56 /**
57 * Available text justifications.
58 */
59 public enum Justification {
60 /**
61 * Left-justified text.
62 */
63 LEFT,
64
65 /**
66 * Centered text.
67 */
68 CENTER,
69
70 /**
71 * Right-justified text.
72 */
73 RIGHT,
74
75 /**
76 * Fully-justified text.
77 */
78 FULL,
79 }
80
81 // ------------------------------------------------------------------------
82 // Variables --------------------------------------------------------------
83 // ------------------------------------------------------------------------
84
85 /**
86 * How to justify the text.
87 */
88 private Justification justification = Justification.LEFT;
89
90 /**
91 * Text to display.
92 */
93 private String text;
94
95 /**
96 * Text converted to lines.
97 */
98 private List<String> lines;
99
100 /**
101 * Text color.
102 */
103 private String colorKey;
104
105 /**
106 * Maximum width of a single line.
107 */
108 private int maxLineWidth;
109
110 /**
111 * Number of lines between each paragraph.
112 */
113 private int lineSpacing = 1;
114
115 // ------------------------------------------------------------------------
116 // Constructors -----------------------------------------------------------
117 // ------------------------------------------------------------------------
118
119 /**
120 * Public constructor.
121 *
122 * @param parent parent widget
123 * @param text text on the screen
124 * @param x column relative to parent
125 * @param y row relative to parent
126 * @param width width of text area
127 * @param height height of text area
128 */
129 public TText(final TWidget parent, final String text, final int x,
130 final int y, final int width, final int height) {
131
132 this(parent, text, x, y, width, height, "ttext");
133 }
134
135 /**
136 * Public constructor.
137 *
138 * @param parent parent widget
139 * @param text text on the screen
140 * @param x column relative to parent
141 * @param y row relative to parent
142 * @param width width of text area
143 * @param height height of text area
144 * @param colorKey ColorTheme key color to use for foreground
145 * text. Default is "ttext".
146 */
147 public TText(final TWidget parent, final String text, final int x,
148 final int y, final int width, final int height,
149 final String colorKey) {
150
151 // Set parent and window
152 super(parent, x, y, width, height);
153
154 this.text = text;
155 this.colorKey = colorKey;
156
157 lines = new LinkedList<String>();
158
159 vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
160 hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
161 reflowData();
162 }
163
164 // ------------------------------------------------------------------------
165 // TScrollableWidget ------------------------------------------------------
166 // ------------------------------------------------------------------------
167
168 /**
169 * Draw the text box.
170 */
171 @Override
172 public void draw() {
173 // Setup my color
174 CellAttributes color = getTheme().getColor(colorKey);
175
176 int begin = vScroller.getValue();
177 int topY = 0;
178 for (int i = begin; i < lines.size(); i++) {
179 String line = lines.get(i);
180 if (hScroller.getValue() < line.length()) {
181 line = line.substring(hScroller.getValue());
182 } else {
183 line = "";
184 }
185 String formatString = "%-" + Integer.toString(getWidth() - 1) + "s";
186 getScreen().putStringXY(0, topY, String.format(formatString, line),
187 color);
188 topY++;
189
190 if (topY >= (getHeight() - 1)) {
191 break;
192 }
193 }
194
195 // Pad the rest with blank lines
196 for (int i = topY; i < (getHeight() - 1); i++) {
197 getScreen().hLineXY(0, i, getWidth() - 1, ' ', color);
198 }
199
200 }
201
202 /**
203 * Handle mouse press events.
204 *
205 * @param mouse mouse button press event
206 */
207 @Override
208 public void onMouseDown(final TMouseEvent mouse) {
209 if (mouse.isMouseWheelUp()) {
210 vScroller.decrement();
211 return;
212 }
213 if (mouse.isMouseWheelDown()) {
214 vScroller.increment();
215 return;
216 }
217
218 // Pass to children
219 super.onMouseDown(mouse);
220 }
221
222 /**
223 * Handle keystrokes.
224 *
225 * @param keypress keystroke event
226 */
227 @Override
228 public void onKeypress(final TKeypressEvent keypress) {
229 if (keypress.equals(kbLeft)) {
230 hScroller.decrement();
231 } else if (keypress.equals(kbRight)) {
232 hScroller.increment();
233 } else if (keypress.equals(kbUp)) {
234 vScroller.decrement();
235 } else if (keypress.equals(kbDown)) {
236 vScroller.increment();
237 } else if (keypress.equals(kbPgUp)) {
238 vScroller.bigDecrement();
239 } else if (keypress.equals(kbPgDn)) {
240 vScroller.bigIncrement();
241 } else if (keypress.equals(kbHome)) {
242 vScroller.toTop();
243 } else if (keypress.equals(kbEnd)) {
244 vScroller.toBottom();
245 } else {
246 // Pass other keys (tab etc.) on
247 super.onKeypress(keypress);
248 }
249 }
250
251 /**
252 * Resize text and scrollbars for a new width/height.
253 */
254 @Override
255 public void reflowData() {
256 // Reset the lines
257 lines.clear();
258
259 // Break up text into paragraphs
260 String[] paragraphs = text.split("\n\n");
261 for (String p : paragraphs) {
262 switch (justification) {
263 case LEFT:
264 lines.addAll(jexer.bits.StringUtils.left(p,
265 getWidth() - 1));
266 break;
267 case CENTER:
268 lines.addAll(jexer.bits.StringUtils.center(p,
269 getWidth() - 1));
270 break;
271 case RIGHT:
272 lines.addAll(jexer.bits.StringUtils.right(p,
273 getWidth() - 1));
274 break;
275 case FULL:
276 lines.addAll(jexer.bits.StringUtils.full(p,
277 getWidth() - 1));
278 break;
279 }
280
281 for (int i = 0; i < lineSpacing; i++) {
282 lines.add("");
283 }
284 }
285 computeBounds();
286 }
287
288 // ------------------------------------------------------------------------
289 // TText ------------------------------------------------------------------
290 // ------------------------------------------------------------------------
291
292 /**
293 * Set the text.
294 *
295 * @param text new text to display
296 */
297 public void setText(final String text) {
298 this.text = text;
299 reflowData();
300 }
301
302 /**
303 * Get the text.
304 *
305 * @return the text
306 */
307 public String getText() {
308 return text;
309 }
310
311 /**
312 * Convenience method used by TWindowLoggerOutput.
313 *
314 * @param line new line to add
315 */
316 public void addLine(final String line) {
317 if (text.length() == 0) {
318 text = line;
319 } else {
320 text += "\n\n";
321 text += line;
322 }
323 reflowData();
324 }
325
326 /**
327 * Recompute the bounds for the scrollbars.
328 */
329 private void computeBounds() {
330 maxLineWidth = 0;
331 for (String line : lines) {
332 if (line.length() > maxLineWidth) {
333 maxLineWidth = line.length();
334 }
335 }
336
337 vScroller.setTopValue(0);
338 vScroller.setBottomValue((lines.size() - getHeight()) + 1);
339 if (vScroller.getBottomValue() < 0) {
340 vScroller.setBottomValue(0);
341 }
342 if (vScroller.getValue() > vScroller.getBottomValue()) {
343 vScroller.setValue(vScroller.getBottomValue());
344 }
345
346 hScroller.setLeftValue(0);
347 hScroller.setRightValue((maxLineWidth - getWidth()) + 1);
348 if (hScroller.getRightValue() < 0) {
349 hScroller.setRightValue(0);
350 }
351 if (hScroller.getValue() > hScroller.getRightValue()) {
352 hScroller.setValue(hScroller.getRightValue());
353 }
354 }
355
356 /**
357 * Set justification.
358 *
359 * @param justification LEFT, CENTER, RIGHT, or FULL
360 */
361 public void setJustification(final Justification justification) {
362 this.justification = justification;
363 reflowData();
364 }
365
366 /**
367 * Left-justify the text.
368 */
369 public void leftJustify() {
370 justification = Justification.LEFT;
371 reflowData();
372 }
373
374 /**
375 * Center-justify the text.
376 */
377 public void centerJustify() {
378 justification = Justification.CENTER;
379 reflowData();
380 }
381
382 /**
383 * Right-justify the text.
384 */
385 public void rightJustify() {
386 justification = Justification.RIGHT;
387 reflowData();
388 }
389
390 /**
391 * Fully-justify the text.
392 */
393 public void fullJustify() {
394 justification = Justification.FULL;
395 reflowData();
396 }
397
398 }