#35 CJK font wip
[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
03ae544a
KL
137 /**
138 * Get the width of a character cell in pixels.
139 *
140 * @return the width in pixels of a character cell
141 */
142 public int getTextWidth() {
143 // Default width is 16 pixels.
144 return 16;
145 }
146
147 /**
148 * Get the height of a character cell in pixels.
149 *
150 * @return the height in pixels of a character cell
151 */
152 public int getTextHeight() {
153 // Default height is 20 pixels.
154 return 20;
155 }
156
48e27807
KL
157 /**
158 * Set drawing offset for x.
159 *
160 * @param offsetX new drawing offset
161 */
162 public final void setOffsetX(final int offsetX) {
163 this.offsetX = offsetX;
164 }
df8de03f 165
48e27807
KL
166 /**
167 * Set drawing offset for y.
168 *
169 * @param offsetY new drawing offset
170 */
171 public final void setOffsetY(final int offsetY) {
172 this.offsetY = offsetY;
173 }
fca67db0 174
48e27807
KL
175 /**
176 * Get right drawing clipping boundary.
177 *
178 * @return drawing boundary
179 */
180 public final int getClipRight() {
181 return clipRight;
182 }
183
184 /**
185 * Set right drawing clipping boundary.
186 *
187 * @param clipRight new boundary
188 */
189 public final void setClipRight(final int clipRight) {
190 this.clipRight = clipRight;
191 }
df8de03f 192
48e27807
KL
193 /**
194 * Get bottom drawing clipping boundary.
195 *
196 * @return drawing boundary
197 */
198 public final int getClipBottom() {
199 return clipBottom;
200 }
201
202 /**
203 * Set bottom drawing clipping boundary.
204 *
205 * @param clipBottom new boundary
206 */
207 public final void setClipBottom(final int clipBottom) {
208 this.clipBottom = clipBottom;
209 }
df8de03f 210
48e27807
KL
211 /**
212 * Get left drawing clipping boundary.
213 *
214 * @return drawing boundary
215 */
216 public final int getClipLeft() {
217 return clipLeft;
218 }
219
220 /**
221 * Set left drawing clipping boundary.
222 *
223 * @param clipLeft new boundary
224 */
225 public final void setClipLeft(final int clipLeft) {
226 this.clipLeft = clipLeft;
227 }
df8de03f 228
48e27807
KL
229 /**
230 * Get top drawing clipping boundary.
231 *
232 * @return drawing boundary
233 */
234 public final int getClipTop() {
235 return clipTop;
236 }
237
238 /**
239 * Set top drawing clipping boundary.
240 *
241 * @param clipTop new boundary
242 */
243 public final void setClipTop(final int clipTop) {
244 this.clipTop = clipTop;
245 }
df8de03f 246
92554d64
KL
247 /**
248 * Get dirty flag.
249 *
250 * @return if true, the logical screen is not in sync with the physical
251 * screen
252 */
253 public final boolean isDirty() {
be72cb5c
KL
254 for (int x = 0; x < width; x++) {
255 for (int y = 0; y < height; y++) {
256 if (!logical[x][y].equals(physical[x][y])) {
257 return true;
258 }
259 if (logical[x][y].isBlink()) {
260 // Blinking screens are always dirty. There is
261 // opportunity for a Netscape blink tag joke here...
262 return true;
263 }
264 }
265 }
266
267 return false;
92554d64
KL
268 }
269
df8de03f
KL
270 /**
271 * Get the attributes at one location.
272 *
273 * @param x column coordinate. 0 is the left-most column.
274 * @param y row coordinate. 0 is the top-most row.
275 * @return attributes at (x, y)
276 */
fca67db0 277 public final CellAttributes getAttrXY(final int x, final int y) {
7b5261bc 278 CellAttributes attr = new CellAttributes();
30bd4abd
KL
279 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
280 attr.setTo(logical[x][y]);
281 }
7b5261bc 282 return attr;
df8de03f
KL
283 }
284
3e074355
KL
285 /**
286 * Get the cell at one location.
287 *
288 * @param x column coordinate. 0 is the left-most column.
289 * @param y row coordinate. 0 is the top-most row.
290 * @return the character + attributes
291 */
292 public Cell getCharXY(final int x, final int y) {
293 Cell cell = new Cell();
294 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
295 cell.setTo(logical[x][y]);
296 }
297 return cell;
298 }
299
df8de03f
KL
300 /**
301 * Set the attributes at one location.
302 *
303 * @param x column coordinate. 0 is the left-most column.
304 * @param y row coordinate. 0 is the top-most row.
305 * @param attr attributes to use (bold, foreColor, backColor)
df8de03f 306 */
fca67db0
KL
307 public final void putAttrXY(final int x, final int y,
308 final CellAttributes attr) {
309
7b5261bc 310 putAttrXY(x, y, attr, true);
df8de03f 311 }
7b5261bc 312
df8de03f
KL
313 /**
314 * Set the attributes at one location.
315 *
316 * @param x column coordinate. 0 is the left-most column.
317 * @param y row coordinate. 0 is the top-most row.
318 * @param attr attributes to use (bold, foreColor, backColor)
319 * @param clip if true, honor clipping/offset
320 */
a1879051
KL
321 public final void putAttrXY(final int x, final int y,
322 final CellAttributes attr, final boolean clip) {
7b5261bc
KL
323
324 int X = x;
325 int Y = y;
326
327 if (clip) {
328 if ((x < clipLeft)
329 || (x >= clipRight)
330 || (y < clipTop)
331 || (y >= clipBottom)
332 ) {
333 return;
334 }
335 X += offsetX;
336 Y += offsetY;
337 }
338
339 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
be72cb5c 340 logical[X][Y].setTo(attr);
24489803
KL
341
342 // If this happens to be the cursor position, make the position
343 // dirty.
344 if ((cursorX == X) && (cursorY == Y)) {
a69ed767
KL
345 physical[cursorX][cursorY].unset();
346 unsetImageRow(cursorY);
24489803 347 }
7b5261bc 348 }
df8de03f
KL
349 }
350
351 /**
352 * Fill the entire screen with one character with attributes.
353 *
354 * @param ch character to draw
355 * @param attr attributes to use (bold, foreColor, backColor)
356 */
fca67db0 357 public final void putAll(final char ch, final CellAttributes attr) {
87a17f3c 358
7b5261bc
KL
359 for (int x = 0; x < width; x++) {
360 for (int y = 0; y < height; y++) {
361 putCharXY(x, y, ch, attr);
362 }
363 }
df8de03f
KL
364 }
365
366 /**
367 * Render one character with attributes.
368 *
369 * @param x column coordinate. 0 is the left-most column.
370 * @param y row coordinate. 0 is the top-most row.
371 * @param ch character + attributes to draw
372 */
fca67db0 373 public final void putCharXY(final int x, final int y, final Cell ch) {
a69ed767
KL
374 if ((x < clipLeft)
375 || (x >= clipRight)
376 || (y < clipTop)
377 || (y >= clipBottom)
378 ) {
379 return;
380 }
381
382 int X = x + offsetX;
383 int Y = y + offsetY;
384
385 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
386
387 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
388
389 // Do not put control characters on the display
390 if (!ch.isImage()) {
391 assert (ch.getChar() >= 0x20);
392 assert (ch.getChar() != 0x7F);
393 }
394 logical[X][Y].setTo(ch);
395
396 // If this happens to be the cursor position, make the position
397 // dirty.
398 if ((cursorX == X) && (cursorY == Y)) {
399 physical[cursorX][cursorY].unset();
400 unsetImageRow(cursorY);
401 }
402 }
df8de03f
KL
403 }
404
405 /**
406 * Render one character with attributes.
407 *
408 * @param x column coordinate. 0 is the left-most column.
409 * @param y row coordinate. 0 is the top-most row.
410 * @param ch character to draw
411 * @param attr attributes to use (bold, foreColor, backColor)
412 */
fca67db0 413 public final void putCharXY(final int x, final int y, final char ch,
7b5261bc
KL
414 final CellAttributes attr) {
415
416 if ((x < clipLeft)
417 || (x >= clipRight)
418 || (y < clipTop)
419 || (y >= clipBottom)
420 ) {
421 return;
422 }
423
424 int X = x + offsetX;
425 int Y = y + offsetY;
426
427 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
428
429 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc
KL
430
431 // Do not put control characters on the display
432 assert (ch >= 0x20);
433 assert (ch != 0x7F);
434
be72cb5c 435 logical[X][Y].setTo(attr);
7b5261bc 436 logical[X][Y].setChar(ch);
24489803
KL
437
438 // If this happens to be the cursor position, make the position
439 // dirty.
440 if ((cursorX == X) && (cursorY == Y)) {
a69ed767
KL
441 physical[cursorX][cursorY].unset();
442 unsetImageRow(cursorY);
24489803 443 }
7b5261bc 444 }
df8de03f
KL
445 }
446
447 /**
448 * Render one character without changing the underlying attributes.
449 *
450 * @param x column coordinate. 0 is the left-most column.
451 * @param y row coordinate. 0 is the top-most row.
452 * @param ch character to draw
453 */
fca67db0 454 public final void putCharXY(final int x, final int y, final char ch) {
87a17f3c 455
7b5261bc
KL
456 if ((x < clipLeft)
457 || (x >= clipRight)
458 || (y < clipTop)
459 || (y >= clipBottom)
460 ) {
461 return;
462 }
df8de03f 463
7b5261bc
KL
464 int X = x + offsetX;
465 int Y = y + offsetY;
df8de03f 466
7b5261bc 467 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
df8de03f 468
7b5261bc 469 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc 470 logical[X][Y].setChar(ch);
24489803
KL
471
472 // If this happens to be the cursor position, make the position
473 // dirty.
474 if ((cursorX == X) && (cursorY == Y)) {
a69ed767
KL
475 physical[cursorX][cursorY].unset();
476 unsetImageRow(cursorY);
24489803 477 }
7b5261bc 478 }
df8de03f
KL
479 }
480
481 /**
482 * Render a string. Does not wrap if the string exceeds the line.
483 *
484 * @param x column coordinate. 0 is the left-most column.
485 * @param y row coordinate. 0 is the top-most row.
486 * @param str string to draw
487 * @param attr attributes to use (bold, foreColor, backColor)
488 */
0d47c546 489 public final void putStringXY(final int x, final int y, final String str,
7b5261bc
KL
490 final CellAttributes attr) {
491
492 int i = x;
493 for (int j = 0; j < str.length(); j++) {
494 char ch = str.charAt(j);
495 putCharXY(i, y, ch, attr);
496 i++;
497 if (i == width) {
498 break;
499 }
500 }
df8de03f
KL
501 }
502
503 /**
504 * Render a string without changing the underlying attribute. Does not
505 * wrap if the string exceeds the line.
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 str string to draw
510 */
0d47c546 511 public final void putStringXY(final int x, final int y, final String str) {
87a17f3c 512
7b5261bc
KL
513 int i = x;
514 for (int j = 0; j < str.length(); j++) {
515 char ch = str.charAt(j);
516 putCharXY(i, y, ch);
517 i++;
518 if (i == width) {
519 break;
520 }
521 }
df8de03f
KL
522 }
523
524 /**
7b5261bc 525 * Draw a vertical line from (x, y) to (x, y + n).
df8de03f
KL
526 *
527 * @param x column coordinate. 0 is the left-most column.
528 * @param y row coordinate. 0 is the top-most row.
529 * @param n number of characters to draw
530 * @param ch character to draw
531 * @param attr attributes to use (bold, foreColor, backColor)
532 */
fca67db0
KL
533 public final void vLineXY(final int x, final int y, final int n,
534 final char ch, final CellAttributes attr) {
7b5261bc
KL
535
536 for (int i = y; i < y + n; i++) {
537 putCharXY(x, i, ch, attr);
538 }
df8de03f
KL
539 }
540
541 /**
7b5261bc 542 * Draw a horizontal line from (x, y) to (x + n, y).
df8de03f
KL
543 *
544 * @param x column coordinate. 0 is the left-most column.
545 * @param y row coordinate. 0 is the top-most row.
546 * @param n number of characters to draw
547 * @param ch character to draw
548 * @param attr attributes to use (bold, foreColor, backColor)
549 */
fca67db0
KL
550 public final void hLineXY(final int x, final int y, final int n,
551 final char ch, final CellAttributes attr) {
7b5261bc
KL
552
553 for (int i = x; i < x + n; i++) {
554 putCharXY(i, y, ch, attr);
555 }
df8de03f
KL
556 }
557
df8de03f
KL
558 /**
559 * Change the width. Everything on-screen will be destroyed and must be
560 * redrawn.
7b5261bc 561 *
df8de03f
KL
562 * @param width new screen width
563 */
87a17f3c 564 public final synchronized void setWidth(final int width) {
7b5261bc 565 reallocate(width, this.height);
df8de03f
KL
566 }
567
568 /**
569 * Change the height. Everything on-screen will be destroyed and must be
570 * redrawn.
571 *
572 * @param height new screen height
573 */
87a17f3c 574 public final synchronized void setHeight(final int height) {
7b5261bc 575 reallocate(this.width, height);
df8de03f
KL
576 }
577
578 /**
579 * Change the width and height. Everything on-screen will be destroyed
580 * and must be redrawn.
581 *
582 * @param width new screen width
583 * @param height new screen height
584 */
fca67db0 585 public final void setDimensions(final int width, final int height) {
7b5261bc 586 reallocate(width, height);
9696a8f6
KL
587 resizeToScreen();
588 }
589
590 /**
591 * Resize the physical screen to match the logical screen dimensions.
592 */
593 public void resizeToScreen() {
594 // Subclasses are expected to override this.
df8de03f
KL
595 }
596
597 /**
598 * Get the height.
599 *
600 * @return current screen height
601 */
87a17f3c 602 public final synchronized int getHeight() {
7b5261bc 603 return this.height;
df8de03f
KL
604 }
605
606 /**
607 * Get the width.
608 *
609 * @return current screen width
610 */
87a17f3c 611 public final synchronized int getWidth() {
7b5261bc 612 return this.width;
df8de03f
KL
613 }
614
df8de03f
KL
615 /**
616 * Reset screen to not-bold, white-on-black. Also flushes the offset and
617 * clip variables.
618 */
87a17f3c 619 public final synchronized void reset() {
7b5261bc
KL
620 for (int row = 0; row < height; row++) {
621 for (int col = 0; col < width; col++) {
622 logical[col][row].reset();
623 }
624 }
625 resetClipping();
df8de03f
KL
626 }
627
628 /**
629 * Flush the offset and clip variables.
630 */
fca67db0 631 public final void resetClipping() {
7b5261bc
KL
632 offsetX = 0;
633 offsetY = 0;
634 clipLeft = 0;
635 clipTop = 0;
636 clipRight = width;
637 clipBottom = height;
df8de03f
KL
638 }
639
640 /**
bd8d51fa 641 * Clear the logical screen.
df8de03f 642 */
fca67db0 643 public final void clear() {
7b5261bc 644 reset();
df8de03f
KL
645 }
646
647 /**
648 * Draw a box with a border and empty background.
649 *
a69ed767 650 * @param left left column of box. 0 is the left-most column.
df8de03f
KL
651 * @param top top row of the box. 0 is the top-most row.
652 * @param right right column of box
653 * @param bottom bottom row of the box
7b5261bc 654 * @param border attributes to use for the border
df8de03f
KL
655 * @param background attributes to use for the background
656 */
fca67db0 657 public final void drawBox(final int left, final int top,
7b5261bc
KL
658 final int right, final int bottom,
659 final CellAttributes border, final CellAttributes background) {
660
661 drawBox(left, top, right, bottom, border, background, 1, false);
df8de03f
KL
662 }
663
664 /**
665 * Draw a box with a border and empty background.
666 *
a69ed767 667 * @param left left column of box. 0 is the left-most column.
df8de03f
KL
668 * @param top top row of the box. 0 is the top-most row.
669 * @param right right column of box
670 * @param bottom bottom row of the box
7b5261bc 671 * @param border attributes to use for the border
df8de03f 672 * @param background attributes to use for the background
7b5261bc
KL
673 * @param borderType if 1, draw a single-line border; if 2, draw a
674 * double-line border; if 3, draw double-line top/bottom edges and
675 * single-line left/right edges (like Qmodem)
df8de03f
KL
676 * @param shadow if true, draw a "shadow" on the box
677 */
fca67db0 678 public final void drawBox(final int left, final int top,
7b5261bc
KL
679 final int right, final int bottom,
680 final CellAttributes border, final CellAttributes background,
681 final int borderType, final boolean shadow) {
682
7b5261bc
KL
683 int boxWidth = right - left;
684 int boxHeight = bottom - top;
685
686 char cTopLeft;
687 char cTopRight;
688 char cBottomLeft;
689 char cBottomRight;
690 char cHSide;
691 char cVSide;
692
693 switch (borderType) {
694 case 1:
695 cTopLeft = GraphicsChars.ULCORNER;
696 cTopRight = GraphicsChars.URCORNER;
697 cBottomLeft = GraphicsChars.LLCORNER;
698 cBottomRight = GraphicsChars.LRCORNER;
699 cHSide = GraphicsChars.SINGLE_BAR;
700 cVSide = GraphicsChars.WINDOW_SIDE;
701 break;
702
703 case 2:
704 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP_DOUBLE;
705 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP_DOUBLE;
706 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM_DOUBLE;
707 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM_DOUBLE;
708 cHSide = GraphicsChars.DOUBLE_BAR;
709 cVSide = GraphicsChars.WINDOW_SIDE_DOUBLE;
710 break;
711
712 case 3:
713 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP;
714 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP;
715 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM;
716 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM;
717 cHSide = GraphicsChars.WINDOW_TOP;
718 cVSide = GraphicsChars.WINDOW_SIDE;
719 break;
720 default:
721 throw new IllegalArgumentException("Invalid border type: "
722 + borderType);
723 }
724
725 // Place the corner characters
726 putCharXY(left, top, cTopLeft, border);
727 putCharXY(left + boxWidth - 1, top, cTopRight, border);
728 putCharXY(left, top + boxHeight - 1, cBottomLeft, border);
729 putCharXY(left + boxWidth - 1, top + boxHeight - 1, cBottomRight,
730 border);
731
732 // Draw the box lines
733 hLineXY(left + 1, top, boxWidth - 2, cHSide, border);
734 vLineXY(left, top + 1, boxHeight - 2, cVSide, border);
735 hLineXY(left + 1, top + boxHeight - 1, boxWidth - 2, cHSide, border);
736 vLineXY(left + boxWidth - 1, top + 1, boxHeight - 2, cVSide, border);
737
738 // Fill in the interior background
739 for (int i = 1; i < boxHeight - 1; i++) {
740 hLineXY(1 + left, i + top, boxWidth - 2, ' ', background);
741 }
742
743 if (shadow) {
744 // Draw a shadow
745 drawBoxShadow(left, top, right, bottom);
746 }
df8de03f
KL
747 }
748
749 /**
fca67db0 750 * Draw a box shadow.
df8de03f 751 *
a69ed767 752 * @param left left column of box. 0 is the left-most column.
df8de03f
KL
753 * @param top top row of the box. 0 is the top-most row.
754 * @param right right column of box
755 * @param bottom bottom row of the box
756 */
fca67db0 757 public final void drawBoxShadow(final int left, final int top,
7b5261bc
KL
758 final int right, final int bottom) {
759
760 int boxTop = top;
761 int boxLeft = left;
762 int boxWidth = right - left;
763 int boxHeight = bottom - top;
764 CellAttributes shadowAttr = new CellAttributes();
765
766 // Shadows do not honor clipping but they DO honor offset.
767 int oldClipRight = clipRight;
768 int oldClipBottom = clipBottom;
a69ed767
KL
769 // When offsetX or offsetY go negative, we need to increase the clip
770 // bounds.
771 clipRight = width - offsetX;
772 clipBottom = height - offsetY;
7b5261bc
KL
773
774 for (int i = 0; i < boxHeight; i++) {
775 putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr);
776 putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr);
777 }
778 for (int i = 0; i < boxWidth; i++) {
779 putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr);
780 }
781 clipRight = oldClipRight;
782 clipBottom = oldClipBottom;
df8de03f
KL
783 }
784
785 /**
42873e30 786 * Default implementation does nothing.
df8de03f 787 */
42873e30 788 public void flushPhysical() {}
df8de03f
KL
789
790 /**
791 * Put the cursor at (x,y).
792 *
793 * @param visible if true, the cursor should be visible
794 * @param x column coordinate to put the cursor on
795 * @param y row coordinate to put the cursor on
796 */
30bd4abd 797 public void putCursor(final boolean visible, final int x, final int y) {
be72cb5c
KL
798 if ((cursorY >= 0)
799 && (cursorX >= 0)
800 && (cursorY <= height - 1)
801 && (cursorX <= width - 1)
802 ) {
803 // Make the current cursor position dirty
a69ed767
KL
804 physical[cursorX][cursorY].unset();
805 unsetImageRow(cursorY);
be72cb5c 806 }
fca67db0 807
7b5261bc
KL
808 cursorVisible = visible;
809 cursorX = x;
810 cursorY = y;
df8de03f
KL
811 }
812
813 /**
fca67db0 814 * Hide the cursor.
df8de03f 815 */
fca67db0 816 public final void hideCursor() {
7b5261bc 817 cursorVisible = false;
df8de03f 818 }
42873e30 819
3e074355
KL
820 /**
821 * Get the cursor visibility.
822 *
823 * @return true if the cursor is visible
824 */
825 public boolean isCursorVisible() {
826 return cursorVisible;
827 }
828
829 /**
830 * Get the cursor X position.
831 *
832 * @return the cursor x column position
833 */
834 public int getCursorX() {
835 return cursorX;
836 }
837
838 /**
839 * Get the cursor Y position.
840 *
841 * @return the cursor y row position
842 */
843 public int getCursorY() {
844 return cursorY;
845 }
846
42873e30
KL
847 /**
848 * Set the window title. Default implementation does nothing.
849 *
850 * @param title the new title
851 */
852 public void setTitle(final String title) {}
853
d36057df
KL
854 // ------------------------------------------------------------------------
855 // LogicalScreen ----------------------------------------------------------
856 // ------------------------------------------------------------------------
857
858 /**
859 * Reallocate screen buffers.
860 *
861 * @param width new width
862 * @param height new height
863 */
864 private synchronized void reallocate(final int width, final int height) {
865 if (logical != null) {
866 for (int row = 0; row < this.height; row++) {
867 for (int col = 0; col < this.width; col++) {
868 logical[col][row] = null;
869 }
870 }
871 logical = null;
872 }
873 logical = new Cell[width][height];
874 if (physical != null) {
875 for (int row = 0; row < this.height; row++) {
876 for (int col = 0; col < this.width; col++) {
877 physical[col][row] = null;
878 }
879 }
880 physical = null;
881 }
882 physical = new Cell[width][height];
883
884 for (int row = 0; row < height; row++) {
885 for (int col = 0; col < width; col++) {
886 physical[col][row] = new Cell();
887 logical[col][row] = new Cell();
888 }
889 }
890
891 this.width = width;
892 this.height = height;
893
894 clipLeft = 0;
895 clipTop = 0;
896 clipRight = width;
897 clipBottom = height;
898
899 reallyCleared = true;
900 }
901
902 /**
903 * Clear the physical screen.
904 */
905 public final void clearPhysical() {
906 for (int row = 0; row < height; row++) {
907 for (int col = 0; col < width; col++) {
a69ed767
KL
908 physical[col][row].unset();
909 }
910 }
911 }
912
913 /**
914 * Unset every image cell on one row of the physical screen, forcing
915 * images on that row to be redrawn.
916 *
917 * @param y row coordinate. 0 is the top-most row.
918 */
919 public final void unsetImageRow(final int y) {
9696a8f6
KL
920 if ((y < 0) || (y >= height)) {
921 return;
922 }
a69ed767
KL
923 for (int x = 0; x < width; x++) {
924 if (logical[x][y].isImage()) {
925 physical[x][y].unset();
d36057df
KL
926 }
927 }
928 }
929
df8de03f 930}