+++ /dev/null
-/*
- * Jexer - Java Text User Interface
- *
- * The MIT License (MIT)
- *
- * Copyright (C) 2019 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:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * 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.
- *
- * @author Kevin Lamonte [kevin.lamonte@gmail.com]
- * @version 1
- */
-package jexer.backend;
-
-import java.awt.image.BufferedImage;
-
-import jexer.backend.GlyphMaker;
-import jexer.bits.Cell;
-import jexer.bits.CellAttributes;
-import jexer.bits.Clipboard;
-import jexer.bits.GraphicsChars;
-import jexer.bits.StringUtils;
-
-/**
- * A logical screen composed of a 2D array of Cells.
- */
-public class LogicalScreen implements Screen {
-
- // ------------------------------------------------------------------------
- // Variables --------------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * Width of the visible window.
- */
- protected int width;
-
- /**
- * Height of the visible window.
- */
- protected int height;
-
- /**
- * Drawing offset for x.
- */
- private int offsetX;
-
- /**
- * Drawing offset for y.
- */
- private int offsetY;
-
- /**
- * Ignore anything drawn right of clipRight.
- */
- private int clipRight;
-
- /**
- * Ignore anything drawn below clipBottom.
- */
- private int clipBottom;
-
- /**
- * Ignore anything drawn left of clipLeft.
- */
- private int clipLeft;
-
- /**
- * Ignore anything drawn above clipTop.
- */
- private int clipTop;
-
- /**
- * The physical screen last sent out on flush().
- */
- protected Cell [][] physical;
-
- /**
- * The logical screen being rendered to.
- */
- protected Cell [][] logical;
-
- /**
- * Set if the user explicitly wants to redraw everything starting with a
- * ECMATerminal.clearAll().
- */
- protected boolean reallyCleared;
-
- /**
- * If true, the cursor is visible and should be placed onscreen at
- * (cursorX, cursorY) during a call to flushPhysical().
- */
- protected boolean cursorVisible;
-
- /**
- * Cursor X position if visible.
- */
- protected int cursorX;
-
- /**
- * Cursor Y position if visible.
- */
- protected int cursorY;
-
- /**
- * The last used height of a character cell in pixels, only used for
- * full-width chars.
- */
- private int lastTextHeight = -1;
-
- /**
- * The glyph drawer for full-width chars.
- */
- private GlyphMaker glyphMaker = null;
-
- // ------------------------------------------------------------------------
- // Constructors -----------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * Public constructor. Sets everything to not-bold, white-on-black.
- */
- protected LogicalScreen() {
- offsetX = 0;
- offsetY = 0;
- width = 80;
- height = 24;
- logical = null;
- physical = null;
- reallocate(width, height);
- }
-
- // ------------------------------------------------------------------------
- // Screen -----------------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * Get the width of a character cell in pixels.
- *
- * @return the width in pixels of a character cell
- */
- public int getTextWidth() {
- // Default width is 16 pixels.
- return 16;
- }
-
- /**
- * Get the height of a character cell in pixels.
- *
- * @return the height in pixels of a character cell
- */
- public int getTextHeight() {
- // Default height is 20 pixels.
- return 20;
- }
-
- /**
- * Set drawing offset for x.
- *
- * @param offsetX new drawing offset
- */
- public final void setOffsetX(final int offsetX) {
- this.offsetX = offsetX;
- }
-
- /**
- * Set drawing offset for y.
- *
- * @param offsetY new drawing offset
- */
- public final void setOffsetY(final int offsetY) {
- this.offsetY = offsetY;
- }
-
- /**
- * 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;
- }
-
- /**
- * Get bottom drawing clipping boundary.
- *
- * @return drawing boundary
- */
- public final int getClipBottom() {
- return clipBottom;
- }
-
- /**
- * Set bottom drawing clipping boundary.
- *
- * @param clipBottom new boundary
- */
- public final void setClipBottom(final int clipBottom) {
- this.clipBottom = clipBottom;
- }
-
- /**
- * Get left drawing clipping boundary.
- *
- * @return drawing boundary
- */
- public final int getClipLeft() {
- return clipLeft;
- }
-
- /**
- * Set left drawing clipping boundary.
- *
- * @param clipLeft new boundary
- */
- public final void setClipLeft(final int clipLeft) {
- this.clipLeft = clipLeft;
- }
-
- /**
- * 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;
- }
-
- /**
- * Get dirty flag.
- *
- * @return if true, the logical screen is not in sync with the physical
- * screen
- */
- public final boolean isDirty() {
- for (int x = 0; x < width; x++) {
- for (int y = 0; y < height; y++) {
- if (!logical[x][y].equals(physical[x][y])) {
- return true;
- }
- if (logical[x][y].isBlink()) {
- // Blinking screens are always dirty. There is
- // opportunity for a Netscape blink tag joke here...
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Get the attributes at one location.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @return attributes at (x, y)
- */
- 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;
- }
-
- /**
- * Get the cell at one location.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @return the character + attributes
- */
- public Cell getCharXY(final int x, final int y) {
- Cell cell = new Cell();
- if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
- cell.setTo(logical[x][y]);
- }
- return cell;
- }
-
- /**
- * Set the attributes at one location.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param attr attributes to use (bold, foreColor, backColor)
- */
- public final void putAttrXY(final int x, final int y,
- final CellAttributes attr) {
-
- putAttrXY(x, y, attr, true);
- }
-
- /**
- * Set the attributes at one location.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param attr attributes to use (bold, foreColor, backColor)
- * @param clip if true, honor clipping/offset
- */
- 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)) {
- logical[X][Y].setTo(attr);
-
- // If this happens to be the cursor position, make the position
- // dirty.
- if ((cursorX == X) && (cursorY == Y)) {
- physical[cursorX][cursorY].unset();
- unsetImageRow(cursorY);
- }
- }
- }
-
- /**
- * Fill the entire screen with one character with attributes.
- *
- * @param ch character to draw
- * @param attr attributes to use (bold, foreColor, backColor)
- */
- public final void putAll(final int ch, final CellAttributes attr) {
-
- for (int x = 0; x < width; x++) {
- for (int y = 0; y < height; y++) {
- putCharXY(x, y, ch, attr);
- }
- }
- }
-
- /**
- * Render one character with attributes.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param ch character + attributes to draw
- */
- public final void putCharXY(final int x, final int y, final Cell ch) {
- if ((x < clipLeft)
- || (x >= clipRight)
- || (y < clipTop)
- || (y >= clipBottom)
- ) {
- return;
- }
-
- if ((StringUtils.width(ch.getChar()) == 2) && (!ch.isImage())) {
- putFullwidthCharXY(x, y, ch);
- 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)) {
-
- // Do not put control characters on the display
- if (!ch.isImage()) {
- assert (ch.getChar() >= 0x20);
- assert (ch.getChar() != 0x7F);
- }
- logical[X][Y].setTo(ch);
-
- // If this happens to be the cursor position, make the position
- // dirty.
- if ((cursorX == X) && (cursorY == Y)) {
- physical[cursorX][cursorY].unset();
- unsetImageRow(cursorY);
- }
- }
- }
-
- /**
- * Render one character with attributes.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param ch character to draw
- * @param attr attributes to use (bold, foreColor, backColor)
- */
- public final void putCharXY(final int x, final int y, final int ch,
- final CellAttributes attr) {
-
- if ((x < clipLeft)
- || (x >= clipRight)
- || (y < clipTop)
- || (y >= clipBottom)
- ) {
- return;
- }
-
- if (StringUtils.width(ch) == 2) {
- putFullwidthCharXY(x, y, ch, attr);
- 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)) {
-
- // Do not put control characters on the display
- assert (ch >= 0x20);
- assert (ch != 0x7F);
-
- logical[X][Y].setTo(attr);
- logical[X][Y].setChar(ch);
-
- // If this happens to be the cursor position, make the position
- // dirty.
- if ((cursorX == X) && (cursorY == Y)) {
- physical[cursorX][cursorY].unset();
- unsetImageRow(cursorY);
- }
- }
- }
-
- /**
- * Render one character without changing the underlying attributes.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param ch character to draw
- */
- public final void putCharXY(final int x, final int y, final int ch) {
- if ((x < clipLeft)
- || (x >= clipRight)
- || (y < clipTop)
- || (y >= clipBottom)
- ) {
- return;
- }
-
- if (StringUtils.width(ch) == 2) {
- putFullwidthCharXY(x, y, ch);
- 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)) {
- logical[X][Y].setChar(ch);
-
- // If this happens to be the cursor position, make the position
- // dirty.
- if ((cursorX == X) && (cursorY == Y)) {
- physical[cursorX][cursorY].unset();
- unsetImageRow(cursorY);
- }
- }
- }
-
- /**
- * Render a string. Does not wrap if the string exceeds the line.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param str string to draw
- * @param attr attributes to use (bold, foreColor, backColor)
- */
- 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();) {
- int ch = str.codePointAt(j);
- j += Character.charCount(ch);
- putCharXY(i, y, ch, attr);
- i += StringUtils.width(ch);
- if (i == width) {
- break;
- }
- }
- }
-
- /**
- * Render a string without changing the underlying attribute. Does not
- * wrap if the string exceeds the line.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param str string to draw
- */
- public final void putStringXY(final int x, final int y, final String str) {
-
- int i = x;
- for (int j = 0; j < str.length();) {
- int ch = str.codePointAt(j);
- j += Character.charCount(ch);
- putCharXY(i, y, ch);
- i += StringUtils.width(ch);
- if (i == width) {
- break;
- }
- }
- }
-
- /**
- * 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.
- * @param n number of characters to draw
- * @param ch character to draw
- * @param attr attributes to use (bold, foreColor, backColor)
- */
- public final void vLineXY(final int x, final int y, final int n,
- final int 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).
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param n number of characters to draw
- * @param ch character to draw
- * @param attr attributes to use (bold, foreColor, backColor)
- */
- public final void hLineXY(final int x, final int y, final int n,
- final int ch, final CellAttributes attr) {
-
- for (int i = x; i < x + n; i++) {
- putCharXY(i, y, ch, attr);
- }
- }
-
- /**
- * Change the width. Everything on-screen will be destroyed and must be
- * redrawn.
- *
- * @param width new screen width
- */
- public final synchronized void setWidth(final int width) {
- reallocate(width, this.height);
- }
-
- /**
- * Change the height. Everything on-screen will be destroyed and must be
- * redrawn.
- *
- * @param height new screen height
- */
- public final synchronized void setHeight(final int height) {
- reallocate(this.width, height);
- }
-
- /**
- * Change the width and height. Everything on-screen will be destroyed
- * and must be redrawn.
- *
- * @param width new screen width
- * @param height new screen height
- */
- public final void setDimensions(final int width, final int height) {
- reallocate(width, height);
- resizeToScreen();
- }
-
- /**
- * Resize the physical screen to match the logical screen dimensions.
- */
- public void resizeToScreen() {
- // Subclasses are expected to override this.
- }
-
- /**
- * Get the height.
- *
- * @return current screen height
- */
- public final synchronized int getHeight() {
- return this.height;
- }
-
- /**
- * Get the width.
- *
- * @return current screen width
- */
- public final synchronized int getWidth() {
- return this.width;
- }
-
- /**
- * Reset screen to not-bold, white-on-black. Also flushes the offset and
- * clip variables.
- */
- public final synchronized void reset() {
- 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 final void resetClipping() {
- offsetX = 0;
- offsetY = 0;
- clipLeft = 0;
- clipTop = 0;
- clipRight = width;
- clipBottom = height;
- }
-
- /**
- * Clear the logical screen.
- */
- public final void clear() {
- reset();
- }
-
- /**
- * Draw a box with a border and empty background.
- *
- * @param left left column of box. 0 is the left-most column.
- * @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
- * @param background attributes to use for the background
- */
- 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);
- }
-
- /**
- * Draw a box with a border and empty background.
- *
- * @param left left column of box. 0 is the left-most column.
- * @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
- * @param background attributes to use for the background
- * @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 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 column.
- * @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 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;
- // When offsetX or offsetY go negative, we need to increase the clip
- // bounds.
- clipRight = width - offsetX;
- clipBottom = height - offsetY;
-
- for (int i = 0; i < boxHeight; i++) {
- Cell cell = getCharXY(offsetX + boxLeft + boxWidth,
- offsetY + boxTop + 1 + i);
- if (cell.getWidth() == Cell.Width.SINGLE) {
- putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr);
- } else {
- putCharXY(boxLeft + boxWidth, boxTop + 1 + i, ' ', shadowAttr);
- }
- cell = getCharXY(offsetX + boxLeft + boxWidth + 1,
- offsetY + boxTop + 1 + i);
- if (cell.getWidth() == Cell.Width.SINGLE) {
- putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr);
- } else {
- putCharXY(boxLeft + boxWidth + 1, boxTop + 1 + i, ' ',
- shadowAttr);
- }
- }
- for (int i = 0; i < boxWidth; i++) {
- Cell cell = getCharXY(offsetX + boxLeft + 2 + i,
- offsetY + boxTop + boxHeight);
- if (cell.getWidth() == Cell.Width.SINGLE) {
- putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr);
- } else {
- putCharXY(boxLeft + 2 + i, boxTop + boxHeight, ' ', shadowAttr);
- }
- }
- clipRight = oldClipRight;
- clipBottom = oldClipBottom;
- }
-
- /**
- * Default implementation does nothing.
- */
- public void flushPhysical() {}
-
- /**
- * Put the cursor at (x,y).
- *
- * @param visible if true, the cursor should be visible
- * @param x column coordinate to put the cursor on
- * @param y row coordinate to put the cursor on
- */
- public void putCursor(final boolean visible, final int x, final int y) {
- if ((cursorY >= 0)
- && (cursorX >= 0)
- && (cursorY <= height - 1)
- && (cursorX <= width - 1)
- ) {
- // Make the current cursor position dirty
- physical[cursorX][cursorY].unset();
- unsetImageRow(cursorY);
- }
-
- cursorVisible = visible;
- cursorX = x;
- cursorY = y;
- }
-
- /**
- * Hide the cursor.
- */
- public final void hideCursor() {
- cursorVisible = false;
- }
-
- /**
- * Get the cursor visibility.
- *
- * @return true if the cursor is visible
- */
- public boolean isCursorVisible() {
- return cursorVisible;
- }
-
- /**
- * Get the cursor X position.
- *
- * @return the cursor x column position
- */
- public int getCursorX() {
- return cursorX;
- }
-
- /**
- * Get the cursor Y position.
- *
- * @return the cursor y row position
- */
- public int getCursorY() {
- return cursorY;
- }
-
- /**
- * Set the window title. Default implementation does nothing.
- *
- * @param title the new title
- */
- public void setTitle(final String title) {}
-
- // ------------------------------------------------------------------------
- // LogicalScreen ----------------------------------------------------------
- // ------------------------------------------------------------------------
-
- /**
- * Reallocate screen buffers.
- *
- * @param width new width
- * @param height new height
- */
- 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;
- }
-
- /**
- * Clear the physical screen.
- */
- public final void clearPhysical() {
- for (int row = 0; row < height; row++) {
- for (int col = 0; col < width; col++) {
- physical[col][row].unset();
- }
- }
- }
-
- /**
- * Unset every image cell on one row of the physical screen, forcing
- * images on that row to be redrawn.
- *
- * @param y row coordinate. 0 is the top-most row.
- */
- public final void unsetImageRow(final int y) {
- if ((y < 0) || (y >= height)) {
- return;
- }
- for (int x = 0; x < width; x++) {
- if (logical[x][y].isImage()) {
- physical[x][y].unset();
- }
- }
- }
-
- /**
- * Render one fullwidth cell.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param cell the cell to draw
- */
- public final void putFullwidthCharXY(final int x, final int y,
- final Cell cell) {
-
- int cellWidth = getTextWidth();
- int cellHeight = getTextHeight();
-
- if (lastTextHeight != cellHeight) {
- glyphMaker = GlyphMaker.getInstance(cellHeight);
- lastTextHeight = cellHeight;
- }
- BufferedImage image = glyphMaker.getImage(cell, cellWidth * 2,
- cellHeight);
- BufferedImage leftImage = image.getSubimage(0, 0, cellWidth,
- cellHeight);
- BufferedImage rightImage = image.getSubimage(cellWidth, 0, cellWidth,
- cellHeight);
-
- Cell left = new Cell(cell);
- left.setImage(leftImage);
- left.setWidth(Cell.Width.LEFT);
- putCharXY(x, y, left);
-
- Cell right = new Cell(cell);
- right.setImage(rightImage);
- right.setWidth(Cell.Width.RIGHT);
- putCharXY(x + 1, y, right);
- }
-
- /**
- * Render one fullwidth character with attributes.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param ch character to draw
- * @param attr attributes to use (bold, foreColor, backColor)
- */
- public final void putFullwidthCharXY(final int x, final int y,
- final int ch, final CellAttributes attr) {
-
- Cell cell = new Cell(ch, attr);
- putFullwidthCharXY(x, y, cell);
- }
-
- /**
- * Render one fullwidth character with attributes.
- *
- * @param x column coordinate. 0 is the left-most column.
- * @param y row coordinate. 0 is the top-most row.
- * @param ch character to draw
- */
- public final void putFullwidthCharXY(final int x, final int y,
- final int ch) {
-
- Cell cell = new Cell(ch);
- cell.setAttr(getAttrXY(x, y));
- putFullwidthCharXY(x, y, cell);
- }
-
- /**
- * Invert the cell color at a position, including both halves of a
- * double-width cell.
- *
- * @param x column position
- * @param y row position
- */
- public void invertCell(final int x, final int y) {
- invertCell(x, y, false);
- }
-
- /**
- * Invert the cell color at a position.
- *
- * @param x column position
- * @param y row position
- * @param onlyThisCell if true, only invert this cell, otherwise invert
- * both halves of a double-width cell if necessary
- */
- public void invertCell(final int x, final int y,
- final boolean onlyThisCell) {
-
- Cell cell = getCharXY(x, y);
- if (cell.isImage()) {
- cell.invertImage();
- }
- if (cell.getForeColorRGB() < 0) {
- cell.setForeColor(cell.getForeColor().invert());
- } else {
- cell.setForeColorRGB(cell.getForeColorRGB() ^ 0x00ffffff);
- }
- if (cell.getBackColorRGB() < 0) {
- cell.setBackColor(cell.getBackColor().invert());
- } else {
- cell.setBackColorRGB(cell.getBackColorRGB() ^ 0x00ffffff);
- }
- putCharXY(x, y, cell);
- if ((onlyThisCell == true) || (cell.getWidth() == Cell.Width.SINGLE)) {
- return;
- }
-
- // This cell is one half of a fullwidth glyph. Invert the other
- // half.
- if (cell.getWidth() == Cell.Width.LEFT) {
- if (x < width - 1) {
- Cell rightHalf = getCharXY(x + 1, y);
- if (rightHalf.getWidth() == Cell.Width.RIGHT) {
- invertCell(x + 1, y, true);
- return;
- }
- }
- }
- if (cell.getWidth() == Cell.Width.RIGHT) {
- if (x > 0) {
- Cell leftHalf = getCharXY(x - 1, y);
- if (leftHalf.getWidth() == Cell.Width.LEFT) {
- invertCell(x - 1, y, true);
- }
- }
- }
- }
-
- /**
- * Set a selection area on the screen.
- *
- * @param x0 the starting X position of the selection
- * @param y0 the starting Y position of the selection
- * @param x1 the ending X position of the selection
- * @param y1 the ending Y position of the selection
- * @param rectangle if true, this is a rectangle select
- */
- public void setSelection(final int x0, final int y0,
- final int x1, final int y1, final boolean rectangle) {
-
- int startX = x0;
- int startY = y0;
- int endX = x1;
- int endY = y1;
-
- if (((x1 < x0) && (y1 <= y0))
- || ((x1 <= x0) && (y1 < y0))
- ) {
- // The user dragged from bottom-right to top-left. Reverse the
- // coordinates for the inverted section.
- startX = x1;
- startY = y1;
- endX = x0;
- endY = y0;
- }
- if (rectangle) {
- for (int y = startY; y <= endY; y++) {
- for (int x = startX; x <= endX; x++) {
- invertCell(x, y);
- }
- }
- } else {
- if (endY > startY) {
- for (int x = startX; x < width; x++) {
- invertCell(x, startY);
- }
- for (int y = startY + 1; y < endY; y++) {
- for (int x = 0; x < width; x++) {
- invertCell(x, y);
- }
- }
- for (int x = 0; x <= endX; x++) {
- invertCell(x, endY);
- }
- } else {
- assert (startY == endY);
- for (int x = startX; x <= endX; x++) {
- invertCell(x, startY);
- }
- }
- }
- }
-
- /**
- * Copy the screen selection area to the clipboard.
- *
- * @param clipboard the clipboard to use
- * @param x0 the starting X position of the selection
- * @param y0 the starting Y position of the selection
- * @param x1 the ending X position of the selection
- * @param y1 the ending Y position of the selection
- * @param rectangle if true, this is a rectangle select
- */
- public void copySelection(final Clipboard clipboard,
- final int x0, final int y0, final int x1, final int y1,
- final boolean rectangle) {
-
- StringBuilder sb = new StringBuilder();
-
- int startX = x0;
- int startY = y0;
- int endX = x1;
- int endY = y1;
-
- if (((x1 < x0) && (y1 <= y0))
- || ((x1 <= x0) && (y1 < y0))
- ) {
- // The user dragged from bottom-right to top-left. Reverse the
- // coordinates for the inverted section.
- startX = x1;
- startY = y1;
- endX = x0;
- endY = y0;
- }
- if (rectangle) {
- for (int y = startY; y <= endY; y++) {
- for (int x = startX; x <= endX; x++) {
- sb.append(Character.toChars(getCharXY(x, y).getChar()));
- }
- sb.append("\n");
- }
- } else {
- if (endY > startY) {
- for (int x = startX; x < width; x++) {
- sb.append(Character.toChars(getCharXY(x, startY).getChar()));
- }
- sb.append("\n");
- for (int y = startY + 1; y < endY; y++) {
- for (int x = 0; x < width; x++) {
- sb.append(Character.toChars(getCharXY(x, y).getChar()));
- }
- sb.append("\n");
- }
- for (int x = 0; x <= endX; x++) {
- sb.append(Character.toChars(getCharXY(x, endY).getChar()));
- }
- } else {
- assert (startY == endY);
- for (int x = startX; x <= endX; x++) {
- sb.append(Character.toChars(getCharXY(x, startY).getChar()));
- }
- }
- }
- clipboard.copyText(sb.toString());
- }
-
-}