TButton and TLabel
[nikiroo-utils.git] / src / jexer / io / ECMA48Screen.java
CommitLineData
217c6107
KL
1/**
2 * Jexer - Java Text User Interface
3 *
217c6107
KL
4 * License: LGPLv3 or later
5 *
7b5261bc
KL
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.
217c6107
KL
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
7b5261bc
KL
27 *
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 * @version 1
217c6107
KL
30 */
31package jexer.io;
32
33import jexer.bits.Cell;
34import jexer.bits.CellAttributes;
35
36/**
37 * This Screen class draws to an xterm/ANSI X3.64/ECMA-48 type terminal.
38 */
39public class ECMA48Screen extends Screen {
40
41 /**
7b5261bc
KL
42 * Emit debugging to stderr.
43 */
44 private boolean debugToStderr;
45
46 /**
47 * We call terminal.cursor() so need the instance.
217c6107
KL
48 */
49 private ECMA48Terminal terminal;
50
51 /**
7b5261bc 52 * Public constructor.
217c6107
KL
53 *
54 * @param terminal ECMA48Terminal to use
55 */
7b5261bc
KL
56 public ECMA48Screen(final ECMA48Terminal terminal) {
57 debugToStderr = false;
58
59 this.terminal = terminal;
217c6107 60
7b5261bc
KL
61 // Query the screen size
62 setDimensions(terminal.getSessionInfo().getWindowWidth(),
63 terminal.getSessionInfo().getWindowHeight());
217c6107
KL
64 }
65
66 /**
7b5261bc 67 * Perform a somewhat-optimal rendering of a line.
217c6107
KL
68 *
69 * @param y row coordinate. 0 is the top-most row.
70 * @param sb StringBuilder to write escape sequences to
71 * @param lastAttr cell attributes from the last call to flushLine
72 */
7b5261bc
KL
73 private void flushLine(final int y, final StringBuilder sb,
74 CellAttributes lastAttr) {
75
76 int lastX = -1;
77 int textEnd = 0;
78 for (int x = 0; x < width; x++) {
79 Cell lCell = logical[x][y];
80 if (!lCell.isBlank()) {
81 textEnd = x;
82 }
83 }
84 // Push textEnd to first column beyond the text area
85 textEnd++;
86
87 // DEBUG
88 // reallyCleared = true;
89
90 for (int x = 0; x < width; x++) {
91 Cell lCell = logical[x][y];
92 Cell pCell = physical[x][y];
93
8dc20d38 94 if (!lCell.equals(pCell) || reallyCleared) {
7b5261bc
KL
95
96 if (debugToStderr) {
97 System.err.printf("\n--\n");
98 System.err.printf(" Y: %d X: %d\n", y, x);
99 System.err.printf(" lCell: %s\n", lCell);
100 System.err.printf(" pCell: %s\n", pCell);
101 System.err.printf(" ==== \n");
102 }
103
104 if (lastAttr == null) {
105 lastAttr = new CellAttributes();
106 sb.append(terminal.normal());
107 }
108
109 // Place the cell
110 if ((lastX != (x - 1)) || (lastX == -1)) {
111 // Advancing at least one cell, or the first gotoXY
112 sb.append(terminal.gotoXY(x, y));
113 }
114
115 assert (lastAttr != null);
116
117 if ((x == textEnd) && (textEnd < width - 1)) {
118 assert (lCell.isBlank());
119
120 for (int i = x; i < width; i++) {
121 assert (logical[i][y].isBlank());
122 // Physical is always updatesd
123 physical[i][y].reset();
124 }
125
126 // Clear remaining line
127 sb.append(terminal.clearRemainingLine());
128 lastAttr.reset();
129 return;
130 }
131
132 // Now emit only the modified attributes
133 if ((lCell.getForeColor() != lastAttr.getForeColor())
134 && (lCell.getBackColor() != lastAttr.getBackColor())
135 && (lCell.getBold() == lastAttr.getBold())
136 && (lCell.getReverse() == lastAttr.getReverse())
137 && (lCell.getUnderline() == lastAttr.getUnderline())
138 && (lCell.getBlink() == lastAttr.getBlink())
139 ) {
140 // Both colors changed, attributes the same
141 sb.append(terminal.color(lCell.getForeColor(),
142 lCell.getBackColor()));
143
144 if (debugToStderr) {
145 System.err.printf("1 Change only fore/back colors\n");
146 }
147 } else if ((lCell.getForeColor() != lastAttr.getForeColor())
148 && (lCell.getBackColor() != lastAttr.getBackColor())
149 && (lCell.getBold() != lastAttr.getBold())
150 && (lCell.getReverse() != lastAttr.getReverse())
151 && (lCell.getUnderline() != lastAttr.getUnderline())
152 && (lCell.getBlink() != lastAttr.getBlink())
153 ) {
154 // Everything is different
155 sb.append(terminal.color(lCell.getForeColor(),
156 lCell.getBackColor(),
157 lCell.getBold(), lCell.getReverse(),
158 lCell.getBlink(),
159 lCell.getUnderline()));
160
161 if (debugToStderr) {
162 System.err.printf("2 Set all attributes\n");
163 }
164 } else if ((lCell.getForeColor() != lastAttr.getForeColor())
165 && (lCell.getBackColor() == lastAttr.getBackColor())
166 && (lCell.getBold() == lastAttr.getBold())
167 && (lCell.getReverse() == lastAttr.getReverse())
168 && (lCell.getUnderline() == lastAttr.getUnderline())
169 && (lCell.getBlink() == lastAttr.getBlink())
170 ) {
171
172 // Attributes same, foreColor different
173 sb.append(terminal.color(lCell.getForeColor(), true));
174
175 if (debugToStderr) {
176 System.err.printf("3 Change foreColor\n");
177 }
178 } else if ((lCell.getForeColor() == lastAttr.getForeColor())
179 && (lCell.getBackColor() != lastAttr.getBackColor())
180 && (lCell.getBold() == lastAttr.getBold())
181 && (lCell.getReverse() == lastAttr.getReverse())
182 && (lCell.getUnderline() == lastAttr.getUnderline())
183 && (lCell.getBlink() == lastAttr.getBlink())
184 ) {
185 // Attributes same, backColor different
186 sb.append(terminal.color(lCell.getBackColor(), false));
187
188 if (debugToStderr) {
189 System.err.printf("4 Change backColor\n");
190 }
191 } else if ((lCell.getForeColor() == lastAttr.getForeColor())
192 && (lCell.getBackColor() == lastAttr.getBackColor())
193 && (lCell.getBold() == lastAttr.getBold())
194 && (lCell.getReverse() == lastAttr.getReverse())
195 && (lCell.getUnderline() == lastAttr.getUnderline())
196 && (lCell.getBlink() == lastAttr.getBlink())
197 ) {
198
199 // All attributes the same, just print the char
200 // NOP
201
202 if (debugToStderr) {
203 System.err.printf("5 Only emit character\n");
204 }
205 } else {
206 // Just reset everything again
207 sb.append(terminal.color(lCell.getForeColor(),
208 lCell.getBackColor(),
209 lCell.getBold(),
210 lCell.getReverse(),
211 lCell.getBlink(),
212 lCell.getUnderline()));
213
214 if (debugToStderr) {
215 System.err.printf("6 Change all attributes\n");
216 }
217 }
218 // Emit the character
219 sb.append(lCell.getChar());
220
221 // Save the last rendered cell
222 lastX = x;
223 lastAttr.setTo(lCell);
224
8dc20d38 225 // Physical is always updated
7b5261bc
KL
226 physical[x][y].setTo(lCell);
227
8dc20d38 228 } // if (!lCell.equals(pCell) || (reallyCleared == true))
7b5261bc
KL
229
230 } // for (int x = 0; x < width; x++)
217c6107
KL
231 }
232
233 /**
234 * Render the screen to a string that can be emitted to something that
235 * knows how to process ECMA-48/ANSI X3.64 escape sequences.
236 *
237 * @return escape sequences string that provides the updates to the
238 * physical screen
239 */
240 public String flushString() {
7b5261bc
KL
241 if (!dirty) {
242 assert (!reallyCleared);
243 return "";
244 }
245
246 CellAttributes attr = null;
247
248 StringBuilder sb = new StringBuilder();
249 if (reallyCleared) {
250 attr = new CellAttributes();
251 sb.append(terminal.clearAll());
252 }
253
254 for (int y = 0; y < height; y++) {
255 flushLine(y, sb, attr);
256 }
257
258 dirty = false;
259 reallyCleared = false;
260
261 String result = sb.toString();
262 if (debugToStderr) {
263 System.err.printf("flushString(): %s\n", result);
264 }
265 return result;
217c6107
KL
266 }
267
268 /**
269 * Push the logical screen to the physical device.
270 */
271 @Override
272 public void flushPhysical() {
7b5261bc
KL
273 String result = flushString();
274 if ((cursorVisible)
275 && (cursorY <= height - 1)
276 && (cursorX <= width - 1)
277 ) {
278 result += terminal.cursor(true);
279 result += terminal.gotoXY(cursorX, cursorY);
280 } else {
281 result += terminal.cursor(false);
282 }
283 terminal.getOutput().write(result);
284 terminal.flush();
217c6107
KL
285 }
286}