X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fio%2FScreen.java;h=720f8a9789f5222182bab77ec5b50fd3f74f318d;hb=e16dda65585466c8987bd1efd718431450a96605;hp=1ecc3040f4add2461348fb70df33848bb5f95328;hpb=05dbb28d6e8613216f43e8d0fae487c1d9c2fcd3;p=nikiroo-utils.git diff --git a/src/jexer/io/Screen.java b/src/jexer/io/Screen.java index 1ecc304..720f8a9 100644 --- a/src/jexer/io/Screen.java +++ b/src/jexer/io/Screen.java @@ -1,34 +1,30 @@ -/** +/* * Jexer - Java Text User Interface * - * Version: $Id$ - * - * Author: Kevin Lamonte, kevin.lamonte@gmail.com - * - * License: LGPLv3 or later + * The MIT License (MIT) * - * Copyright: This module is licensed under the GNU Lesser General - * Public License Version 3. Please see the file "COPYING" in this - * directory for more information about the GNU Lesser General Public - * License Version 3. + * Copyright (C) 2016 Kevin Lamonte * - * Copyright (C) 2015 Kevin Lamonte + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 3 of - * the License, or (at your option) any later version. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, see - * http://www.gnu.org/licenses/, or write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA + * @author Kevin Lamonte [kevin.lamonte@gmail.com] + * @version 1 */ package jexer.io; @@ -43,84 +39,179 @@ import jexer.bits.GraphicsChars; public abstract class Screen { /** - * Emit debugging to stderr + * Width of the visible window. */ - public boolean debugToStderr; + protected int width; /** - * Width of the visible window + * Height of the visible window. */ - protected int width; + protected int height; /** - * Height of the visible window + * Drawing offset for x. */ - protected int height; + private int offsetX; + + /** + * Set drawing offset for x. + * + * @param offsetX new drawing offset + */ + public final void setOffsetX(final int offsetX) { + this.offsetX = offsetX; + } + + /** + * Drawing offset for y. + */ + private int offsetY; + + /** + * Set drawing offset for y. + * + * @param offsetY new drawing offset + */ + public final void setOffsetY(final int offsetY) { + this.offsetY = offsetY; + } + + /** + * Ignore anything drawn right of clipRight. + */ + private int clipRight; + + /** + * Get right drawing clipping boundary. + * + * @return drawing boundary + */ + public final int getClipRight() { + return clipRight; + } + + /** + * Set right drawing clipping boundary. + * + * @param clipRight new boundary + */ + public final void setClipRight(final int clipRight) { + this.clipRight = clipRight; + } /** - * Drawing offset for x + * Ignore anything drawn below clipBottom. */ - public int offsetX; + private int clipBottom; /** - * Drawing offset for y + * Get bottom drawing clipping boundary. + * + * @return drawing boundary */ - public int offsetY; + public final int getClipBottom() { + return clipBottom; + } /** - * Ignore anything drawn right of clipRight + * Set bottom drawing clipping boundary. + * + * @param clipBottom new boundary */ - public int clipRight; + public final void setClipBottom(final int clipBottom) { + this.clipBottom = clipBottom; + } /** - * Ignore anything drawn below clipBottom + * Ignore anything drawn left of clipLeft. */ - public int clipBottom; + private int clipLeft; /** - * Ignore anything drawn left of clipLeft + * Get left drawing clipping boundary. + * + * @return drawing boundary */ - public int clipLeft; + public final int getClipLeft() { + return clipLeft; + } /** - * Ignore anything drawn above clipTop + * Set left drawing clipping boundary. + * + * @param clipLeft new boundary */ - public int clipTop; + public final void setClipLeft(final int clipLeft) { + this.clipLeft = clipLeft; + } /** - * The physical screen last sent out on flush() + * Ignore anything drawn above clipTop. + */ + private int clipTop; + + /** + * Get top drawing clipping boundary. + * + * @return drawing boundary + */ + public final int getClipTop() { + return clipTop; + } + + /** + * Set top drawing clipping boundary. + * + * @param clipTop new boundary + */ + public final void setClipTop(final int clipTop) { + this.clipTop = clipTop; + } + + /** + * The physical screen last sent out on flush(). */ protected Cell [][] physical; /** - * The logical screen being rendered to + * The logical screen being rendered to. */ protected Cell [][] logical; /** - * When true, logical != physical + * When true, logical != physical. + */ + protected volatile boolean dirty; + + /** + * Get dirty flag. + * + * @return if true, the logical screen is not in sync with the physical + * screen */ - public boolean dirty; + public final boolean isDirty() { + return dirty; + } /** * Set if the user explicitly wants to redraw everything starting with a - * ECMATerminal.clearAll() + * ECMATerminal.clearAll(). */ protected boolean reallyCleared; /** * If true, the cursor is visible and should be placed onscreen at - * (cursorX, cursorY) during a call to flushPhysical() + * (cursorX, cursorY) during a call to flushPhysical(). */ protected boolean cursorVisible; /** - * Cursor X position if visible + * Cursor X position if visible. */ protected int cursorX; /** - * Cursor Y position if visible + * Cursor Y position if visible. */ protected int cursorY; @@ -131,10 +222,12 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @return attributes at (x, y) */ - public CellAttributes getAttrXY(int x, int y) { - CellAttributes attr = new CellAttributes(); - attr.setTo(logical[x][y]); - return attr; + public final CellAttributes getAttrXY(final int x, final int y) { + CellAttributes attr = new CellAttributes(); + if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) { + attr.setTo(logical[x][y]); + } + return attr; } /** @@ -144,10 +237,12 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param attr attributes to use (bold, foreColor, backColor) */ - public void putAttrXY(int x, int y, CellAttributes attr) { - putAttrXY(x, y, attr, true); + public final void putAttrXY(final int x, final int y, + final CellAttributes attr) { + + putAttrXY(x, y, attr, true); } - + /** * Set the attributes at one location. * @@ -156,30 +251,34 @@ public abstract class Screen { * @param attr attributes to use (bold, foreColor, backColor) * @param clip if true, honor clipping/offset */ - public void putAttrXY(int x, int y, CellAttributes attr, boolean clip) { - - int X = x; - int Y = y; - - if (clip) { - if ((x < clipLeft) || (x >= clipRight) || - (y < clipTop) || (y >= clipBottom)) { - return; - } - X += offsetX; - Y += offsetY; - } - - if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) { - dirty = true; - logical[X][Y].foreColor = attr.foreColor; - logical[X][Y].backColor = attr.backColor; - logical[X][Y].bold = attr.bold; - logical[X][Y].blink = attr.blink; - logical[X][Y].reverse = attr.reverse; - logical[X][Y].underline = attr.underline; - logical[X][Y].protect = attr.protect; - } + public final void putAttrXY(final int x, final int y + , final CellAttributes attr, final boolean clip) { + + int X = x; + int Y = y; + + if (clip) { + if ((x < clipLeft) + || (x >= clipRight) + || (y < clipTop) + || (y >= clipBottom) + ) { + return; + } + X += offsetX; + Y += offsetY; + } + + if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) { + dirty = true; + logical[X][Y].setForeColor(attr.getForeColor()); + logical[X][Y].setBackColor(attr.getBackColor()); + logical[X][Y].setBold(attr.isBold()); + logical[X][Y].setBlink(attr.isBlink()); + logical[X][Y].setReverse(attr.isReverse()); + logical[X][Y].setUnderline(attr.isUnderline()); + logical[X][Y].setProtect(attr.isProtect()); + } } /** @@ -188,12 +287,13 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void putAll(char ch, CellAttributes attr) { - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - putCharXY(x, y, ch, attr); - } - } + public final void putAll(final char ch, final CellAttributes attr) { + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + putCharXY(x, y, ch, attr); + } + } } /** @@ -203,8 +303,8 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param ch character + attributes to draw */ - public void putCharXY(int x, int y, Cell ch) { - putCharXY(x, y, ch.ch, ch); + public final void putCharXY(final int x, final int y, final Cell ch) { + putCharXY(x, y, ch.getChar(), ch); } /** @@ -215,33 +315,38 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void putCharXY(int x, int y, char ch, CellAttributes attr) { - if ((x < clipLeft) || (x >= clipRight) || - (y < clipTop) || (y >= clipBottom)) { - return; - } - - int X = x + offsetX; - int Y = y + offsetY; - - // stderr.writefln("putCharXY: %d, %d, %c", X, Y, ch); - - if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) { - dirty = true; - - // Do not put control characters on the display - assert(ch >= 0x20); - assert(ch != 0x7F); - - logical[X][Y].ch = ch; - logical[X][Y].foreColor = attr.foreColor; - logical[X][Y].backColor = attr.backColor; - logical[X][Y].bold = attr.bold; - logical[X][Y].blink = attr.blink; - logical[X][Y].reverse = attr.reverse; - logical[X][Y].underline = attr.underline; - logical[X][Y].protect = attr.protect; - } + public final void putCharXY(final int x, final int y, final char ch, + final CellAttributes attr) { + + if ((x < clipLeft) + || (x >= clipRight) + || (y < clipTop) + || (y >= clipBottom) + ) { + return; + } + + int X = x + offsetX; + int Y = y + offsetY; + + // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch); + + if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) { + dirty = true; + + // Do not put control characters on the display + assert (ch >= 0x20); + assert (ch != 0x7F); + + logical[X][Y].setChar(ch); + logical[X][Y].setForeColor(attr.getForeColor()); + logical[X][Y].setBackColor(attr.getBackColor()); + logical[X][Y].setBold(attr.isBold()); + logical[X][Y].setBlink(attr.isBlink()); + logical[X][Y].setReverse(attr.isReverse()); + logical[X][Y].setUnderline(attr.isUnderline()); + logical[X][Y].setProtect(attr.isProtect()); + } } /** @@ -251,21 +356,25 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param ch character to draw */ - public void putCharXY(int x, int y, char ch) { - if ((x < clipLeft) || (x >= clipRight) || - (y < clipTop) || (y >= clipBottom)) { - return; - } + public final void putCharXY(final int x, final int y, final char ch) { + + if ((x < clipLeft) + || (x >= clipRight) + || (y < clipTop) + || (y >= clipBottom) + ) { + return; + } - int X = x + offsetX; - int Y = y + offsetY; + int X = x + offsetX; + int Y = y + offsetY; - // stderr.writefln("putCharXY: %d, %d, %c", X, Y, ch); + // System.err.printf("putCharXY: %d, %d, %c\n", X, Y, ch); - if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) { - dirty = true; - logical[X][Y].ch = ch; - } + if ((X >= 0) && (X < width) && (Y >= 0) && (Y < height)) { + dirty = true; + logical[X][Y].setChar(ch); + } } /** @@ -276,16 +385,18 @@ public abstract class Screen { * @param str string to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void putStrXY(int x, int y, String str, CellAttributes attr) { - int i = x; - for (int j = 0; j < str.length(); j++) { - char ch = str.charAt(j); - putCharXY(i, y, ch, attr); - i++; - if (i == width) { - break; - } - } + public final void putStringXY(final int x, final int y, final String str, + final CellAttributes attr) { + + int i = x; + for (int j = 0; j < str.length(); j++) { + char ch = str.charAt(j); + putCharXY(i, y, ch, attr); + i++; + if (i == width) { + break; + } + } } /** @@ -296,20 +407,21 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param str string to draw */ - public void putStrXY(int x, int y, String str) { - int i = x; - for (int j = 0; j < str.length(); j++) { - char ch = str.charAt(j); - putCharXY(i, y, ch); - i++; - if (i == width) { - break; - } - } + public final void putStringXY(final int x, final int y, final String str) { + + int i = x; + for (int j = 0; j < str.length(); j++) { + char ch = str.charAt(j); + putCharXY(i, y, ch); + i++; + if (i == width) { + break; + } + } } /** - * Draw a vertical line from (x, y) to (x, y + n) + * Draw a vertical line from (x, y) to (x, y + n). * * @param x column coordinate. 0 is the left-most column. * @param y row coordinate. 0 is the top-most row. @@ -317,14 +429,16 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void vLineXY(int x, int y, int n, char ch, CellAttributes attr) { - for (int i = y; i < y + n; i++) { - putCharXY(x, i, ch, attr); - } + public final void vLineXY(final int x, final int y, final int n, + final char ch, final CellAttributes attr) { + + for (int i = y; i < y + n; i++) { + putCharXY(x, i, ch, attr); + } } /** - * Draw a horizontal line from (x, y) to (x + n, y) + * Draw a horizontal line from (x, y) to (x + n, y). * * @param x column coordinate. 0 is the left-most column. * @param y row coordinate. 0 is the top-most row. @@ -332,10 +446,12 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void hLineXY(int x, int y, int n, char ch, CellAttributes attr) { - for (int i = x; i < x + n; i++) { - putCharXY(i, y, ch, attr); - } + public final void hLineXY(final int x, final int y, final int n, + final char ch, final CellAttributes attr) { + + for (int i = x; i < x + n; i++) { + putCharXY(i, y, ch, attr); + } } /** @@ -344,53 +460,53 @@ public abstract class Screen { * @param width new width * @param height new height */ - private void reallocate(int width, int height) { - if (logical != null) { - for (int row = 0; row < this.height; row++) { - for (int col = 0; col < this.width; col++) { - logical[col][row] = null; - } - } - logical = null; - } - logical = new Cell[width][height]; - if (physical != null) { - for (int row = 0; row < this.height; row++) { - for (int col = 0; col < this.width; col++) { - physical[col][row] = null; - } - } - physical = null; - } - physical = new Cell[width][height]; - - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - physical[col][row] = new Cell(); - logical[col][row] = new Cell(); - } - } - - this.width = width; - this.height = height; - - clipLeft = 0; - clipTop = 0; - clipRight = width; - clipBottom = height; - - reallyCleared = true; - dirty = true; + private synchronized void reallocate(final int width, final int height) { + if (logical != null) { + for (int row = 0; row < this.height; row++) { + for (int col = 0; col < this.width; col++) { + logical[col][row] = null; + } + } + logical = null; + } + logical = new Cell[width][height]; + if (physical != null) { + for (int row = 0; row < this.height; row++) { + for (int col = 0; col < this.width; col++) { + physical[col][row] = null; + } + } + physical = null; + } + physical = new Cell[width][height]; + + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + physical[col][row] = new Cell(); + logical[col][row] = new Cell(); + } + } + + this.width = width; + this.height = height; + + clipLeft = 0; + clipTop = 0; + clipRight = width; + clipBottom = height; + + reallyCleared = true; + dirty = true; } /** * Change the width. Everything on-screen will be destroyed and must be * redrawn. - * + * * @param width new screen width */ - public void setWidth(int width) { - reallocate(width, this.height); + public final synchronized void setWidth(final int width) { + reallocate(width, this.height); } /** @@ -399,8 +515,8 @@ public abstract class Screen { * * @param height new screen height */ - public void setHeight(int height) { - reallocate(this.width, height); + public final synchronized void setHeight(final int height) { + reallocate(this.width, height); } /** @@ -410,8 +526,8 @@ public abstract class Screen { * @param width new screen width * @param height new screen height */ - public void setDimensions(int width, int height) { - reallocate(width, height); + public final void setDimensions(final int width, final int height) { + reallocate(width, height); } /** @@ -419,8 +535,8 @@ public abstract class Screen { * * @return current screen height */ - public int getHeight() { - return this.height; + public final synchronized int getHeight() { + return this.height; } /** @@ -428,56 +544,66 @@ public abstract class Screen { * * @return current screen width */ - public int getWidth() { - return this.width; + public final synchronized int getWidth() { + return this.width; } /** * Public constructor. Sets everything to not-bold, white-on-black. */ - public Screen() { - debugToStderr = false; - - offsetX = 0; - offsetY = 0; - width = 80; - height = 24; - logical = null; - physical = null; - reallocate(width, height); + protected Screen() { + offsetX = 0; + offsetY = 0; + width = 80; + height = 24; + logical = null; + physical = null; + reallocate(width, height); } /** * Reset screen to not-bold, white-on-black. Also flushes the offset and * clip variables. */ - public void reset() { - dirty = true; - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - logical[col][row].reset(); - } - } - resetClipping(); + public final synchronized void reset() { + dirty = true; + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + logical[col][row].reset(); + } + } + resetClipping(); } /** * Flush the offset and clip variables. */ - public void resetClipping() { - offsetX = 0; - offsetY = 0; - clipLeft = 0; - clipTop = 0; - clipRight = width; - clipBottom = height; + public final void resetClipping() { + offsetX = 0; + offsetY = 0; + clipLeft = 0; + clipTop = 0; + clipRight = width; + clipBottom = height; } /** - * Force the screen to be fully cleared and redrawn on the next flush(). + * Clear the logical screen. */ - public void clear() { - reset(); + public final void clear() { + reset(); + } + + /** + * Clear the physical screen. + */ + public final void clearPhysical() { + dirty = true; + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + physical[col][row].reset(); + } + } } /** @@ -487,12 +613,14 @@ public abstract class Screen { * @param top top row of the box. 0 is the top-most row. * @param right right column of box * @param bottom bottom row of the box - * @param border attributes to use for the border (bold, foreColor, backColor) + * @param border attributes to use for the border * @param background attributes to use for the background */ - public void drawBox(int left, int top, int right, int bottom, - CellAttributes border, CellAttributes background) { - drawBox(left, top, right, bottom, border, background, 1, false); + public final void drawBox(final int left, final int top, + final int right, final int bottom, + final CellAttributes border, final CellAttributes background) { + + drawBox(left, top, right, bottom, border, background, 1, false); } /** @@ -502,126 +630,127 @@ public abstract class Screen { * @param top top row of the box. 0 is the top-most row. * @param right right column of box * @param bottom bottom row of the box - * @param border attributes to use for the border (bold, foreColor, backColor) + * @param border attributes to use for the border * @param background attributes to use for the background - * @param borderType = 1: single-line border - * 2: double-line borders - * 3: double-line top/bottom edges and single-line left/right edges + * @param borderType if 1, draw a single-line border; if 2, draw a + * double-line border; if 3, draw double-line top/bottom edges and + * single-line left/right edges (like Qmodem) * @param shadow if true, draw a "shadow" on the box */ - public void drawBox(int left, int top, int right, int bottom, - CellAttributes border, CellAttributes background, int borderType, - boolean shadow) { - - int boxTop = top; - int boxLeft = left; - int boxWidth = right - left; - int boxHeight = bottom - top; - - char cTopLeft; - char cTopRight; - char cBottomLeft; - char cBottomRight; - char cHSide; - char cVSide; - - switch (borderType) { - case 1: - cTopLeft = GraphicsChars.ULCORNER; - cTopRight = GraphicsChars.URCORNER; - cBottomLeft = GraphicsChars.LLCORNER; - cBottomRight = GraphicsChars.LRCORNER; - cHSide = GraphicsChars.SINGLE_BAR; - cVSide = GraphicsChars.WINDOW_SIDE; - break; - - case 2: - cTopLeft = GraphicsChars.WINDOW_LEFT_TOP_DOUBLE; - cTopRight = GraphicsChars.WINDOW_RIGHT_TOP_DOUBLE; - cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM_DOUBLE; - cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM_DOUBLE; - cHSide = GraphicsChars.DOUBLE_BAR; - cVSide = GraphicsChars.WINDOW_SIDE_DOUBLE; - break; - - case 3: - cTopLeft = GraphicsChars.WINDOW_LEFT_TOP; - cTopRight = GraphicsChars.WINDOW_RIGHT_TOP; - cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM; - cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM; - cHSide = GraphicsChars.WINDOW_TOP; - cVSide = GraphicsChars.WINDOW_SIDE; - break; - default: - throw new IllegalArgumentException("Invalid border type: " + borderType); - } - - // Place the corner characters - putCharXY(left, top, cTopLeft, border); - putCharXY(left + boxWidth - 1, top, cTopRight, border); - putCharXY(left, top + boxHeight - 1, cBottomLeft, border); - putCharXY(left + boxWidth - 1, top + boxHeight - 1, cBottomRight, - border); - - // Draw the box lines - hLineXY(left + 1, top, boxWidth - 2, cHSide, border); - vLineXY(left, top + 1, boxHeight - 2, cVSide, border); - hLineXY(left + 1, top + boxHeight - 1, boxWidth - 2, cHSide, border); - vLineXY(left + boxWidth - 1, top + 1, boxHeight - 2, cVSide, border); - - // Fill in the interior background - for (int i = 1; i < boxHeight - 1; i++) { - hLineXY(1 + left, i + top, boxWidth - 2, ' ', background); - } - - if (shadow) { - // Draw a shadow - drawBoxShadow(left, top, right, bottom); - } - } - - /** - * Draw a box shadow + public final void drawBox(final int left, final int top, + final int right, final int bottom, + final CellAttributes border, final CellAttributes background, + final int borderType, final boolean shadow) { + + int boxWidth = right - left; + int boxHeight = bottom - top; + + char cTopLeft; + char cTopRight; + char cBottomLeft; + char cBottomRight; + char cHSide; + char cVSide; + + switch (borderType) { + case 1: + cTopLeft = GraphicsChars.ULCORNER; + cTopRight = GraphicsChars.URCORNER; + cBottomLeft = GraphicsChars.LLCORNER; + cBottomRight = GraphicsChars.LRCORNER; + cHSide = GraphicsChars.SINGLE_BAR; + cVSide = GraphicsChars.WINDOW_SIDE; + break; + + case 2: + cTopLeft = GraphicsChars.WINDOW_LEFT_TOP_DOUBLE; + cTopRight = GraphicsChars.WINDOW_RIGHT_TOP_DOUBLE; + cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM_DOUBLE; + cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM_DOUBLE; + cHSide = GraphicsChars.DOUBLE_BAR; + cVSide = GraphicsChars.WINDOW_SIDE_DOUBLE; + break; + + case 3: + cTopLeft = GraphicsChars.WINDOW_LEFT_TOP; + cTopRight = GraphicsChars.WINDOW_RIGHT_TOP; + cBottomLeft = GraphicsChars.WINDOW_LEFT_BOTTOM; + cBottomRight = GraphicsChars.WINDOW_RIGHT_BOTTOM; + cHSide = GraphicsChars.WINDOW_TOP; + cVSide = GraphicsChars.WINDOW_SIDE; + break; + default: + throw new IllegalArgumentException("Invalid border type: " + + borderType); + } + + // Place the corner characters + putCharXY(left, top, cTopLeft, border); + putCharXY(left + boxWidth - 1, top, cTopRight, border); + putCharXY(left, top + boxHeight - 1, cBottomLeft, border); + putCharXY(left + boxWidth - 1, top + boxHeight - 1, cBottomRight, + border); + + // Draw the box lines + hLineXY(left + 1, top, boxWidth - 2, cHSide, border); + vLineXY(left, top + 1, boxHeight - 2, cVSide, border); + hLineXY(left + 1, top + boxHeight - 1, boxWidth - 2, cHSide, border); + vLineXY(left + boxWidth - 1, top + 1, boxHeight - 2, cVSide, border); + + // Fill in the interior background + for (int i = 1; i < boxHeight - 1; i++) { + hLineXY(1 + left, i + top, boxWidth - 2, ' ', background); + } + + if (shadow) { + // Draw a shadow + drawBoxShadow(left, top, right, bottom); + } + } + + /** + * Draw a box shadow. * * @param left left column of box. 0 is the left-most row. * @param top top row of the box. 0 is the top-most row. * @param right right column of box * @param bottom bottom row of the box */ - public void drawBoxShadow(int left, int top, int right, int bottom) { - - int boxTop = top; - int boxLeft = left; - int boxWidth = right - left; - int boxHeight = bottom - top; - CellAttributes shadowAttr = new CellAttributes(); - - // Shadows do not honor clipping but they DO honor offset. - int oldClipRight = clipRight; - int oldClipBottom = clipBottom; - /* - clipRight = boxWidth + 2; - clipBottom = boxHeight + 1; - */ - clipRight = width; - clipBottom = height; - - for (int i = 0; i < boxHeight; i++) { - putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr); - putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr); - } - for (int i = 0; i < boxWidth; i++) { - putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr); - } - clipRight = oldClipRight; - clipBottom = oldClipBottom; + public final void drawBoxShadow(final int left, final int top, + final int right, final int bottom) { + + int boxTop = top; + int boxLeft = left; + int boxWidth = right - left; + int boxHeight = bottom - top; + CellAttributes shadowAttr = new CellAttributes(); + + // Shadows do not honor clipping but they DO honor offset. + int oldClipRight = clipRight; + int oldClipBottom = clipBottom; + /* + clipRight = boxWidth + 2; + clipBottom = boxHeight + 1; + */ + clipRight = width; + clipBottom = height; + + for (int i = 0; i < boxHeight; i++) { + putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr); + putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr); + } + for (int i = 0; i < boxWidth; i++) { + putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr); + } + clipRight = oldClipRight; + clipBottom = oldClipBottom; } /** * Subclasses must provide an implementation to push the logical screen * to the physical device. */ - abstract public void flushPhysical(); + public abstract void flushPhysical(); /** * Put the cursor at (x,y). @@ -630,16 +759,17 @@ public abstract class Screen { * @param x column coordinate to put the cursor on * @param y row coordinate to put the cursor on */ - public void putCursor(boolean visible, int x, int y) { - cursorVisible = visible; - cursorX = x; - cursorY = y; + public void putCursor(final boolean visible, final int x, final int y) { + + cursorVisible = visible; + cursorX = x; + cursorY = y; } /** - * Hide the cursor + * Hide the cursor. */ - public void hideCursor() { - cursorVisible = false; + public final void hideCursor() { + cursorVisible = false; } }