All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.lanterna.graphics.AbstractTextGraphics Maven / Gradle / Ivy

There is a newer version: 3.2.0-alpha1
Show newest version
/*
 * 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-2017 Martin Berglund
 */
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) {
        string = prepareStringForPut(column, string);
        int offset = 0;
        for(int i = 0; i < string.length(); i++) {
            char character = string.charAt(i);
            setCharacter(column + offset, row, newTextCharacter(character));
            offset += getOffsetToNextCharacter(character);
        }
        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 synchronized TextGraphics putCSIStyledString(int column, int row, String string) {
        StyleSet.Set original = new StyleSet.Set(this);
        string = prepareStringForPut(column, string);
        int offset = 0;
        for(int i = 0; i < string.length(); i++) {
            char character = string.charAt(i);
            String controlSequence = TerminalTextUtils.getANSIControlSequenceAt(string, i);
            if(controlSequence != null) {
                TerminalTextUtils.updateModifiersFromCSICode(controlSequence, this, original);

                // Skip the control sequence, leaving one extra, since we'll add it when we loop
                i += controlSequence.length() - 1;
                continue;
            }

            setCharacter(column + offset, row, newTextCharacter(character));
            offset += getOffsetToNextCharacter(character);
        }

        setStyleFrom(original);
        return this;
    }

    @Override
    public TextGraphics putCSIStyledString(TerminalPosition position, String string) {
        return putCSIStyledString(position.getColumn(), position.getRow(), string);
    }

    @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);
    }

    private String prepareStringForPut(int column, 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);
        return string;
    }

    private int getOffsetToNextCharacter(char character) {
        if(TerminalTextUtils.isCharDoubleWidth(character)) {
            //CJK characters are twice the normal characters in width, so next character position is two columns forward
            return 2;
        }
        else {
            //For "normal" characters we advance to the next column
            return 1;
        }
    }

    @Override
    public TextGraphics setStyleFrom(StyleSet source) {
        setBackgroundColor(source.getBackgroundColor());
        setForegroundColor(source.getForegroundColor());
        setModifiers(source.getActiveModifiers());
        return this;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy