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