update terminals list
[fanfix.git] / src / jexer / backend / LogicalScreen.java
CommitLineData
daa4106c 1/*
df8de03f
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
df8de03f 5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
df8de03f 7 *
e16dda65
KL
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
df8de03f 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
df8de03f 17 *
e16dda65
KL
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
7b5261bc
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
df8de03f 28 */
42873e30 29package jexer.backend;
df8de03f
KL
30
31import jexer.bits.Cell;
32import jexer.bits.CellAttributes;
33import jexer.bits.GraphicsChars;
34
35/**
42873e30 36 * A logical screen composed of a 2D array of Cells.
df8de03f 37 */
42873e30 38public class LogicalScreen implements Screen {
df8de03f 39
d36057df
KL
40 // ------------------------------------------------------------------------
41 // Variables --------------------------------------------------------------
42 // ------------------------------------------------------------------------
43
df8de03f 44 /**
7b5261bc 45 * Width of the visible window.
df8de03f
KL
46 */
47 protected int width;
48
49 /**
7b5261bc 50 * Height of the visible window.
df8de03f
KL
51 */
52 protected int height;
53
54 /**
7b5261bc 55 * Drawing offset for x.
df8de03f 56 */
fca67db0 57 private int offsetX;
48e27807 58
d36057df
KL
59 /**
60 * Drawing offset for y.
61 */
62 private int offsetY;
63
64 /**
65 * Ignore anything drawn right of clipRight.
66 */
67 private int clipRight;
68
69 /**
70 * Ignore anything drawn below clipBottom.
71 */
72 private int clipBottom;
73
74 /**
75 * Ignore anything drawn left of clipLeft.
76 */
77 private int clipLeft;
78
79 /**
80 * Ignore anything drawn above clipTop.
81 */
82 private int clipTop;
83
84 /**
85 * The physical screen last sent out on flush().
86 */
87 protected Cell [][] physical;
88
89 /**
90 * The logical screen being rendered to.
91 */
92 protected Cell [][] logical;
93
94 /**
95 * Set if the user explicitly wants to redraw everything starting with a
96 * ECMATerminal.clearAll().
97 */
98 protected boolean reallyCleared;
99
100 /**
101 * If true, the cursor is visible and should be placed onscreen at
102 * (cursorX, cursorY) during a call to flushPhysical().
103 */
104 protected boolean cursorVisible;
105
106 /**
107 * Cursor X position if visible.
108 */
109 protected int cursorX;
110
111 /**
112 * Cursor Y position if visible.
113 */
114 protected int cursorY;
115
116 // ------------------------------------------------------------------------
117 // Constructors -----------------------------------------------------------
118 // ------------------------------------------------------------------------
119
120 /**
121 * Public constructor. Sets everything to not-bold, white-on-black.
122 */
123 protected LogicalScreen() {
124 offsetX = 0;
125 offsetY = 0;
126 width = 80;
127 height = 24;
128 logical = null;
129 physical = null;
130 reallocate(width, height);
131 }
132
133 // ------------------------------------------------------------------------
134 // Screen -----------------------------------------------------------------
135 // ------------------------------------------------------------------------
136
48e27807
KL
137 /**
138 * Set drawing offset for x.
139 *
140 * @param offsetX new drawing offset
141 */
142 public final void setOffsetX(final int offsetX) {
143 this.offsetX = offsetX;
144 }
df8de03f 145
48e27807
KL
146 /**
147 * Set drawing offset for y.
148 *
149 * @param offsetY new drawing offset
150 */
151 public final void setOffsetY(final int offsetY) {
152 this.offsetY = offsetY;
153 }
fca67db0 154
48e27807
KL
155 /**
156 * Get right drawing clipping boundary.
157 *
158 * @return drawing boundary
159 */
160 public final int getClipRight() {
161 return clipRight;
162 }
163
164 /**
165 * Set right drawing clipping boundary.
166 *
167 * @param clipRight new boundary
168 */
169 public final void setClipRight(final int clipRight) {
170 this.clipRight = clipRight;
171 }
df8de03f 172
48e27807
KL
173 /**
174 * Get bottom drawing clipping boundary.
175 *
176 * @return drawing boundary
177 */
178 public final int getClipBottom() {
179 return clipBottom;
180 }
181
182 /**
183 * Set bottom drawing clipping boundary.
184 *
185 * @param clipBottom new boundary
186 */
187 public final void setClipBottom(final int clipBottom) {
188 this.clipBottom = clipBottom;
189 }
df8de03f 190
48e27807
KL
191 /**
192 * Get left drawing clipping boundary.
193 *
194 * @return drawing boundary
195 */
196 public final int getClipLeft() {
197 return clipLeft;
198 }
199
200 /**
201 * Set left drawing clipping boundary.
202 *
203 * @param clipLeft new boundary
204 */
205 public final void setClipLeft(final int clipLeft) {
206 this.clipLeft = clipLeft;
207 }
df8de03f 208
48e27807
KL
209 /**
210 * Get top drawing clipping boundary.
211 *
212 * @return drawing boundary
213 */
214 public final int getClipTop() {
215 return clipTop;
216 }
217
218 /**
219 * Set top drawing clipping boundary.
220 *
221 * @param clipTop new boundary
222 */
223 public final void setClipTop(final int clipTop) {
224 this.clipTop = clipTop;
225 }
df8de03f 226
92554d64
KL
227 /**
228 * Get dirty flag.
229 *
230 * @return if true, the logical screen is not in sync with the physical
231 * screen
232 */
233 public final boolean isDirty() {
be72cb5c
KL
234 for (int x = 0; x < width; x++) {
235 for (int y = 0; y < height; y++) {
236 if (!logical[x][y].equals(physical[x][y])) {
237 return true;
238 }
239 if (logical[x][y].isBlink()) {
240 // Blinking screens are always dirty. There is
241 // opportunity for a Netscape blink tag joke here...
242 return true;
243 }
244 }
245 }
246
247 return false;
92554d64
KL
248 }
249
df8de03f
KL
250 /**
251 * Get the attributes at one location.
252 *
253 * @param x column coordinate. 0 is the left-most column.
254 * @param y row coordinate. 0 is the top-most row.
255 * @return attributes at (x, y)
256 */
fca67db0 257 public final CellAttributes getAttrXY(final int x, final int y) {
7b5261bc 258 CellAttributes attr = new CellAttributes();
30bd4abd
KL
259 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
260 attr.setTo(logical[x][y]);
261 }
7b5261bc 262 return attr;
df8de03f
KL
263 }
264
3e074355
KL
265 /**
266 * Get the cell at one location.
267 *
268 * @param x column coordinate. 0 is the left-most column.
269 * @param y row coordinate. 0 is the top-most row.
270 * @return the character + attributes
271 */
272 public Cell getCharXY(final int x, final int y) {
273 Cell cell = new Cell();
274 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
275 cell.setTo(logical[x][y]);
276 }
277 return cell;
278 }
279
df8de03f
KL
280 /**
281 * Set the attributes at one location.
282 *
283 * @param x column coordinate. 0 is the left-most column.
284 * @param y row coordinate. 0 is the top-most row.
285 * @param attr attributes to use (bold, foreColor, backColor)
df8de03f 286 */
fca67db0
KL
287 public final void putAttrXY(final int x, final int y,
288 final CellAttributes attr) {
289
7b5261bc 290 putAttrXY(x, y, attr, true);
df8de03f 291 }
7b5261bc 292
df8de03f
KL
293 /**
294 * Set the attributes at one location.
295 *
296 * @param x column coordinate. 0 is the left-most column.
297 * @param y row coordinate. 0 is the top-most row.
298 * @param attr attributes to use (bold, foreColor, backColor)
299 * @param clip if true, honor clipping/offset
300 */
a1879051
KL
301 public final void putAttrXY(final int x, final int y,
302 final CellAttributes attr, final boolean clip) {
7b5261bc
KL
303
304 int X = x;
305 int Y = y;
306
307 if (clip) {
308 if ((x < clipLeft)
309 || (x >= clipRight)
310 || (y < clipTop)
311 || (y >= clipBottom)
312 ) {
313 return;
314 }
315 X += offsetX;
316 Y += offsetY;
317 }
318
319 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
be72cb5c 320 logical[X][Y].setTo(attr);
24489803
KL
321
322 // If this happens to be the cursor position, make the position
323 // dirty.
324 if ((cursorX == X) && (cursorY == Y)) {
a69ed767
KL
325 physical[cursorX][cursorY].unset();
326 unsetImageRow(cursorY);
24489803 327 }
7b5261bc 328 }
df8de03f
KL
329 }
330
331 /**
332 * Fill the entire screen with one character with attributes.
333 *
334 * @param ch character to draw
335 * @param attr attributes to use (bold, foreColor, backColor)
336 */
fca67db0 337 public final void putAll(final char ch, final CellAttributes attr) {
87a17f3c 338
7b5261bc
KL
339 for (int x = 0; x < width; x++) {
340 for (int y = 0; y < height; y++) {
341 putCharXY(x, y, ch, attr);
342 }
343 }
df8de03f
KL
344 }
345
346 /**
347 * Render one character with attributes.
348 *
349 * @param x column coordinate. 0 is the left-most column.
350 * @param y row coordinate. 0 is the top-most row.
351 * @param ch character + attributes to draw
352 */
fca67db0 353 public final void putCharXY(final int x, final int y, final Cell ch) {
a69ed767
KL
354 if ((x < clipLeft)
355 || (x >= clipRight)
356 || (y < clipTop)
357 || (y >= clipBottom)
358 ) {
359 return;
360 }
361
362 int X = x + offsetX;
363 int Y = y + offsetY;
364
365 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
366
367 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
368
369 // Do not put control characters on the display
370 if (!ch.isImage()) {
371 assert (ch.getChar() >= 0x20);
372 assert (ch.getChar() != 0x7F);
373 }
374 logical[X][Y].setTo(ch);
375
376 // If this happens to be the cursor position, make the position
377 // dirty.
378 if ((cursorX == X) && (cursorY == Y)) {
379 physical[cursorX][cursorY].unset();
380 unsetImageRow(cursorY);
381 }
382 }
df8de03f
KL
383 }
384
385 /**
386 * Render one character with attributes.
387 *
388 * @param x column coordinate. 0 is the left-most column.
389 * @param y row coordinate. 0 is the top-most row.
390 * @param ch character to draw
391 * @param attr attributes to use (bold, foreColor, backColor)
392 */
fca67db0 393 public final void putCharXY(final int x, final int y, final char ch,
7b5261bc
KL
394 final CellAttributes attr) {
395
396 if ((x < clipLeft)
397 || (x >= clipRight)
398 || (y < clipTop)
399 || (y >= clipBottom)
400 ) {
401 return;
402 }
403
404 int X = x + offsetX;
405 int Y = y + offsetY;
406
407 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
408
409 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc
KL
410
411 // Do not put control characters on the display
412 assert (ch >= 0x20);
413 assert (ch != 0x7F);
414
be72cb5c 415 logical[X][Y].setTo(attr);
7b5261bc 416 logical[X][Y].setChar(ch);
24489803
KL
417
418 // If this happens to be the cursor position, make the position
419 // dirty.
420 if ((cursorX == X) && (cursorY == Y)) {
a69ed767
KL
421 physical[cursorX][cursorY].unset();
422 unsetImageRow(cursorY);
24489803 423 }
7b5261bc 424 }
df8de03f
KL
425 }
426
427 /**
428 * Render one character without changing the underlying attributes.
429 *
430 * @param x column coordinate. 0 is the left-most column.
431 * @param y row coordinate. 0 is the top-most row.
432 * @param ch character to draw
433 */
fca67db0 434 public final void putCharXY(final int x, final int y, final char ch) {
87a17f3c 435
7b5261bc
KL
436 if ((x < clipLeft)
437 || (x >= clipRight)
438 || (y < clipTop)
439 || (y >= clipBottom)
440 ) {
441 return;
442 }
df8de03f 443
7b5261bc
KL
444 int X = x + offsetX;
445 int Y = y + offsetY;
df8de03f 446
7b5261bc 447 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
df8de03f 448
7b5261bc 449 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc 450 logical[X][Y].setChar(ch);
24489803
KL
451
452 // If this happens to be the cursor position, make the position
453 // dirty.
454 if ((cursorX == X) && (cursorY == Y)) {
a69ed767
KL
455 physical[cursorX][cursorY].unset();
456 unsetImageRow(cursorY);
24489803 457 }
7b5261bc 458 }
df8de03f
KL
459 }
460
461 /**
462 * Render a string. Does not wrap if the string exceeds the line.
463 *
464 * @param x column coordinate. 0 is the left-most column.
465 * @param y row coordinate. 0 is the top-most row.
466 * @param str string to draw
467 * @param attr attributes to use (bold, foreColor, backColor)
468 */
0d47c546 469 public final void putStringXY(final int x, final int y, final String str,
7b5261bc
KL
470 final CellAttributes attr) {
471
472 int i = x;
473 for (int j = 0; j < str.length(); j++) {
474 char ch = str.charAt(j);
475 putCharXY(i, y, ch, attr);
476 i++;
477 if (i == width) {
478 break;
479 }
480 }
df8de03f
KL
481 }
482
483 /**
484 * Render a string without changing the underlying attribute. Does not
485 * wrap if the string exceeds the line.
486 *
487 * @param x column coordinate. 0 is the left-most column.
488 * @param y row coordinate. 0 is the top-most row.
489 * @param str string to draw
490 */
0d47c546 491 public final void putStringXY(final int x, final int y, final String str) {
87a17f3c 492
7b5261bc
KL
493 int i = x;
494 for (int j = 0; j < str.length(); j++) {
495 char ch = str.charAt(j);
496 putCharXY(i, y, ch);
497 i++;
498 if (i == width) {
499 break;
500 }
501 }
df8de03f
KL
502 }
503
504 /**
7b5261bc 505 * Draw a vertical line from (x, y) to (x, y + n).
df8de03f
KL
506 *
507 * @param x column coordinate. 0 is the left-most column.
508 * @param y row coordinate. 0 is the top-most row.
509 * @param n number of characters to draw
510 * @param ch character to draw
511 * @param attr attributes to use (bold, foreColor, backColor)
512 */
fca67db0
KL
513 public final void vLineXY(final int x, final int y, final int n,
514 final char ch, final CellAttributes attr) {
7b5261bc
KL
515
516 for (int i = y; i < y + n; i++) {
517 putCharXY(x, i, ch, attr);
518 }
df8de03f
KL
519 }
520
521 /**
7b5261bc 522 * Draw a horizontal line from (x, y) to (x + n, y).
df8de03f
KL
523 *
524 * @param x column coordinate. 0 is the left-most column.
525 * @param y row coordinate. 0 is the top-most row.
526 * @param n number of characters to draw
527 * @param ch character to draw
528 * @param attr attributes to use (bold, foreColor, backColor)
529 */
fca67db0
KL
530 public final void hLineXY(final int x, final int y, final int n,
531 final char ch, final CellAttributes attr) {
7b5261bc
KL
532
533 for (int i = x; i < x + n; i++) {
534 putCharXY(i, y, ch, attr);
535 }
df8de03f
KL
536 }
537
df8de03f
KL
538 /**
539 * Change the width. Everything on-screen will be destroyed and must be
540 * redrawn.
7b5261bc 541 *
df8de03f
KL
542 * @param width new screen width
543 */
87a17f3c 544 public final synchronized void setWidth(final int width) {
7b5261bc 545 reallocate(width, this.height);
df8de03f
KL
546 }
547
548 /**
549 * Change the height. Everything on-screen will be destroyed and must be
550 * redrawn.
551 *
552 * @param height new screen height
553 */
87a17f3c 554 public final synchronized void setHeight(final int height) {
7b5261bc 555 reallocate(this.width, height);
df8de03f
KL
556 }
557
558 /**
559 * Change the width and height. Everything on-screen will be destroyed
560 * and must be redrawn.
561 *
562 * @param width new screen width
563 * @param height new screen height
564 */
fca67db0 565 public final void setDimensions(final int width, final int height) {
7b5261bc 566 reallocate(width, height);
9696a8f6
KL
567 resizeToScreen();
568 }
569
570 /**
571 * Resize the physical screen to match the logical screen dimensions.
572 */
573 public void resizeToScreen() {
574 // Subclasses are expected to override this.
df8de03f
KL
575 }
576
577 /**
578 * Get the height.
579 *
580 * @return current screen height
581 */
87a17f3c 582 public final synchronized int getHeight() {
7b5261bc 583 return this.height;
df8de03f
KL
584 }
585
586 /**
587 * Get the width.
588 *
589 * @return current screen width
590 */
87a17f3c 591 public final synchronized int getWidth() {
7b5261bc 592 return this.width;
df8de03f
KL
593 }
594
df8de03f
KL
595 /**
596 * Reset screen to not-bold, white-on-black. Also flushes the offset and
597 * clip variables.
598 */
87a17f3c 599 public final synchronized void reset() {
7b5261bc
KL
600 for (int row = 0; row < height; row++) {
601 for (int col = 0; col < width; col++) {
602 logical[col][row].reset();
603 }
604 }
605 resetClipping();
df8de03f
KL
606 }
607
608 /**
609 * Flush the offset and clip variables.
610 */
fca67db0 611 public final void resetClipping() {
7b5261bc
KL
612 offsetX = 0;
613 offsetY = 0;
614 clipLeft = 0;
615 clipTop = 0;
616 clipRight = width;
617 clipBottom = height;
df8de03f
KL
618 }
619
620 /**
bd8d51fa 621 * Clear the logical screen.
df8de03f 622 */
fca67db0 623 public final void clear() {
7b5261bc 624 reset();
df8de03f
KL
625 }
626
627 /**
628 * Draw a box with a border and empty background.
629 *
a69ed767 630 * @param left left column of box. 0 is the left-most column.
df8de03f
KL
631 * @param top top row of the box. 0 is the top-most row.
632 * @param right right column of box
633 * @param bottom bottom row of the box
7b5261bc 634 * @param border attributes to use for the border
df8de03f
KL
635 * @param background attributes to use for the background
636 */
fca67db0 637 public final void drawBox(final int left, final int top,
7b5261bc
KL
638 final int right, final int bottom,
639 final CellAttributes border, final CellAttributes background) {
640
641 drawBox(left, top, right, bottom, border, background, 1, false);
df8de03f
KL
642 }
643
644 /**
645 * Draw a box with a border and empty background.
646 *
a69ed767 647 * @param left left column of box. 0 is the left-most column.
df8de03f
KL
648 * @param top top row of the box. 0 is the top-most row.
649 * @param right right column of box
650 * @param bottom bottom row of the box
7b5261bc 651 * @param border attributes to use for the border
df8de03f 652 * @param background attributes to use for the background
7b5261bc
KL
653 * @param borderType if 1, draw a single-line border; if 2, draw a
654 * double-line border; if 3, draw double-line top/bottom edges and
655 * single-line left/right edges (like Qmodem)
df8de03f
KL
656 * @param shadow if true, draw a "shadow" on the box
657 */
fca67db0 658 public final void drawBox(final int left, final int top,
7b5261bc
KL
659 final int right, final int bottom,
660 final CellAttributes border, final CellAttributes background,
661 final int borderType, final boolean shadow) {
662
7b5261bc
KL
663 int boxWidth = right - left;
664 int boxHeight = bottom - top;
665
666 char cTopLeft;
667 char cTopRight;
668 char cBottomLeft;
669 char cBottomRight;
670 char cHSide;
671 char cVSide;
672
673 switch (borderType) {
674 case 1:
675 cTopLeft = GraphicsChars.ULCORNER;
676 cTopRight = GraphicsChars.URCORNER;
677 cBottomLeft = GraphicsChars.LLCORNER;
678 cBottomRight = GraphicsChars.LRCORNER;
679 cHSide = GraphicsChars.SINGLE_BAR;
680 cVSide = GraphicsChars.WINDOW_SIDE;
681 break;
682
683 case 2:
684 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP_DOUBLE;
685 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP_DOUBLE;
686 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM_DOUBLE;
687 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM_DOUBLE;
688 cHSide = GraphicsChars.DOUBLE_BAR;
689 cVSide = GraphicsChars.WINDOW_SIDE_DOUBLE;
690 break;
691
692 case 3:
693 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP;
694 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP;
695 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM;
696 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM;
697 cHSide = GraphicsChars.WINDOW_TOP;
698 cVSide = GraphicsChars.WINDOW_SIDE;
699 break;
700 default:
701 throw new IllegalArgumentException("Invalid border type: "
702 + borderType);
703 }
704
705 // Place the corner characters
706 putCharXY(left, top, cTopLeft, border);
707 putCharXY(left + boxWidth - 1, top, cTopRight, border);
708 putCharXY(left, top + boxHeight - 1, cBottomLeft, border);
709 putCharXY(left + boxWidth - 1, top + boxHeight - 1, cBottomRight,
710 border);
711
712 // Draw the box lines
713 hLineXY(left + 1, top, boxWidth - 2, cHSide, border);
714 vLineXY(left, top + 1, boxHeight - 2, cVSide, border);
715 hLineXY(left + 1, top + boxHeight - 1, boxWidth - 2, cHSide, border);
716 vLineXY(left + boxWidth - 1, top + 1, boxHeight - 2, cVSide, border);
717
718 // Fill in the interior background
719 for (int i = 1; i < boxHeight - 1; i++) {
720 hLineXY(1 + left, i + top, boxWidth - 2, ' ', background);
721 }
722
723 if (shadow) {
724 // Draw a shadow
725 drawBoxShadow(left, top, right, bottom);
726 }
df8de03f
KL
727 }
728
729 /**
fca67db0 730 * Draw a box shadow.
df8de03f 731 *
a69ed767 732 * @param left left column of box. 0 is the left-most column.
df8de03f
KL
733 * @param top top row of the box. 0 is the top-most row.
734 * @param right right column of box
735 * @param bottom bottom row of the box
736 */
fca67db0 737 public final void drawBoxShadow(final int left, final int top,
7b5261bc
KL
738 final int right, final int bottom) {
739
740 int boxTop = top;
741 int boxLeft = left;
742 int boxWidth = right - left;
743 int boxHeight = bottom - top;
744 CellAttributes shadowAttr = new CellAttributes();
745
746 // Shadows do not honor clipping but they DO honor offset.
747 int oldClipRight = clipRight;
748 int oldClipBottom = clipBottom;
a69ed767
KL
749 // When offsetX or offsetY go negative, we need to increase the clip
750 // bounds.
751 clipRight = width - offsetX;
752 clipBottom = height - offsetY;
7b5261bc
KL
753
754 for (int i = 0; i < boxHeight; i++) {
755 putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr);
756 putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr);
757 }
758 for (int i = 0; i < boxWidth; i++) {
759 putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr);
760 }
761 clipRight = oldClipRight;
762 clipBottom = oldClipBottom;
df8de03f
KL
763 }
764
765 /**
42873e30 766 * Default implementation does nothing.
df8de03f 767 */
42873e30 768 public void flushPhysical() {}
df8de03f
KL
769
770 /**
771 * Put the cursor at (x,y).
772 *
773 * @param visible if true, the cursor should be visible
774 * @param x column coordinate to put the cursor on
775 * @param y row coordinate to put the cursor on
776 */
30bd4abd 777 public void putCursor(final boolean visible, final int x, final int y) {
be72cb5c
KL
778 if ((cursorY >= 0)
779 && (cursorX >= 0)
780 && (cursorY <= height - 1)
781 && (cursorX <= width - 1)
782 ) {
783 // Make the current cursor position dirty
a69ed767
KL
784 physical[cursorX][cursorY].unset();
785 unsetImageRow(cursorY);
be72cb5c 786 }
fca67db0 787
7b5261bc
KL
788 cursorVisible = visible;
789 cursorX = x;
790 cursorY = y;
df8de03f
KL
791 }
792
793 /**
fca67db0 794 * Hide the cursor.
df8de03f 795 */
fca67db0 796 public final void hideCursor() {
7b5261bc 797 cursorVisible = false;
df8de03f 798 }
42873e30 799
3e074355
KL
800 /**
801 * Get the cursor visibility.
802 *
803 * @return true if the cursor is visible
804 */
805 public boolean isCursorVisible() {
806 return cursorVisible;
807 }
808
809 /**
810 * Get the cursor X position.
811 *
812 * @return the cursor x column position
813 */
814 public int getCursorX() {
815 return cursorX;
816 }
817
818 /**
819 * Get the cursor Y position.
820 *
821 * @return the cursor y row position
822 */
823 public int getCursorY() {
824 return cursorY;
825 }
826
42873e30
KL
827 /**
828 * Set the window title. Default implementation does nothing.
829 *
830 * @param title the new title
831 */
832 public void setTitle(final String title) {}
833
d36057df
KL
834 // ------------------------------------------------------------------------
835 // LogicalScreen ----------------------------------------------------------
836 // ------------------------------------------------------------------------
837
838 /**
839 * Reallocate screen buffers.
840 *
841 * @param width new width
842 * @param height new height
843 */
844 private synchronized void reallocate(final int width, final int height) {
845 if (logical != null) {
846 for (int row = 0; row < this.height; row++) {
847 for (int col = 0; col < this.width; col++) {
848 logical[col][row] = null;
849 }
850 }
851 logical = null;
852 }
853 logical = new Cell[width][height];
854 if (physical != null) {
855 for (int row = 0; row < this.height; row++) {
856 for (int col = 0; col < this.width; col++) {
857 physical[col][row] = null;
858 }
859 }
860 physical = null;
861 }
862 physical = new Cell[width][height];
863
864 for (int row = 0; row < height; row++) {
865 for (int col = 0; col < width; col++) {
866 physical[col][row] = new Cell();
867 logical[col][row] = new Cell();
868 }
869 }
870
871 this.width = width;
872 this.height = height;
873
874 clipLeft = 0;
875 clipTop = 0;
876 clipRight = width;
877 clipBottom = height;
878
879 reallyCleared = true;
880 }
881
882 /**
883 * Clear the physical screen.
884 */
885 public final void clearPhysical() {
886 for (int row = 0; row < height; row++) {
887 for (int col = 0; col < width; col++) {
a69ed767
KL
888 physical[col][row].unset();
889 }
890 }
891 }
892
893 /**
894 * Unset every image cell on one row of the physical screen, forcing
895 * images on that row to be redrawn.
896 *
897 * @param y row coordinate. 0 is the top-most row.
898 */
899 public final void unsetImageRow(final int y) {
9696a8f6
KL
900 if ((y < 0) || (y >= height)) {
901 return;
902 }
a69ed767
KL
903 for (int x = 0; x < width; x++) {
904 if (logical[x][y].isImage()) {
905 physical[x][y].unset();
d36057df
KL
906 }
907 }
908 }
909
df8de03f 910}