/*
* This file is part of lanterna (http://code.google.com/p/lanterna/).
*
* lanterna 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.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* Copyright (C) 2010-2015 Martin
*/
package com.googlecode.lanterna.graphics;
import com.googlecode.lanterna.*;
import com.googlecode.lanterna.screen.TabBehaviour;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
/**
* This class hold the default logic for drawing the basic text graphic as exposed by TextGraphic. All implementations
* rely on a setCharacter method being implemented in subclasses.
* @author Martin
*/
public abstract class AbstractTextGraphics implements TextGraphics {
protected TextColor foregroundColor;
protected TextColor backgroundColor;
protected TabBehaviour tabBehaviour;
protected final EnumSet activeModifiers;
private final ShapeRenderer shapeRenderer;
protected AbstractTextGraphics() {
this.activeModifiers = EnumSet.noneOf(SGR.class);
this.tabBehaviour = TabBehaviour.ALIGN_TO_COLUMN_4;
this.foregroundColor = TextColor.ANSI.DEFAULT;
this.backgroundColor = TextColor.ANSI.DEFAULT;
this.shapeRenderer = new DefaultShapeRenderer(new DefaultShapeRenderer.Callback() {
@Override
public void onPoint(int column, int row, TextCharacter character) {
setCharacter(column, row, character);
}
});
}
@Override
public TextColor getBackgroundColor() {
return backgroundColor;
}
@Override
public TextGraphics setBackgroundColor(final TextColor backgroundColor) {
this.backgroundColor = backgroundColor;
return this;
}
@Override
public TextColor getForegroundColor() {
return foregroundColor;
}
@Override
public TextGraphics setForegroundColor(final TextColor foregroundColor) {
this.foregroundColor = foregroundColor;
return this;
}
@Override
public TextGraphics enableModifiers(SGR... modifiers) {
enableModifiers(Arrays.asList(modifiers));
return this;
}
private void enableModifiers(Collection modifiers) {
this.activeModifiers.addAll(modifiers);
}
@Override
public TextGraphics disableModifiers(SGR... modifiers) {
disableModifiers(Arrays.asList(modifiers));
return this;
}
private void disableModifiers(Collection modifiers) {
this.activeModifiers.removeAll(modifiers);
}
@Override
public synchronized TextGraphics setModifiers(EnumSet modifiers) {
activeModifiers.clear();
activeModifiers.addAll(modifiers);
return this;
}
@Override
public TextGraphics clearModifiers() {
this.activeModifiers.clear();
return this;
}
@Override
public EnumSet getActiveModifiers() {
return activeModifiers;
}
@Override
public TabBehaviour getTabBehaviour() {
return tabBehaviour;
}
@Override
public TextGraphics setTabBehaviour(TabBehaviour tabBehaviour) {
if(tabBehaviour != null) {
this.tabBehaviour = tabBehaviour;
}
return this;
}
@Override
public TextGraphics fill(char c) {
fillRectangle(TerminalPosition.TOP_LEFT_CORNER, getSize(), c);
return this;
}
@Override
public TextGraphics setCharacter(int column, int row, char character) {
return setCharacter(column, row, newTextCharacter(character));
}
@Override
public TextGraphics setCharacter(TerminalPosition position, TextCharacter textCharacter) {
setCharacter(position.getColumn(), position.getRow(), textCharacter);
return this;
}
@Override
public TextGraphics setCharacter(TerminalPosition position, char character) {
return setCharacter(position.getColumn(), position.getRow(), character);
}
@Override
public TextGraphics drawLine(TerminalPosition fromPosition, TerminalPosition toPoint, char character) {
return drawLine(fromPosition, toPoint, newTextCharacter(character));
}
@Override
public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, TextCharacter character) {
shapeRenderer.drawLine(fromPoint, toPoint, character);
return this;
}
@Override
public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, char character) {
return drawLine(fromX, fromY, toX, toY, newTextCharacter(character));
}
@Override
public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, TextCharacter character) {
return drawLine(new TerminalPosition(fromX, fromY), new TerminalPosition(toX, toY), character);
}
@Override
public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
return drawTriangle(p1, p2, p3, newTextCharacter(character));
}
@Override
public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
shapeRenderer.drawTriangle(p1, p2, p3, character);
return this;
}
@Override
public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
return fillTriangle(p1, p2, p3, newTextCharacter(character));
}
@Override
public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
shapeRenderer.fillTriangle(p1, p2, p3, character);
return this;
}
@Override
public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
return drawRectangle(topLeft, size, newTextCharacter(character));
}
@Override
public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
shapeRenderer.drawRectangle(topLeft, size, character);
return this;
}
@Override
public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
return fillRectangle(topLeft, size, newTextCharacter(character));
}
@Override
public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
shapeRenderer.fillRectangle(topLeft, size, character);
return this;
}
@Override
public TextGraphics drawImage(TerminalPosition topLeft, TextImage image) {
return drawImage(topLeft, image, TerminalPosition.TOP_LEFT_CORNER, image.getSize());
}
@Override
public TextGraphics drawImage(
TerminalPosition topLeft,
TextImage image,
TerminalPosition sourceImageTopLeft,
TerminalSize sourceImageSize) {
// If the source image position is negative, offset the whole image
if(sourceImageTopLeft.getColumn() < 0) {
topLeft = topLeft.withRelativeColumn(-sourceImageTopLeft.getColumn());
sourceImageSize = sourceImageSize.withRelativeColumns(sourceImageTopLeft.getColumn());
sourceImageTopLeft = sourceImageTopLeft.withColumn(0);
}
if(sourceImageTopLeft.getRow() < 0) {
topLeft = topLeft.withRelativeRow(-sourceImageTopLeft.getRow());
sourceImageSize = sourceImageSize.withRelativeRows(sourceImageTopLeft.getRow());
sourceImageTopLeft = sourceImageTopLeft.withRow(0);
}
// cropping specified image-subrectangle to the image itself:
int fromRow = Math.max(sourceImageTopLeft.getRow(), 0);
int untilRow = Math.min(sourceImageTopLeft.getRow() + sourceImageSize.getRows(), image.getSize().getRows());
int fromColumn = Math.max(sourceImageTopLeft.getColumn(), 0);
int untilColumn = Math.min(sourceImageTopLeft.getColumn() + sourceImageSize.getColumns(), image.getSize().getColumns());
// difference between position in image and position on target:
int diffRow = topLeft.getRow() - sourceImageTopLeft.getRow();
int diffColumn = topLeft.getColumn() - sourceImageTopLeft.getColumn();
// top/left-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative coordinate)
fromRow = Math.max(fromRow, -diffRow);
fromColumn = Math.max(fromColumn, -diffColumn);
// bot/right-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative coordinate)
untilRow = Math.min(untilRow, getSize().getRows() - diffRow);
untilColumn = Math.min(untilColumn, getSize().getColumns() - diffColumn);
if (fromRow >= untilRow || fromColumn >= untilColumn) {
return this;
}
for (int row = fromRow; row < untilRow; row++) {
for (int column = fromColumn; column < untilColumn; column++) {
setCharacter(column + diffColumn, row + diffRow, image.getCharacterAt(column, row));
}
}
return this;
}
@Override
public TextGraphics putString(int column, int row, String string) {
if(string.contains("\n")) {
string = string.substring(0, string.indexOf("\n"));
}
if(string.contains("\r")) {
string = string.substring(0, string.indexOf("\r"));
}
string = tabBehaviour.replaceTabs(string, column);
int offset = 0;
for(int i = 0; i < string.length(); i++) {
char character = string.charAt(i);
setCharacter(
column + offset,
row,
new TextCharacter(
character,
foregroundColor,
backgroundColor,
activeModifiers.clone()));
if(TerminalTextUtils.isCharCJK(character)) {
//CJK characters are twice the normal characters in width, so next character position is two columns forward
offset += 2;
}
else {
//For "normal" characters we advance to the next column
offset += 1;
}
}
return this;
}
@Override
public TextGraphics putString(TerminalPosition position, String string) {
putString(position.getColumn(), position.getRow(), string);
return this;
}
@Override
public TextGraphics putString(int column, int row, String string, SGR extraModifier, SGR... optionalExtraModifiers) {clearModifiers();
return putString(column, row, string, EnumSet.of(extraModifier, optionalExtraModifiers));
}
@Override
public TextGraphics putString(int column, int row, String string, Collection extraModifiers) {
extraModifiers.removeAll(activeModifiers);
enableModifiers(extraModifiers);
putString(column, row, string);
disableModifiers(extraModifiers);
return this;
}
@Override
public TextGraphics putString(TerminalPosition position, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
putString(position.getColumn(), position.getRow(), string, extraModifier, optionalExtraModifiers);
return this;
}
@Override
public TextCharacter getCharacter(TerminalPosition position) {
return getCharacter(position.getColumn(), position.getRow());
}
@Override
public TextGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException {
TerminalSize writableArea = getSize();
if(topLeftCorner.getColumn() + size.getColumns() <= 0 ||
topLeftCorner.getColumn() >= writableArea.getColumns() ||
topLeftCorner.getRow() + size.getRows() <= 0 ||
topLeftCorner.getRow() >= writableArea.getRows()) {
//The area selected is completely outside of this TextGraphics, so we can return a "null" object that doesn't
//do anything because it is impossible to change anything anyway
return new NullTextGraphics(size);
}
return new SubTextGraphics(this, topLeftCorner, size);
}
private TextCharacter newTextCharacter(char character) {
return new TextCharacter(character, foregroundColor, backgroundColor, activeModifiers);
}
}