Expose width/height in TApplication constructor, attempt on ECMA48
[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 *
a2018e99 6 * Copyright (C) 2017 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
KL
39
40 /**
7b5261bc 41 * Width of the visible window.
df8de03f
KL
42 */
43 protected int width;
44
45 /**
7b5261bc 46 * Height of the visible window.
df8de03f
KL
47 */
48 protected int height;
49
50 /**
7b5261bc 51 * Drawing offset for x.
df8de03f 52 */
fca67db0 53 private int offsetX;
48e27807
KL
54
55 /**
56 * Set drawing offset for x.
57 *
58 * @param offsetX new drawing offset
59 */
60 public final void setOffsetX(final int offsetX) {
61 this.offsetX = offsetX;
62 }
df8de03f
KL
63
64 /**
7b5261bc 65 * Drawing offset for y.
df8de03f 66 */
fca67db0 67 private int offsetY;
df8de03f 68
48e27807
KL
69 /**
70 * Set drawing offset for y.
71 *
72 * @param offsetY new drawing offset
73 */
74 public final void setOffsetY(final int offsetY) {
75 this.offsetY = offsetY;
76 }
fca67db0 77
df8de03f 78 /**
7b5261bc 79 * Ignore anything drawn right of clipRight.
df8de03f 80 */
fca67db0 81 private int clipRight;
48e27807
KL
82
83 /**
84 * Get right drawing clipping boundary.
85 *
86 * @return drawing boundary
87 */
88 public final int getClipRight() {
89 return clipRight;
90 }
91
92 /**
93 * Set right drawing clipping boundary.
94 *
95 * @param clipRight new boundary
96 */
97 public final void setClipRight(final int clipRight) {
98 this.clipRight = clipRight;
99 }
df8de03f
KL
100
101 /**
7b5261bc 102 * Ignore anything drawn below clipBottom.
df8de03f 103 */
fca67db0 104 private int clipBottom;
48e27807
KL
105
106 /**
107 * Get bottom drawing clipping boundary.
108 *
109 * @return drawing boundary
110 */
111 public final int getClipBottom() {
112 return clipBottom;
113 }
114
115 /**
116 * Set bottom drawing clipping boundary.
117 *
118 * @param clipBottom new boundary
119 */
120 public final void setClipBottom(final int clipBottom) {
121 this.clipBottom = clipBottom;
122 }
df8de03f
KL
123
124 /**
7b5261bc 125 * Ignore anything drawn left of clipLeft.
df8de03f 126 */
fca67db0 127 private int clipLeft;
48e27807
KL
128
129 /**
130 * Get left drawing clipping boundary.
131 *
132 * @return drawing boundary
133 */
134 public final int getClipLeft() {
135 return clipLeft;
136 }
137
138 /**
139 * Set left drawing clipping boundary.
140 *
141 * @param clipLeft new boundary
142 */
143 public final void setClipLeft(final int clipLeft) {
144 this.clipLeft = clipLeft;
145 }
df8de03f
KL
146
147 /**
7b5261bc 148 * Ignore anything drawn above clipTop.
df8de03f 149 */
fca67db0 150 private int clipTop;
48e27807
KL
151
152 /**
153 * Get top drawing clipping boundary.
154 *
155 * @return drawing boundary
156 */
157 public final int getClipTop() {
158 return clipTop;
159 }
160
161 /**
162 * Set top drawing clipping boundary.
163 *
164 * @param clipTop new boundary
165 */
166 public final void setClipTop(final int clipTop) {
167 this.clipTop = clipTop;
168 }
df8de03f
KL
169
170 /**
7b5261bc 171 * The physical screen last sent out on flush().
df8de03f
KL
172 */
173 protected Cell [][] physical;
174
175 /**
7b5261bc 176 * The logical screen being rendered to.
df8de03f
KL
177 */
178 protected Cell [][] logical;
179
92554d64
KL
180 /**
181 * Get dirty flag.
182 *
183 * @return if true, the logical screen is not in sync with the physical
184 * screen
185 */
186 public final boolean isDirty() {
be72cb5c
KL
187 for (int x = 0; x < width; x++) {
188 for (int y = 0; y < height; y++) {
189 if (!logical[x][y].equals(physical[x][y])) {
190 return true;
191 }
192 if (logical[x][y].isBlink()) {
193 // Blinking screens are always dirty. There is
194 // opportunity for a Netscape blink tag joke here...
195 return true;
196 }
197 }
198 }
199
200 return false;
92554d64
KL
201 }
202
df8de03f
KL
203 /**
204 * Set if the user explicitly wants to redraw everything starting with a
7b5261bc 205 * ECMATerminal.clearAll().
df8de03f
KL
206 */
207 protected boolean reallyCleared;
208
209 /**
210 * If true, the cursor is visible and should be placed onscreen at
7b5261bc 211 * (cursorX, cursorY) during a call to flushPhysical().
df8de03f
KL
212 */
213 protected boolean cursorVisible;
214
215 /**
7b5261bc 216 * Cursor X position if visible.
df8de03f
KL
217 */
218 protected int cursorX;
219
220 /**
7b5261bc 221 * Cursor Y position if visible.
df8de03f
KL
222 */
223 protected int cursorY;
224
225 /**
226 * Get the attributes at one location.
227 *
228 * @param x column coordinate. 0 is the left-most column.
229 * @param y row coordinate. 0 is the top-most row.
230 * @return attributes at (x, y)
231 */
fca67db0 232 public final CellAttributes getAttrXY(final int x, final int y) {
7b5261bc 233 CellAttributes attr = new CellAttributes();
30bd4abd
KL
234 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
235 attr.setTo(logical[x][y]);
236 }
7b5261bc 237 return attr;
df8de03f
KL
238 }
239
3e074355
KL
240 /**
241 * Get the cell at one location.
242 *
243 * @param x column coordinate. 0 is the left-most column.
244 * @param y row coordinate. 0 is the top-most row.
245 * @return the character + attributes
246 */
247 public Cell getCharXY(final int x, final int y) {
248 Cell cell = new Cell();
249 if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
250 cell.setTo(logical[x][y]);
251 }
252 return cell;
253 }
254
df8de03f
KL
255 /**
256 * Set the attributes at one location.
257 *
258 * @param x column coordinate. 0 is the left-most column.
259 * @param y row coordinate. 0 is the top-most row.
260 * @param attr attributes to use (bold, foreColor, backColor)
df8de03f 261 */
fca67db0
KL
262 public final void putAttrXY(final int x, final int y,
263 final CellAttributes attr) {
264
7b5261bc 265 putAttrXY(x, y, attr, true);
df8de03f 266 }
7b5261bc 267
df8de03f
KL
268 /**
269 * Set the attributes at one location.
270 *
271 * @param x column coordinate. 0 is the left-most column.
272 * @param y row coordinate. 0 is the top-most row.
273 * @param attr attributes to use (bold, foreColor, backColor)
274 * @param clip if true, honor clipping/offset
275 */
a1879051
KL
276 public final void putAttrXY(final int x, final int y,
277 final CellAttributes attr, final boolean clip) {
7b5261bc
KL
278
279 int X = x;
280 int Y = y;
281
282 if (clip) {
283 if ((x < clipLeft)
284 || (x >= clipRight)
285 || (y < clipTop)
286 || (y >= clipBottom)
287 ) {
288 return;
289 }
290 X += offsetX;
291 Y += offsetY;
292 }
293
294 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
be72cb5c 295 logical[X][Y].setTo(attr);
24489803
KL
296
297 // If this happens to be the cursor position, make the position
298 // dirty.
299 if ((cursorX == X) && (cursorY == Y)) {
300 if (physical[cursorX][cursorY].getChar() == 'Q') {
301 physical[cursorX][cursorY].setChar('X');
302 } else {
303 physical[cursorX][cursorY].setChar('Q');
304 }
305 }
7b5261bc 306 }
df8de03f
KL
307 }
308
309 /**
310 * Fill the entire screen with one character with attributes.
311 *
312 * @param ch character to draw
313 * @param attr attributes to use (bold, foreColor, backColor)
314 */
fca67db0 315 public final void putAll(final char ch, final CellAttributes attr) {
87a17f3c 316
7b5261bc
KL
317 for (int x = 0; x < width; x++) {
318 for (int y = 0; y < height; y++) {
319 putCharXY(x, y, ch, attr);
320 }
321 }
df8de03f
KL
322 }
323
324 /**
325 * Render one character with attributes.
326 *
327 * @param x column coordinate. 0 is the left-most column.
328 * @param y row coordinate. 0 is the top-most row.
329 * @param ch character + attributes to draw
330 */
fca67db0 331 public final void putCharXY(final int x, final int y, final Cell ch) {
7b5261bc 332 putCharXY(x, y, ch.getChar(), ch);
df8de03f
KL
333 }
334
335 /**
336 * Render one character with attributes.
337 *
338 * @param x column coordinate. 0 is the left-most column.
339 * @param y row coordinate. 0 is the top-most row.
340 * @param ch character to draw
341 * @param attr attributes to use (bold, foreColor, backColor)
342 */
fca67db0 343 public final void putCharXY(final int x, final int y, final char ch,
7b5261bc
KL
344 final CellAttributes attr) {
345
346 if ((x < clipLeft)
347 || (x >= clipRight)
348 || (y < clipTop)
349 || (y >= clipBottom)
350 ) {
351 return;
352 }
353
354 int X = x + offsetX;
355 int Y = y + offsetY;
356
357 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
358
359 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc
KL
360
361 // Do not put control characters on the display
362 assert (ch >= 0x20);
363 assert (ch != 0x7F);
364
be72cb5c 365 logical[X][Y].setTo(attr);
7b5261bc 366 logical[X][Y].setChar(ch);
24489803
KL
367
368 // If this happens to be the cursor position, make the position
369 // dirty.
370 if ((cursorX == X) && (cursorY == Y)) {
371 if (physical[cursorX][cursorY].getChar() == 'Q') {
372 physical[cursorX][cursorY].setChar('X');
373 } else {
374 physical[cursorX][cursorY].setChar('Q');
375 }
376 }
7b5261bc 377 }
df8de03f
KL
378 }
379
380 /**
381 * Render one character without changing the underlying attributes.
382 *
383 * @param x column coordinate. 0 is the left-most column.
384 * @param y row coordinate. 0 is the top-most row.
385 * @param ch character to draw
386 */
fca67db0 387 public final void putCharXY(final int x, final int y, final char ch) {
87a17f3c 388
7b5261bc
KL
389 if ((x < clipLeft)
390 || (x >= clipRight)
391 || (y < clipTop)
392 || (y >= clipBottom)
393 ) {
394 return;
395 }
df8de03f 396
7b5261bc
KL
397 int X = x + offsetX;
398 int Y = y + offsetY;
df8de03f 399
7b5261bc 400 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
df8de03f 401
7b5261bc 402 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc 403 logical[X][Y].setChar(ch);
24489803
KL
404
405 // If this happens to be the cursor position, make the position
406 // dirty.
407 if ((cursorX == X) && (cursorY == Y)) {
408 if (physical[cursorX][cursorY].getChar() == 'Q') {
409 physical[cursorX][cursorY].setChar('X');
410 } else {
411 physical[cursorX][cursorY].setChar('Q');
412 }
413 }
7b5261bc 414 }
df8de03f
KL
415 }
416
417 /**
418 * Render a string. Does not wrap if the string exceeds the line.
419 *
420 * @param x column coordinate. 0 is the left-most column.
421 * @param y row coordinate. 0 is the top-most row.
422 * @param str string to draw
423 * @param attr attributes to use (bold, foreColor, backColor)
424 */
0d47c546 425 public final void putStringXY(final int x, final int y, final String str,
7b5261bc
KL
426 final CellAttributes attr) {
427
428 int i = x;
429 for (int j = 0; j < str.length(); j++) {
430 char ch = str.charAt(j);
431 putCharXY(i, y, ch, attr);
432 i++;
433 if (i == width) {
434 break;
435 }
436 }
df8de03f
KL
437 }
438
439 /**
440 * Render a string without changing the underlying attribute. Does not
441 * wrap if the string exceeds the line.
442 *
443 * @param x column coordinate. 0 is the left-most column.
444 * @param y row coordinate. 0 is the top-most row.
445 * @param str string to draw
446 */
0d47c546 447 public final void putStringXY(final int x, final int y, final String str) {
87a17f3c 448
7b5261bc
KL
449 int i = x;
450 for (int j = 0; j < str.length(); j++) {
451 char ch = str.charAt(j);
452 putCharXY(i, y, ch);
453 i++;
454 if (i == width) {
455 break;
456 }
457 }
df8de03f
KL
458 }
459
460 /**
7b5261bc 461 * Draw a vertical line from (x, y) to (x, y + n).
df8de03f
KL
462 *
463 * @param x column coordinate. 0 is the left-most column.
464 * @param y row coordinate. 0 is the top-most row.
465 * @param n number of characters to draw
466 * @param ch character to draw
467 * @param attr attributes to use (bold, foreColor, backColor)
468 */
fca67db0
KL
469 public final void vLineXY(final int x, final int y, final int n,
470 final char ch, final CellAttributes attr) {
7b5261bc
KL
471
472 for (int i = y; i < y + n; i++) {
473 putCharXY(x, i, ch, attr);
474 }
df8de03f
KL
475 }
476
477 /**
7b5261bc 478 * Draw a horizontal line from (x, y) to (x + n, y).
df8de03f
KL
479 *
480 * @param x column coordinate. 0 is the left-most column.
481 * @param y row coordinate. 0 is the top-most row.
482 * @param n number of characters to draw
483 * @param ch character to draw
484 * @param attr attributes to use (bold, foreColor, backColor)
485 */
fca67db0
KL
486 public final void hLineXY(final int x, final int y, final int n,
487 final char ch, final CellAttributes attr) {
7b5261bc
KL
488
489 for (int i = x; i < x + n; i++) {
490 putCharXY(i, y, ch, attr);
491 }
df8de03f
KL
492 }
493
494 /**
495 * Reallocate screen buffers.
496 *
497 * @param width new width
498 * @param height new height
499 */
87a17f3c 500 private synchronized void reallocate(final int width, final int height) {
7b5261bc
KL
501 if (logical != null) {
502 for (int row = 0; row < this.height; row++) {
503 for (int col = 0; col < this.width; col++) {
504 logical[col][row] = null;
505 }
506 }
507 logical = null;
508 }
509 logical = new Cell[width][height];
510 if (physical != null) {
511 for (int row = 0; row < this.height; row++) {
512 for (int col = 0; col < this.width; col++) {
513 physical[col][row] = null;
514 }
515 }
516 physical = null;
517 }
518 physical = new Cell[width][height];
519
520 for (int row = 0; row < height; row++) {
521 for (int col = 0; col < width; col++) {
522 physical[col][row] = new Cell();
523 logical[col][row] = new Cell();
524 }
525 }
526
527 this.width = width;
528 this.height = height;
529
530 clipLeft = 0;
531 clipTop = 0;
532 clipRight = width;
533 clipBottom = height;
534
535 reallyCleared = true;
df8de03f
KL
536 }
537
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);
df8de03f
KL
567 }
568
569 /**
570 * Get the height.
571 *
572 * @return current screen height
573 */
87a17f3c 574 public final synchronized int getHeight() {
7b5261bc 575 return this.height;
df8de03f
KL
576 }
577
578 /**
579 * Get the width.
580 *
581 * @return current screen width
582 */
87a17f3c 583 public final synchronized int getWidth() {
7b5261bc 584 return this.width;
df8de03f
KL
585 }
586
587 /**
588 * Public constructor. Sets everything to not-bold, white-on-black.
589 */
42873e30 590 protected LogicalScreen() {
7b5261bc
KL
591 offsetX = 0;
592 offsetY = 0;
593 width = 80;
594 height = 24;
595 logical = null;
596 physical = null;
597 reallocate(width, height);
df8de03f
KL
598 }
599
600 /**
601 * Reset screen to not-bold, white-on-black. Also flushes the offset and
602 * clip variables.
603 */
87a17f3c 604 public final synchronized void reset() {
7b5261bc
KL
605 for (int row = 0; row < height; row++) {
606 for (int col = 0; col < width; col++) {
607 logical[col][row].reset();
608 }
609 }
610 resetClipping();
df8de03f
KL
611 }
612
613 /**
614 * Flush the offset and clip variables.
615 */
fca67db0 616 public final void resetClipping() {
7b5261bc
KL
617 offsetX = 0;
618 offsetY = 0;
619 clipLeft = 0;
620 clipTop = 0;
621 clipRight = width;
622 clipBottom = height;
df8de03f
KL
623 }
624
625 /**
bd8d51fa 626 * Clear the logical screen.
df8de03f 627 */
fca67db0 628 public final void clear() {
7b5261bc 629 reset();
df8de03f
KL
630 }
631
bd8d51fa
KL
632 /**
633 * Clear the physical screen.
634 */
635 public final void clearPhysical() {
bd8d51fa
KL
636 for (int row = 0; row < height; row++) {
637 for (int col = 0; col < width; col++) {
638 physical[col][row].reset();
639 }
640 }
641 }
642
df8de03f
KL
643 /**
644 * Draw a box with a border and empty background.
645 *
646 * @param left left column of box. 0 is the left-most row.
647 * @param top top row of the box. 0 is the top-most row.
648 * @param right right column of box
649 * @param bottom bottom row of the box
7b5261bc 650 * @param border attributes to use for the border
df8de03f
KL
651 * @param background attributes to use for the background
652 */
fca67db0 653 public final void drawBox(final int left, final int top,
7b5261bc
KL
654 final int right, final int bottom,
655 final CellAttributes border, final CellAttributes background) {
656
657 drawBox(left, top, right, bottom, border, background, 1, false);
df8de03f
KL
658 }
659
660 /**
661 * Draw a box with a border and empty background.
662 *
663 * @param left left column of box. 0 is the left-most row.
664 * @param top top row of the box. 0 is the top-most row.
665 * @param right right column of box
666 * @param bottom bottom row of the box
7b5261bc 667 * @param border attributes to use for the border
df8de03f 668 * @param background attributes to use for the background
7b5261bc
KL
669 * @param borderType if 1, draw a single-line border; if 2, draw a
670 * double-line border; if 3, draw double-line top/bottom edges and
671 * single-line left/right edges (like Qmodem)
df8de03f
KL
672 * @param shadow if true, draw a "shadow" on the box
673 */
fca67db0 674 public final void drawBox(final int left, final int top,
7b5261bc
KL
675 final int right, final int bottom,
676 final CellAttributes border, final CellAttributes background,
677 final int borderType, final boolean shadow) {
678
7b5261bc
KL
679 int boxWidth = right - left;
680 int boxHeight = bottom - top;
681
682 char cTopLeft;
683 char cTopRight;
684 char cBottomLeft;
685 char cBottomRight;
686 char cHSide;
687 char cVSide;
688
689 switch (borderType) {
690 case 1:
691 cTopLeft = GraphicsChars.ULCORNER;
692 cTopRight = GraphicsChars.URCORNER;
693 cBottomLeft = GraphicsChars.LLCORNER;
694 cBottomRight = GraphicsChars.LRCORNER;
695 cHSide = GraphicsChars.SINGLE_BAR;
696 cVSide = GraphicsChars.WINDOW_SIDE;
697 break;
698
699 case 2:
700 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP_DOUBLE;
701 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP_DOUBLE;
702 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM_DOUBLE;
703 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM_DOUBLE;
704 cHSide = GraphicsChars.DOUBLE_BAR;
705 cVSide = GraphicsChars.WINDOW_SIDE_DOUBLE;
706 break;
707
708 case 3:
709 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP;
710 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP;
711 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM;
712 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM;
713 cHSide = GraphicsChars.WINDOW_TOP;
714 cVSide = GraphicsChars.WINDOW_SIDE;
715 break;
716 default:
717 throw new IllegalArgumentException("Invalid border type: "
718 + borderType);
719 }
720
721 // Place the corner characters
722 putCharXY(left, top, cTopLeft, border);
723 putCharXY(left + boxWidth - 1, top, cTopRight, border);
724 putCharXY(left, top + boxHeight - 1, cBottomLeft, border);
725 putCharXY(left + boxWidth - 1, top + boxHeight - 1, cBottomRight,
726 border);
727
728 // Draw the box lines
729 hLineXY(left + 1, top, boxWidth - 2, cHSide, border);
730 vLineXY(left, top + 1, boxHeight - 2, cVSide, border);
731 hLineXY(left + 1, top + boxHeight - 1, boxWidth - 2, cHSide, border);
732 vLineXY(left + boxWidth - 1, top + 1, boxHeight - 2, cVSide, border);
733
734 // Fill in the interior background
735 for (int i = 1; i < boxHeight - 1; i++) {
736 hLineXY(1 + left, i + top, boxWidth - 2, ' ', background);
737 }
738
739 if (shadow) {
740 // Draw a shadow
741 drawBoxShadow(left, top, right, bottom);
742 }
df8de03f
KL
743 }
744
745 /**
fca67db0 746 * Draw a box shadow.
df8de03f
KL
747 *
748 * @param left left column of box. 0 is the left-most row.
749 * @param top top row of the box. 0 is the top-most row.
750 * @param right right column of box
751 * @param bottom bottom row of the box
752 */
fca67db0 753 public final void drawBoxShadow(final int left, final int top,
7b5261bc
KL
754 final int right, final int bottom) {
755
756 int boxTop = top;
757 int boxLeft = left;
758 int boxWidth = right - left;
759 int boxHeight = bottom - top;
760 CellAttributes shadowAttr = new CellAttributes();
761
762 // Shadows do not honor clipping but they DO honor offset.
763 int oldClipRight = clipRight;
764 int oldClipBottom = clipBottom;
765 /*
766 clipRight = boxWidth + 2;
767 clipBottom = boxHeight + 1;
768 */
769 clipRight = width;
770 clipBottom = height;
771
772 for (int i = 0; i < boxHeight; i++) {
773 putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr);
774 putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr);
775 }
776 for (int i = 0; i < boxWidth; i++) {
777 putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr);
778 }
779 clipRight = oldClipRight;
780 clipBottom = oldClipBottom;
df8de03f
KL
781 }
782
783 /**
42873e30 784 * Default implementation does nothing.
df8de03f 785 */
42873e30 786 public void flushPhysical() {}
df8de03f
KL
787
788 /**
789 * Put the cursor at (x,y).
790 *
791 * @param visible if true, the cursor should be visible
792 * @param x column coordinate to put the cursor on
793 * @param y row coordinate to put the cursor on
794 */
30bd4abd 795 public void putCursor(final boolean visible, final int x, final int y) {
be72cb5c
KL
796 if ((cursorY >= 0)
797 && (cursorX >= 0)
798 && (cursorY <= height - 1)
799 && (cursorX <= width - 1)
800 ) {
801 // Make the current cursor position dirty
802 if (physical[cursorX][cursorY].getChar() == 'Q') {
803 physical[cursorX][cursorY].setChar('X');
804 } else {
805 physical[cursorX][cursorY].setChar('Q');
806 }
807 }
fca67db0 808
7b5261bc
KL
809 cursorVisible = visible;
810 cursorX = x;
811 cursorY = y;
df8de03f
KL
812 }
813
814 /**
fca67db0 815 * Hide the cursor.
df8de03f 816 */
fca67db0 817 public final void hideCursor() {
7b5261bc 818 cursorVisible = false;
df8de03f 819 }
42873e30 820
3e074355
KL
821 /**
822 * Get the cursor visibility.
823 *
824 * @return true if the cursor is visible
825 */
826 public boolean isCursorVisible() {
827 return cursorVisible;
828 }
829
830 /**
831 * Get the cursor X position.
832 *
833 * @return the cursor x column position
834 */
835 public int getCursorX() {
836 return cursorX;
837 }
838
839 /**
840 * Get the cursor Y position.
841 *
842 * @return the cursor y row position
843 */
844 public int getCursorY() {
845 return cursorY;
846 }
847
42873e30
KL
848 /**
849 * Set the window title. Default implementation does nothing.
850 *
851 * @param title the new title
852 */
853 public void setTitle(final String title) {}
854
df8de03f 855}