2 * Jexer - Java Text User Interface
6 * Author: Kevin Lamonte, <a href="mailto:kevin.lamonte@gmail.com">kevin.lamonte@gmail.com</a>
8 * License: LGPLv3 or later
10 * Copyright: This module is licensed under the GNU Lesser General
11 * Public License Version 3. Please see the file "COPYING" in this
12 * directory for more information about the GNU Lesser General Public
15 * Copyright (C) 2015 Kevin Lamonte
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Lesser General Public License
19 * as published by the Free Software Foundation; either version 3 of
20 * the License, or (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
27 * You should have received a copy of the GNU Lesser General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/licenses/, or write to the Free Software
30 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
35 import jexer
.bits
.Cell
;
36 import jexer
.bits
.CellAttributes
;
37 import jexer
.bits
.GraphicsChars
;
40 * This class represents a text-based screen. Drawing operations write to a
43 public abstract class Screen
{
46 * Emit debugging to stderr
48 public boolean debugToStderr
;
51 * Width of the visible window
56 * Height of the visible window
61 * Drawing offset for x
66 * Drawing offset for y
71 * Ignore anything drawn right of clipRight
76 * Ignore anything drawn below clipBottom
78 public int clipBottom
;
81 * Ignore anything drawn left of clipLeft
86 * Ignore anything drawn above clipTop
91 * The physical screen last sent out on flush()
93 protected Cell
[][] physical
;
96 * The logical screen being rendered to
98 protected Cell
[][] logical
;
101 * When true, logical != physical
103 public boolean dirty
;
106 * Set if the user explicitly wants to redraw everything starting with a
107 * ECMATerminal.clearAll()
109 protected boolean reallyCleared
;
112 * If true, the cursor is visible and should be placed onscreen at
113 * (cursorX, cursorY) during a call to flushPhysical()
115 protected boolean cursorVisible
;
118 * Cursor X position if visible
120 protected int cursorX
;
123 * Cursor Y position if visible
125 protected int cursorY
;
128 * Get the attributes at one location.
130 * @param x column coordinate. 0 is the left-most column.
131 * @param y row coordinate. 0 is the top-most row.
132 * @return attributes at (x, y)
134 public CellAttributes
getAttrXY(int x
, int y
) {
135 CellAttributes attr
= new CellAttributes();
136 attr
.setTo(logical
[x
][y
]);
141 * Set the attributes at one location.
143 * @param x column coordinate. 0 is the left-most column.
144 * @param y row coordinate. 0 is the top-most row.
145 * @param attr attributes to use (bold, foreColor, backColor)
146 * @param clip if true, honor clipping/offset
148 public void putAttrXY(int x
, int y
, CellAttributes attr
) {
149 putAttrXY(x
, y
, attr
, true);
153 * Set the attributes at one location.
155 * @param x column coordinate. 0 is the left-most column.
156 * @param y row coordinate. 0 is the top-most row.
157 * @param attr attributes to use (bold, foreColor, backColor)
158 * @param clip if true, honor clipping/offset
160 public void putAttrXY(int x
, int y
, CellAttributes attr
, boolean clip
) {
166 if ((x
< clipLeft
) || (x
>= clipRight
) ||
167 (y
< clipTop
) || (y
>= clipBottom
)) {
174 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
176 logical
[X
][Y
].foreColor
= attr
.foreColor
;
177 logical
[X
][Y
].backColor
= attr
.backColor
;
178 logical
[X
][Y
].bold
= attr
.bold
;
179 logical
[X
][Y
].blink
= attr
.blink
;
180 logical
[X
][Y
].reverse
= attr
.reverse
;
181 logical
[X
][Y
].underline
= attr
.underline
;
182 logical
[X
][Y
].protect
= attr
.protect
;
187 * Fill the entire screen with one character with attributes.
189 * @param ch character to draw
190 * @param attr attributes to use (bold, foreColor, backColor)
192 public void putAll(char ch
, CellAttributes attr
) {
193 for (int x
= 0; x
< width
; x
++) {
194 for (int y
= 0; y
< height
; y
++) {
195 putCharXY(x
, y
, ch
, attr
);
201 * Render one character with attributes.
203 * @param x column coordinate. 0 is the left-most column.
204 * @param y row coordinate. 0 is the top-most row.
205 * @param ch character + attributes to draw
207 public void putCharXY(int x
, int y
, Cell ch
) {
208 putCharXY(x
, y
, ch
.ch
, ch
);
212 * Render one character with attributes.
214 * @param x column coordinate. 0 is the left-most column.
215 * @param y row coordinate. 0 is the top-most row.
216 * @param ch character to draw
217 * @param attr attributes to use (bold, foreColor, backColor)
219 public void putCharXY(int x
, int y
, char ch
, CellAttributes attr
) {
220 if ((x
< clipLeft
) || (x
>= clipRight
) ||
221 (y
< clipTop
) || (y
>= clipBottom
)) {
228 // stderr.writefln("putCharXY: %d, %d, %c", X, Y, ch);
230 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
233 // Do not put control characters on the display
237 logical
[X
][Y
].ch
= ch
;
238 logical
[X
][Y
].foreColor
= attr
.foreColor
;
239 logical
[X
][Y
].backColor
= attr
.backColor
;
240 logical
[X
][Y
].bold
= attr
.bold
;
241 logical
[X
][Y
].blink
= attr
.blink
;
242 logical
[X
][Y
].reverse
= attr
.reverse
;
243 logical
[X
][Y
].underline
= attr
.underline
;
244 logical
[X
][Y
].protect
= attr
.protect
;
249 * Render one character without changing the underlying attributes.
251 * @param x column coordinate. 0 is the left-most column.
252 * @param y row coordinate. 0 is the top-most row.
253 * @param ch character to draw
255 public void putCharXY(int x
, int y
, char ch
) {
256 if ((x
< clipLeft
) || (x
>= clipRight
) ||
257 (y
< clipTop
) || (y
>= clipBottom
)) {
264 // stderr.writefln("putCharXY: %d, %d, %c", X, Y, ch);
266 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
268 logical
[X
][Y
].ch
= ch
;
273 * Render a string. Does not wrap if the string exceeds the line.
275 * @param x column coordinate. 0 is the left-most column.
276 * @param y row coordinate. 0 is the top-most row.
277 * @param str string to draw
278 * @param attr attributes to use (bold, foreColor, backColor)
280 public void putStrXY(int x
, int y
, String str
, CellAttributes attr
) {
282 for (int j
= 0; j
< str
.length(); j
++) {
283 char ch
= str
.charAt(j
);
284 putCharXY(i
, y
, ch
, attr
);
293 * Render a string without changing the underlying attribute. Does not
294 * wrap if the string exceeds the line.
296 * @param x column coordinate. 0 is the left-most column.
297 * @param y row coordinate. 0 is the top-most row.
298 * @param str string to draw
300 public void putStrXY(int x
, int y
, String str
) {
302 for (int j
= 0; j
< str
.length(); j
++) {
303 char ch
= str
.charAt(j
);
313 * Draw a vertical line from (x, y) to (x, y + n)
315 * @param x column coordinate. 0 is the left-most column.
316 * @param y row coordinate. 0 is the top-most row.
317 * @param n number of characters to draw
318 * @param ch character to draw
319 * @param attr attributes to use (bold, foreColor, backColor)
321 public void vLineXY(int x
, int y
, int n
, char ch
, CellAttributes attr
) {
322 for (int i
= y
; i
< y
+ n
; i
++) {
323 putCharXY(x
, i
, ch
, attr
);
328 * Draw a horizontal line from (x, y) to (x + n, y)
330 * @param x column coordinate. 0 is the left-most column.
331 * @param y row coordinate. 0 is the top-most row.
332 * @param n number of characters to draw
333 * @param ch character to draw
334 * @param attr attributes to use (bold, foreColor, backColor)
336 public void hLineXY(int x
, int y
, int n
, char ch
, CellAttributes attr
) {
337 for (int i
= x
; i
< x
+ n
; i
++) {
338 putCharXY(i
, y
, ch
, attr
);
343 * Reallocate screen buffers.
345 * @param width new width
346 * @param height new height
348 private void reallocate(int width
, int height
) {
349 if (logical
!= null) {
350 for (int row
= 0; row
< this.height
; row
++) {
351 for (int col
= 0; col
< this.width
; col
++) {
352 logical
[col
][row
] = null;
357 logical
= new Cell
[width
][height
];
358 if (physical
!= null) {
359 for (int row
= 0; row
< this.height
; row
++) {
360 for (int col
= 0; col
< this.width
; col
++) {
361 physical
[col
][row
] = null;
366 physical
= new Cell
[width
][height
];
368 for (int row
= 0; row
< height
; row
++) {
369 for (int col
= 0; col
< width
; col
++) {
370 physical
[col
][row
] = new Cell();
371 logical
[col
][row
] = new Cell();
376 this.height
= height
;
383 reallyCleared
= true;
388 * Change the width. Everything on-screen will be destroyed and must be
391 * @param width new screen width
393 public void setWidth(int width
) {
394 reallocate(width
, this.height
);
398 * Change the height. Everything on-screen will be destroyed and must be
401 * @param height new screen height
403 public void setHeight(int height
) {
404 reallocate(this.width
, height
);
408 * Change the width and height. Everything on-screen will be destroyed
409 * and must be redrawn.
411 * @param width new screen width
412 * @param height new screen height
414 public void setDimensions(int width
, int height
) {
415 reallocate(width
, height
);
421 * @return current screen height
423 public int getHeight() {
430 * @return current screen width
432 public int getWidth() {
437 * Public constructor. Sets everything to not-bold, white-on-black.
440 debugToStderr
= false;
448 reallocate(width
, height
);
452 * Reset screen to not-bold, white-on-black. Also flushes the offset and
455 public void reset() {
457 for (int row
= 0; row
< height
; row
++) {
458 for (int col
= 0; col
< width
; col
++) {
459 logical
[col
][row
].reset();
466 * Flush the offset and clip variables.
468 public void resetClipping() {
478 * Force the screen to be fully cleared and redrawn on the next flush().
480 public void clear() {
485 * Draw a box with a border and empty background.
487 * @param left left column of box. 0 is the left-most row.
488 * @param top top row of the box. 0 is the top-most row.
489 * @param right right column of box
490 * @param bottom bottom row of the box
491 * @param border attributes to use for the border (bold, foreColor, backColor)
492 * @param background attributes to use for the background
494 public void drawBox(int left
, int top
, int right
, int bottom
,
495 CellAttributes border
, CellAttributes background
) {
496 drawBox(left
, top
, right
, bottom
, border
, background
, 1, false);
500 * Draw a box with a border and empty background.
502 * @param left left column of box. 0 is the left-most row.
503 * @param top top row of the box. 0 is the top-most row.
504 * @param right right column of box
505 * @param bottom bottom row of the box
506 * @param border attributes to use for the border (bold, foreColor, backColor)
507 * @param background attributes to use for the background
508 * @param borderType = 1: single-line border
509 * 2: double-line borders
510 * 3: double-line top/bottom edges and single-line left/right edges
511 * @param shadow if true, draw a "shadow" on the box
513 public void drawBox(int left
, int top
, int right
, int bottom
,
514 CellAttributes border
, CellAttributes background
, int borderType
,
519 int boxWidth
= right
- left
;
520 int boxHeight
= bottom
- top
;
529 switch (borderType
) {
531 cTopLeft
= GraphicsChars
.ULCORNER
;
532 cTopRight
= GraphicsChars
.URCORNER
;
533 cBottomLeft
= GraphicsChars
.LLCORNER
;
534 cBottomRight
= GraphicsChars
.LRCORNER
;
535 cHSide
= GraphicsChars
.SINGLE_BAR
;
536 cVSide
= GraphicsChars
.WINDOW_SIDE
;
540 cTopLeft
= GraphicsChars
.WINDOW_LEFT_TOP_DOUBLE
;
541 cTopRight
= GraphicsChars
.WINDOW_RIGHT_TOP_DOUBLE
;
542 cBottomLeft
= GraphicsChars
.WINDOW_LEFT_BOTTOM_DOUBLE
;
543 cBottomRight
= GraphicsChars
.WINDOW_RIGHT_BOTTOM_DOUBLE
;
544 cHSide
= GraphicsChars
.DOUBLE_BAR
;
545 cVSide
= GraphicsChars
.WINDOW_SIDE_DOUBLE
;
549 cTopLeft
= GraphicsChars
.WINDOW_LEFT_TOP
;
550 cTopRight
= GraphicsChars
.WINDOW_RIGHT_TOP
;
551 cBottomLeft
= GraphicsChars
.WINDOW_LEFT_BOTTOM
;
552 cBottomRight
= GraphicsChars
.WINDOW_RIGHT_BOTTOM
;
553 cHSide
= GraphicsChars
.WINDOW_TOP
;
554 cVSide
= GraphicsChars
.WINDOW_SIDE
;
557 throw new IllegalArgumentException("Invalid border type: " + borderType
);
560 // Place the corner characters
561 putCharXY(left
, top
, cTopLeft
, border
);
562 putCharXY(left
+ boxWidth
- 1, top
, cTopRight
, border
);
563 putCharXY(left
, top
+ boxHeight
- 1, cBottomLeft
, border
);
564 putCharXY(left
+ boxWidth
- 1, top
+ boxHeight
- 1, cBottomRight
,
567 // Draw the box lines
568 hLineXY(left
+ 1, top
, boxWidth
- 2, cHSide
, border
);
569 vLineXY(left
, top
+ 1, boxHeight
- 2, cVSide
, border
);
570 hLineXY(left
+ 1, top
+ boxHeight
- 1, boxWidth
- 2, cHSide
, border
);
571 vLineXY(left
+ boxWidth
- 1, top
+ 1, boxHeight
- 2, cVSide
, border
);
573 // Fill in the interior background
574 for (int i
= 1; i
< boxHeight
- 1; i
++) {
575 hLineXY(1 + left
, i
+ top
, boxWidth
- 2, ' ', background
);
580 drawBoxShadow(left
, top
, right
, bottom
);
587 * @param left left column of box. 0 is the left-most row.
588 * @param top top row of the box. 0 is the top-most row.
589 * @param right right column of box
590 * @param bottom bottom row of the box
592 public void drawBoxShadow(int left
, int top
, int right
, int bottom
) {
596 int boxWidth
= right
- left
;
597 int boxHeight
= bottom
- top
;
598 CellAttributes shadowAttr
= new CellAttributes();
600 // Shadows do not honor clipping but they DO honor offset.
601 int oldClipRight
= clipRight
;
602 int oldClipBottom
= clipBottom
;
604 clipRight = boxWidth + 2;
605 clipBottom = boxHeight + 1;
610 for (int i
= 0; i
< boxHeight
; i
++) {
611 putAttrXY(boxLeft
+ boxWidth
, boxTop
+ 1 + i
, shadowAttr
);
612 putAttrXY(boxLeft
+ boxWidth
+ 1, boxTop
+ 1 + i
, shadowAttr
);
614 for (int i
= 0; i
< boxWidth
; i
++) {
615 putAttrXY(boxLeft
+ 2 + i
, boxTop
+ boxHeight
, shadowAttr
);
617 clipRight
= oldClipRight
;
618 clipBottom
= oldClipBottom
;
622 * Subclasses must provide an implementation to push the logical screen
623 * to the physical device.
625 abstract public void flushPhysical();
628 * Put the cursor at (x,y).
630 * @param visible if true, the cursor should be visible
631 * @param x column coordinate to put the cursor on
632 * @param y row coordinate to put the cursor on
634 public void putCursor(boolean visible
, int x
, int y
) {
635 cursorVisible
= visible
;
643 public void hideCursor() {
644 cursorVisible
= false;