import jexer.bits.Cell;
import jexer.bits.CellAttributes;
+import jexer.bits.Clipboard;
import jexer.bits.ColorTheme;
import jexer.bits.StringUtils;
import jexer.event.TCommandEvent;
*/
private Backend backend;
+ /**
+ * The clipboard for copy and paste.
+ */
+ private Clipboard clipboard = new Clipboard();
+
/**
* Actual mouse coordinate X.
*/
*/
private long screenResizeTime = 0;
+ /**
+ * If true, screen selection is a rectangle.
+ */
+ private boolean screenSelectionRectangle = false;
+
+ /**
+ * If true, the mouse is dragging a screen selection.
+ */
+ private boolean inScreenSelection = false;
+
+ /**
+ * Screen selection starting X.
+ */
+ private int screenSelectionX0;
+
+ /**
+ * Screen selection starting Y.
+ */
+ private int screenSelectionY0;
+
+ /**
+ * Screen selection ending X.
+ */
+ private int screenSelectionX1;
+
+ /**
+ * Screen selection ending Y.
+ */
+ private int screenSelectionY1;
+
/**
* WidgetEventHandler is the main event consumer loop. There are at most
* two such threads in existence: the primary for normal case and a
typingHidMouse = false;
TMouseEvent mouse = (TMouseEvent) event;
+ if (mouse.isMouse1() && (mouse.isShift() || mouse.isCtrl())) {
+ // Screen selection.
+ if (inScreenSelection) {
+ screenSelectionX1 = mouse.getX();
+ screenSelectionY1 = mouse.getY();
+ } else {
+ inScreenSelection = true;
+ screenSelectionX0 = mouse.getX();
+ screenSelectionY0 = mouse.getY();
+ screenSelectionX1 = mouse.getX();
+ screenSelectionY1 = mouse.getY();
+ screenSelectionRectangle = mouse.isCtrl();
+ }
+ } else {
+ if (inScreenSelection) {
+ getScreen().copySelection(clipboard, screenSelectionX0,
+ screenSelectionY0, screenSelectionX1, screenSelectionY1,
+ screenSelectionRectangle);
+ }
+ inScreenSelection = false;
+ }
+
if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) {
oldMouseX = mouseX;
oldMouseY = mouseY;
}
}
+ if (inScreenSelection) {
+ getScreen().setSelection(screenSelectionX0,
+ screenSelectionY0, screenSelectionX1, screenSelectionY1,
+ screenSelectionRectangle);
+ }
+
if ((textMouse == true) && (typingHidMouse == false)) {
// Draw mouse at the new position.
drawTextMouse(mouseX, mouseY);
getScreen().unsetImageRow(mouseY);
}
}
+
+ if (inScreenSelection) {
+ getScreen().setSelection(screenSelectionX0, screenSelectionY0,
+ screenSelectionX1, screenSelectionY1, screenSelectionRectangle);
+ }
+
if ((textMouse == true) && (typingHidMouse == false)) {
drawTextMouse(mouseX, mouseY);
}
import jexer.backend.GlyphMaker;
import jexer.bits.Cell;
import jexer.bits.CellAttributes;
+import jexer.bits.Clipboard;
import jexer.bits.GraphicsChars;
import jexer.bits.StringUtils;
}
}
+ /**
+ * 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());
+ }
+
}
import jexer.bits.Cell;
import jexer.bits.CellAttributes;
+import jexer.bits.Clipboard;
/**
* MultiScreen mirrors its I/O to several screens.
* @return drawing boundary
*/
public int getClipRight() {
- return screens.get(0).getClipRight();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipRight();
+ }
+ return 0;
}
/**
* @return drawing boundary
*/
public int getClipBottom() {
- return screens.get(0).getClipBottom();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipBottom();
+ }
+ return 0;
}
/**
* @return drawing boundary
*/
public int getClipLeft() {
- return screens.get(0).getClipLeft();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipLeft();
+ }
+ return 0;
}
/**
* @return drawing boundary
*/
public int getClipTop() {
- return screens.get(0).getClipTop();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipTop();
+ }
+ return 0;
}
/**
* @return attributes at (x, y)
*/
public CellAttributes getAttrXY(final int x, final int y) {
- return screens.get(0).getAttrXY(x, y);
+ if (screens.size() > 0) {
+ return screens.get(0).getAttrXY(x, y);
+ }
+ return new CellAttributes();
}
/**
* @return the character + attributes
*/
public Cell getCharXY(final int x, final int y) {
- return screens.get(0).getCharXY(x, y);
+ if (screens.size() > 0) {
+ return screens.get(0).getCharXY(x, y);
+ }
+ return new Cell();
}
/**
*/
public int getHeight() {
// Return the smallest height of the screens.
- int height = screens.get(0).getHeight();
+ int height = 25;
+ if (screens.size() > 0) {
+ height = screens.get(0).getHeight();
+ }
for (Screen screen: screens) {
if (screen.getHeight() < height) {
height = screen.getHeight();
*/
public int getWidth() {
// Return the smallest width of the screens.
- int width = screens.get(0).getWidth();
+ int width = 80;
+ if (screens.size() > 0) {
+ width = screens.get(0).getWidth();
+ }
for (Screen screen: screens) {
if (screen.getWidth() < width) {
width = screen.getWidth();
* @return true if the cursor is visible
*/
public boolean isCursorVisible() {
- return screens.get(0).isCursorVisible();
+ if (screens.size() > 0) {
+ return screens.get(0).isCursorVisible();
+ }
+ return true;
}
/**
* @return the cursor x column position
*/
public int getCursorX() {
- return screens.get(0).getCursorX();
+ if (screens.size() > 0) {
+ return screens.get(0).getCursorX();
+ }
+ return 0;
}
/**
* @return the cursor y row position
*/
public int getCursorY() {
- return screens.get(0).getCursorY();
+ if (screens.size() > 0) {
+ return screens.get(0).getCursorY();
+ }
+ return 0;
}
/**
}
}
+ /**
+ * 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) {
+
+ for (Screen screen: screens) {
+ screen.setSelection(x0, y0, x1, y1, rectangle);
+ }
+ }
+
+ /**
+ * 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) {
+
+ // Only copy from the first screen.
+ if (screens.size() > 0) {
+ screens.get(0).copySelection(clipboard, x0, y0, x1, y1, rectangle);
+ }
+ }
+
}
import jexer.bits.Cell;
import jexer.bits.CellAttributes;
+import jexer.bits.Clipboard;
/**
* Drawing operations API.
public void invertCell(final int x, final int y,
final boolean onlyThisCell);
+ /**
+ * 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);
+
+ /**
+ * 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);
+
}
*/
private static final int PROTECT = 0x10;
-
// ------------------------------------------------------------------------
// Variables --------------------------------------------------------------
// ------------------------------------------------------------------------
--- /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.bits;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+/**
+ * Clipboard provides convenience methods to copy text and images to and from
+ * a shared clipboard. When the system clipboard is available it is used.
+ */
+public class Clipboard {
+
+ // ------------------------------------------------------------------------
+ // Variables --------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
+ /**
+ * The image last copied to the clipboard.
+ */
+ private BufferedImage image = null;
+
+ /**
+ * The text string last copied to the clipboard.
+ */
+ private String text = null;
+
+ /**
+ * The system clipboard, or null if it is not available.
+ */
+ private java.awt.datatransfer.Clipboard systemClipboard = null;
+
+ // ------------------------------------------------------------------------
+ // Constructors -----------------------------------------------------------
+ // ------------------------------------------------------------------------
+
+ /**
+ * Public constructor.
+ */
+ public Clipboard() {
+ try {
+ systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ } catch (java.awt.HeadlessException e) {
+ // SQUASH
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Clipboard --------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
+ /**
+ * Copy an image to the clipboard.
+ *
+ * @param image image to copy
+ */
+ public void copyImage(final BufferedImage image) {
+ this.image = image;
+ if (systemClipboard != null) {
+ // TODO
+ }
+ }
+
+ /**
+ * Copy a text string to the clipboard.
+ *
+ * @param text string to copy
+ */
+ public void copyText(final String text) {
+ this.text = text;
+ if (systemClipboard != null) {
+ StringSelection stringSelection = new StringSelection(text);
+ systemClipboard.setContents(stringSelection, null);
+ }
+ }
+
+ /**
+ * Obtain an image from the clipboard.
+ *
+ * @return image from the clipboard, or null if no image is available
+ */
+ public BufferedImage pasteImage() {
+ if (systemClipboard != null) {
+ getClipboardImage();
+ }
+ return image;
+ }
+
+ /**
+ * Obtain a text string from the clipboard.
+ *
+ * @return text string from the clipboard, or null if no text is
+ * available
+ */
+ public String pasteText() {
+ if (systemClipboard != null) {
+ getClipboardText();
+ }
+ return text;
+ }
+
+ /**
+ * Returns true if the clipboard has an image.
+ *
+ * @return true if an image is available from the clipboard
+ */
+ public boolean isImage() {
+ if (image == null) {
+ getClipboardImage();
+ }
+ return (image != null);
+ }
+
+ /**
+ * Returns true if the clipboard has a text string.
+ *
+ * @return true if a text string is available from the clipboard
+ */
+ public boolean isText() {
+ if (text == null) {
+ getClipboardText();
+ }
+ return (text != null);
+ }
+
+ /**
+ * Returns true if the clipboard is empty.
+ *
+ * @return true if the clipboard is empty
+ */
+ public boolean isEmpty() {
+ return ((isText() == false) && (isImage() == false));
+ }
+
+ /**
+ * Copy image from the clipboard to text.
+ */
+ private void getClipboardImage() {
+ if (systemClipboard != null) {
+ Transferable contents = systemClipboard.getContents(null);
+ if (contents != null) {
+ if (contents.isDataFlavorSupported(DataFlavor.imageFlavor)) {
+ try {
+ Image img = (Image) contents.getTransferData(DataFlavor.imageFlavor);
+ image = new BufferedImage(img.getWidth(null),
+ img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
+ image.getGraphics().drawImage(img, 0, 0, null);
+ } catch (IOException e) {
+ // SQUASH
+ } catch (UnsupportedFlavorException e) {
+ // SQUASH
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy text string from the clipboard to text.
+ */
+ private void getClipboardText() {
+ if (systemClipboard != null) {
+ Transferable contents = systemClipboard.getContents(null);
+ if (contents != null) {
+ if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+ try {
+ text = (String) contents.getTransferData(DataFlavor.stringFlavor);
+ } catch (IOException e) {
+ // SQUASH
+ } catch (UnsupportedFlavorException e) {
+ // SQUASH
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Clear whatever is on the local clipboard. Note that this will not
+ * clear the system clipboard.
+ */
+ public void clear() {
+ image = null;
+ text = null;
+ }
+
+}
*
* - Read/write a line of RFC4180 comma-separated values strings to/from a
* list of strings.
+ *
+ * - Compute number of visible text cells for a given Unicode codepoint or
+ * string.
+ *
+ * - Convert bytes to and from base-64 encoding.
*/
public class StringUtils {