matteroverdrive.gui.element.MOElementTextField.java Source code

Java tutorial

Introduction

Here is the source code for matteroverdrive.gui.element.MOElementTextField.java

Source

/*
 * This file is part of Matter Overdrive
 * Copyright (c) 2015., Simeon Radivoev, All rights reserved.
 *
 * Matter Overdrive is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Matter Overdrive 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Matter Overdrive.  If not, see <http://www.gnu.org/licenses>.
 */

package matteroverdrive.gui.element;

import matteroverdrive.Reference;
import matteroverdrive.client.data.Color;
import matteroverdrive.client.render.HoloIcon;
import matteroverdrive.data.ScaleTexture;
import matteroverdrive.gui.MOGuiBase;
import matteroverdrive.gui.events.ITextHandler;
import matteroverdrive.proxy.ClientProxy;
import matteroverdrive.util.MOStringHelper;
import matteroverdrive.util.RenderUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.util.ChatAllowedCharacters;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.MinecraftForgeClient;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MOElementTextField extends MOElementBase {
    public int borderColor = new Color(55, 55, 55).getColor();
    public int backgroundColor = new Color(139, 139, 139).getColor();
    public int disabledColor = new Color(198, 198, 198).getColor();
    public int selectedLineColor = new Color(160, 160, 224).getColor();
    public int textColor = new Color(224, 224, 224).getColor();
    public int selectedTextColor = new Color(224, 224, 224).getColor();
    public int defaultCaretColor = new Color(255, 255, 255).getColor();
    protected int renderStart;
    protected char[] text;
    protected int textLength;
    protected int selectionStart, selectionEnd;
    protected int renderStartX, renderStartY;
    protected int caret, prevCaret, caretX;

    private boolean isFocused;
    private boolean canFocusChange = true;

    private boolean selecting, pressed;

    private byte caretCounter;
    protected boolean caretInsert;
    protected boolean smartCaret = true;
    protected boolean smartCaretCase = true;

    protected boolean multiline = false;

    protected boolean enableStencil = true;
    protected boolean includeVanilla = true;
    protected CharSequence seq;
    protected Matcher filter;
    protected int textOffsetX, textOffsetY;
    private ScaleTexture background;
    ITextHandler textHandler;
    private HoloIcon holoIcon;
    private Color color;

    public MOElementTextField(MOGuiBase gui, ITextHandler textHandler, int posX, int posY, int width, int height) {
        this(gui, textHandler, posX, posY, width, height, (short) 32);
    }

    public MOElementTextField(MOGuiBase gui, ITextHandler textHandler, int posX, int posY, int width, int height,
            short limit) {
        super(gui, posX, posY, width, height);
        background = new ScaleTexture(new ResourceLocation(Reference.PATH_ELEMENTS + "search_field.png"), 166, 14)
                .setOffsets(18, 12, 9, 3);
        this.textHandler = textHandler;
        setMaxLength(limit);
    }

    public MOElementTextField(MOGuiBase gui, int posX, int posY, int width, int height) {
        this(gui, gui, posX, posY, width, height);
    }

    public MOElementTextField(MOGuiBase gui, int posX, int posY, int width, int height, short limit) {
        this(gui, gui, posX, posY, width, height, limit);
    }

    public MOElementTextField setTextColor(Number textColor, Number selectedTextColor) {

        if (textColor != null) {
            this.textColor = textColor.intValue();
        }
        if (selectedTextColor != null) {
            this.selectedTextColor = selectedTextColor.intValue();
        }
        return this;
    }

    public MOElementTextField setSelectionColor(Number selectedLineColor, Number defaultCaretColor) {

        if (selectedLineColor != null) {
            this.selectedLineColor = selectedLineColor.intValue();
        }
        if (defaultCaretColor != null) {
            this.defaultCaretColor = defaultCaretColor.intValue();
        }
        return this;
    }

    public MOElementTextField setBackgroundColor(Number backgroundColor, Number disabledColor, Number borderColor) {

        if (backgroundColor != null) {
            this.backgroundColor = backgroundColor.intValue();
        }
        if (disabledColor != null) {
            this.disabledColor = disabledColor.intValue();
        }
        if (borderColor != null) {
            this.borderColor = borderColor.intValue();
        }
        return this;
    }

    public MOElementTextField setMultiline(boolean multi) {

        multiline = multi;
        return this;
    }

    public MOElementTextField setFocusable(boolean focusable) {

        canFocusChange = focusable;
        return this;
    }

    public MOElementTextField setFocused(boolean focused) {

        if (isFocusable()) {
            isFocused = focused;
            caretCounter = 0;
        }
        return this;
    }

    public MOElementTextField setText(String text) {

        selectionStart = 0;
        selectionEnd = textLength;
        writeText(text);
        return this;
    }

    public MOElementTextField setMaxLength(short limit) {

        char[] oldText = text;
        text = new char[limit];
        textLength = Math.min(limit, textLength);
        if (oldText != null) {
            System.arraycopy(oldText, 0, text, 0, textLength);
        }
        findRenderStart();
        return this;
    }

    public boolean isFocused() {

        return isEnabled() && isFocused;
    }

    public boolean isFocusable() {

        return canFocusChange;
    }

    public int getContentHeight() {

        FontRenderer font = getFontRenderer();
        int height = font.FONT_HEIGHT;
        if (multiline) {
            for (int i = 0; i < textLength; ++i) {
                if (text[i] == '\n') {
                    height += font.FONT_HEIGHT;
                }
            }
        }
        return height;
    }

    public int getVisibleHeight() {

        FontRenderer font = getFontRenderer();
        int height = font.FONT_HEIGHT;
        if (multiline) {
            for (int i = 0; i < textLength; ++i) {
                if (text[i] == '\n') {
                    height += font.FONT_HEIGHT;
                }
            }
        }
        return Math.min(height - renderStartY, sizeY);
    }

    public int getContentWidth() {

        FontRenderer font = getFontRenderer();
        int width = 0;
        for (int i = 0; i < textLength; ++i) {
            width += font.getCharWidth(text[i]);
        }
        return width;
    }

    public int getVisibleWidth() {

        FontRenderer font = getFontRenderer();
        int width = 0, endX = sizeX - 1, maxWidth = 0;
        if (multiline) {
            for (int i = 0; i < textLength; ++i) {
                char c = text[i];
                int charW = font.getCharWidth(c);
                if (c == '\n') {
                    maxWidth = Math.max(maxWidth, width);
                    width = 0;
                } else {
                    width += charW;
                }
                if ((width - renderStartX) >= endX) {
                    maxWidth = endX + renderStartX;
                    break;
                }
            }
            maxWidth -= renderStartX;
        } else {
            for (int i = renderStartX; i < textLength; ++i) {
                char c = text[i];
                int charW = font.getCharWidth(c);
                maxWidth += charW;
                if (maxWidth >= endX) {
                    maxWidth = endX;
                    break;
                }
            }
        }
        return maxWidth;
    }

    public String getText() {

        return new String(text, 0, textLength);
    }

    public String getSelectedText() {

        if (selectionStart != selectionEnd) {
            return new String(text, selectionStart, selectionEnd);
        }
        return getText();
    }

    public void writeText(String text) {

        int i = 0;
        for (int e = text.length(); i < e; ++i) {
            if (!insertCharacter(text.charAt(i))) {
                break;
            }
        }
        clearSelection();
        findRenderStart();
        onCharacterEntered(i > 0);
    }

    public boolean isAllowedCharacter(char charTyped) {

        return (multiline && charTyped == '\n') || ChatAllowedCharacters.isAllowedCharacter(charTyped);
    }

    protected boolean onEnter() {

        if (multiline) {
            boolean typed;
            if (caretInsert && selectionStart == selectionEnd) {
                caretInsert = false;
                typed = insertCharacter('\n');
                caretInsert = true;
            } else {
                typed = insertCharacter('\n');
            }
            clearSelection();
            findRenderStart();
            onCharacterEntered(typed);
            return true;
        }
        return false;
    }

    protected void onFocusLost() {

    }

    protected boolean insertCharacter(char charTyped) {

        if (isAllowedCharacter(charTyped)) {

            if (selectionStart != selectionEnd) {
                if (caret == selectionStart) {
                    ++caret;
                }
                text[selectionStart++] = charTyped;
                return true;
            }

            int len = getMaxLength();
            if ((caretInsert && caret == len) || textLength == len) {
                return false;
            }

            if (!caretInsert || (multiline && text[caret] == '\n')) {
                if (caret < textLength) {
                    System.arraycopy(text, caret, text, caret + 1, textLength - caret);
                }
                ++textLength;
            }
            text[caret++] = charTyped;
            return true;
        } else {
            return true;
        }
    }

    protected void findRenderStart() {

        caret = MathHelper.clamp_int(caret, 0, textLength);
        if (selectionStart == selectionEnd) {
            selectionStart = selectionEnd = caret;
        }

        if (multiline) {
            findRenderStartML();
            return;
        }

        renderStartY = 0;

        if (caret < renderStartX) {
            renderStartX = caret;
            return;
        }

        FontRenderer font = getFontRenderer();
        int endX = sizeX - 2;

        for (int i = renderStartX, width = 0; i < caret; ++i) {
            width += font.getCharWidth(text[i]);
            while (width >= endX) {
                width -= font.getCharWidth(text[renderStartX++]);
                if (renderStartX >= textLength) {
                    return;
                }
            }
        }
    }

    public int getContentLength() {

        return textLength;
    }

    public int getMaxLength() {

        return text.length;
    }

    protected void findRenderStartML() {

        if (caret == textLength && textLength == 0) {
            renderStartX = renderStartY = 0;
            return;
        }
        FontRenderer font = getFontRenderer();
        int widthLeft = 0;
        int breaksAbove = 0;
        for (int i = caret; i-- > 0;) {
            char c = text[i];
            if (c == '\n') {
                for (; i > 0; --i) {
                    c = text[i];
                    if (c == '\n') {
                        breaksAbove += font.FONT_HEIGHT;
                    }
                }
                break;
            }
            widthLeft += font.getCharWidth(c);
        }
        caretX = widthLeft;

        int pos = Math.max(0, (sizeY - 2) / font.FONT_HEIGHT) * font.FONT_HEIGHT;
        if (caret > 0 && text[caret - 1] == '\n') {
            renderStartX = 0;
            if (caret == textLength) {
                renderStartY -= pos;
                renderStartY &= ~renderStartY >> 31;
            }
        }

        while ((breaksAbove - renderStartY) < 0) {
            renderStartY -= font.FONT_HEIGHT;
        }
        while ((breaksAbove - renderStartY) >= pos) {
            renderStartY += font.FONT_HEIGHT;
        }

        int dir = prevCaret > caret ? 1 : -1;
        for (int i = 0; (widthLeft - renderStartX) < 0; i += dir) {
            char c = text[caret + i];
            if (c == '\n') {
                break;
            }
            renderStartX -= font.getCharWidth(c);
        }
        renderStartX &= ~renderStartX >> 31;
        pos = sizeX - 2 - 3;
        for (int i = 0; (widthLeft - renderStartX) >= pos; ++i) {
            renderStartX += font.getCharWidth(text[caret - i]);
        }
        prevCaret = caret;
    }

    protected void clearSelection() {

        if (selectionStart != selectionEnd) {
            if (selectionEnd < textLength) {
                System.arraycopy(text, selectionEnd, text, selectionStart, textLength - selectionEnd);
            }
            textLength -= selectionEnd - selectionStart;

            selectionEnd = caret = selectionStart;
            findRenderStart();
            onCharacterEntered(true);
        }
    }

    protected final int seekNextCaretLocation(int pos) {

        return seekNextCaretLocation(pos, true);
    }

    protected int seekNextCaretLocation(int pos, boolean forward) {

        int dir = forward ? 1 : -1;
        int e = forward ? textLength : 0;
        if (pos != e) {
            pos += dir;
        }
        char prevChar = pos == textLength ? 0 : text[pos];
        if (!forward) {
            if (pos != e && Character.isSpaceChar(prevChar)) {
                pos += !Character.isSpaceChar(text[pos + dir]) ? dir : 0;
            }
        } else if (pos != e && Character.isSpaceChar(prevChar)) {
            pos -= !Character.isSpaceChar(text[pos - dir]) ? dir : 0;
        }
        prevChar = text[pos];
        int i = pos;

        if (smartCaret) {
            for (; i != e; i += dir) {
                char curChar = text[i];
                boolean dig = Character.isLetterOrDigit(curChar) != Character.isLetterOrDigit(prevChar);
                boolean caze = !dig && Character.isUpperCase(curChar) != Character.isUpperCase(prevChar);
                boolean space = Character.isWhitespace(prevChar) != Character.isWhitespace(curChar);
                if (dig || caze || space) {
                    int o = 0;
                    if (smartCaretCase && caze) {
                        o = !forward ? 0 : -dir;
                    } else {
                        if (space) {
                            if (forward) {
                                if (i != e && !Character.isWhitespace(text[i + dir])) {
                                    o = Character.isWhitespace(curChar) ? 1 : 0;
                                }
                            } else {
                                o = 1;
                            }
                        }
                    }
                    return i + o;
                }
                prevChar = curChar;
            }
        } else {
            for (; i != e; i += dir) {
                char curChar = text[i];
                if (Character.isSpaceChar(curChar) != Character.isSpaceChar(prevChar)) {
                    return i;
                }
            }
        }
        return forward ? textLength : 0;
    }

    @Override
    public boolean onKeyTyped(char charTyped, int keyTyped) {

        if (!isFocused()) {
            return false;
        }

        switch (charTyped) {
        case 1: // ^A
            selectionEnd = caret = textLength;
            selectionStart = 0;
            findRenderStart();
            return true;
        case 3: // ^C
            if (selectionStart != selectionEnd) {
                GuiScreen.setClipboardString(getSelectedText());
            }
            return true;
        case 24: // ^X
            if (selectionStart != selectionEnd) {
                GuiScreen.setClipboardString(getSelectedText());
                clearSelection();
            }

            return true;
        case 22: // ^V
            writeText(GuiScreen.getClipboardString());

            return true;
        default:
            switch (keyTyped) {
            case Keyboard.KEY_ESCAPE:
                setFocused(false);
                return !isFocused();
            case Keyboard.KEY_RETURN:
            case Keyboard.KEY_NUMPADENTER:
                return onEnter();
            case Keyboard.KEY_INSERT:
                if (GuiScreen.isShiftKeyDown()) {
                    writeText(GuiScreen.getClipboardString());
                } else {
                    caretInsert = !caretInsert;
                }

                return true;
            case Keyboard.KEY_CLEAR: // mac only (clear selection)
                clearSelection();

                return true;
            case Keyboard.KEY_DELETE: // delete
                boolean changed = false;
                if (!GuiScreen.isShiftKeyDown()) {
                    if (selectionStart != selectionEnd) {
                        clearSelection();
                    } else if (GuiScreen.isCtrlKeyDown()) {
                        int size = seekNextCaretLocation(caret, true) - caret;
                        selectionStart = caret;
                        selectionEnd = caret + size;
                        clearSelection();
                    } else {
                        if (caret < textLength && textLength > 0) {
                            --textLength;
                            System.arraycopy(text, caret + 1, text, caret, textLength - caret);
                            changed = true;
                        }
                        findRenderStart();

                        onCharacterEntered(changed);
                    }

                    return true;
                }
                // continue.. (shift+delete = backspace)
            case Keyboard.KEY_BACK: // backspace
                changed = false;
                boolean calledEntered = true, onBreak = false;
                if (selectionStart != selectionEnd) {
                    clearSelection();
                } else if (GuiScreen.isCtrlKeyDown()) {
                    int size = seekNextCaretLocation(caret, false) - caret;
                    selectionStart = caret + size;
                    selectionEnd = caret;
                    clearSelection();
                } else {
                    calledEntered = false;
                    if (caret > 0 && textLength > 0) {
                        if (caret != textLength) {
                            System.arraycopy(text, caret, text, caret - 1, textLength - caret);
                        }
                        onBreak = text[--caret] == '\n';
                        --textLength;
                        changed = true;
                    }
                }
                int old = caret;
                if (!onBreak) {
                    for (int i = 3; i-- > 0 && caret > 1 && text[caret - 1] != '\n'; --caret) {
                        ;
                    }
                }
                findRenderStart();
                caret = old;

                if (!calledEntered) {
                    onCharacterEntered(changed);
                }

                return true;
            case Keyboard.KEY_HOME: // home
                int begin = 0;
                if (!GuiScreen.isCtrlKeyDown()) {
                    for (int i = caret - 1; i > 0; --i) {
                        if (text[i] == '\n') {
                            begin = Math.min(i + 1, textLength);
                            break;
                        }
                    }
                }

                if (GuiScreen.isShiftKeyDown()) {
                    if (caret >= selectionEnd) {
                        selectionEnd = selectionStart;
                    }
                    selectionStart = begin;
                } else {
                    selectionStart = selectionEnd = begin;
                }
                caret = begin;
                findRenderStart();

                return true;
            case Keyboard.KEY_END: // end
                int end = textLength;
                if (!GuiScreen.isCtrlKeyDown()) {
                    for (int i = caret; i < textLength; ++i) {
                        if (text[i] == '\n') {
                            end = i;
                            break;
                        }
                    }
                }

                if (GuiScreen.isShiftKeyDown()) {
                    if (caret <= selectionStart) {
                        selectionStart = selectionEnd;
                    }
                    selectionEnd = end;
                } else {
                    selectionStart = selectionEnd = end;
                }
                caret = end;
                findRenderStart();

                return true;
            case Keyboard.KEY_LEFT: // left arrow
            case Keyboard.KEY_RIGHT: // right arrow
                int size = keyTyped == 203 ? -1 : 1;
                boolean shiftCaret = false;
                if (GuiScreen.isCtrlKeyDown()) {
                    size = seekNextCaretLocation(caret, keyTyped == 205) - caret;
                } else if (MOStringHelper.isAltKeyDown() && GuiScreen.isShiftKeyDown()) {
                    caret = seekNextCaretLocation(caret, keyTyped == 205);
                    selectionStart = selectionEnd = caret;
                    size = seekNextCaretLocation(caret, keyTyped != 205) - caret;
                    shiftCaret = true;
                }

                if (!GuiScreen.isShiftKeyDown()) {
                    selectionStart = selectionEnd = caret;
                }

            {
                int t = caret;
                caret = MathHelper.clamp_int(caret + size, 0, textLength);
                size = caret - t;
            }

                if (GuiScreen.isShiftKeyDown()) {
                    if (caret == selectionStart + size) {
                        selectionStart = caret;
                    } else if (caret == selectionEnd + size) {
                        selectionEnd = caret;
                    }

                    if (selectionStart > selectionEnd) {
                        int t = selectionStart;
                        selectionStart = selectionEnd;
                        selectionEnd = t;
                    }
                }

                if (shiftCaret) {
                    caret = caret - size;
                }
                findRenderStart();

                return true;
            case Keyboard.KEY_UP:
            case Keyboard.KEY_DOWN:
                if (!multiline) {
                    return false;
                }

                if (!GuiScreen.isShiftKeyDown()) {
                    selectionStart = selectionEnd = caret;
                }
                int dir = keyTyped == Keyboard.KEY_UP ? -1 : 1;
                end = dir == -1 ? 0 : textLength;
                int i = caret, pos = caretX;
                old = i;
                for (; i != end; i += dir) {
                    if ((dir == -1 ? i != caret : true) && text[i] == '\n') {
                        if (i != end) {
                            i += dir;
                        } else {
                            return true;
                        }
                        break;
                    }
                }
                l: if (dir == -1) {
                    for (; i > 0 && text[i] != '\n'; --i) {
                        ;
                    }
                    if (i == 0) {
                        if (text[0] == '\n') {
                            caret = 0;
                            findRenderStart();
                            caretX = pos;
                        }
                        break l;
                    }
                    ++i;
                }
                FontRenderer font = getFontRenderer();
                for (int width = 0; i <= textLength; ++i) {
                    char c = i < textLength ? text[i] : 0;
                    if (i == textLength || c == '\n' || width >= pos) {
                        caret = i;
                        findRenderStart();
                        caretX = pos;
                        break;
                    } else {
                        width += font.getCharWidth(c);
                    }
                }

                size = caret - old;

                if (GuiScreen.isShiftKeyDown()) {
                    if (selectionStart == selectionEnd) {
                        selectionStart = selectionEnd = old;
                    }
                    if (caret == selectionStart + size) {
                        selectionStart = caret;
                    } else if (caret == selectionEnd + size) {
                        selectionEnd = caret;
                    }

                    if (selectionStart > selectionEnd) {
                        int t = selectionStart;
                        selectionStart = selectionEnd;
                        selectionEnd = t;
                    }
                }

                return true;
            default:
                if (isAllowedCharacter(charTyped)) {
                    boolean typed = insertCharacter(charTyped);
                    clearSelection();
                    findRenderStart();
                    onCharacterEntered(typed);
                    return true;
                } else {
                    return false;
                }
            }
        }
    }

    @Override
    public boolean onMousePressed(int mouseX, int mouseY, int mouseButton) {

        pressed = mouseButton == 0;
        selecting = mouseButton == 0 && isFocused();
        l: if (selecting) {
            if (textLength == 0) {
                selectionStart = selectionEnd = caret = 0;
                break l;
            }
            FontRenderer font = getFontRenderer();
            int posX = mouseX - this.posX - 1, posY = mouseY - this.posY - 1;
            s: if (!multiline) {
                for (int i = renderStartX, width = 0;;) {
                    int charW = font.getCharWidth(text[i]);
                    if ((width += charW) > posX || ++i >= textLength) {
                        selectionStart = selectionEnd = caret = i;
                        break;
                    }
                }
            } else {
                posX += renderStartX;
                posY += renderStartY;
                int maxX = 0;
                boolean found = false;
                for (int i = 0, width = 0, height = font.FONT_HEIGHT; i < textLength;) {
                    char c = text[i];
                    int charW = 0;
                    if (c == '\n') {
                        if (height > posY) {
                            maxX = i;
                            break;
                        }
                        found = false;
                        width = 0;
                        height += font.FONT_HEIGHT;
                    } else {
                        charW = font.getCharWidth(c);
                    }
                    if (!found) {
                        maxX = i;
                    }
                    if ((width += charW) > posX || ++i >= textLength) {
                        if (posY < height || i >= textLength) {
                            selectionStart = selectionEnd = caret = i;
                            break s;
                        } else {
                            ++i;
                            found = true;
                        }
                    }
                }
                selectionStart = selectionEnd = caret = maxX;
            }
            findRenderStart();
        }

        setFocused(true);
        return true;
    }

    @Override
    public void update(int mouseX, int mouseY) {

        caretCounter = (byte) (Minecraft.getMinecraft().ingameGUI.getUpdateCounter() & 0xFF);
        // if (selecting) {
        // FontRenderer font = getFontRenderer();
        // int pos = mouseX - posX - 1;
        // for (int i = renderStart, width = 0; i < textLength; ++i) {
        // }
        // }
    }

    @Override
    public void drawBackground(int mouseX, int mouseY, float gameTicks) {
        if (background != null) {
            background.render(posX - textOffsetX, posY - textOffsetY, sizeX + textOffsetX, sizeY + textOffsetY);
        }
    }

    public void setBackground(ScaleTexture texture) {
        background = texture;
    }

    public void setTextOffset(int x, int y) {
        textOffsetX = x;
        textOffsetY = y;
    }

    @Override
    public void drawForeground(int mouseX, int mouseY) {

        boolean enableStencil = this.enableStencil;
        int bit = -1;
        l: if (enableStencil) {
            bit = MinecraftForgeClient.reserveStencilBit();
            if (bit == -1) {
                enableStencil = false;
                break l;
            }
            GL11.glEnable(GL11.GL_STENCIL_TEST);
            drawStencil(posX + 1, posY + 1, posX + sizeX - 1, posY + sizeY - 1, 1 << bit);
        }

        FontRenderer font = getFontRenderer();
        char[] text = this.text;
        int startX = posX + 1 - (multiline ? renderStartX : 0), endX = sizeX - 1;
        int startY = posY + 1 - renderStartY, endY = startY + font.FONT_HEIGHT;
        int drawY = renderStartY + Math.max(0, (sizeY - 2) / font.FONT_HEIGHT) * font.FONT_HEIGHT;
        if (enableStencil) {
            if (sizeY - (drawY - renderStartY) > 2) {
                drawY += font.FONT_HEIGHT;
            }
        }
        int drawX = endX + (multiline ? renderStartX : 0);
        for (int i = multiline ? 0 : renderStartX, width = 0, height = 0; i <= textLength; ++i) {
            boolean end = i == textLength, draw = height >= renderStartY && width < drawX && height < drawY;
            int charW = 2;
            char c = 0;
            if (!end) {
                c = text[i];
                if (draw) {
                    charW = multiline && c == '\n' ? 2 : font.getCharWidth(c);
                }
                int tWidth = width + charW;
                if (multiline) {
                    if (!enableStencil) {
                        draw &= width >= renderStartX;
                    }
                    draw &= tWidth > renderStartX;
                }
                l: if (!enableStencil && tWidth > endX) {
                    draw = false;
                    if (multiline) {
                        if (c == '\n') {
                            break l;
                        }
                        continue;
                    }
                    break;
                }
            }

            boolean drawCaret = draw && i == caret && (caretCounter &= 31) < 16 && isFocused();
            if (drawCaret) {
                int caretEnd = width + 2;
                if (caretInsert) {
                    caretEnd = width + charW;
                }
                drawModalRect(startX + width, startY - 1 + height, startX + caretEnd, endY + height,
                        (0xFF000000 & defaultCaretColor) | (~defaultCaretColor & 0xFFFFFF));
            }

            if (draw && !end) {
                boolean selected = i >= selectionStart & i < selectionEnd;
                if (selected) {
                    drawModalRect(startX + width, startY + height, startX + width + charW, endY + height,
                            selectedLineColor);
                }
                if (c != '\n') {
                    font.drawString(String.valueOf(c), startX + width, startY + height,
                            selected ? selectedTextColor : textColor);
                }
            }

            if (drawCaret) {
                int caretEnd = width + 2;
                if (caretInsert) {
                    caretEnd = width + charW;
                }

                GL11.glEnable(GL11.GL_BLEND);
                GL11.glBlendFunc(GL11.GL_ONE_MINUS_DST_COLOR, GL11.GL_ZERO);
                gui.drawSizedRect(startX + width, startY - 1 + height, startX + caretEnd, endY + height, -1);
                GL11.glDisable(GL11.GL_BLEND);
            }

            if (c == '\n') {
                height += font.FONT_HEIGHT;
                charW = width = 0;
                if (height > drawY) {
                    break;
                }
            }

            width += charW;
            if (!multiline && width > endX) {
                break;
            }
        }

        if (enableStencil) {
            GL11.glDisable(GL11.GL_STENCIL_TEST);
            MinecraftForgeClient.releaseStencilBit(bit);
        }

        if (holoIcon != null) {
            if (color != null)
                RenderUtils.applyColor(color);

            float heightScale = (float) Math.min(holoIcon.getOriginalHeight(), sizeY)
                    / (float) holoIcon.getOriginalHeight();
            ClientProxy.holoIcons.renderIcon(holoIcon, posX - holoIcon.getOriginalWidth() - 2, posY,
                    (int) (holoIcon.getOriginalWidth() * heightScale),
                    (int) (holoIcon.getOriginalHeight() * heightScale));
        }
    }

    public void setHoloIcon(HoloIcon holoIcon) {
        this.holoIcon = holoIcon;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    @Override
    public void updateInfo() {

    }

    @Override
    public void init() {

    }

    @Override
    public void addTooltip(List<String> var1, int mouseX, int mouseY) {

    }

    protected void onCharacterEntered(boolean success) {
        if (isFocused())
            textHandler.textChanged(getName(), getText(), success);
    }

    public MOElementTextField setFilter(Pattern pattern, boolean includeVanilla) {

        filter = pattern.matcher(seq);
        this.includeVanilla = includeVanilla;
        return this;
    }
}