/*
* 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 <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2010-2012 Martin
*/
package com.googlecode.lanterna.gui;
import com.googlecode.lanterna.screen.Screen;
import com.googlecode.lanterna.screen.ScreenCharacterStyle;
import com.googlecode.lanterna.screen.TabBehaviour;
import com.googlecode.lanterna.terminal.Terminal.Color;
import com.googlecode.lanterna.terminal.TerminalPosition;
import com.googlecode.lanterna.terminal.TerminalSize;
import java.util.Arrays;
import java.util.EnumSet;
/**
* This class works as a kind of 'pencil', able to output text graphics. The
* main use for it is not only to provide a way to draw text through, it is also
* the carrier of context information, such as which screen we are associated
* with, what the current theme is and how big size the drawing area has.
* @author Martin
*/
class TextGraphicsImpl implements TextGraphics
{
private final TerminalPosition topLeft;
private final TerminalSize areaSize;
private final Screen screen;
private Theme theme;
private Color foregroundColor;
private Color backgroundColor;
private boolean currentlyBold;
TextGraphicsImpl(final TerminalPosition topLeft, final TerminalSize areaSize, final Screen screen, final Theme theme)
{
this.topLeft = topLeft;
this.areaSize = areaSize;
this.screen = screen;
this.theme = theme;
this.currentlyBold = false;
this.foregroundColor = Color.DEFAULT;
this.backgroundColor = Color.DEFAULT;
}
private TextGraphicsImpl(final TextGraphicsImpl graphics, final TerminalPosition topLeft, final TerminalSize areaSize)
{
this(new TerminalPosition(topLeft.getColumn() + graphics.topLeft.getColumn(), topLeft.getRow() + graphics.topLeft.getRow()),
new TerminalSize(areaSize.getColumns() < (graphics.getWidth() - topLeft.getColumn()) ? areaSize.getColumns() : (graphics.getWidth() - topLeft.getColumn()),
areaSize.getRows() < (graphics.getHeight() - topLeft.getRow()) ? areaSize.getRows() : (graphics.getHeight() - topLeft.getRow())),
graphics.screen, graphics.theme);
foregroundColor = graphics.foregroundColor;
backgroundColor = graphics.backgroundColor;
currentlyBold = graphics.currentlyBold;
}
/**
* Creates a new TextGraphics object using the same area or smaller. Use the
* terminalPosition variable to determine what the new TextGraphics object
* will cover.
* @param terminalPosition In local coordinates of the current TextGraphics,
* the left-left coordinates of the new TextGraphics
* @return A new TextGraphics object covering the same or smaller area as
* this
*/
@Override
public TextGraphics subAreaGraphics(final TerminalPosition terminalPosition)
{
terminalPosition.ensurePositivePosition();
TerminalSize newArea = new TerminalSize(areaSize);
newArea.setColumns(newArea.getColumns() - terminalPosition.getColumn());
newArea.setRows(newArea.getRows() - terminalPosition.getRow());
return subAreaGraphics(terminalPosition, newArea);
}
/**
* Creates a new TextGraphics object using the same area or smaller. Use the
* topLeft and subAreaSize variable to determine what the new TextGraphics
* object will cover.
* @param topLeft In local coordinates of the current TextGraphics,
* the left-left coordinates of the new TextGraphics
* @param subAreaSize Size of the area the new TextGraphics will cover
* @return A new TextGraphics object covering the same or smaller area as
* this
*/
@Override
public TextGraphics subAreaGraphics(final TerminalPosition topLeft, final TerminalSize subAreaSize)
{
if(topLeft.getColumn() < 0)
topLeft.setColumn(0);
if(topLeft.getRow() < 0)
topLeft.setRow(0);
if(subAreaSize.getColumns() < 0)
subAreaSize.setColumns(-subAreaSize.getColumns());
if(subAreaSize.getRows() < 0)
subAreaSize.setRows(-subAreaSize.getRows());
if(topLeft.getColumn() >= areaSize.getColumns() ||
topLeft.getRow() >= areaSize.getRows()) {
return new NullTextGraphics(); //Return something that doesn't do anything
}
if(topLeft.getColumn() + subAreaSize.getColumns() > areaSize.getColumns())
subAreaSize.setColumns(areaSize.getColumns() - topLeft.getColumn());
if(topLeft.getRow() + subAreaSize.getRows() > areaSize.getRows())
subAreaSize.setRows(areaSize.getRows() - topLeft.getRow());
return new TextGraphicsImpl(this, topLeft, subAreaSize);
}
/**
* Draws a string to the terminal, with the first character starting at
* specified coordinates and which an option list of styles applied. All
* coordinates are local to the top-left corner of the TextGraphics object
* @param column Column of the first character in the string
* @param row Row of the first character in the string
* @param string String to print to terminal
* @param styles Which styles to apply to the string
*/
@Override
public void drawString(int column, int row, String string, ScreenCharacterStyle... styles)
{
if(column >= areaSize.getColumns() || row >= areaSize.getRows() || string == null)
return;
string = TabBehaviour.ALIGN_TO_COLUMN_4.replaceTabs(string, column + topLeft.getColumn());
if(string.length() + column > areaSize.getColumns())
string = string.substring(0, areaSize.getColumns() - column);
EnumSet<ScreenCharacterStyle> stylesSet = EnumSet.noneOf(ScreenCharacterStyle.class);
if(styles != null && styles.length != 0)
stylesSet = EnumSet.copyOf(Arrays.asList(styles));
if(currentlyBold)
stylesSet.add(ScreenCharacterStyle.Bold);
screen.putString(column + topLeft.getColumn(), row + topLeft.getRow(), string,
foregroundColor, backgroundColor, stylesSet);
}
@Override
public Color getBackgroundColor()
{
return backgroundColor;
}
@Override
public Color getForegroundColor()
{
return foregroundColor;
}
@Override
public void setBackgroundColor(Color backgroundColor)
{
this.backgroundColor = backgroundColor;
}
@Override
public void setForegroundColor(Color foregroundColor)
{
this.foregroundColor = foregroundColor;
}
/**
* Width, in columns, of the TextGraphics drawing area. Attemps to draw
* outside of this will be ignored
* @return Size of the TextGraphics area, in columns
*/
@Override
public int getWidth()
{
return areaSize.getColumns();
}
/**
* Height, in rows, of the TextGraphics drawing area. Attemps to draw
* outside of this will be ignored
* @return Size of the TextGraphics area, in rows
*/
@Override
public int getHeight()
{
return areaSize.getRows();
}
/**
* Size of the area the {@code TextGraphics} can edit, as a {@code TerminalSize} object,
* any attempts to draw outside of this area will be ignored.
* @return Size of the area the {@code TextGraphics} can edit, as a {@code TerminalSize} object
*/
@Override
public TerminalSize getSize() {
return new TerminalSize(getWidth(), getHeight());
}
@Override
public void setBoldMask(boolean enabledBoldMask)
{
currentlyBold = enabledBoldMask;
}
@Override
public Theme getTheme()
{
return theme;
}
/**
* Translates local coordinates of this TextGraphics object to global
* @param pointInArea Point in local coordinates
* @return The point in global coordinates
*/
@Override
public TerminalPosition translateToGlobalCoordinates(TerminalPosition pointInArea)
{
return new TerminalPosition(pointInArea.getColumn() + topLeft.getColumn(), pointInArea.getRow() + topLeft.getRow());
}
/**
* Applies theme-specific settings according to the category supplied. This
* may modify the foreground color, background color and/or styles. Any
* string drawn after this call will have these settings applied
* @param category Category to use
*/
@Override
public void applyTheme(Theme.Category category)
{
applyTheme(getTheme().getDefinition(category));
}
/**
* Applies theme-specific settings according to the definition supplied. This
* may modify the foreground color, background color and/or styles. Any
* string drawn after this call will have these settings applied
* @param themeItem Definition to use
*/
@Override
public void applyTheme(Theme.Definition themeItem)
{
setForegroundColor(themeItem.foreground());
setBackgroundColor(themeItem.background());
setBoldMask(themeItem.isHighlighted());
}
/**
* Replaces the content of the entire TextGraphic object with one character
* @param character Character to fill the area with
*/
@Override
public void fillArea(char character)
{
fillRectangle(character, new TerminalPosition(0, 0), new TerminalSize(areaSize));
}
/**
* Replaces the content of a rectangle within the TextGraphic drawing area
* with a specified character
* @param character Character to fill the area with
*/
@Override
public void fillRectangle(char character, TerminalPosition topLeft, TerminalSize rectangleSize)
{
StringBuilder emptyLineBuilder = new StringBuilder();
for(int i = 0; i < rectangleSize.getColumns(); i++)
emptyLineBuilder.append(character);
String emptyLine = emptyLineBuilder.toString();
for(int i = 0; i < rectangleSize.getRows(); i++)
drawString(topLeft.getColumn(), topLeft.getRow() + i, emptyLine);
}
@Override
public String toString()
{
return "TextGraphics {topLeft: " + topLeft.toString() + ", size: " + areaSize.toString() + "}";
}
private static class NullTextGraphics implements TextGraphics {
@Override
public void applyTheme(Theme.Category category) { }
@Override
public void applyTheme(Theme.Definition themeItem) { }
@Override
public void drawString(int column, int row, String string, ScreenCharacterStyle... styles) { }
@Override
public void fillArea(char character) { }
@Override
public void fillRectangle(char character, TerminalPosition topLeft, TerminalSize rectangleSize) { }
@Override
public Color getBackgroundColor() { return Color.DEFAULT; }
@Override
public Color getForegroundColor() { return Color.DEFAULT; }
@Override
public int getHeight() { return 0; }
@Override
public TerminalSize getSize() { return new TerminalSize(0, 0); }
@Override
public Theme getTheme() { return Theme.getDefaultTheme(); }
@Override
public int getWidth() { return 0; }
@Override
public void setBackgroundColor(Color backgroundColor) { }
@Override
public void setBoldMask(boolean enabledBoldMask) { }
@Override
public void setForegroundColor(Color foregroundColor) { }
@Override
public TextGraphics subAreaGraphics(TerminalPosition terminalPosition) {
return new NullTextGraphics();
}
@Override
public TextGraphics subAreaGraphics(TerminalPosition topLeft, TerminalSize subAreaSize) {
return new NullTextGraphics();
}
@Override
public TerminalPosition translateToGlobalCoordinates(TerminalPosition pointInArea) {
return new TerminalPosition(0, 0);
}
}
}