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

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

There is a newer version: 3.2.0-alpha1
Show newest version
package com.googlecode.lanterna.graphics;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;

import com.googlecode.lanterna.SGR;
import com.googlecode.lanterna.TerminalPosition;
import com.googlecode.lanterna.TerminalTextUtils;
import com.googlecode.lanterna.TextCharacter;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.screen.TabBehaviour;
import com.googlecode.lanterna.screen.WrapBehaviour;

public class TextGraphicsWriter implements StyleSet {
    private final TextGraphics backend;
    private TerminalPosition cursorPosition;
    private TextColor foregroundColor, backgroundColor;
    private final EnumSet style = EnumSet.noneOf(SGR.class);
    private WrapBehaviour wrapBehaviour = WrapBehaviour.WORD;
    private boolean styleable = true;
    
    public TextGraphicsWriter(TextGraphics backend) {
        this.backend = backend;
        setStyleFrom( backend );
        cursorPosition = new TerminalPosition(0, 0);
    }

    public TextGraphicsWriter putString(String string) {
        StringBuilder wordpart = new StringBuilder();
        StyleSet.Set originalStyle = new StyleSet.Set(backend);
        backend.setStyleFrom(this);

        int wordlen = 0; // the whole column-length of the word.
        for (int i = 0; i < string.length(); i++) {
            char ch = string.charAt(i);
            switch (ch) {
            case '\n':
                flush(wordpart,wordlen); wordlen = 0;
                linefeed(-1); // -1 means explicit.
                break;
            case '\t':
                flush(wordpart,wordlen); wordlen = 0;
                if (backend.getTabBehaviour() != TabBehaviour.IGNORE) {
                    String repl = backend.getTabBehaviour()
                            .getTabReplacement(cursorPosition.getColumn());
                    for(int j = 0; j < repl.length(); j++) {
                        backend.setCharacter(cursorPosition.withRelativeColumn(j), repl.charAt(j));
                    }
                    cursorPosition = cursorPosition.withRelativeColumn(repl.length());
                } else {
                    linefeed(2); putControlChar(ch);
                }
                break;
            case '\033':
                if (isStyleable()) {
                    stash(wordpart,wordlen);
                    String seq = TerminalTextUtils.getANSIControlSequenceAt(string, i);
                    TerminalTextUtils.updateModifiersFromCSICode(seq, this, originalStyle);
                    backend.setStyleFrom(this);
                    i += seq.length() - 1;
                } else {
                    flush(wordpart,wordlen); wordlen = 0;
                    linefeed(2); putControlChar(ch);
                }
                break;
            default:
                if (Character.isISOControl(ch)) {
                    flush(wordpart,wordlen); wordlen = 0;
                    linefeed(1); putControlChar(ch);
                } else if (Character.isWhitespace(ch)) {
                    flush(wordpart,wordlen); wordlen = 0;
                    backend.setCharacter(cursorPosition, ch);
                    cursorPosition = cursorPosition.withRelativeColumn(1);
                } else if (TerminalTextUtils.isCharCJK(ch)) {
                    flush(wordpart, wordlen); wordlen = 0;
                    linefeed(2);
                    backend.setCharacter(cursorPosition, ch);
                    cursorPosition = cursorPosition.withRelativeColumn(2);
                } else {
                    if (wrapBehaviour.keepWords()) {
                        // TODO: if at end of line despite starting at col 0, then split word.
                        wordpart.append(ch); wordlen++;
                    } else {
                        linefeed(1);
                        backend.setCharacter(cursorPosition, ch);
                        cursorPosition = cursorPosition.withRelativeColumn(1);
                    }
                }
            }
            linefeed(wordlen);
        }
        flush(wordpart,wordlen);
        backend.setStyleFrom(originalStyle);
        return this;
    }
    private void linefeed(int lenToFit) {
        int curCol = cursorPosition.getColumn();
        int spaceLeft = backend.getSize().getColumns() - curCol;
        if (wrapBehaviour.allowLineFeed()) {
            boolean wantWrap = curCol > 0 && lenToFit > spaceLeft;
            if (lenToFit < 0 || ( wantWrap && wrapBehaviour.autoWrap() ) ) {
                // TODO: clear to end of current line?
                cursorPosition = cursorPosition.withColumn(0).withRelativeRow(1);
            }
        } else {
            if (lenToFit < 0) { // encode explicit line feed
                putControlChar('\n');
            }
        }
    }
    public void putControlChar(char ch) {
        char subst;
        switch (ch) {
        case '\033': subst = '['; break;
        case '\034': subst = '\\'; break;
        case '\035': subst = ']'; break;
        case '\036': subst = '^'; break;
        case '\037': subst = '_'; break;
        case '\177': subst = '?'; break;
        default:
            if (ch <= 26) {
                subst = (char)(ch + '@');
            } else { // normal character - or 0x80-0x9F
                // just write it out, anyway:
                backend.setCharacter(cursorPosition, ch);
                cursorPosition = cursorPosition.withRelativeColumn(1);
                return;
            }
        }
        EnumSet style = getActiveModifiers();
        if (style.contains(SGR.REVERSE)) {
            style.remove(SGR.REVERSE);
        } else {
            style.add(SGR.REVERSE);
        }
        TextCharacter tc = new TextCharacter('^',
                getForegroundColor(), getBackgroundColor(), style);
        backend.setCharacter(cursorPosition, tc);
        cursorPosition = cursorPosition.withRelativeColumn(1);
        tc = tc.withCharacter(subst);
        backend.setCharacter(cursorPosition, tc);
        cursorPosition = cursorPosition.withRelativeColumn(1);
    }
    // A word (a sequence of characters that is kept together when word-wrapping)
    // may consist of differently styled parts. This class describes one such
    // part.
    private static class WordPart extends StyleSet.Set {
        private final String word;
        private final int wordlen;

        WordPart(String word, int wordlen, StyleSet style) {
            this.word = word; this.wordlen = wordlen;
            setStyleFrom(style);
        }
    }

    private final List chunk_queue = new ArrayList<>();

    private void stash(StringBuilder word, int wordlen) {
        if (word.length() > 0) {
            WordPart chunk = new WordPart(word.toString(),wordlen, this);
            chunk_queue.add(chunk);
            // for convenience the StringBuilder is reset:
            word.setLength(0);
        }
    }
    private void flush(StringBuilder word, int wordlen) {
        stash(word, wordlen);
        if (chunk_queue.isEmpty()) {
            return;
        }
        int row = cursorPosition.getRow();
        int col = cursorPosition.getColumn();
        int offset = 0;
        for (WordPart chunk : chunk_queue) {
            backend.setStyleFrom(chunk);
            backend.putString(col+offset,row, chunk.word);
            offset = chunk.wordlen;
        }
        chunk_queue.clear(); // they're done.
        // set cursor right behind the word:
        cursorPosition = cursorPosition.withColumn(col+offset);
        backend.setStyleFrom(this);
    }

    /**
     * @return the cursor position
     */
    public TerminalPosition getCursorPosition() {
        return cursorPosition;
    }

    /**
     * @param cursorPosition the cursor position to set
     */
    public void setCursorPosition(TerminalPosition cursorPosition) {
        this.cursorPosition = cursorPosition;
    }

    /**
     * @return the foreground color
     */
    public TextColor getForegroundColor() {
        return foregroundColor;
    }

    /**
     * @param foreground the foreground color to set
     */
    public TextGraphicsWriter setForegroundColor(TextColor foreground) {
        this.foregroundColor = foreground;
        return this;
    }

    /**
     * @return the background color
     */
    public TextColor getBackgroundColor() {
        return backgroundColor;
    }

    /**
     * @param background the background color to set
     */
    public TextGraphicsWriter setBackgroundColor(TextColor background) {
        this.backgroundColor = background;
        return this;
    }
    
    @Override
    public TextGraphicsWriter enableModifiers(SGR... modifiers) {
        style.addAll(Arrays.asList(modifiers));
        return this;
    }

    @Override
    public TextGraphicsWriter disableModifiers(SGR... modifiers) {
        style.removeAll(Arrays.asList(modifiers));
        return this;
    }

    @Override
    public TextGraphicsWriter setModifiers(EnumSet modifiers) {
        style.clear(); style.addAll(modifiers);
        return this;
    }

    @Override
    public TextGraphicsWriter clearModifiers() {
        style.clear();
        return this;
    }

    @Override
    public EnumSet getActiveModifiers() {
        return EnumSet.copyOf(style);
    }

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

    /**
     * @return the wrapBehaviour
     */
    public WrapBehaviour getWrapBehaviour() {
        return wrapBehaviour;
    }

    /**
     * @param wrapBehaviour the wrapBehaviour to set
     */
    public void setWrapBehaviour(WrapBehaviour wrapBehaviour) {
        this.wrapBehaviour = wrapBehaviour;
    }

    /**
     * @return whether styles in strings are handled.
     */
    public boolean isStyleable() {
        return styleable;
    }

    /**
     * @param styleable whether styles in strings should be handled.
     */
    public void setStyleable(boolean styleable) {
        this.styleable = styleable;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy