2 * Jexer - Java Text User Interface
4 * License: LGPLv3 or later
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.
10 * Copyright (C) 2015 Kevin Lamonte
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.
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.
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
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
33 import jexer
.bits
.Cell
;
34 import jexer
.bits
.CellAttributes
;
35 import jexer
.bits
.GraphicsChars
;
38 * This class represents a text-based screen. Drawing operations write to a
41 public abstract class Screen
{
44 * Width of the visible window.
49 * Height of the visible window.
54 * Drawing offset for x.
59 * Drawing offset for y.
64 * Ignore anything drawn right of clipRight.
69 * Ignore anything drawn below clipBottom.
71 public int clipBottom
;
74 * Ignore anything drawn left of clipLeft.
79 * Ignore anything drawn above clipTop.
84 * The physical screen last sent out on flush().
86 protected Cell
[][] physical
;
89 * The logical screen being rendered to.
91 protected Cell
[][] logical
;
94 * When true, logical != physical.
99 * Set if the user explicitly wants to redraw everything starting with a
100 * ECMATerminal.clearAll().
102 protected boolean reallyCleared
;
105 * If true, the cursor is visible and should be placed onscreen at
106 * (cursorX, cursorY) during a call to flushPhysical().
108 protected boolean cursorVisible
;
111 * Cursor X position if visible.
113 protected int cursorX
;
116 * Cursor Y position if visible.
118 protected int cursorY
;
121 * Get the attributes at one location.
123 * @param x column coordinate. 0 is the left-most column.
124 * @param y row coordinate. 0 is the top-most row.
125 * @return attributes at (x, y)
127 public CellAttributes
getAttrXY(final int x
, final int y
) {
128 CellAttributes attr
= new CellAttributes();
129 attr
.setTo(logical
[x
][y
]);
134 * Set the attributes at one location.
136 * @param x column coordinate. 0 is the left-most column.
137 * @param y row coordinate. 0 is the top-most row.
138 * @param attr attributes to use (bold, foreColor, backColor)
140 public void putAttrXY(final int x
, final int y
, final CellAttributes attr
) {
141 putAttrXY(x
, y
, attr
, true);
145 * Set the attributes at one location.
147 * @param x column coordinate. 0 is the left-most column.
148 * @param y row coordinate. 0 is the top-most row.
149 * @param attr attributes to use (bold, foreColor, backColor)
150 * @param clip if true, honor clipping/offset
152 public void putAttrXY(final int x
, final int y
, final CellAttributes attr
,
153 final boolean clip
) {
170 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
172 logical
[X
][Y
].setForeColor(attr
.getForeColor());
173 logical
[X
][Y
].setBackColor(attr
.getBackColor());
174 logical
[X
][Y
].setBold(attr
.getBold());
175 logical
[X
][Y
].setBlink(attr
.getBlink());
176 logical
[X
][Y
].setReverse(attr
.getReverse());
177 logical
[X
][Y
].setUnderline(attr
.getUnderline());
178 logical
[X
][Y
].setProtect(attr
.getProtect());
183 * Fill the entire screen with one character with attributes.
185 * @param ch character to draw
186 * @param attr attributes to use (bold, foreColor, backColor)
188 public void putAll(final char ch
, final CellAttributes attr
) {
189 for (int x
= 0; x
< width
; x
++) {
190 for (int y
= 0; y
< height
; y
++) {
191 putCharXY(x
, y
, ch
, attr
);
197 * Render one character with attributes.
199 * @param x column coordinate. 0 is the left-most column.
200 * @param y row coordinate. 0 is the top-most row.
201 * @param ch character + attributes to draw
203 public void putCharXY(final int x
, final int y
, final Cell ch
) {
204 putCharXY(x
, y
, ch
.getChar(), ch
);
208 * Render one character with attributes.
210 * @param x column coordinate. 0 is the left-most column.
211 * @param y row coordinate. 0 is the top-most row.
212 * @param ch character to draw
213 * @param attr attributes to use (bold, foreColor, backColor)
215 public void putCharXY(final int x
, final int y
, final char ch
,
216 final CellAttributes attr
) {
229 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
231 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
234 // Do not put control characters on the display
238 logical
[X
][Y
].setChar(ch
);
239 logical
[X
][Y
].setForeColor(attr
.getForeColor());
240 logical
[X
][Y
].setBackColor(attr
.getBackColor());
241 logical
[X
][Y
].setBold(attr
.getBold());
242 logical
[X
][Y
].setBlink(attr
.getBlink());
243 logical
[X
][Y
].setReverse(attr
.getReverse());
244 logical
[X
][Y
].setUnderline(attr
.getUnderline());
245 logical
[X
][Y
].setProtect(attr
.getProtect());
250 * Render one character without changing the underlying attributes.
252 * @param x column coordinate. 0 is the left-most column.
253 * @param y row coordinate. 0 is the top-most row.
254 * @param ch character to draw
256 public void putCharXY(final int x
, final int y
, final char ch
) {
268 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
270 if ((X
>= 0) && (X
< width
) && (Y
>= 0) && (Y
< height
)) {
272 logical
[X
][Y
].setChar(ch
);
277 * Render a string. Does not wrap if the string exceeds the line.
279 * @param x column coordinate. 0 is the left-most column.
280 * @param y row coordinate. 0 is the top-most row.
281 * @param str string to draw
282 * @param attr attributes to use (bold, foreColor, backColor)
284 public void putStrXY(final int x
, final int y
, final String str
,
285 final CellAttributes attr
) {
288 for (int j
= 0; j
< str
.length(); j
++) {
289 char ch
= str
.charAt(j
);
290 putCharXY(i
, y
, ch
, attr
);
299 * Render a string without changing the underlying attribute. Does not
300 * wrap if the string exceeds the line.
302 * @param x column coordinate. 0 is the left-most column.
303 * @param y row coordinate. 0 is the top-most row.
304 * @param str string to draw
306 public void putStrXY(final int x
, final int y
, final String str
) {
308 for (int j
= 0; j
< str
.length(); j
++) {
309 char ch
= str
.charAt(j
);
319 * Draw a vertical line from (x, y) to (x, y + n).
321 * @param x column coordinate. 0 is the left-most column.
322 * @param y row coordinate. 0 is the top-most row.
323 * @param n number of characters to draw
324 * @param ch character to draw
325 * @param attr attributes to use (bold, foreColor, backColor)
327 public void vLineXY(final int x
, final int y
, final int n
, final char ch
,
328 final CellAttributes attr
) {
330 for (int i
= y
; i
< y
+ n
; i
++) {
331 putCharXY(x
, i
, ch
, attr
);
336 * Draw a horizontal line from (x, y) to (x + n, y).
338 * @param x column coordinate. 0 is the left-most column.
339 * @param y row coordinate. 0 is the top-most row.
340 * @param n number of characters to draw
341 * @param ch character to draw
342 * @param attr attributes to use (bold, foreColor, backColor)
344 public void hLineXY(final int x
, final int y
, final int n
, final char ch
,
345 final CellAttributes attr
) {
347 for (int i
= x
; i
< x
+ n
; i
++) {
348 putCharXY(i
, y
, ch
, attr
);
353 * Reallocate screen buffers.
355 * @param width new width
356 * @param height new height
358 private void reallocate(final int width
, final int height
) {
359 if (logical
!= null) {
360 for (int row
= 0; row
< this.height
; row
++) {
361 for (int col
= 0; col
< this.width
; col
++) {
362 logical
[col
][row
] = null;
367 logical
= new Cell
[width
][height
];
368 if (physical
!= null) {
369 for (int row
= 0; row
< this.height
; row
++) {
370 for (int col
= 0; col
< this.width
; col
++) {
371 physical
[col
][row
] = null;
376 physical
= new Cell
[width
][height
];
378 for (int row
= 0; row
< height
; row
++) {
379 for (int col
= 0; col
< width
; col
++) {
380 physical
[col
][row
] = new Cell();
381 logical
[col
][row
] = new Cell();
386 this.height
= height
;
393 reallyCleared
= true;
398 * Change the width. Everything on-screen will be destroyed and must be
401 * @param width new screen width
403 public void setWidth(final int width
) {
404 reallocate(width
, this.height
);
408 * Change the height. Everything on-screen will be destroyed and must be
411 * @param height new screen height
413 public void setHeight(final int height
) {
414 reallocate(this.width
, height
);
418 * Change the width and height. Everything on-screen will be destroyed
419 * and must be redrawn.
421 * @param width new screen width
422 * @param height new screen height
424 public void setDimensions(final int width
, final int height
) {
425 reallocate(width
, height
);
431 * @return current screen height
433 public int getHeight() {
440 * @return current screen width
442 public int getWidth() {
447 * Public constructor. Sets everything to not-bold, white-on-black.
456 reallocate(width
, height
);
460 * Reset screen to not-bold, white-on-black. Also flushes the offset and
463 public void reset() {
465 for (int row
= 0; row
< height
; row
++) {
466 for (int col
= 0; col
< width
; col
++) {
467 logical
[col
][row
].reset();
474 * Flush the offset and clip variables.
476 public void resetClipping() {
486 * Force the screen to be fully cleared and redrawn on the next flush().
488 public void clear() {
493 * Draw a box with a border and empty background.
495 * @param left left column of box. 0 is the left-most row.
496 * @param top top row of the box. 0 is the top-most row.
497 * @param right right column of box
498 * @param bottom bottom row of the box
499 * @param border attributes to use for the border
500 * @param background attributes to use for the background
502 public void drawBox(final int left
, final int top
,
503 final int right
, final int bottom
,
504 final CellAttributes border
, final CellAttributes background
) {
506 drawBox(left
, top
, right
, bottom
, border
, background
, 1, false);
510 * Draw a box with a border and empty background.
512 * @param left left column of box. 0 is the left-most row.
513 * @param top top row of the box. 0 is the top-most row.
514 * @param right right column of box
515 * @param bottom bottom row of the box
516 * @param border attributes to use for the border
517 * @param background attributes to use for the background
518 * @param borderType if 1, draw a single-line border; if 2, draw a
519 * double-line border; if 3, draw double-line top/bottom edges and
520 * single-line left/right edges (like Qmodem)
521 * @param shadow if true, draw a "shadow" on the box
523 public void drawBox(final int left
, final int top
,
524 final int right
, final int bottom
,
525 final CellAttributes border
, final CellAttributes background
,
526 final int borderType
, final boolean shadow
) {
530 int boxWidth
= right
- left
;
531 int boxHeight
= bottom
- top
;
540 switch (borderType
) {
542 cTopLeft
= GraphicsChars
.ULCORNER
;
543 cTopRight
= GraphicsChars
.URCORNER
;
544 cBottomLeft
= GraphicsChars
.LLCORNER
;
545 cBottomRight
= GraphicsChars
.LRCORNER
;
546 cHSide
= GraphicsChars
.SINGLE_BAR
;
547 cVSide
= GraphicsChars
.WINDOW_SIDE
;
551 cTopLeft
= GraphicsChars
.WINDOW_LEFT_TOP_DOUBLE
;
552 cTopRight
= GraphicsChars
.WINDOW_RIGHT_TOP_DOUBLE
;
553 cBottomLeft
= GraphicsChars
.WINDOW_LEFT_BOTTOM_DOUBLE
;
554 cBottomRight
= GraphicsChars
.WINDOW_RIGHT_BOTTOM_DOUBLE
;
555 cHSide
= GraphicsChars
.DOUBLE_BAR
;
556 cVSide
= GraphicsChars
.WINDOW_SIDE_DOUBLE
;
560 cTopLeft
= GraphicsChars
.WINDOW_LEFT_TOP
;
561 cTopRight
= GraphicsChars
.WINDOW_RIGHT_TOP
;
562 cBottomLeft
= GraphicsChars
.WINDOW_LEFT_BOTTOM
;
563 cBottomRight
= GraphicsChars
.WINDOW_RIGHT_BOTTOM
;
564 cHSide
= GraphicsChars
.WINDOW_TOP
;
565 cVSide
= GraphicsChars
.WINDOW_SIDE
;
568 throw new IllegalArgumentException("Invalid border type: "
572 // Place the corner characters
573 putCharXY(left
, top
, cTopLeft
, border
);
574 putCharXY(left
+ boxWidth
- 1, top
, cTopRight
, border
);
575 putCharXY(left
, top
+ boxHeight
- 1, cBottomLeft
, border
);
576 putCharXY(left
+ boxWidth
- 1, top
+ boxHeight
- 1, cBottomRight
,
579 // Draw the box lines
580 hLineXY(left
+ 1, top
, boxWidth
- 2, cHSide
, border
);
581 vLineXY(left
, top
+ 1, boxHeight
- 2, cVSide
, border
);
582 hLineXY(left
+ 1, top
+ boxHeight
- 1, boxWidth
- 2, cHSide
, border
);
583 vLineXY(left
+ boxWidth
- 1, top
+ 1, boxHeight
- 2, cVSide
, border
);
585 // Fill in the interior background
586 for (int i
= 1; i
< boxHeight
- 1; i
++) {
587 hLineXY(1 + left
, i
+ top
, boxWidth
- 2, ' ', background
);
592 drawBoxShadow(left
, top
, right
, bottom
);
599 * @param left left column of box. 0 is the left-most row.
600 * @param top top row of the box. 0 is the top-most row.
601 * @param right right column of box
602 * @param bottom bottom row of the box
604 public void drawBoxShadow(final int left
, final int top
,
605 final int right
, final int bottom
) {
609 int boxWidth
= right
- left
;
610 int boxHeight
= bottom
- top
;
611 CellAttributes shadowAttr
= new CellAttributes();
613 // Shadows do not honor clipping but they DO honor offset.
614 int oldClipRight
= clipRight
;
615 int oldClipBottom
= clipBottom
;
617 clipRight = boxWidth + 2;
618 clipBottom = boxHeight + 1;
623 for (int i
= 0; i
< boxHeight
; i
++) {
624 putAttrXY(boxLeft
+ boxWidth
, boxTop
+ 1 + i
, shadowAttr
);
625 putAttrXY(boxLeft
+ boxWidth
+ 1, boxTop
+ 1 + i
, shadowAttr
);
627 for (int i
= 0; i
< boxWidth
; i
++) {
628 putAttrXY(boxLeft
+ 2 + i
, boxTop
+ boxHeight
, shadowAttr
);
630 clipRight
= oldClipRight
;
631 clipBottom
= oldClipBottom
;
635 * Subclasses must provide an implementation to push the logical screen
636 * to the physical device.
638 abstract public void flushPhysical();
641 * Put the cursor at (x,y).
643 * @param visible if true, the cursor should be visible
644 * @param x column coordinate to put the cursor on
645 * @param y row coordinate to put the cursor on
647 public void putCursor(final boolean visible
, final int x
, final int y
) {
648 cursorVisible
= visible
;
656 public void hideCursor() {
657 cursorVisible
= false;