oops
[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);
7b5261bc 296 }
df8de03f
KL
297 }
298
299 /**
300 * Fill the entire screen with one character with attributes.
301 *
302 * @param ch character to draw
303 * @param attr attributes to use (bold, foreColor, backColor)
304 */
fca67db0 305 public final void putAll(final char ch, final CellAttributes attr) {
87a17f3c 306
7b5261bc
KL
307 for (int x = 0; x < width; x++) {
308 for (int y = 0; y < height; y++) {
309 putCharXY(x, y, ch, attr);
310 }
311 }
df8de03f
KL
312 }
313
314 /**
315 * Render one character with attributes.
316 *
317 * @param x column coordinate. 0 is the left-most column.
318 * @param y row coordinate. 0 is the top-most row.
319 * @param ch character + attributes to draw
320 */
fca67db0 321 public final void putCharXY(final int x, final int y, final Cell ch) {
7b5261bc 322 putCharXY(x, y, ch.getChar(), ch);
df8de03f
KL
323 }
324
325 /**
326 * Render one character with attributes.
327 *
328 * @param x column coordinate. 0 is the left-most column.
329 * @param y row coordinate. 0 is the top-most row.
330 * @param ch character to draw
331 * @param attr attributes to use (bold, foreColor, backColor)
332 */
fca67db0 333 public final void putCharXY(final int x, final int y, final char ch,
7b5261bc
KL
334 final CellAttributes attr) {
335
336 if ((x < clipLeft)
337 || (x >= clipRight)
338 || (y < clipTop)
339 || (y >= clipBottom)
340 ) {
341 return;
342 }
343
344 int X = x + offsetX;
345 int Y = y + offsetY;
346
347 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
348
349 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc
KL
350
351 // Do not put control characters on the display
352 assert (ch >= 0x20);
353 assert (ch != 0x7F);
354
be72cb5c 355 logical[X][Y].setTo(attr);
7b5261bc 356 logical[X][Y].setChar(ch);
7b5261bc 357 }
df8de03f
KL
358 }
359
360 /**
361 * Render one character without changing the underlying attributes.
362 *
363 * @param x column coordinate. 0 is the left-most column.
364 * @param y row coordinate. 0 is the top-most row.
365 * @param ch character to draw
366 */
fca67db0 367 public final void putCharXY(final int x, final int y, final char ch) {
87a17f3c 368
7b5261bc
KL
369 if ((x < clipLeft)
370 || (x >= clipRight)
371 || (y < clipTop)
372 || (y >= clipBottom)
373 ) {
374 return;
375 }
df8de03f 376
7b5261bc
KL
377 int X = x + offsetX;
378 int Y = y + offsetY;
df8de03f 379
7b5261bc 380 // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch);
df8de03f 381
7b5261bc 382 if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) {
7b5261bc
KL
383 logical[X][Y].setChar(ch);
384 }
df8de03f
KL
385 }
386
387 /**
388 * Render a string. Does not wrap if the string exceeds the line.
389 *
390 * @param x column coordinate. 0 is the left-most column.
391 * @param y row coordinate. 0 is the top-most row.
392 * @param str string to draw
393 * @param attr attributes to use (bold, foreColor, backColor)
394 */
0d47c546 395 public final void putStringXY(final int x, final int y, final String str,
7b5261bc
KL
396 final CellAttributes attr) {
397
398 int i = x;
399 for (int j = 0; j < str.length(); j++) {
400 char ch = str.charAt(j);
401 putCharXY(i, y, ch, attr);
402 i++;
403 if (i == width) {
404 break;
405 }
406 }
df8de03f
KL
407 }
408
409 /**
410 * Render a string without changing the underlying attribute. Does not
411 * wrap if the string exceeds the line.
412 *
413 * @param x column coordinate. 0 is the left-most column.
414 * @param y row coordinate. 0 is the top-most row.
415 * @param str string to draw
416 */
0d47c546 417 public final void putStringXY(final int x, final int y, final String str) {
87a17f3c 418
7b5261bc
KL
419 int i = x;
420 for (int j = 0; j < str.length(); j++) {
421 char ch = str.charAt(j);
422 putCharXY(i, y, ch);
423 i++;
424 if (i == width) {
425 break;
426 }
427 }
df8de03f
KL
428 }
429
430 /**
7b5261bc 431 * Draw a vertical line from (x, y) to (x, y + n).
df8de03f
KL
432 *
433 * @param x column coordinate. 0 is the left-most column.
434 * @param y row coordinate. 0 is the top-most row.
435 * @param n number of characters to draw
436 * @param ch character to draw
437 * @param attr attributes to use (bold, foreColor, backColor)
438 */
fca67db0
KL
439 public final void vLineXY(final int x, final int y, final int n,
440 final char ch, final CellAttributes attr) {
7b5261bc
KL
441
442 for (int i = y; i < y + n; i++) {
443 putCharXY(x, i, ch, attr);
444 }
df8de03f
KL
445 }
446
447 /**
7b5261bc 448 * Draw a horizontal line from (x, y) to (x + n, y).
df8de03f
KL
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 n number of characters to draw
453 * @param ch character to draw
454 * @param attr attributes to use (bold, foreColor, backColor)
455 */
fca67db0
KL
456 public final void hLineXY(final int x, final int y, final int n,
457 final char ch, final CellAttributes attr) {
7b5261bc
KL
458
459 for (int i = x; i < x + n; i++) {
460 putCharXY(i, y, ch, attr);
461 }
df8de03f
KL
462 }
463
464 /**
465 * Reallocate screen buffers.
466 *
467 * @param width new width
468 * @param height new height
469 */
87a17f3c 470 private synchronized void reallocate(final int width, final int height) {
7b5261bc
KL
471 if (logical != null) {
472 for (int row = 0; row < this.height; row++) {
473 for (int col = 0; col < this.width; col++) {
474 logical[col][row] = null;
475 }
476 }
477 logical = null;
478 }
479 logical = new Cell[width][height];
480 if (physical != null) {
481 for (int row = 0; row < this.height; row++) {
482 for (int col = 0; col < this.width; col++) {
483 physical[col][row] = null;
484 }
485 }
486 physical = null;
487 }
488 physical = new Cell[width][height];
489
490 for (int row = 0; row < height; row++) {
491 for (int col = 0; col < width; col++) {
492 physical[col][row] = new Cell();
493 logical[col][row] = new Cell();
494 }
495 }
496
497 this.width = width;
498 this.height = height;
499
500 clipLeft = 0;
501 clipTop = 0;
502 clipRight = width;
503 clipBottom = height;
504
505 reallyCleared = true;
df8de03f
KL
506 }
507
508 /**
509 * Change the width. Everything on-screen will be destroyed and must be
510 * redrawn.
7b5261bc 511 *
df8de03f
KL
512 * @param width new screen width
513 */
87a17f3c 514 public final synchronized void setWidth(final int width) {
7b5261bc 515 reallocate(width, this.height);
df8de03f
KL
516 }
517
518 /**
519 * Change the height. Everything on-screen will be destroyed and must be
520 * redrawn.
521 *
522 * @param height new screen height
523 */
87a17f3c 524 public final synchronized void setHeight(final int height) {
7b5261bc 525 reallocate(this.width, height);
df8de03f
KL
526 }
527
528 /**
529 * Change the width and height. Everything on-screen will be destroyed
530 * and must be redrawn.
531 *
532 * @param width new screen width
533 * @param height new screen height
534 */
fca67db0 535 public final void setDimensions(final int width, final int height) {
7b5261bc 536 reallocate(width, height);
df8de03f
KL
537 }
538
539 /**
540 * Get the height.
541 *
542 * @return current screen height
543 */
87a17f3c 544 public final synchronized int getHeight() {
7b5261bc 545 return this.height;
df8de03f
KL
546 }
547
548 /**
549 * Get the width.
550 *
551 * @return current screen width
552 */
87a17f3c 553 public final synchronized int getWidth() {
7b5261bc 554 return this.width;
df8de03f
KL
555 }
556
557 /**
558 * Public constructor. Sets everything to not-bold, white-on-black.
559 */
42873e30 560 protected LogicalScreen() {
7b5261bc
KL
561 offsetX = 0;
562 offsetY = 0;
563 width = 80;
564 height = 24;
565 logical = null;
566 physical = null;
567 reallocate(width, height);
df8de03f
KL
568 }
569
570 /**
571 * Reset screen to not-bold, white-on-black. Also flushes the offset and
572 * clip variables.
573 */
87a17f3c 574 public final synchronized void reset() {
7b5261bc
KL
575 for (int row = 0; row < height; row++) {
576 for (int col = 0; col < width; col++) {
577 logical[col][row].reset();
578 }
579 }
580 resetClipping();
df8de03f
KL
581 }
582
583 /**
584 * Flush the offset and clip variables.
585 */
fca67db0 586 public final void resetClipping() {
7b5261bc
KL
587 offsetX = 0;
588 offsetY = 0;
589 clipLeft = 0;
590 clipTop = 0;
591 clipRight = width;
592 clipBottom = height;
df8de03f
KL
593 }
594
595 /**
bd8d51fa 596 * Clear the logical screen.
df8de03f 597 */
fca67db0 598 public final void clear() {
7b5261bc 599 reset();
df8de03f
KL
600 }
601
bd8d51fa
KL
602 /**
603 * Clear the physical screen.
604 */
605 public final void clearPhysical() {
bd8d51fa
KL
606 for (int row = 0; row < height; row++) {
607 for (int col = 0; col < width; col++) {
608 physical[col][row].reset();
609 }
610 }
611 }
612
df8de03f
KL
613 /**
614 * Draw a box with a border and empty background.
615 *
616 * @param left left column of box. 0 is the left-most row.
617 * @param top top row of the box. 0 is the top-most row.
618 * @param right right column of box
619 * @param bottom bottom row of the box
7b5261bc 620 * @param border attributes to use for the border
df8de03f
KL
621 * @param background attributes to use for the background
622 */
fca67db0 623 public final void drawBox(final int left, final int top,
7b5261bc
KL
624 final int right, final int bottom,
625 final CellAttributes border, final CellAttributes background) {
626
627 drawBox(left, top, right, bottom, border, background, 1, false);
df8de03f
KL
628 }
629
630 /**
631 * Draw a box with a border and empty background.
632 *
633 * @param left left column of box. 0 is the left-most row.
634 * @param top top row of the box. 0 is the top-most row.
635 * @param right right column of box
636 * @param bottom bottom row of the box
7b5261bc 637 * @param border attributes to use for the border
df8de03f 638 * @param background attributes to use for the background
7b5261bc
KL
639 * @param borderType if 1, draw a single-line border; if 2, draw a
640 * double-line border; if 3, draw double-line top/bottom edges and
641 * single-line left/right edges (like Qmodem)
df8de03f
KL
642 * @param shadow if true, draw a "shadow" on the box
643 */
fca67db0 644 public final void drawBox(final int left, final int top,
7b5261bc
KL
645 final int right, final int bottom,
646 final CellAttributes border, final CellAttributes background,
647 final int borderType, final boolean shadow) {
648
7b5261bc
KL
649 int boxWidth = right - left;
650 int boxHeight = bottom - top;
651
652 char cTopLeft;
653 char cTopRight;
654 char cBottomLeft;
655 char cBottomRight;
656 char cHSide;
657 char cVSide;
658
659 switch (borderType) {
660 case 1:
661 cTopLeft = GraphicsChars.ULCORNER;
662 cTopRight = GraphicsChars.URCORNER;
663 cBottomLeft = GraphicsChars.LLCORNER;
664 cBottomRight = GraphicsChars.LRCORNER;
665 cHSide = GraphicsChars.SINGLE_BAR;
666 cVSide = GraphicsChars.WINDOW_SIDE;
667 break;
668
669 case 2:
670 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP_DOUBLE;
671 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP_DOUBLE;
672 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM_DOUBLE;
673 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM_DOUBLE;
674 cHSide = GraphicsChars.DOUBLE_BAR;
675 cVSide = GraphicsChars.WINDOW_SIDE_DOUBLE;
676 break;
677
678 case 3:
679 cTopLeft = GraphicsChars.WINDOW_LEFT_TOP;
680 cTopRight = GraphicsChars.WINDOW_RIGHT_TOP;
681 cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM;
682 cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM;
683 cHSide = GraphicsChars.WINDOW_TOP;
684 cVSide = GraphicsChars.WINDOW_SIDE;
685 break;
686 default:
687 throw new IllegalArgumentException("Invalid border type: "
688 + borderType);
689 }
690
691 // Place the corner characters
692 putCharXY(left, top, cTopLeft, border);
693 putCharXY(left + boxWidth - 1, top, cTopRight, border);
694 putCharXY(left, top + boxHeight - 1, cBottomLeft, border);
695 putCharXY(left + boxWidth - 1, top + boxHeight - 1, cBottomRight,
696 border);
697
698 // Draw the box lines
699 hLineXY(left + 1, top, boxWidth - 2, cHSide, border);
700 vLineXY(left, top + 1, boxHeight - 2, cVSide, border);
701 hLineXY(left + 1, top + boxHeight - 1, boxWidth - 2, cHSide, border);
702 vLineXY(left + boxWidth - 1, top + 1, boxHeight - 2, cVSide, border);
703
704 // Fill in the interior background
705 for (int i = 1; i < boxHeight - 1; i++) {
706 hLineXY(1 + left, i + top, boxWidth - 2, ' ', background);
707 }
708
709 if (shadow) {
710 // Draw a shadow
711 drawBoxShadow(left, top, right, bottom);
712 }
df8de03f
KL
713 }
714
715 /**
fca67db0 716 * Draw a box shadow.
df8de03f
KL
717 *
718 * @param left left column of box. 0 is the left-most row.
719 * @param top top row of the box. 0 is the top-most row.
720 * @param right right column of box
721 * @param bottom bottom row of the box
722 */
fca67db0 723 public final void drawBoxShadow(final int left, final int top,
7b5261bc
KL
724 final int right, final int bottom) {
725
726 int boxTop = top;
727 int boxLeft = left;
728 int boxWidth = right - left;
729 int boxHeight = bottom - top;
730 CellAttributes shadowAttr = new CellAttributes();
731
732 // Shadows do not honor clipping but they DO honor offset.
733 int oldClipRight = clipRight;
734 int oldClipBottom = clipBottom;
735 /*
736 clipRight = boxWidth + 2;
737 clipBottom = boxHeight + 1;
738 */
739 clipRight = width;
740 clipBottom = height;
741
742 for (int i = 0; i < boxHeight; i++) {
743 putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr);
744 putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr);
745 }
746 for (int i = 0; i < boxWidth; i++) {
747 putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr);
748 }
749 clipRight = oldClipRight;
750 clipBottom = oldClipBottom;
df8de03f
KL
751 }
752
753 /**
42873e30 754 * Default implementation does nothing.
df8de03f 755 */
42873e30 756 public void flushPhysical() {}
df8de03f
KL
757
758 /**
759 * Put the cursor at (x,y).
760 *
761 * @param visible if true, the cursor should be visible
762 * @param x column coordinate to put the cursor on
763 * @param y row coordinate to put the cursor on
764 */
30bd4abd 765 public void putCursor(final boolean visible, final int x, final int y) {
be72cb5c
KL
766 if ((cursorY >= 0)
767 && (cursorX >= 0)
768 && (cursorY <= height - 1)
769 && (cursorX <= width - 1)
770 ) {
771 // Make the current cursor position dirty
772 if (physical[cursorX][cursorY].getChar() == 'Q') {
773 physical[cursorX][cursorY].setChar('X');
774 } else {
775 physical[cursorX][cursorY].setChar('Q');
776 }
777 }
fca67db0 778
7b5261bc
KL
779 cursorVisible = visible;
780 cursorX = x;
781 cursorY = y;
df8de03f
KL
782 }
783
784 /**
fca67db0 785 * Hide the cursor.
df8de03f 786 */
fca67db0 787 public final void hideCursor() {
7b5261bc 788 cursorVisible = false;
df8de03f 789 }
42873e30 790
3e074355
KL
791 /**
792 * Get the cursor visibility.
793 *
794 * @return true if the cursor is visible
795 */
796 public boolean isCursorVisible() {
797 return cursorVisible;
798 }
799
800 /**
801 * Get the cursor X position.
802 *
803 * @return the cursor x column position
804 */
805 public int getCursorX() {
806 return cursorX;
807 }
808
809 /**
810 * Get the cursor Y position.
811 *
812 * @return the cursor y row position
813 */
814 public int getCursorY() {
815 return cursorY;
816 }
817
42873e30
KL
818 /**
819 * Set the window title. Default implementation does nothing.
820 *
821 * @param title the new title
822 */
823 public void setTitle(final String title) {}
824
df8de03f 825}