X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fbackend%2FLogicalScreen.java;h=22b7e95f6564aad97431ca2e60a2fec09e9d2365;hb=505be508ae7d3fb48122be548b310a238cfb91eb;hp=8432e4eae526f8a74f395303ebb8fbd3d5f3c09e;hpb=42873e30bf487bc0b695d60652dba44f82185dbb;p=fanfix.git diff --git a/src/jexer/backend/LogicalScreen.java b/src/jexer/backend/LogicalScreen.java index 8432e4e..22b7e95 100644 --- a/src/jexer/backend/LogicalScreen.java +++ b/src/jexer/backend/LogicalScreen.java @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (C) 2017 Kevin Lamonte + * 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"), @@ -28,15 +28,24 @@ */ 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. */ @@ -52,6 +61,115 @@ public class LogicalScreen implements Screen { */ 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. * @@ -61,11 +179,6 @@ public class LogicalScreen implements Screen { this.offsetX = offsetX; } - /** - * Drawing offset for y. - */ - private int offsetY; - /** * Set drawing offset for y. * @@ -75,11 +188,6 @@ public class LogicalScreen implements Screen { this.offsetY = offsetY; } - /** - * Ignore anything drawn right of clipRight. - */ - private int clipRight; - /** * Get right drawing clipping boundary. * @@ -98,11 +206,6 @@ public class LogicalScreen implements Screen { this.clipRight = clipRight; } - /** - * Ignore anything drawn below clipBottom. - */ - private int clipBottom; - /** * Get bottom drawing clipping boundary. * @@ -121,11 +224,6 @@ public class LogicalScreen implements Screen { this.clipBottom = clipBottom; } - /** - * Ignore anything drawn left of clipLeft. - */ - private int clipLeft; - /** * Get left drawing clipping boundary. * @@ -144,11 +242,6 @@ public class LogicalScreen implements Screen { this.clipLeft = clipLeft; } - /** - * Ignore anything drawn above clipTop. - */ - private int clipTop; - /** * Get top drawing clipping boundary. * @@ -167,21 +260,6 @@ public class LogicalScreen implements Screen { this.clipTop = clipTop; } - /** - * The physical screen last sent out on flush(). - */ - protected Cell [][] physical; - - /** - * The logical screen being rendered to. - */ - protected Cell [][] logical; - - /** - * When true, logical != physical. - */ - protected volatile boolean dirty; - /** * Get dirty flag. * @@ -189,30 +267,21 @@ public class LogicalScreen implements Screen { * screen */ public final boolean isDirty() { - return dirty; - } - - /** - * 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; + 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; + } + } + } - /** - * Cursor Y position if visible. - */ - protected int cursorY; + return false; + } /** * Get the attributes at one location. @@ -229,6 +298,21 @@ public class LogicalScreen implements Screen { 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. * @@ -269,14 +353,14 @@ public class LogicalScreen implements Screen { } 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()); + 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); + } } } @@ -286,7 +370,7 @@ public class LogicalScreen implements Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public final void putAll(final char ch, final CellAttributes attr) { + public final void putAll(final int ch, final CellAttributes attr) { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { @@ -303,7 +387,40 @@ public class LogicalScreen implements Screen { * @param ch character + attributes to draw */ public final void putCharXY(final int x, final int y, final Cell ch) { - putCharXY(x, y, ch.getChar(), 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); + } + } } /** @@ -314,7 +431,7 @@ public class LogicalScreen implements Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public final void putCharXY(final int x, final int y, final char ch, + public final void putCharXY(final int x, final int y, final int ch, final CellAttributes attr) { if ((x < clipLeft) @@ -325,26 +442,31 @@ public class LogicalScreen implements Screen { 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)) { - dirty = true; // Do not put control characters on the display assert (ch >= 0x20); assert (ch != 0x7F); + logical[X][Y].setTo(attr); 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()); + + // If this happens to be the cursor position, make the position + // dirty. + if ((cursorX == X) && (cursorY == Y)) { + physical[cursorX][cursorY].unset(); + unsetImageRow(cursorY); + } } } @@ -355,8 +477,7 @@ public class LogicalScreen implements Screen { * @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 char ch) { - + public final void putCharXY(final int x, final int y, final int ch) { if ((x < clipLeft) || (x >= clipRight) || (y < clipTop) @@ -365,14 +486,25 @@ public class LogicalScreen implements Screen { 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)) { - dirty = true; 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); + } } } @@ -388,10 +520,11 @@ public class LogicalScreen implements Screen { final CellAttributes attr) { int i = x; - for (int j = 0; j < str.length(); j++) { - char ch = str.charAt(j); + for (int j = 0; j < str.length();) { + int ch = str.codePointAt(j); + j += Character.charCount(ch); putCharXY(i, y, ch, attr); - i++; + i += StringUtils.width(ch); if (i == width) { break; } @@ -409,10 +542,11 @@ public class LogicalScreen implements Screen { 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); + for (int j = 0; j < str.length();) { + int ch = str.codePointAt(j); + j += Character.charCount(ch); putCharXY(i, y, ch); - i++; + i += StringUtils.width(ch); if (i == width) { break; } @@ -429,7 +563,7 @@ public class LogicalScreen implements Screen { * @param attr attributes to use (bold, foreColor, backColor) */ public final void vLineXY(final int x, final int y, final int n, - final char ch, final CellAttributes attr) { + final int ch, final CellAttributes attr) { for (int i = y; i < y + n; i++) { putCharXY(x, i, ch, attr); @@ -446,58 +580,13 @@ public class LogicalScreen implements Screen { * @param attr attributes to use (bold, foreColor, backColor) */ public final void hLineXY(final int x, final int y, final int n, - final char ch, final CellAttributes attr) { + final int ch, final CellAttributes attr) { for (int i = x; i < x + n; i++) { putCharXY(i, y, ch, attr); } } - /** - * 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; - dirty = true; - } - /** * Change the width. Everything on-screen will be destroyed and must be * redrawn. @@ -527,6 +616,14 @@ public class LogicalScreen implements Screen { */ 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. } /** @@ -547,25 +644,11 @@ public class LogicalScreen implements Screen { return this.width; } - /** - * 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); - } - /** * Reset screen to not-bold, white-on-black. Also flushes the offset and * clip variables. */ 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(); @@ -593,22 +676,10 @@ public class LogicalScreen implements Screen { 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(); - } - } - } - /** * Draw a box with a border and empty background. * - * @param left left column of box. 0 is the left-most row. + * @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 @@ -625,7 +696,7 @@ public class LogicalScreen implements Screen { /** * Draw a box with a border and empty background. * - * @param left left column of box. 0 is the left-most row. + * @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 @@ -710,7 +781,7 @@ public class LogicalScreen implements Screen { /** * Draw a box shadow. * - * @param left left column of box. 0 is the left-most row. + * @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 @@ -727,19 +798,36 @@ public class LogicalScreen implements Screen { // 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; + // 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++) { - putAttrXY(boxLeft + boxWidth, boxTop + 1 + i, shadowAttr); - putAttrXY(boxLeft + boxWidth + 1, boxTop + 1 + i, shadowAttr); + 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++) { - putAttrXY(boxLeft + 2 + i, boxTop + boxHeight, shadowAttr); + 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; @@ -758,6 +846,15 @@ public class LogicalScreen implements Screen { * @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; @@ -771,6 +868,33 @@ public class LogicalScreen implements Screen { 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. * @@ -778,4 +902,325 @@ public class LogicalScreen implements Screen { */ 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)) + || (y1 < y0) + ) { + // The user dragged from bottom-to-top and/or right-to-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)) + || (y1 < y0) + ) { + // The user dragged from bottom-to-top and/or right-to-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()); + } + }