Package lcmc.common.ui

Source Code of lcmc.common.ui.TerminalPanel$MyDocument

/*
* This file is part of DRBD Management Console by LINBIT HA-Solutions GmbH
* written by Rasto Levrinc.
*
* Copyright (C) 2009, LINBIT HA-Solutions GmbH.
* Copyright (C) 2011-2012, Rastislav Levrinc.
*
* DRBD Management Console 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 2, or (at your option)
* any later version.
*
* DRBD Management Console 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 drbd; see the file COPYING.  If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package lcmc.common.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;
import javax.inject.Named;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.plaf.TextUI;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import lcmc.common.domain.Application;
import lcmc.host.domain.Host;
import lcmc.robotest.RoboTest;
import lcmc.robotest.StartTests;
import lcmc.robotest.Test;
import lcmc.common.domain.ExecCallback;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import lcmc.common.domain.util.Tools;
import lcmc.cluster.service.ssh.ExecCommandConfig;

/**
* An implementation of a terminal panel that show commands and output from
* remote host. It is also possible to write commands and execute them.
*/
@Named
public class TerminalPanel extends JScrollPane {
    private static final Logger LOG = LoggerFactory.getLogger(TerminalPanel.class);
    /** Command to list all the cheats. */
    private static final String CHEAT_LIST  = "cheatlist";
    /** Command to turn off the god mode. */
    private static final String GOD_OFF = "nogodmode";
    /** Command to turn on the god mode. */
    private static final String GOD_ON  = "godmode";
    private static final String RUN_GC  = "rungc";
    /** Allocate 10MB of memory for 1 minute. */
    private static final String ALLOCATE_10 = "allocate10";
    /** Command to start frenzy clicking for short period. */
    private static final String CLICKTEST_SHORT = "lclicksh";
    /** Command to start frenzy clicking for longer period. */
    private static final String CLICKTEST_LONG = "lclicklo";
    /** Command to start lazy clicking for short period. */
    private static final String CLICKTEST_LAZY_SHORT = "lclicklazysh";
    /** Command to start lazy clicking for longer period. */
    private static final String CLICKTEST_LAZY_LONG = "lclicklazylo";
    /** Command to start frenzy rigth clicking for short period. */
    private static final String RIGHT_CLICKTEST_SHORT = "rclicksh";
    /** Command to start frenzy rigth clicking for longer period. */
    private static final String RIGHT_CLICKTEST_LONG = "rclicklo";
    /** Command to start lazy rigth clicking for short period. */
    private static final String RIGHT_CLICKTEST_LAZY_SHORT = "rclicklazysh";
    /** Command to start lazy rigth clicking for longer period. */
    private static final String RIGHT_CLICKTEST_LAZY_LONG = "rclicklazylo";
    /** Command to start short mouse moving. */
    private static final String MOVETEST_SHORT = "movetestsh";
    /** Command to start mouse moving. */
    private static final String MOVETEST_LONG = "movetestlo";
    /** Command to start mouse moving. */
    private static final String MOVETEST_LAZY_SHORT = "movetestlazysh";
    /** Command to start mouse moving. */
    private static final String MOVETEST_LAZY_LONG = "movetestlazylo";
    /** Command to increment debug level. */
    private static final String DEBUG_INC = "debuginc";
    /** Command to decrement debug level. */
    private static final String DEBUG_DEC = "debugdec";
    /** Register mouse movement. */
    private static final String REGISTER_MOVEMENT = "registermovement";
    /** List of cheats, with positions while typing them. */
    private static final Map<String, Integer> CHEATS_MAP = new LinkedHashMap<String, Integer>();
    @Inject
    private RoboTest roboTest;
    private static final Map<String, Test> TEST_CHEATS = new HashMap<String, Test>();
    static {
        for (final StartTests.Type type : new StartTests.Type[]{StartTests.Type.PCMK,
                                                                StartTests.Type.DRBD,
                                                                StartTests.Type.VM,
                                                                StartTests.Type.GUI}) {
            for (final char index : new Character[]{
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}) {
                TEST_CHEATS.put(type.getTestName() + index, new Test(type, index));
            }
        }
    }
    static {
        CHEATS_MAP.put(CHEAT_LIST, 0);
        CHEATS_MAP.put(GOD_OFF, 0); /* off must be before on */
        CHEATS_MAP.put(GOD_ON, 0);
        CHEATS_MAP.put(RUN_GC, 0);
        CHEATS_MAP.put(ALLOCATE_10, 0);
        CHEATS_MAP.put(CLICKTEST_SHORT, 0);
        CHEATS_MAP.put(CLICKTEST_LONG, 0);
        CHEATS_MAP.put(CLICKTEST_LAZY_SHORT, 0);
        CHEATS_MAP.put(CLICKTEST_LAZY_LONG, 0);
        CHEATS_MAP.put(RIGHT_CLICKTEST_SHORT, 0);
        CHEATS_MAP.put(RIGHT_CLICKTEST_LONG, 0);
        CHEATS_MAP.put(RIGHT_CLICKTEST_LAZY_SHORT, 0);
        CHEATS_MAP.put(RIGHT_CLICKTEST_LAZY_LONG, 0);
        CHEATS_MAP.put(MOVETEST_SHORT, 0);
        CHEATS_MAP.put(MOVETEST_LONG, 0);
        CHEATS_MAP.put(MOVETEST_LAZY_SHORT, 0);
        CHEATS_MAP.put(MOVETEST_LAZY_LONG, 0);
        CHEATS_MAP.put(DEBUG_INC, 0);
        CHEATS_MAP.put(DEBUG_DEC, 0);
        for (final String test : TEST_CHEATS.keySet()) {
            CHEATS_MAP.put(test, 0);
        }
        CHEATS_MAP.put(REGISTER_MOVEMENT, 0);
    }
    private Host host;
    private JTextPane terminalArea;
    private MutableAttributeSet commandColor;
    private MutableAttributeSet errorColor;
    private MutableAttributeSet outputColor;
    private MutableAttributeSet promptColor;
    private int commandOffset = 0;
    private boolean userCommand = false;
    private boolean editEnabled = false;
    /** Beginning of the previous line. */
    private int prevLine = 0;
    /** Position of the cursor in the text. */
    private int pos = 0;
    /** Position in terminal area lock. */
    private final Lock mPosLock = new ReentrantLock();
    /** Maximum position of the cursor in the text. */
    private int maxPos = 0;
    /** Terminal output colors. */
    private final Map<String, Color> terminalColor = new HashMap<String, Color>();
    private Color defaultOutputColor;
    @Inject
    private GUIData guiData;
    @Inject
    private Application application;
    @Inject
    private StartTests srartTests;

    public void initWithHost(final Host host0) {
        host = host0;
        /* Sets terminal some of the output colors. This is in no way complete
         * or correct and probably doesn't have to be. */
        terminalColor.put("0", Tools.getDefaultColor("TerminalPanel.TerminalWhite"));
        terminalColor.put("30", Tools.getDefaultColor("TerminalPanel.TerminalBlack"));
        terminalColor.put("31", Tools.getDefaultColor("TerminalPanel.TerminalRed"));
        terminalColor.put("32", Tools.getDefaultColor("TerminalPanel.TerminalGreen"));
        terminalColor.put("33", Tools.getDefaultColor("TerminalPanel.TerminalYellow"));
        terminalColor.put("34", Tools.getDefaultColor("TerminalPanel.TerminalBlue"));
        terminalColor.put("35", Tools.getDefaultColor("TerminalPanel.TerminalPurple"));
        terminalColor.put("36", Tools.getDefaultColor("TerminalPanel.TerminalCyan"));
        final Font f = new Font("Monospaced", Font.PLAIN, application.scaled(14));
        terminalArea = new JTextPane();
        terminalArea.setStyledDocument(new MyDocument());
        final Caret caret = new DefaultCaret() {
            @Override
            protected synchronized void damage(final Rectangle r) {
                if (r != null) {
                    x = r.x;
                    y = r.y;
                    width = 8;
                    height = r.height;
                    repaint();
                }
            }

            @Override
            public void paint(final Graphics g) {
                /* painting cursor. If it is not visible it is out of focus, we
                 * make it barely visible. */
                try {
                    final TextUI mapper = getComponent().getUI();
                    final Rectangle r = mapper.modelToView(getComponent(), getDot(), getDotBias());
                    if (r == null) {
                        return;
                    }
                    g.setColor(getComponent().getCaretColor());
                    if (isVisible() && editEnabled) {
                        g.fillRect(r.x, r.y, 8, r.height);
                    } else {
                        g.drawRect(r.x, r.y, 8, r.height);
                    }
                } catch (final BadLocationException e) {
                    LOG.appError("paint: drawing of cursor failed", e);
                }
            }
        };
        terminalArea.setCaret(caret);
        terminalArea.addCaretListener(new CaretListener() {
            @Override
            public void caretUpdate(final CaretEvent e) {
                /* don't do this if caret moved because of selection */
                mPosLock.lock();
                try {
                    if (e != null && e.getDot() < commandOffset && e.getDot() == e.getMark()) {
                        terminalArea.setCaretPosition(commandOffset);
                    }
                } finally {
                    mPosLock.unlock();
                }
            }
        });

        /* set font and colors */
        terminalArea.setFont(f);
        terminalArea.setBackground(Tools.getDefaultColor("TerminalPanel.Background"));

        commandColor = new SimpleAttributeSet();
        StyleConstants.setForeground(commandColor, Tools.getDefaultColor("TerminalPanel.Command"));

        errorColor = new SimpleAttributeSet();
        StyleConstants.setForeground(errorColor,
            Tools.getDefaultColor("TerminalPanel.Error"));

        outputColor = new SimpleAttributeSet();
        defaultOutputColor = Tools.getDefaultColor("TerminalPanel.Output");
        StyleConstants.setForeground(outputColor, defaultOutputColor);

        promptColor = new SimpleAttributeSet();
        StyleConstants.setForeground(promptColor, host.getPmColors()[0]);

        append(prompt(), promptColor);
        terminalArea.setEditable(true);
        getViewport().add(terminalArea, BorderLayout.PAGE_END);
        setPreferredSize(new Dimension(Short.MAX_VALUE, Tools.getDefaultInt("MainPanel.TerminalPanelHeight")));
        setMinimumSize(getPreferredSize());
        setMaximumSize(getPreferredSize());
    }

    /** Returns terminal output color. */
    private Color getColorFromString(final CharSequence s) {
        /* "]" default color */
        if ("[".equals(s)) {
            return null;
        }
        final Pattern p1 = Pattern.compile("^\\[\\d+;(\\d+)$");
        final Matcher m1 = p1.matcher(s);
        if (m1.matches()) {
            final String c = m1.group(1);
            /* can be null */
            return terminalColor.get(c);
        }
        /* [0;1;32 */
        final Pattern p2 = Pattern.compile("^\\[\\d+;\\d+;(\\d+)$");
        final Matcher m2 = p2.matcher(s);
        if (m2.matches()) {
            final String c = m2.group(1);
            /* can be null */
            return terminalColor.get(c);
        }
        return null;
    }

    /** Get char count. */
    private int getCharCount(final CharSequence s) {
        final Pattern p1 = Pattern.compile("^\\[(\\d+)$");
        final Matcher m1 = p1.matcher(s);
        if (m1.matches()) {
            return Integer.parseInt(m1.group(1));
        }
        return 0;
    }


    /** Appends a text whith specified color to the terminal area. */
    private void append(final String text, final MutableAttributeSet colorAS) {
        userCommand = false;
        final MyDocument doc = (MyDocument) terminalArea.getStyledDocument();
        mPosLock.lock();
        final int end = terminalArea.getDocument().getLength();
        pos = end + pos - maxPos;
        maxPos = end;
        final char[] chars = text.toCharArray();
        StringBuilder colorString = new StringBuilder(10);
        boolean inside = false;
        for (int i = 0; i < chars.length; i++) {
            final char c = chars[i];
            final String s = Character.toString(c);
            boolean printit = true;
            if (c == 8) { /* one position to the left */
                printit = false;
                pos--;
            } else if (i < chars.length - 1 && c == 13 && chars[i + 1] == 10) { /* new line */
                prevLine = maxPos + 2;
                pos = maxPos;
            } else if (c == 13) { /* beginning of the same line */
                pos = prevLine;
                printit = false;
            } else if (c == 27) {
                /* funny colors, e.g. in sles */
                inside = true;
                printit = false;
                colorString = new StringBuilder(10);
            }
            if (inside) {
                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
                    /* we are done */
                    inside = false;
                    if (c == 'm') {
                        Color newColor = getColorFromString(colorString.toString());

                        if (newColor == null) {
                            newColor = defaultOutputColor;
                        }
                        StyleConstants.setForeground(colorAS, newColor);
                        colorString = new StringBuilder(10);
                    } else if (c == 'G') {
                        final int g = getCharCount(colorString.toString());
                        pos = prevLine + g;
                        while (pos > maxPos) {
                            try {
                                doc.insertString(maxPos, " ", colorAS);
                                maxPos++;
                            } catch (final BadLocationException e1) {
                                LOG.appError("append: terminalPanel pos: " + pos, e1);
                            }
                        }
                    }
                } else if (printit) {
                    colorString.append(s);
                }
                printit = false;
            }

            if (printit) {
                if (pos < maxPos) {
                    try {
                        commandOffset = pos - 1;
                        doc.removeForced(pos, 1);
                    } catch (final BadLocationException e) {
                        LOG.appError("append: terminalPanel pos: " + pos, e);
                    }
                }
                try {
                    doc.insertString(pos, s, colorAS);
                } catch (final BadLocationException e1) {
                    LOG.appError("append: terminalPanel pos: " + pos, e1);
                }
                pos++;
                if (maxPos < pos) {
                    maxPos = pos;
                }
            }
        }
        commandOffset = terminalArea.getDocument().getLength();
        terminalArea.setCaretPosition(terminalArea.getDocument().getLength());
        mPosLock.unlock();
        userCommand = true;
    }

    /** Sets the terminal area editable. */
    void setEditable(final boolean editable) {
        terminalArea.setEditable(editable);
    }

    /** Executes the specified command, if the host is connected. */
    void execCommand(final String command) {
        final String hostName = host.getName();

        if (!host.isConnected()) {
            return;
        }
        if (command != null && !command.isEmpty()) {
            guiData.startProgressIndicator(hostName, "Executing command");
        }
        host.execCommand(new ExecCommandConfig().command(command)
                                                .execCallback(new ExecCallback() {
                                                    @Override
                                                    public void done(final String answer) {
                                                        if (command != null && !command.isEmpty()) {
                                                            guiData.stopProgressIndicator(hostName, "Executing command");
                                                        }
                                                    }

                                                    @Override
                                                    public void doneError(final String answer, final int errorCode) {
                                                        if (command != null && !command.isEmpty()) {
                                                            guiData.stopProgressIndicator(hostName, "Executing command");
                                                        }
                                                    }
                                                }));
    }

    /**
     * Sets the prompt color to the host's color when it is added to the
     * cluster.
     */
    public void resetPromptColor() {
        StyleConstants.setForeground(promptColor, host.getPmColors()[0]);
        addCommand("");
        nextCommand();
    }

    /** Returns a prompt. */
    private String prompt() {
        if (host.isConnected()) {
            return '[' + host.getUserAtHost() + ":~#] ";
        } else {
            return "# ";
        }
    }

    /**
     * Is called after execution of command is finnished. It shows the prompt
     * and scrolls the text up.
     */
    public void nextCommand() {
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                append(prompt(), promptColor);
            }
        });
    }

    /** Adds command to the terminal textarea and scrolls up. */
    public void addCommand(final String command) {
        final String[] lines = command.split("\\r?\\n");

        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                append(lines[0], commandColor);
                for (int i = 1; i < lines.length; i++) {
                    append(" \\\n> " + lines[i], commandColor);
                }
                append("\n", commandColor);
            }
        });
    }

    /** Adds command output to the terminal textarea and scrolls up. */
    public void addCommandOutput(final String output) {
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                append(output, outputColor);
            }
        });
    }

    /** Adds array of command output to the terminal textarea and scrolls up. */
    public void addCommandOutput(final String[] output) {
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < output.length; i++) {
                    if (output[i] != null) {
                        String newLine = "";
                        if (i != output.length - 1) {
                            newLine = "\n";
                        }
                        append(output[i] + newLine, outputColor);
                    }
                }
            }
        });
    }

    /** Adds content string (output of a command) to the terminal area. */
    public void addContent(final String c) {
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                append(c, outputColor);
            }
        });
    }

    /** Adds content to the terminal textarea and scrolls up. */
    public void addContentErr(final String c) {
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                append(c, errorColor);
            }
        });
    }

    /** Starts action after cheat was entered. */
    private void startCheat(final String cheat) {
        if (!editEnabled) {
            addCommand(cheat);
        }
        if (!editEnabled && GOD_ON.equals(cheat)) {
            editEnabled = true;
            guiData.godModeChanged(editEnabled);
        } else if (editEnabled && GOD_OFF.equals(cheat)) {
            editEnabled = false;
            guiData.godModeChanged(editEnabled);
        } else if (CHEAT_LIST.equals(cheat)) {
            final StringBuilder list = new StringBuilder();
            for (final String ch : CHEATS_MAP.keySet()) {
                list.append(ch);
                list.append('\n');
            }
            addCommandOutput(list.toString());
        } else if (RUN_GC.equals(cheat)) {
            System.gc();
            LOG.info("startCheat: run gc");
        } else if (ALLOCATE_10.equals(cheat)) {
            final Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    LOG.info("startCheat: allocate mem");
                    LOG.info("startCheat: allocate mem done");

                    System.gc();
                    LOG.info("startCheat: run gc");
                    Tools.sleep(60000);
                    LOG.info("startCheat: free mem");
                }
            });
            t.start();
        } else if (CLICKTEST_SHORT.equals(cheat)) {
            roboTest.startClicker(1, false);
        } else if (CLICKTEST_LONG.equals(cheat)) {
            roboTest.startClicker(8 * 60, false);
        } else if (CLICKTEST_LAZY_SHORT.equals(cheat)) {
            roboTest.startClicker(1, true);
        } else if (CLICKTEST_LAZY_LONG.equals(cheat)) {
            roboTest.startClicker(8 * 60, true); /* 8 hours */
        } else if (RIGHT_CLICKTEST_SHORT.equals(cheat)) {
            roboTest.startRightClicker(1, false);
        } else if (RIGHT_CLICKTEST_LONG.equals(cheat)) {
            roboTest.startRightClicker(8 * 60, false);
        } else if (RIGHT_CLICKTEST_LAZY_SHORT.equals(cheat)) {
            roboTest.startRightClicker(1, true);
        } else if (RIGHT_CLICKTEST_LAZY_LONG.equals(cheat)) {
            roboTest.startRightClicker(8 * 60, true); /* 8 hours */
        } else if (MOVETEST_SHORT.equals(cheat)) {
            roboTest.startMover(1, false);
        } else if (MOVETEST_LONG.equals(cheat)) {
            roboTest.startMover(8 * 60, false);
        } else if (MOVETEST_LAZY_SHORT.equals(cheat)) {
            roboTest.startMover(1, true);
        } else if (MOVETEST_LAZY_LONG.equals(cheat)) {
            roboTest.startMover(8 * 60, true);
        } else if (DEBUG_INC.equals(cheat)) {
            LoggerFactory.incrementDebugLevel();
        } else if (DEBUG_DEC.equals(cheat)) {
            LoggerFactory.decrementDebugLevel();
        } else if (TEST_CHEATS.containsKey(cheat)) {
            srartTests.startTest(TEST_CHEATS.get(cheat), host.getCluster());
        } else if (REGISTER_MOVEMENT.equals(cheat)) {
            roboTest.registerMovement();
        }
        nextCommand();
    }

    public void resetTerminalArea() {
        for (int i = 0; i < 10; i++) {
            try {
                final MyDocument doc = (MyDocument) terminalArea.getStyledDocument();
                mPosLock.lock();
                try {
                    commandOffset = 0;
                    pos = 0;
                    maxPos = 0;
                    doc.removeForced(0, doc.getLength());
                } finally {
                    mPosLock.unlock();
                }
                return;
            } catch (final BadLocationException e) {
                LOG.appWarning("resetTerminalArea: " + e);
            }
        }
    }

    /**
     * This class overwrites the DefaultStyledDocument in order to add godmode
     * feature.
     */
    class MyDocument extends DefaultStyledDocument {

        /** Serial version UID. */
        private static final long serialVersionUID = 1L;

        /**
         * Is called while a string is inserted. It checks if a cheat code is
         * in the string. */
        @Override
        public void insertString(int offs, final String str, final AttributeSet a)
            throws BadLocationException {
            mPosLock.lock();
            if (offs < commandOffset) {
                terminalArea.setCaretPosition(commandOffset);
                offs = commandOffset;
            }
            if (userCommand) {
                if (editEnabled) {
                    if (str.charAt(str.length() - 1) == '\n') {
                        final int end = terminalArea.getDocument().getLength();
                        super.insertString(end, "\n", commandColor);
                        final String command = (getText(commandOffset, end - commandOffset) + str).trim();
                        prevLine = end + 1;
                        pos = end;
                        maxPos = end;
                        execCommand(command);
                    } else {
                        super.insertString(offs, str, commandColor);
                    }
                }
                for (final Map.Entry<String, Integer> cheatEntry : CHEATS_MAP.entrySet()) {
                    int cheatPos = cheatEntry.getValue();
                    if (str.equals(cheatEntry.getKey().substring(cheatPos, cheatPos + 1))) {
                        cheatPos++;
                        CHEATS_MAP.put(cheatEntry.getKey(), cheatPos);
                        if (cheatPos == cheatEntry.getKey().length()) {
                            for (final String ch : CHEATS_MAP.keySet()) {
                                CHEATS_MAP.put(ch, 0);
                            }
                            startCheat(cheatEntry.getKey());
                        }
                    } else {
                        CHEATS_MAP.put(cheatEntry.getKey(), 0);
                    }
                }
            } else {
                super.insertString(offs, str, a);
            }
            mPosLock.unlock();
        }

        /** Is called while characters is removed. */
        @Override
        public void remove(final int offs, final int len) throws BadLocationException {
            mPosLock.lock();
            try {
                if (offs >= commandOffset) {
                    for (final Map.Entry<String, Integer> cheatEntry : CHEATS_MAP.entrySet()) {
                        final int cheatPos = cheatEntry.getValue();
                        if (cheatPos > 0) {
                            CHEATS_MAP.put(cheatEntry.getKey(), cheatPos - 1);
                        }
                    }
                    if (editEnabled) {
                        super.remove(offs, len);
                    }
                }
            } finally {
                mPosLock.unlock();
            }
        }

        /** Same as remove. */
        void removeForced(final int offs, final int len)
            throws BadLocationException {
            super.remove(offs, len);
        }
    }
}
TOP

Related Classes of lcmc.common.ui.TerminalPanel$MyDocument

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.