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)
147 public void putAttrXY(int x
, int y
, CellAttributes attr
) {
148 putAttrXY(x
, y
, attr
, true);
152 * Set the attributes at one location.
154 * @param x column coordinate. 0 is the left-most column.
155 * @param y row coordinate. 0 is the top-most row.
156 * @param attr attributes to use (bold, foreColor, backColor)
157 * @param clip if true, honor clipping/offset
159 public void putAttrXY(int x
, int y
, CellAttributes attr
, boolean clip
) {
165 if ((x
< clipLeft
) || (x
>= clipRight
) ||
166 (y
< clipTop
) || (y
>= clipBottom
)) {
173 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
175 logical
[X
][Y
].foreColor
= attr
.foreColor
;
176 logical
[X
][Y
].backColor
= attr
.backColor
;
177 logical
[X
][Y
].bold
= attr
.bold
;
178 logical
[X
][Y
].blink
= attr
.blink
;
179 logical
[X
][Y
].reverse
= attr
.reverse
;
180 logical
[X
][Y
].underline
= attr
.underline
;
181 logical
[X
][Y
].protect
= attr
.protect
;
186 * Fill the entire screen with one character with attributes.
188 * @param ch character to draw
189 * @param attr attributes to use (bold, foreColor, backColor)
191 public void putAll(char ch
, CellAttributes attr
) {
192 for (int x
= 0; x
< width
; x
++) {
193 for (int y
= 0; y
< height
; y
++) {
194 putCharXY(x
, y
, ch
, attr
);
200 * Render one character with attributes.
202 * @param x column coordinate. 0 is the left-most column.
203 * @param y row coordinate. 0 is the top-most row.
204 * @param ch character + attributes to draw
206 public void putCharXY(int x
, int y
, Cell ch
) {
207 putCharXY(x
, y
, ch
.ch
, ch
);
211 * Render one character with attributes.
213 * @param x column coordinate. 0 is the left-most column.
214 * @param y row coordinate. 0 is the top-most row.
215 * @param ch character to draw
216 * @param attr attributes to use (bold, foreColor, backColor)
218 public void putCharXY(int x
, int y
, char ch
, CellAttributes attr
) {
219 if ((x
< clipLeft
) || (x
>= clipRight
) ||
220 (y
< clipTop
) || (y
>= clipBottom
)) {
227 // stderr.writefln("putCharXY: %d, %d, %c", X, Y, ch);
229 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
232 // Do not put control characters on the display
236 logical
[X
][Y
].ch
= ch
;
237 logical
[X
][Y
].foreColor
= attr
.foreColor
;
238 logical
[X
][Y
].backColor
= attr
.backColor
;
239 logical
[X
][Y
].bold
= attr
.bold
;
240 logical
[X
][Y
].blink
= attr
.blink
;
241 logical
[X
][Y
].reverse
= attr
.reverse
;
242 logical
[X
][Y
].underline
= attr
.underline
;
243 logical
[X
][Y
].protect
= attr
.protect
;
248 * Render one character without changing the underlying attributes.
250 * @param x column coordinate. 0 is the left-most column.
251 * @param y row coordinate. 0 is the top-most row.
252 * @param ch character to draw
254 public void putCharXY(int x
, int y
, char ch
) {
255 if ((x
< clipLeft
) || (x
>= clipRight
) ||
256 (y
< clipTop
) || (y
>= clipBottom
)) {
263 // stderr.writefln("putCharXY: %d, %d, %c", X, Y, ch);
265 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
267 logical
[X
][Y
].ch
= ch
;
272 * Render a string. Does not wrap if the string exceeds the line.
274 * @param x column coordinate. 0 is the left-most column.
275 * @param y row coordinate. 0 is the top-most row.
276 * @param str string to draw
277 * @param attr attributes to use (bold, foreColor, backColor)
279 public void putStrXY(int x
, int y
, String str
, CellAttributes attr
) {
281 for (int j
= 0; j
< str
.length(); j
++) {
282 char ch
= str
.charAt(j
);
283 putCharXY(i
, y
, ch
, attr
);
292 * Render a string without changing the underlying attribute. Does not
293 * wrap if the string exceeds the line.
295 * @param x column coordinate. 0 is the left-most column.
296 * @param y row coordinate. 0 is the top-most row.
297 * @param str string to draw
299 public void putStrXY(int x
, int y
, String str
) {
301 for (int j
= 0; j
< str
.length(); j
++) {
302 char ch
= str
.charAt(j
);
312 * Draw a vertical line from (x, y) to (x, y + n)
314 * @param x column coordinate. 0 is the left-most column.
315 * @param y row coordinate. 0 is the top-most row.
316 * @param n number of characters to draw
317 * @param ch character to draw
318 * @param attr attributes to use (bold, foreColor, backColor)
320 public void vLineXY(int x
, int y
, int n
, char ch
, CellAttributes attr
) {
321 for (int i
= y
; i
< y
+ n
; i
++) {
322 putCharXY(x
, i
, ch
, attr
);
327 * Draw a horizontal line from (x, y) to (x + n, y)
329 * @param x column coordinate. 0 is the left-most column.
330 * @param y row coordinate. 0 is the top-most row.
331 * @param n number of characters to draw
332 * @param ch character to draw
333 * @param attr attributes to use (bold, foreColor, backColor)
335 public void hLineXY(int x
, int y
, int n
, char ch
, CellAttributes attr
) {
336 for (int i
= x
; i
< x
+ n
; i
++) {
337 putCharXY(i
, y
, ch
, attr
);
342 * Reallocate screen buffers.
344 * @param width new width
345 * @param height new height
347 private void reallocate(int width
, int height
) {
348 if (logical
!= null) {
349 for (int row
= 0; row
< this.height
; row
++) {
350 for (int col
= 0; col
< this.width
; col
++) {
351 logical
[col
][row
] = null;
356 logical
= new Cell
[width
][height
];
357 if (physical
!= null) {
358 for (int row
= 0; row
< this.height
; row
++) {
359 for (int col
= 0; col
< this.width
; col
++) {
360 physical
[col
][row
] = null;
365 physical
= new Cell
[width
][height
];
367 for (int row
= 0; row
< height
; row
++) {
368 for (int col
= 0; col
< width
; col
++) {
369 physical
[col
][row
] = new Cell();
370 logical
[col
][row
] = new Cell();
375 this.height
= height
;
382 reallyCleared
= true;
387 * Change the width. Everything on-screen will be destroyed and must be
390 * @param width new screen width
392 public void setWidth(int width
) {
393 reallocate(width
, this.height
);
397 * Change the height. Everything on-screen will be destroyed and must be
400 * @param height new screen height
402 public void setHeight(int height
) {
403 reallocate(this.width
, height
);
407 * Change the width and height. Everything on-screen will be destroyed
408 * and must be redrawn.
410 * @param width new screen width
411 * @param height new screen height
413 public void setDimensions(int width
, int height
) {
414 reallocate(width
, height
);
420 * @return current screen height
422 public int getHeight() {
429 * @return current screen width
431 public int getWidth() {
436 * Public constructor. Sets everything to not-bold, white-on-black.
439 debugToStderr
= false;
447 reallocate(width
, height
);
451 * Reset screen to not-bold, white-on-black. Also flushes the offset and
454 public void reset() {
456 for (int row
= 0; row
< height
; row
++) {
457 for (int col
= 0; col
< width
; col
++) {
458 logical
[col
][row
].reset();
465 * Flush the offset and clip variables.
467 public void resetClipping() {
477 * Force the screen to be fully cleared and redrawn on the next flush().
479 public void clear() {
484 * Draw a box with a border and empty background.
486 * @param left left column of box. 0 is the left-most row.
487 * @param top top row of the box. 0 is the top-most row.
488 * @param right right column of box
489 * @param bottom bottom row of the box
490 * @param border attributes to use for the border (bold, foreColor, backColor)
491 * @param background attributes to use for the background
493 public void drawBox(int left
, int top
, int right
, int bottom
,
494 CellAttributes border
, CellAttributes background
) {
495 drawBox(left
, top
, right
, bottom
, border
, background
, 1, false);
499 * Draw a box with a border and empty background.
501 * @param left left column of box. 0 is the left-most row.
502 * @param top top row of the box. 0 is the top-most row.
503 * @param right right column of box
504 * @param bottom bottom row of the box
505 * @param border attributes to use for the border (bold, foreColor, backColor)
506 * @param background attributes to use for the background
507 * @param borderType = 1: single-line border
508 * 2: double-line borders
509 * 3: double-line top/bottom edges and single-line left/right edges
510 * @param shadow if true, draw a "shadow" on the box
512 public void drawBox(int left
, int top
, int right
, int bottom
,
513 CellAttributes border
, CellAttributes background
, int borderType
,
518 int boxWidth
= right
- left
;
519 int boxHeight
= bottom
- top
;
528 switch (borderType
) {
530 cTopLeft
= GraphicsChars
.ULCORNER
;
531 cTopRight
= GraphicsChars
.URCORNER
;
532 cBottomLeft
= GraphicsChars
.LLCORNER
;
533 cBottomRight
= GraphicsChars
.LRCORNER
;
534 cHSide
= GraphicsChars
.SINGLE_BAR
;
535 cVSide
= GraphicsChars
.WINDOW_SIDE
;
539 cTopLeft
= GraphicsChars
.WINDOW_LEFT_TOP_DOUBLE
;
540 cTopRight
= GraphicsChars
.WINDOW_RIGHT_TOP_DOUBLE
;
541 cBottomLeft
= GraphicsChars
.WINDOW_LEFT_BOTTOM_DOUBLE
;
542 cBottomRight
= GraphicsChars
.WINDOW_RIGHT_BOTTOM_DOUBLE
;
543 cHSide
= GraphicsChars
.DOUBLE_BAR
;
544 cVSide
= GraphicsChars
.WINDOW_SIDE_DOUBLE
;
548 cTopLeft
= GraphicsChars
.WINDOW_LEFT_TOP
;
549 cTopRight
= GraphicsChars
.WINDOW_RIGHT_TOP
;
550 cBottomLeft
= GraphicsChars
.WINDOW_LEFT_BOTTOM
;
551 cBottomRight
= GraphicsChars
.WINDOW_RIGHT_BOTTOM
;
552 cHSide
= GraphicsChars
.WINDOW_TOP
;
553 cVSide
= GraphicsChars
.WINDOW_SIDE
;
556 throw new IllegalArgumentException("Invalid border type: " + borderType
);
559 // Place the corner characters
560 putCharXY(left
, top
, cTopLeft
, border
);
561 putCharXY(left
+ boxWidth
- 1, top
, cTopRight
, border
);
562 putCharXY(left
, top
+ boxHeight
- 1, cBottomLeft
, border
);
563 putCharXY(left
+ boxWidth
- 1, top
+ boxHeight
- 1, cBottomRight
,
566 // Draw the box lines
567 hLineXY(left
+ 1, top
, boxWidth
- 2, cHSide
, border
);
568 vLineXY(left
, top
+ 1, boxHeight
- 2, cVSide
, border
);
569 hLineXY(left
+ 1, top
+ boxHeight
- 1, boxWidth
- 2, cHSide
, border
);
570 vLineXY(left
+ boxWidth
- 1, top
+ 1, boxHeight
- 2, cVSide
, border
);
572 // Fill in the interior background
573 for (int i
= 1; i
< boxHeight
- 1; i
++) {
574 hLineXY(1 + left
, i
+ top
, boxWidth
- 2, ' ', background
);
579 drawBoxShadow(left
, top
, right
, bottom
);
586 * @param left left column of box. 0 is the left-most row.
587 * @param top top row of the box. 0 is the top-most row.
588 * @param right right column of box
589 * @param bottom bottom row of the box
591 public void drawBoxShadow(int left
, int top
, int right
, int bottom
) {
595 int boxWidth
= right
- left
;
596 int boxHeight
= bottom
- top
;
597 CellAttributes shadowAttr
= new CellAttributes();
599 // Shadows do not honor clipping but they DO honor offset.
600 int oldClipRight
= clipRight
;
601 int oldClipBottom
= clipBottom
;
603 clipRight = boxWidth + 2;
604 clipBottom = boxHeight + 1;
609 for (int i
= 0; i
< boxHeight
; i
++) {
610 putAttrXY(boxLeft
+ boxWidth
, boxTop
+ 1 + i
, shadowAttr
);
611 putAttrXY(boxLeft
+ boxWidth
+ 1, boxTop
+ 1 + i
, shadowAttr
);
613 for (int i
= 0; i
< boxWidth
; i
++) {
614 putAttrXY(boxLeft
+ 2 + i
, boxTop
+ boxHeight
, shadowAttr
);
616 clipRight
= oldClipRight
;
617 clipBottom
= oldClipBottom
;
621 * Subclasses must provide an implementation to push the logical screen
622 * to the physical device.
624 abstract public void flushPhysical();
627 * Put the cursor at (x,y).
629 * @param visible if true, the cursor should be visible
630 * @param x column coordinate to put the cursor on
631 * @param y row coordinate to put the cursor on
633 public void putCursor(boolean visible
, int x
, int y
) {
634 cursorVisible
= visible
;
642 public void hideCursor() {
643 cursorVisible
= false;