Package org.vaadin.console.client.ui

Source Code of org.vaadin.console.client.ui.TextConsole

package org.vaadin.console.client.ui;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.FocusWidget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Util;

/**
* GWT Console / Console Widget.
*
* @author Sami Ekblad / Vaadin
*
*/
public class TextConsole extends FocusWidget {

    /* Control characters as in http://en.wikipedia.org/wiki/Control_character */
    public static final char CTRL_BELL = 'G';
    public static final char CTRL_BACKSPACE = 'H';
    public static final char CTRL_TAB = 'I';
    public static final char CTRL_LINE_FEED = 'J';
    public static final char CTRL_FORM_FEED = 'L';
    public static final char CTRL_CARRIAGE_RETURN = 'M';
    public static final char CTRL_ESCAPE = '[';
    public static final char CTRL_DELETE = '?';

    private static final char[] CTRL = { CTRL_BELL, CTRL_BACKSPACE, CTRL_TAB,
            CTRL_LINE_FEED, CTRL_FORM_FEED, CTRL_CARRIAGE_RETURN, CTRL_ESCAPE,
            CTRL_DELETE };

    public static char getControlKey(int kc) {
        for (char c : CTRL) {
            if (kc == c) {
                return c;
            }
        }
        return 0;
    }

    private static final String DEFAULT_TABS = "    ";
    private static final int BIG_NUMBER = 100000;
    private DivElement term;
    private TextConsoleConfig config;
    private TextConsoleHandler handler;
    private Element buffer;
    private TableElement prompt;
    private Element ps;
    private InputElement input;
    private List<String> cmdHistory = new ArrayList<String>();
    private int cmdHistoryIndex = -1;
    private HandlerRegistration clickHandler;
    private HandlerRegistration keyHandler;
    private HandlerRegistration focusHandler;
    private DivElement test;
    private int fontW;
    private int scrollbarW;
    private int fontH;
    private int rows;
    private int cols;
    private String tabs = DEFAULT_TABS;
    private boolean focused;
    private int promptRows;
    private int padding;
    private DivElement promptWrap;
    private Timer timer;
    private int maxBufferSize;
    private String cleanPs;
    private int paddingW;

    public TextConsole() {

        // Main element
        term = Document.get().createDivElement();
        term.addClassName("term");
        setElement(term);
        setTabIndex(0);

        // Test element for font size
        test = Document.get().createDivElement();
        test
                .setAttribute("style",
                        "position: absolute; visibility: hidden;height: auto;width: auto;");
        test.setInnerHTML("X");
        term.appendChild(test);

        // Buffer
        buffer = Document.get().createElement("pre");
        buffer.addClassName("b");
        term.appendChild(buffer);

        // Prompt elements
        promptWrap = Document.get().createDivElement();
        promptWrap.addClassName("pw");
        term.appendChild(promptWrap);

        prompt = Document.get().createTableElement();
        promptWrap.appendChild(prompt);
        prompt.setAttribute("cellpadding", "0");
        prompt.setAttribute("cellspacing", "0");
        prompt.setAttribute("border", "0");
        prompt.addClassName("p");

        TableSectionElement tbody = Document.get().createTBodyElement();
        prompt.appendChild(tbody);

        TableRowElement tr = Document.get().createTRElement();
        tbody.appendChild(tr);

        TableCellElement psTd = Document.get().createTDElement();
        psTd.addClassName("psw");
        tr.appendChild(psTd);

        ps = Document.get().createElement("nobr");
        ps.addClassName("ps");
        psTd.appendChild(ps);

        TableCellElement inputTd = Document.get().createTDElement();
        inputTd.addClassName("iw");
        tr.appendChild(inputTd);

        input = (InputElement) Document.get().createElement("input");
        inputTd.appendChild(input);
        input.addClassName("i");
        input.setTabIndex(-1);
        input.setAttribute("spellcheck", "false");

        config = TextConsoleConfig.newInstance();

        setPromtActive(false);

        clickHandler = addDomHandler(new ClickHandler() {

            public void onClick(ClickEvent event) {
                setFocus(true);
            }
        }, ClickEvent.getType());

        keyHandler = addDomHandler(new KeyDownHandler() {

            public void onKeyDown(KeyDownEvent event) {

                // (re-)show the prompt
                setPromtActive(true);

                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    event.preventDefault();
                    carriageReturn();
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_UP
                        || event.getNativeKeyCode() == KeyCodes.KEY_DOWN) {
                    event.preventDefault();
                    handleCommandHistoryBrowse(event.getNativeKeyCode());
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_TAB) {
                    event.preventDefault();
                    suggest();
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE
                        && getInputLenght() == 0) {
                    bell();
                } else if (event.getNativeEvent().getCtrlKey()) {
                    char ctrlChar = getControlKey(event.getNativeKeyCode());
                    if (ctrlChar > 0) {
                        event.preventDefault();
                        handleControlChar(ctrlChar);
                    }
                }

            }
        }, KeyDownEvent.getType());
    }

    protected int getInputLenght() {
        String v = input.getValue();
        if (v != null) {
           return  v.length();
        }
        return -1;
    }

    protected void handleControlChar(char c) {
        switch (c) {
        case TextConsole.CTRL_BACKSPACE:
            backspace();
            break;
        case TextConsole.CTRL_BELL:
            bell();
            break;
        case TextConsole.CTRL_CARRIAGE_RETURN:
            carriageReturn();
            break;
        case TextConsole.CTRL_DELETE:
            bell(); // TODO: not supported yet
            break;
        case TextConsole.CTRL_ESCAPE:
            bell(); // TODO: not supported yet
            break;
        case TextConsole.CTRL_FORM_FEED:
            formFeed();
            break;
        case TextConsole.CTRL_LINE_FEED:
            lineFeed();
            break;
        case TextConsole.CTRL_TAB:
            tab();
            break;

        default:
            bell();
            break;
        }
    }

    protected void suggest() {
        // No suggest by default. Implement by subclassing.
    }

    protected void handleCommandHistoryBrowse(int i) {
        cmdHistoryIndex = i == KeyCodes.KEY_UP ? cmdHistoryIndex - 1
                : cmdHistoryIndex + 1;
        if (cmdHistoryIndex >= 0 && cmdHistoryIndex < cmdHistory.size()) {
            prompt(cmdHistory.get(cmdHistoryIndex));
        } else {
            prompt();
        }
    }

    public String getInput() {
        return input.getValue();
    }

    protected void setInput(String inputText) {
        if (inputText != null) {
            input.setValue(inputText);
        } else {
            input.setValue("");
        }
        if (isFocused()) {
            focusPrompt();
        }
    }

    public void lineFeed() {
        carriageReturn();
    }

    protected void tab() {
        prompt(getInput() + "\t");
    }

    protected void backspace() {
        bell();
    }

    protected void carriageReturn() {
        setPromtActive(false);
        // Append newline first if not there yet
        if (!bufferIsEmpty() && !bufferEndsWithNewLine()) {
            newLine();
            reducePrompt(-1);
        }
        print(getCurrentPromptContent());
        newLine();
        if (promptRows > 1) {
            reducePrompt(-1);
        }
        String lineBuffer = getInput();
        lineBuffer = lineBuffer.trim();
        if (!"".equals(lineBuffer)) {
            cmdHistory.add(lineBuffer);
            cmdHistoryIndex = cmdHistory.size();
        }
        if (handler != null) {
            handler.terminalInput(this, lineBuffer);
        }
    }

    private boolean bufferIsEmpty() {
        return !buffer.hasChildNodes();
    }

    private void setPromtActive(boolean active) {
        if (active && !isPromptActive()) {
            prompt.getStyle().setDisplay(Display.BLOCK);
        } else if (!active && isPromptActive()) {
            prompt.getStyle().setDisplay(Display.NONE);
        }
    }

    private boolean isPromptActive() {
        return !Display.NONE.getCssName()
                .equals(prompt.getStyle().getDisplay());
    }

    private boolean isFocused() {
        return focused;
    }

    public void setConfig(TextConsoleConfig cfg) {
        config = cfg;

        // Update font dimensions
        fontW = test.getClientWidth();
        fontH = test.getClientHeight();

        scrollbarW = getScrollbarWidth();
        String padStr = term.getStyle().getPadding();
        if (padStr != null && padStr.endsWith("px")) {
            padding = Integer.parseInt(padStr.substring(0,padStr.length()-2));
        } else {
            //_log("using default padding: 1x2");
            padding = 1;
            paddingW = 2;
        }

        //_log("setConfig: font=" + fontW + "x" + fontH + ";scrollbar="
        //        + scrollbarW + ";cols=" + config.getCols() + ";rows="
        //        + config.getRows() + ";size=" + getWidth() + "x" + getHeight());

        setPs(config.getPs());
        setCols(config.getCols());
        setRows(config.getRows());
        setMaxBufferSize(config.getMaxBufferSize());
    }

    // Debug instrumentation. TODO: Remove.
    protected void _log(String string) {
        ApplicationConnection.getConsole().log(string);
    }

    public TextConsoleConfig getConfig() {
        return config;
    }

    private boolean bufferEndsWithNewLine() {
        Node last = buffer != null ? buffer.getLastChild() : null;
        //_log("last node: " + (last != null ? last.getNodeName() : "<null>"));
        return last != null && "br".equals(last.getNodeName().toLowerCase());
    }

    private Node createTextNode(String text) {
        return Document.get().createTextNode(text);
    }

    private Node createBr() {
        return Document.get().createBRElement();
    }

    public void focusPrompt() {
        focusPrompt(-1);
    }

    public void focusPrompt(int cursorPos) {
        input.focus();

        // Focus to end
        String s = getInput();
        if (s != null && s.length() > 0) {
            setSelectionRange(input, s.length(), s.length());
        }
    }

    private native void setSelectionRange(Element input, int selectionStart,
            int selectionEnd)/*-{
                             if (input.setSelectionRange) {
                             input.focus();
                             input.setSelectionRange(selectionStart, selectionEnd);
                             }
                             else if (input.createTextRange) {
                             var range = input.createTextRange();
                             range.collapse(true);
                             range.moveEnd('character', selectionEnd);
                             range.moveStart('character', selectionStart);
                             range.select();
                             }
                             }-*/;

    private native int getScrollbarWidth()/*-{

                                          var i = $doc.createElement('p');
                                          i.style.width = '100%';
                                          i.style.height = '200px';
                                          var o = $doc.createElement('div');
                                          o.style.position = 'absolute';
                                          o.style.top = '0px';
                                          o.style.left = '0px';
                                          o.style.visibility = 'hidden';
                                          o.style.width = '200px';
                                          o.style.height = '150px';
                                          o.style.overflow = 'hidden';
                                          o.appendChild(i);
                                          $doc.body.appendChild(o);
                                          var w1 = i.offsetWidth;
                                          var h1 = i.offsetHeight;
                                          o.style.overflow = 'scroll';
                                          var w2 = i.offsetWidth;
                                          var h2 = i.offsetHeight;
                                          if (w1 == w2) w2 = o.clientWidth;
                                          if (h1 == h2) h2 = o.clientWidth;
                                          $doc.body.removeChild(o);
                                          return w1-w2;
                                          }-*/;

    public void newLine() {
        buffer.appendChild(createBr());
        checkBufferLimit();
        reducePrompt(1);
    }

    protected void setPs(String string) {
        cleanPs = Util.escapeHTML(string);
        cleanPs = cleanPs.replaceAll(" ", "&nbsp;");
    }

    public void prompt(String inputText) {
        setPromtActive(true);
        scrollToEnd();
        ps.setInnerHTML(cleanPs);
        setInput(inputText);
    }

    public void scrollToEnd() {
        term.setScrollTop(BIG_NUMBER);
    }

    public void prompt() {
        prompt(null);
    }

    public void print(String string) {
        if (isPromptActive()) {
            setPromtActive(false);
            if (!bufferIsEmpty() && !bufferEndsWithNewLine()) {
                newLine();
                reducePrompt(-1);
            }
            string = getCurrentPromptContent() + string;
        }
        boolean doWrap = config.isWrap();
        //_log("print original: '" + string + "' (" + doWrap + ")");
        String str = string.replaceAll("\t", tabs);

        // Continue to the last text node if available
        Node last = getLastTextNode();
        int linesAdded = 0;
        if (last != null) {
            //_log("print append to old node: '" + last.getNodeValue() + "'");
            str = last.getNodeValue() + str;
            buffer.removeChild(last);
            linesAdded--;
        }

        // Split by the newlines anyway
        int s = 0, e = str.indexOf('\n');
        while (e >= s) {
            String line = str.substring(s, e);
            linesAdded += appendLine(buffer, line, doWrap ? cols : -1);
            buffer.appendChild(createBr());
            s = e + 1;
            e = str.indexOf('\n', s);
        }

        // Print the remaining string
        linesAdded += appendLine(buffer, str.substring(s), doWrap ? cols : -1);

        reducePrompt(linesAdded);
    }

    private String getCurrentPromptContent() {
        return prompt.getInnerText() + getInput();
    }

    private void reducePrompt(int rows) {
        int newRows = promptRows - rows;
        if (newRows < 1) {
            newRows = 1;
        }
        //_log("prompt reduced from " + promptRows + " to " + newRows);
        setPromptHeight(newRows);
    }

    private void setPromptHeight(int rows) {
        int min = 1;
        int max = getRows();
        promptRows = rows < min ? min : (rows > max ? max : rows);
        int newHeight = fontH * rows;
        //_log("Prompt height=" + newHeight);
        promptWrap.getStyle().setHeight(newHeight, Unit.PX);
    }

    /**
     * Split long text based on length.
     *
     * @param parent
     * @param doWrap
     * @param str
     * @return
     */
    private int appendLine(Node parent, String str, int maxLine) {
        int linesAdded = 0;
        boolean doWrap = maxLine > 0;
        if (!doWrap) {
            parent.appendChild(createTextNode(str));
            //_log("append: '" + str + "'");
            linesAdded++;
        } else {
            while (str.length() > maxLine) {
                String piece = str.substring(0, maxLine);
                parent.appendChild(createTextNode(piece));
                parent.appendChild(createBr());
                linesAdded++;
                //_log("append: '" + piece + "'");
                str = str.substring(maxLine);
            }
            parent.appendChild(createTextNode(str));
            //_log("append rest: '" + str + "'");
            linesAdded++;
        }

        // make sore we don't exceed the maximum buffer size
        checkBufferLimit();

        return linesAdded;
    }

    private void checkBufferLimit() {

        // Buffer means only offscreen lines
        int maxb = maxBufferSize + (rows - promptRows);
        while (getBufferSize() > maxb && buffer.hasChildNodes()) {
            buffer.removeChild(buffer.getFirstChild());
        }

    }

    private Node getLastTextNode() {
        if (buffer == null) {
            return null;
        }
        Node l = buffer.getLastChild();
        if (l != null && l.getNodeType() == Node.TEXT_NODE) {
            return l;
        }
        return null;
    }

    public void println(String string) {
        print(string + "\n");
    }

    @Override
    public void setHeight(String height) {
        int oldh = term.getClientHeight();
        super.setHeight(height);
        int newh = term.getClientHeight();
        //_log("set height=" + height  + " clientHeight="+oldh+" to "+newh);
        if (newh != oldh) {
            calculateRowsFromHeight();
        }
    }

    protected void calculateRowsFromHeight() {
        int h = term.getClientHeight() - (2 * padding);
        rows = h / fontH;
        config.setRows(rows);

        //_log("calculateRowsFromHeight: font=" + fontW + "x" + fontH
        //        + ";scrollbar=" + scrollbarW + ";cols=" + cols + ";rows="
        //        + rows + ";size=" + getWidth() + "x" + getHeight());

    }

    protected void calculateHeightFromRows() {
        super.setHeight((rows * fontH) + "px");

        //_log("calculateHeightFromRows: font=" + fontW + "x" + fontH
        //        + ";scrollbar=" + scrollbarW + ";cols=" + cols + ";rows="
        //        + rows + ";size=" + getWidth() + "x" + getHeight());

    }

    protected void calculateColsFromWidth() {
        int w = term.getClientWidth();
        cols = (w - 2 * paddingW) / fontW;
        config.setCols(cols);
        buffer.getStyle().setWidth((cols * fontW), Unit.PX);
        prompt.getStyle().setWidth((cols * fontW), Unit.PX);
        //_log("calculateColsFromWidth: font=" + fontW + "x" + fontH
        //        + ";scrollbar=" + scrollbarW + ";cols=" + cols + ";rows="
        //        + rows + ";size=" + getWidth() + "x" + getHeight());

    }

    protected void calculateWidthFromCols() {
        int w = cols * fontW;
        super.setWidth((w + scrollbarW) + "px");
        buffer.getStyle().setWidth(w, Unit.PX);
        prompt.getStyle().setWidth(w, Unit.PX);

        //_log("calculateWidthFromCols: font=" + fontW + "x" + fontH
        //        + ";scrollbar=" + scrollbarW + ";cols=" + cols + ";rows="
        //        + rows + ";size=" + getWidth() + "x" + getHeight());
    }

    @Override
    public void setWidth(String width) {
        int oldw = term.getClientWidth();
        super.setWidth(width);
        int neww = term.getClientWidth();
        //_log("set width=" + width  + " clientWidth="+oldw+" to "+neww);
        if (neww != oldw) {
            calculateColsFromWidth();
        }
    }

    @Override
    public void setFocus(boolean focused) {
        this.focused = focused;
        super.setFocus(focused);
        if (focused) {
            focusPrompt();
        }
    }

    public void setHandler(TextConsoleHandler handler) {
        this.handler = handler;

    }

    public void setRows(int rows) {
        if (rows > 0) {
            this.rows = rows;
            calculateHeightFromRows();
        } else {
            calculateRowsFromHeight();
        }
    }

    public int getRows() {
        return rows;
    }

    public void setCols(int cols) {
        if (cols > 0) {
            this.cols = cols;
            calculateWidthFromCols();
        } else {
            calculateColsFromWidth();
        }
    }

    public int getCols() {
        return cols;
    }

    public void reset() {
        setPromtActive(false);
        clearBuffer();
        setPromptHeight(getRows());
        print(config.getGreeting());
        prompt();
    }

    public int getBufferSize() {
        return (buffer.getClientHeight() / fontH);
    }

    public int getMaxBufferSize() {
        return maxBufferSize;
    }

    public void setMaxBufferSize(int maxBuffer) {
        maxBufferSize = maxBuffer > 0 ? maxBuffer : 0;
        checkBufferLimit();
    }

    public void clearBuffer() {
        // Remove all children.
        while (buffer.hasChildNodes()) {
            buffer.removeChild(buffer.getFirstChild());
        }
    }

    public void formFeed() {
        for (int i = 0; i < promptRows; i++) {
            newLine();
        }
        setPromptHeight(getRows());
        scrollToEnd();

        checkBufferLimit();
    }

    protected void clearCommandHistory() {
        cmdHistory = new ArrayList<String>();
        cmdHistoryIndex = -1;
    }

    public String getHeight() {
        return (term.getClientHeight()-2*padding) + "px";
    }

    public String getWidth() {
        return (term.getClientWidth() + scrollbarW-2*paddingW)+ "px";
    }

    protected void bell() {
        // Clear previous
        if (timer != null) {
            timer.cancel();
            timer = null;
            term.removeClassName("term-rev");
            input.removeClassName("term-rev");
        }
        // Add styles and start the timer
        input.addClassName("term-rev");
        term.addClassName("term-rev");
        timer = new Timer() {

            @Override
            public void run() {
                term.removeClassName("term-rev");
                input.removeClassName("term-rev");
            }
        };
        timer.schedule(150);
    }

    @Override
    protected void onUnload() {
        super.onUnload();

        if (clickHandler != null) {
            clickHandler.removeHandler();
        }
        if (focusHandler != null) {
            focusHandler.removeHandler();
        }
        if (keyHandler != null) {
            keyHandler.removeHandler();
        }
    }
}
TOP

Related Classes of org.vaadin.console.client.ui.TextConsole

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.