misc cleanup
[fanfix.git] / src / jexer / TText.java
1 /**
2 * Jexer - Java Text User Interface
3 *
4 * License: LGPLv3 or later
5 *
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
9 *
10 * Copyright (C) 2015 Kevin Lamonte
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * 02110-1301 USA
27 *
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 * @version 1
30 */
31 package jexer;
32
33 import java.util.LinkedList;
34 import java.util.List;
35
36 import jexer.bits.CellAttributes;
37 import jexer.event.TKeypressEvent;
38 import jexer.event.TMouseEvent;
39 import static jexer.TKeypress.*;
40
41 /**
42 * TText implements a simple text windget.
43 */
44 public final class TText extends TWidget {
45
46 /**
47 * Text to display.
48 */
49 private String text;
50
51 /**
52 * Text converted to lines.
53 */
54 private List<String> lines;
55
56 /**
57 * Text color.
58 */
59 private String colorKey;
60
61 /**
62 * Vertical scrollbar.
63 */
64 private TVScroller vScroller;
65
66 /**
67 * Horizontal scrollbar.
68 */
69 private THScroller hScroller;
70
71 /**
72 * Maximum width of a single line.
73 */
74 private int maxLineWidth;
75
76 /**
77 * Number of lines between each paragraph.
78 */
79 private int lineSpacing = 1;
80
81 /**
82 * Convenience method used by TWindowLoggerOutput.
83 *
84 * @param line new line to add
85 */
86 public void addLine(final String line) {
87 if (text.length() == 0) {
88 text = line;
89 } else {
90 text += "\n\n";
91 text += line;
92 }
93 reflow();
94 }
95
96 /**
97 * Recompute the bounds for the scrollbars.
98 */
99 private void computeBounds() {
100 maxLineWidth = 0;
101 for (String line: lines) {
102 if (line.length() > maxLineWidth) {
103 maxLineWidth = line.length();
104 }
105 }
106
107 vScroller.setBottomValue(lines.size() - getHeight() + 1);
108 if (vScroller.getBottomValue() < 0) {
109 vScroller.setBottomValue(0);
110 }
111 if (vScroller.getValue() > vScroller.getBottomValue()) {
112 vScroller.setValue(vScroller.getBottomValue());
113 }
114
115 hScroller.setRightValue(maxLineWidth - getWidth() + 1);
116 if (hScroller.getRightValue() < 0) {
117 hScroller.setRightValue(0);
118 }
119 if (hScroller.getValue() > hScroller.getRightValue()) {
120 hScroller.setValue(hScroller.getRightValue());
121 }
122 }
123
124 /**
125 * Insert newlines into a string to wrap it to a maximum column.
126 * Terminate the final string with a newline. Note that interior
127 * newlines are converted to spaces.
128 *
129 * @param str the string
130 * @param n the maximum number of characters in a line
131 * @return the wrapped string
132 */
133 private String wrap(final String str, final int n) {
134 assert (n > 0);
135
136 StringBuilder sb = new StringBuilder();
137 StringBuilder word = new StringBuilder();
138 int col = 0;
139 for (int i = 0; i < str.length(); i++) {
140 char ch = str.charAt(i);
141 if (ch == '\n') {
142 ch = ' ';
143 }
144 if (ch == ' ') {
145 sb.append(word.toString());
146 sb.append(ch);
147 if (word.length() >= n - 1) {
148 sb.append('\n');
149 col = 0;
150 }
151 word = new StringBuilder();
152 } else {
153 word.append(ch);
154 }
155
156 col++;
157 if (col >= n - 1) {
158 sb.append('\n');
159 col = 0;
160 }
161 }
162 sb.append(word.toString());
163 sb.append('\n');
164 return sb.toString();
165 }
166
167
168 /**
169 * Resize text and scrollbars for a new width/height.
170 */
171 public void reflow() {
172 // Reset the lines
173 lines.clear();
174
175 // Break up text into paragraphs
176 String [] paragraphs = text.split("\n\n");
177 for (String p: paragraphs) {
178 String paragraph = wrap(p, getWidth() - 1);
179 for (String line: paragraph.split("\n")) {
180 lines.add(line);
181 }
182 for (int i = 0; i < lineSpacing; i++) {
183 lines.add("");
184 }
185 }
186
187 // Start at the top
188 if (vScroller == null) {
189 vScroller = new TVScroller(this, getWidth() - 1, 0,
190 getHeight() - 1);
191 vScroller.setTopValue(0);
192 vScroller.setValue(0);
193 } else {
194 vScroller.setX(getWidth() - 1);
195 vScroller.setHeight(getHeight() - 1);
196 }
197 vScroller.setBigChange(getHeight() - 1);
198
199 // Start at the left
200 if (hScroller == null) {
201 hScroller = new THScroller(this, 0, getHeight() - 1,
202 getWidth() - 1);
203 hScroller.setLeftValue(0);
204 hScroller.setValue(0);
205 } else {
206 hScroller.setY(getHeight() - 1);
207 hScroller.setWidth(getWidth() - 1);
208 }
209 hScroller.setBigChange(getWidth() - 1);
210
211 computeBounds();
212 }
213
214 /**
215 * Public constructor.
216 *
217 * @param parent parent widget
218 * @param text text on the screen
219 * @param x column relative to parent
220 * @param y row relative to parent
221 * @param width width of text area
222 * @param height height of text area
223 */
224 public TText(final TWidget parent, final String text, final int x,
225 final int y, final int width, final int height) {
226
227 this(parent, text, x, y, width, height, "ttext");
228 }
229
230 /**
231 * Public constructor.
232 *
233 * @param parent parent widget
234 * @param text text on the screen
235 * @param x column relative to parent
236 * @param y row relative to parent
237 * @param width width of text area
238 * @param height height of text area
239 * @param colorKey ColorTheme key color to use for foreground text.
240 * Default is "ttext"
241 */
242 public TText(final TWidget parent, final String text, final int x,
243 final int y, final int width, final int height, final String colorKey) {
244
245 // Set parent and window
246 super(parent, x, y, width, height);
247
248 this.text = text;
249 this.colorKey = colorKey;
250
251 lines = new LinkedList<String>();
252
253 reflow();
254 }
255
256 /**
257 * Draw the text box.
258 */
259 @Override
260 public void draw() {
261 // Setup my color
262 CellAttributes color = getTheme().getColor(colorKey);
263
264 int begin = vScroller.getValue();
265 int topY = 0;
266 for (int i = begin; i < lines.size(); i++) {
267 String line = lines.get(i);
268 if (hScroller.getValue() < line.length()) {
269 line = line.substring(hScroller.getValue());
270 } else {
271 line = "";
272 }
273 String formatString = "%-" + Integer.toString(getWidth() - 1) + "s";
274 getScreen().putStrXY(0, topY, String.format(formatString, line),
275 color);
276 topY++;
277
278 if (topY >= getHeight() - 1) {
279 break;
280 }
281 }
282
283 // Pad the rest with blank lines
284 for (int i = topY; i < getHeight() - 1; i++) {
285 getScreen().hLineXY(0, i, getWidth() - 1, ' ', color);
286 }
287
288 }
289
290 /**
291 * Handle mouse press events.
292 *
293 * @param mouse mouse button press event
294 */
295 @Override
296 public void onMouseDown(final TMouseEvent mouse) {
297 if (mouse.getMouseWheelUp()) {
298 vScroller.decrement();
299 return;
300 }
301 if (mouse.getMouseWheelDown()) {
302 vScroller.increment();
303 return;
304 }
305
306 // Pass to children
307 super.onMouseDown(mouse);
308 }
309
310 /**
311 * Handle keystrokes.
312 *
313 * @param keypress keystroke event
314 */
315 @Override
316 public void onKeypress(final TKeypressEvent keypress) {
317 if (keypress.equals(kbLeft)) {
318 hScroller.decrement();
319 } else if (keypress.equals(kbRight)) {
320 hScroller.increment();
321 } else if (keypress.equals(kbUp)) {
322 vScroller.decrement();
323 } else if (keypress.equals(kbDown)) {
324 vScroller.increment();
325 } else if (keypress.equals(kbPgUp)) {
326 vScroller.bigDecrement();
327 } else if (keypress.equals(kbPgDn)) {
328 vScroller.bigIncrement();
329 } else if (keypress.equals(kbHome)) {
330 vScroller.toTop();
331 } else if (keypress.equals(kbEnd)) {
332 vScroller.toBottom();
333 } else {
334 // Pass other keys (tab etc.) on
335 super.onKeypress(keypress);
336 }
337 }
338
339 }