Update copyright
[fanfix.git] / src / jexer / backend / MultiScreen.java
CommitLineData
88a99379
KL
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
88a99379
KL
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
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.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer.backend;
30
31import java.util.LinkedList;
32import java.util.List;
33
34import jexer.bits.Cell;
35import jexer.bits.CellAttributes;
36
37/**
38 * MultiScreen mirrors its I/O to several screens.
39 */
40public class MultiScreen implements Screen {
41
d36057df
KL
42 // ------------------------------------------------------------------------
43 // Variables --------------------------------------------------------------
44 // ------------------------------------------------------------------------
45
88a99379
KL
46 /**
47 * The list of screens to use.
48 */
49 private List<Screen> screens = new LinkedList<Screen>();
50
d36057df
KL
51 // ------------------------------------------------------------------------
52 // Constructors -----------------------------------------------------------
53 // ------------------------------------------------------------------------
54
88a99379
KL
55 /**
56 * Public constructor requires one screen.
57 *
58 * @param screen the screen to add
59 */
60 public MultiScreen(final Screen screen) {
61 screens.add(screen);
62 }
63
d36057df
KL
64 // ------------------------------------------------------------------------
65 // Screen -----------------------------------------------------------------
66 // ------------------------------------------------------------------------
88a99379
KL
67
68 /**
69 * Set drawing offset for x.
70 *
71 * @param offsetX new drawing offset
72 */
73 public void setOffsetX(final int offsetX) {
74 for (Screen screen: screens) {
75 screen.setOffsetX(offsetX);
76 }
77 }
78
79 /**
80 * Set drawing offset for y.
81 *
82 * @param offsetY new drawing offset
83 */
84 public void setOffsetY(final int offsetY) {
85 for (Screen screen: screens) {
86 screen.setOffsetY(offsetY);
87 }
88 }
89
90 /**
91 * Get right drawing clipping boundary.
92 *
93 * @return drawing boundary
94 */
95 public int getClipRight() {
96 return screens.get(0).getClipRight();
97 }
98
99 /**
100 * Set right drawing clipping boundary.
101 *
102 * @param clipRight new boundary
103 */
104 public void setClipRight(final int clipRight) {
105 for (Screen screen: screens) {
106 screen.setClipRight(clipRight);
107 }
108 }
109
110 /**
111 * Get bottom drawing clipping boundary.
112 *
113 * @return drawing boundary
114 */
115 public int getClipBottom() {
116 return screens.get(0).getClipBottom();
117 }
118
119 /**
120 * Set bottom drawing clipping boundary.
121 *
122 * @param clipBottom new boundary
123 */
124 public void setClipBottom(final int clipBottom) {
125 for (Screen screen: screens) {
126 screen.setClipBottom(clipBottom);
127 }
128 }
129
130 /**
131 * Get left drawing clipping boundary.
132 *
133 * @return drawing boundary
134 */
135 public int getClipLeft() {
136 return screens.get(0).getClipLeft();
137 }
138
139 /**
140 * Set left drawing clipping boundary.
141 *
142 * @param clipLeft new boundary
143 */
144 public void setClipLeft(final int clipLeft) {
145 for (Screen screen: screens) {
146 screen.setClipLeft(clipLeft);
147 }
148 }
149
150 /**
151 * Get top drawing clipping boundary.
152 *
153 * @return drawing boundary
154 */
155 public int getClipTop() {
156 return screens.get(0).getClipTop();
157 }
158
159 /**
160 * Set top drawing clipping boundary.
161 *
162 * @param clipTop new boundary
163 */
164 public void setClipTop(final int clipTop) {
165 for (Screen screen: screens) {
166 screen.setClipTop(clipTop);
167 }
168 }
169
170 /**
171 * Get dirty flag.
172 *
173 * @return if true, the logical screen is not in sync with the physical
174 * screen
175 */
176 public boolean isDirty() {
be72cb5c
KL
177 for (Screen screen: screens) {
178 if (screen.isDirty()) {
179 return true;
180 }
181 }
182 return false;
88a99379
KL
183 }
184
185 /**
186 * Get the attributes at one location.
187 *
188 * @param x column coordinate. 0 is the left-most column.
189 * @param y row coordinate. 0 is the top-most row.
190 * @return attributes at (x, y)
191 */
192 public CellAttributes getAttrXY(final int x, final int y) {
193 return screens.get(0).getAttrXY(x, y);
194 }
195
3e074355
KL
196 /**
197 * Get the cell at one location.
198 *
199 * @param x column coordinate. 0 is the left-most column.
200 * @param y row coordinate. 0 is the top-most row.
201 * @return the character + attributes
202 */
203 public Cell getCharXY(final int x, final int y) {
204 return screens.get(0).getCharXY(x, y);
205 }
206
88a99379
KL
207 /**
208 * Set the attributes at one location.
209 *
210 * @param x column coordinate. 0 is the left-most column.
211 * @param y row coordinate. 0 is the top-most row.
212 * @param attr attributes to use (bold, foreColor, backColor)
213 */
214 public void putAttrXY(final int x, final int y,
215 final CellAttributes attr) {
216
217 for (Screen screen: screens) {
218 screen.putAttrXY(x, y, attr);
219 }
220 }
221
222 /**
223 * Set the attributes at one location.
224 *
225 * @param x column coordinate. 0 is the left-most column.
226 * @param y row coordinate. 0 is the top-most row.
227 * @param attr attributes to use (bold, foreColor, backColor)
228 * @param clip if true, honor clipping/offset
229 */
230 public void putAttrXY(final int x, final int y,
231 final CellAttributes attr, final boolean clip) {
232
233 for (Screen screen: screens) {
234 screen.putAttrXY(x, y, attr, clip);
235 }
236 }
237
238 /**
239 * Fill the entire screen with one character with attributes.
240 *
241 * @param ch character to draw
242 * @param attr attributes to use (bold, foreColor, backColor)
243 */
244 public void putAll(final char ch, final CellAttributes attr) {
245 for (Screen screen: screens) {
246 screen.putAll(ch, attr);
247 }
248 }
249
250 /**
251 * Render one character with attributes.
252 *
253 * @param x column coordinate. 0 is the left-most column.
254 * @param y row coordinate. 0 is the top-most row.
255 * @param ch character + attributes to draw
256 */
257 public void putCharXY(final int x, final int y, final Cell ch) {
258 for (Screen screen: screens) {
259 screen.putCharXY(x, y, ch);
260 }
261 }
262
263 /**
264 * Render one character with attributes.
265 *
266 * @param x column coordinate. 0 is the left-most column.
267 * @param y row coordinate. 0 is the top-most row.
268 * @param ch character to draw
269 * @param attr attributes to use (bold, foreColor, backColor)
270 */
271 public void putCharXY(final int x, final int y, final char ch,
272 final CellAttributes attr) {
273
274 for (Screen screen: screens) {
275 screen.putCharXY(x, y, ch, attr);
276 }
277 }
278
279 /**
280 * Render one character without changing the underlying attributes.
281 *
282 * @param x column coordinate. 0 is the left-most column.
283 * @param y row coordinate. 0 is the top-most row.
284 * @param ch character to draw
285 */
286 public void putCharXY(final int x, final int y, final char ch) {
287 for (Screen screen: screens) {
288 screen.putCharXY(x, y, ch);
289 }
290 }
291
292 /**
293 * Render a string. Does not wrap if the string exceeds the line.
294 *
295 * @param x column coordinate. 0 is the left-most column.
296 * @param y row coordinate. 0 is the top-most row.
297 * @param str string to draw
298 * @param attr attributes to use (bold, foreColor, backColor)
299 */
300 public void putStringXY(final int x, final int y, final String str,
301 final CellAttributes attr) {
302
303 for (Screen screen: screens) {
304 screen.putStringXY(x, y, str, attr);
305 }
306 }
307
308 /**
309 * Render a string without changing the underlying attribute. Does not
310 * wrap if the string exceeds the line.
311 *
312 * @param x column coordinate. 0 is the left-most column.
313 * @param y row coordinate. 0 is the top-most row.
314 * @param str string to draw
315 */
316 public void putStringXY(final int x, final int y, final String str) {
317 for (Screen screen: screens) {
318 screen.putStringXY(x, y, str);
319 }
320 }
321
322 /**
323 * Draw a vertical line from (x, y) to (x, y + n).
324 *
325 * @param x column coordinate. 0 is the left-most column.
326 * @param y row coordinate. 0 is the top-most row.
327 * @param n number of characters to draw
328 * @param ch character to draw
329 * @param attr attributes to use (bold, foreColor, backColor)
330 */
331 public void vLineXY(final int x, final int y, final int n,
332 final char ch, final CellAttributes attr) {
333
334 for (Screen screen: screens) {
335 screen.vLineXY(x, y, n, ch, attr);
336 }
337 }
338
339 /**
340 * Draw a horizontal line from (x, y) to (x + n, y).
341 *
342 * @param x column coordinate. 0 is the left-most column.
343 * @param y row coordinate. 0 is the top-most row.
344 * @param n number of characters to draw
345 * @param ch character to draw
346 * @param attr attributes to use (bold, foreColor, backColor)
347 */
348 public void hLineXY(final int x, final int y, final int n,
349 final char ch, final CellAttributes attr) {
350
351 for (Screen screen: screens) {
352 screen.hLineXY(x, y, n, ch, attr);
353 }
354 }
355
356 /**
357 * Change the width. Everything on-screen will be destroyed and must be
358 * redrawn.
359 *
360 * @param width new screen width
361 */
362 public void setWidth(final int width) {
363 for (Screen screen: screens) {
364 screen.setWidth(width);
365 }
366 }
367
368 /**
369 * Change the height. Everything on-screen will be destroyed and must be
370 * redrawn.
371 *
372 * @param height new screen height
373 */
374 public void setHeight(final int height) {
375 for (Screen screen: screens) {
376 screen.setHeight(height);
377 }
378 }
379
380 /**
381 * Change the width and height. Everything on-screen will be destroyed
382 * and must be redrawn.
383 *
384 * @param width new screen width
385 * @param height new screen height
386 */
387 public void setDimensions(final int width, final int height) {
388 for (Screen screen: screens) {
389 screen.setDimensions(width, height);
390 }
391 }
392
393 /**
394 * Get the height.
395 *
396 * @return current screen height
397 */
398 public int getHeight() {
a69ed767
KL
399 // Return the smallest height of the screens.
400 int height = screens.get(0).getHeight();
401 for (Screen screen: screens) {
402 if (screen.getHeight() < height) {
403 height = screen.getHeight();
404 }
405 }
406 return height;
88a99379
KL
407 }
408
409 /**
410 * Get the width.
411 *
412 * @return current screen width
413 */
414 public int getWidth() {
a69ed767
KL
415 // Return the smallest width of the screens.
416 int width = screens.get(0).getWidth();
417 for (Screen screen: screens) {
418 if (screen.getWidth() < width) {
419 width = screen.getWidth();
420 }
421 }
422 return width;
88a99379
KL
423 }
424
425 /**
426 * Reset screen to not-bold, white-on-black. Also flushes the offset and
427 * clip variables.
428 */
429 public void reset() {
430 for (Screen screen: screens) {
431 screen.reset();
432 }
433 }
434
435 /**
436 * Flush the offset and clip variables.
437 */
438 public void resetClipping() {
439 for (Screen screen: screens) {
440 screen.resetClipping();
441 }
442 }
443
444 /**
445 * Clear the logical screen.
446 */
447 public void clear() {
448 for (Screen screen: screens) {
449 screen.clear();
450 }
451 }
452
453 /**
454 * Draw a box with a border and empty background.
455 *
456 * @param left left column of box. 0 is the left-most row.
457 * @param top top row of the box. 0 is the top-most row.
458 * @param right right column of box
459 * @param bottom bottom row of the box
460 * @param border attributes to use for the border
461 * @param background attributes to use for the background
462 */
463 public void drawBox(final int left, final int top,
464 final int right, final int bottom,
465 final CellAttributes border, final CellAttributes background) {
466
467 for (Screen screen: screens) {
468 screen.drawBox(left, top, right, bottom, border, background);
469 }
470 }
471
472 /**
473 * Draw a box with a border and empty background.
474 *
475 * @param left left column of box. 0 is the left-most row.
476 * @param top top row of the box. 0 is the top-most row.
477 * @param right right column of box
478 * @param bottom bottom row of the box
479 * @param border attributes to use for the border
480 * @param background attributes to use for the background
481 * @param borderType if 1, draw a single-line border; if 2, draw a
482 * double-line border; if 3, draw double-line top/bottom edges and
483 * single-line left/right edges (like Qmodem)
484 * @param shadow if true, draw a "shadow" on the box
485 */
486 public void drawBox(final int left, final int top,
487 final int right, final int bottom,
488 final CellAttributes border, final CellAttributes background,
489 final int borderType, final boolean shadow) {
490
491 for (Screen screen: screens) {
492 screen.drawBox(left, top, right, bottom, border, background,
493 borderType, shadow);
494 }
495 }
496
497 /**
498 * Draw a box shadow.
499 *
500 * @param left left column of box. 0 is the left-most row.
501 * @param top top row of the box. 0 is the top-most row.
502 * @param right right column of box
503 * @param bottom bottom row of the box
504 */
505 public void drawBoxShadow(final int left, final int top,
506 final int right, final int bottom) {
507
508 for (Screen screen: screens) {
509 screen.drawBoxShadow(left, top, right, bottom);
510 }
511 }
512
a69ed767
KL
513 /**
514 * Clear the physical screen.
515 */
516 public void clearPhysical() {
517 for (Screen screen: screens) {
518 screen.clearPhysical();
519 }
520 }
521
522 /**
523 * Unset every image cell on one row of the physical screen, forcing
524 * images on that row to be redrawn.
525 *
526 * @param y row coordinate. 0 is the top-most row.
527 */
528 public final void unsetImageRow(final int y) {
529 for (Screen screen: screens) {
530 screen.unsetImageRow(y);
531 }
532 }
533
88a99379
KL
534 /**
535 * Classes must provide an implementation to push the logical screen to
536 * the physical device.
537 */
538 public void flushPhysical() {
539 for (Screen screen: screens) {
540 screen.flushPhysical();
541 }
542 }
543
544 /**
545 * Put the cursor at (x,y).
546 *
547 * @param visible if true, the cursor should be visible
548 * @param x column coordinate to put the cursor on
549 * @param y row coordinate to put the cursor on
550 */
551 public void putCursor(final boolean visible, final int x, final int y) {
552 for (Screen screen: screens) {
553 screen.putCursor(visible, x, y);
554 }
555 }
556
557 /**
558 * Hide the cursor.
559 */
560 public void hideCursor() {
561 for (Screen screen: screens) {
562 screen.hideCursor();
563 }
564 }
565
3e074355
KL
566 /**
567 * Get the cursor visibility.
568 *
569 * @return true if the cursor is visible
570 */
571 public boolean isCursorVisible() {
572 return screens.get(0).isCursorVisible();
573 }
574
575 /**
576 * Get the cursor X position.
577 *
578 * @return the cursor x column position
579 */
580 public int getCursorX() {
581 return screens.get(0).getCursorX();
582 }
583
584 /**
585 * Get the cursor Y position.
586 *
587 * @return the cursor y row position
588 */
589 public int getCursorY() {
590 return screens.get(0).getCursorY();
591 }
592
88a99379
KL
593 /**
594 * Set the window title.
595 *
596 * @param title the new title
597 */
598 public void setTitle(final String title) {
599 for (Screen screen: screens) {
600 screen.setTitle(title);
601 }
602 }
603
d36057df
KL
604 // ------------------------------------------------------------------------
605 // MultiScreen ------------------------------------------------------------
606 // ------------------------------------------------------------------------
607
608 /**
609 * Add a screen to the list.
610 *
611 * @param screen the screen to add
612 */
613 public void addScreen(final Screen screen) {
614 screens.add(screen);
615 }
616
617 /**
618 * Remove a screen from the list.
619 *
620 * @param screen the screen to remove
621 */
622 public void removeScreen(final Screen screen) {
623 if (screens.size() > 1) {
624 screens.remove(screen);
625 }
626 }
627
a69ed767
KL
628 /**
629 * Get the width of a character cell in pixels.
630 *
631 * @return the width in pixels of a character cell
632 */
633 public int getTextWidth() {
634 int textWidth = 16;
635 for (Screen screen: screens) {
636 int newTextWidth = textWidth;
637 if (screen instanceof MultiScreen) {
638 newTextWidth = ((MultiScreen) screen).getTextWidth();
639 } else if (screen instanceof ECMA48Terminal) {
640 newTextWidth = ((ECMA48Terminal) screen).getTextWidth();
641 } else if (screen instanceof SwingTerminal) {
642 newTextWidth = ((SwingTerminal) screen).getTextWidth();
643 }
644 if (newTextWidth < textWidth) {
645 textWidth = newTextWidth;
646 }
647 }
648 return textWidth;
649 }
650
651 /**
652 * Get the height of a character cell in pixels.
653 *
654 * @return the height in pixels of a character cell
655 */
656 public int getTextHeight() {
657 int textHeight = 20;
658 for (Screen screen: screens) {
659 int newTextHeight = textHeight;
660 if (screen instanceof MultiScreen) {
661 newTextHeight = ((MultiScreen) screen).getTextHeight();
662 } else if (screen instanceof ECMA48Terminal) {
663 newTextHeight = ((ECMA48Terminal) screen).getTextHeight();
664 } else if (screen instanceof SwingTerminal) {
665 newTextHeight = ((SwingTerminal) screen).getTextHeight();
666 }
667 if (newTextHeight < textHeight) {
668 textHeight = newTextHeight;
669 }
670 }
671 return textHeight;
672 }
673
88a99379 674}