view app/src/main/java/org/tn5250j/framework/tn5250/Screen5250.java @ 510:7953570e5210

update to ganymed-ssh2 tag 263 and fix hmac-sha2-512
author Carl Byington <carl@five-ten-sg.com>
date Wed, 01 Feb 2023 17:55:29 -0700
parents d29cce60f393
children
line wrap: on
line source

/*
 * Title: Screen5250.java
 * Copyright:   Copyright (c) 2001 - 2004
 * Company:
 * @author  Kenneth J. Pouncey
 * @version 0.5
 *
 * Description:
 *
 * This program 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA
 *
 */
package org.tn5250j.framework.tn5250;

import static org.tn5250j.TN5250jConstants.*;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Vector;

import org.tn5250j.TN5250jConstants;

import android.util.Log;
import de.mud.terminal.VDUBuffer;
import de.mud.terminal.vt320;
import com.five_ten_sg.connectbot.service.FontSizeChangedListener;


public class Screen5250 implements FontSizeChangedListener {
    private static final String TAG = "Screen5250";
    private ScreenFields screenFields;
    private int lastAttr;
    private int lastPos;
    private int lenScreen;
    private KeyStrokenizer strokenizer;
    private tnvt sessionVT;
    private vt320 buffer;       // used to draw the screen
    private int numRows = 0;
    private int numCols = 0;
    protected static final int initAttr = 32;
    protected static final char initChar = 0;
    public boolean cursorActive = false;
    public boolean cursorShown = false;
    protected boolean insertMode = false;
    private boolean keyProcessed = false;
    private Rect dirtyScreen = new Rect();

    public int homePos = 0;
    public int saveHomePos = 0;
    private String bufferedKeys;
    public boolean pendingInsert = false;

    public final static byte STATUS_SYSTEM = 1;
    public final static byte STATUS_ERROR_CODE = 2;
    public final static byte STATUS_VALUE_ON = 1;
    public final static byte STATUS_VALUE_OFF = 2;

    private StringBuffer hsMore = new StringBuffer("More...");
    private StringBuffer hsBottom = new StringBuffer("Bottom");

    // error codes to be sent to the host on an error
    private final static int ERR_CURSOR_PROTECTED = 0x05;
    private final static int ERR_INVALID_SIGN = 0x11;
    private final static int ERR_NO_ROOM_INSERT = 0x12;
    private final static int ERR_NUMERIC_ONLY = 0x09;
    private final static int ERR_DUP_KEY_NOT_ALLOWED = 0x19;
    private final static int ERR_NUMERIC_09 = 0x10;
    private final static int ERR_FIELD_MINUS = 0x16;
    private final static int ERR_FIELD_EXIT_INVALID = 0x18;
    private final static int ERR_ENTER_NO_ALLOWED = 0x20;
    private final static int ERR_MANDATORY_ENTER = 0x21;

    private boolean guiInterface = false;
    private boolean resetRequired = false;
    private boolean backspaceError = true;
    private boolean feError;

    // Operator Information Area
    private ScreenOIA oia;

    // screen planes
    protected ScreenPlanes planes;



    public Screen5250() {
        try {
            jbInit();
        }
        catch (Exception ex) {
            Log.w(TAG, "In constructor: ", ex);
        }
    }

    void jbInit() throws Exception {
        lastAttr = 32;
        // default number of rows and columns
        numRows = 24;
        numCols = 80;
        setCursor(1, 1); // set initial cursor position
        oia = new ScreenOIA(this);
        oia.setKeyBoardLocked(true);
        lenScreen = numRows * numCols;
        planes = new ScreenPlanes(this, numRows);
        screenFields = new ScreenFields(this);
        strokenizer = new KeyStrokenizer();
    }

    protected ScreenPlanes getPlanes() {
        return planes;
    }

    public final ScreenOIA getOIA() {
        return oia;
    }

    protected final void setRowsCols(int rows, int cols) {
        int oldRows = numRows;
        int oldCols = numCols;
        // default number of rows and columns
        numRows = rows;
        numCols = cols;
        lenScreen = numRows * numCols;
        planes.setSize(rows);

        //  If they are not the same then we need to inform the listeners that
        //  the size changed.
        if (oldRows != numRows || oldCols != numCols)
            fireScreenSizeChanged();
    }


    public boolean isCursorActive() {
        return cursorActive;
    }

    public boolean isCursorShown() {
        return cursorShown;
    }

    public void setUseGUIInterface(boolean gui) {
        guiInterface = gui;
    }

    public void toggleGUIInterface() {
        guiInterface = !guiInterface;
    }

    public void setResetRequired(boolean reset) {
        resetRequired = reset;
    }

    public void setBackspaceError(boolean onError) {
        backspaceError = onError;
    }

    /**
     * Copy & Paste support
     *
     * @see {@link #pasteText(String, boolean)}
     * @see {@link #copyTextField(int)}
     */
    public final String copyText(Rect area) {
        StringBuilder sb = new StringBuilder();
        Rect workR = new Rect();
        workR.setBounds(area);
        Log.d(TAG, "Copying " + workR);
        // loop through all the screen characters to send them to the clip board
        int m = workR.x;
        int i = 0;
        int t = 0;

        while (workR.height-- > 0) {
            t = workR.width;
            i = workR.y;

            while (t-- > 0) {
                // only copy printable characters (in this case >= ' ')
                char c = planes.getChar(getPos(m - 1, i - 1));

                if (c >= ' ' && (planes.screenExtended[getPos(m - 1, i - 1)] & EXTENDED_5250_NON_DSP)
                        == 0)
                    sb.append(c);
                else
                    sb.append(' ');

                i++;
            }

            sb.append('\n');
            m++;
        }

        return sb.toString();
    }

    /**
     * Copy & Paste support
     *
     * @param content
     * @see {@link #copyText(Rectangle)}
     */
    public final void pasteText(String content, boolean special) {
        Log.d(TAG, "Pasting, special:" + special);
        setCursorActive(false);
        StringBuilder sb = new StringBuilder(content);
        StringBuilder pd = new StringBuilder();
        // character counters within the string to be pasted.
        int nextChar = 0;
        int nChars = sb.length();
        int lr = getRow(lastPos);
        int lc = getCol(lastPos);
        resetDirty(lastPos);
        int cpos = lastPos;
        int length = getScreenLength();
        char c = 0;
        boolean setIt;
        // save our current place within the FFT.
        screenFields.saveCurrentField();

        for (int x = nextChar; x < nChars; x++) {
            c = sb.charAt(x);

            if ((c == '\n') || (c == '\r')) {
                Log.i(TAG, "pasted cr-lf>" + pd + "<");
                pd.setLength(0);
                // if we read in a cr lf in the data stream we need to go
                // to the starting column of the next row and start from there
                cpos = getPos(getRow(cpos) + 1, lc);

                // If we go paste the end of the screen then let's start over from
                //   the beginning of the screen space.
                if (cpos > length)
                    cpos = 0;
            }
            else {
                // we will default to set the character always.
                setIt = true;

                // If we are in a special paste scenario then we check for valid
                //   characters to paste.
                if (special && (!Character.isLetter(c) && !Character.isDigit(c)))
                    setIt = false;

                // we will only push a character to the screen space if we are in
                //  a field
                if (isInField(cpos) && setIt) {
                    planes.setChar(cpos, c);
                    setDirty(cpos);
                    screenFields.setCurrentFieldMDT();
                }

                //  If we placed a character then we go to the next position.
                if (setIt)
                    cpos++;

                // we will append the information to our debug buffer.
                pd.append(c);
            }
        }

        // if we have anything else not logged then log it out.
        if (pd.length() > 0)
            Log.i(TAG, "pasted >" + pd + "<");

        // restore out position within the FFT.
        screenFields.restoreCurrentField();
        updateDirty();
        // restore our cursor position.
        setCursor(lr + 1, lc + 1);
        setCursorActive(true);
    }

    /**
     * Copy & Paste support
     *
     * @param position
     * @return
     * @see {@link #copyText(int)}
     */
    public final String copyTextField(int position) {
        screenFields.saveCurrentField();
        isInField(position);
        String result = screenFields.getCurrentFieldText();
        screenFields.restoreCurrentField();
        return result;
    }

    /**
     *
     * Copy & Paste end code
     *
     */

    /**
     * Sum them
     *
     * @param which
     *            formatting option to use
     * @return vector string of numberic values
     */
    public final Vector<Double> sumThem(boolean which, Rect area) {
        StringBuilder sb = new StringBuilder();
        Rect workR = new Rect();
        workR.setBounds(area);
        //      gui.rubberband.reset();
        //      gui.repaint();
        Log.d(TAG, "Summing");
        // obtain the decimal format for parsing
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
        DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

        if (which) {
            dfs.setDecimalSeparator('.');
            dfs.setGroupingSeparator(',');
        }
        else {
            dfs.setDecimalSeparator(',');
            dfs.setGroupingSeparator('.');
        }

        df.setDecimalFormatSymbols(dfs);
        Vector<Double> sumVector = new Vector<Double>();
        // loop through all the screen characters to send them to the clip board
        int m = workR.x;
        int i = 0;
        int t = 0;
        double sum = 0.0;

        while (workR.height-- > 0) {
            t = workR.width;
            i = workR.y;

            while (t-- > 0) {
                // only copy printable numeric characters (in this case >= ' ')
                //              char c = screen[getPos(m - 1, i - 1)].getChar();
                char c = planes.getChar(getPos(m - 1, i - 1));
                //              if (((c >= '0' && c <= '9') || c == '.' || c == ',' || c == '-')
                //                      && !screen[getPos(m - 1, i - 1)].nonDisplay) {

                // TODO: update me here to implement the nonDisplay check as well
                if (((c >= '0' && c <= '9') || c == '.' || c == ',' || c == '-')) {
                    sb.append(c);
                }

                i++;
            }

            if (sb.length() > 0) {
                if (sb.charAt(sb.length() - 1) == '-') {
                    sb.insert(0, '-');
                    sb.deleteCharAt(sb.length() - 1);
                }

                try {
                    Number n = df.parse(sb.toString());
                    //               System.out.println(s + " " + n.doubleValue());
                    sumVector.add(new Double(n.doubleValue()));
                    sum += n.doubleValue();
                }
                catch (ParseException pe) {
                    Log.w(TAG, pe.getMessage() + " at "
                          + pe.getErrorOffset());
                }
            }

            sb.setLength(0);
            m++;
        }

        Log.d(TAG, "" + sum);
        return sumVector;
    }

    public void setVT(tnvt v) {
        sessionVT = v;
    }

    public void setBuffer(vt320 buffer) {
        this.buffer = buffer;
    }

    /**
     * converts mnemonic string values into aid integers
     *
     * @see #sendKeys
     * @param mnem string mnemonic value
     * @return key value of Mnemonic
     */
    private int getMnemonicValue(String mnem) {
        if (mnemonicMap.containsKey(mnem)) return mnemonicMap.get(mnem);

        return 0;
    }

    protected void setPrehelpState(boolean setErrorCode, boolean lockKeyboard,
                                   boolean unlockIfLocked) {
        if (oia.isKeyBoardLocked() && unlockIfLocked)
            oia.setKeyBoardLocked(false);
        else
            oia.setKeyBoardLocked(lockKeyboard);

        bufferedKeys = null;
        oia.setKeysBuffered(false);
    }

    /**
     * Activate the cursor on screen
     *
     * @param activate
     */
    public void setCursorActive(boolean activate) {
        //      System.out.println("cursor active " + updateCursorLoc + " " +
        // cursorActive + " " + activate);
        if (cursorActive && !activate) {
            setCursorOff();
            cursorActive = activate;
        }
        else {
            if (!cursorActive && activate) {
                cursorActive = activate;
                setCursorOn();
            }
        }
    }

    /**
     * Set the cursor on
     */
    public void setCursorOn() {
        cursorShown = true;
        updateCursorLoc();
    }

    /**
     * Set the cursor off
     */
    public void setCursorOff() {
        cursorShown = false;
        updateCursorLoc();
        //      System.out.println("cursor off " + updateCursorLoc + " " +
        // cursorActive);
    }

    /**
     *
     */
    private void updateCursorLoc() {
        if (cursorActive) {
            fireCursorChanged();
        }
    }

    /**
     * The sendKeys method sends a string of keys to the virtual screen. This
     * method acts as if keystrokes were being typed from the keyboard. The
     * keystrokes will be sent to the location given. The string being passed
     * can also contain mnemonic values such as [enter] enter key,[tab] tab key,
     * [pf1] pf1 etc...
     *
     * These will be processed as if you had pressed these keys from the
     * keyboard. All the valid special key values are contained in the MNEMONIC
     * enumeration:
     *
     * <table BORDER COLS=2 WIDTH="50%" >
     *
     * <tr>
     * <td>MNEMONIC_CLEAR</td>
     * <td>[clear]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_ENTER</td>
     * <td>[enter]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_HELP</td>
     * <td>[help]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PAGE_DOWN</td>
     * <td>[pgdown]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PAGE_UP</td>
     * <td>[pgup]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PRINT</td>
     * <td>[print]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF1</td>
     * <td>[pf1]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF2</td>
     * <td>[pf2]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF3</td>
     * <td>[pf3]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF4</td>
     * <td>[pf4]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF5</td>
     * <td>[pf5]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF6</td>
     * <td>[pf6]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF7</td>
     * <td>[pf7]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF8</td>
     * <td>[pf8]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF9</td>
     * <td>[pf9]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF10</td>
     * <td>[pf10]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF11</td>
     * <td>[pf11]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF12</td>
     * <td>[pf12]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF13</td>
     * <td>[pf13]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF14</td>
     * <td>[pf14]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF15</td>
     * <td>[pf15]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF16</td>
     * <td>[pf16]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF17</td>
     * <td>[pf17]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF18</td>
     * <td>[pf18]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF19</td>
     * <td>[pf19]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF20</td>
     * <td>[pf20]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF21</td>
     * <td>[pf21]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF22</td>
     * <td>[pf22]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF23</td>
     * <td>[pf23]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PF24</td>
     * <td>[pf24]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_BACK_SPACE</td>
     * <td>[backspace]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_BACK_TAB</td>
     * <td>[backtab]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_UP</td>
     * <td>[up]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_DOWN</td>
     * <td>[down]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_LEFT</td>
     * <td>[left]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_RIGHT</td>
     * <td>[right]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_DELETE</td>
     * <td>[delete]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_TAB</td>
     * <td>"[tab]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_END_OF_FIELD</td>
     * <td>[eof]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_ERASE_EOF</td>
     * <td>[eraseeof]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_ERASE_FIELD</td>
     * <td>[erasefld]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_INSERT</td>
     * <td>[insert]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_HOME</td>
     * <td>[home]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD0</td>
     * <td>[keypad0]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD1</td>
     * <td>[keypad1]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD2</td>
     * <td>[keypad2]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD3</td>
     * <td>[keypad3]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD4</td>
     * <td>[keypad4]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD5</td>
     * <td>[keypad5]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD6</td>
     * <td>[keypad6]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD7</td>
     * <td>[keypad7]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD8</td>
     * <td>[keypad8]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD9</td>
     * <td>[keypad9]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD_PERIOD</td>
     * <td>[keypad.]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD_COMMA</td>
     * <td>[keypad,]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_KEYPAD_MINUS</td>
     * <td>[keypad-]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_FIELD_EXIT</td>
     * <td>[fldext]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_FIELD_PLUS</td>
     * <td>[field+]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_FIELD_MINUS</td>
     * <td>[field-]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_BEGIN_OF_FIELD</td>
     * <td>[bof]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PA1</td>
     * <td>[pa1]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PA2</td>
     * <td>[pa2]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_PA3</td>
     * <td>[pa3]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_SYSREQ</td>
     * <td>[sysreq]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_RESET</td>
     * <td>[reset]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_ATTN</td>
     * <td>[attn]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_MARK_LEFT</td>
     * <td>[markleft]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_MARK_RIGHT</td>
     * <td>[markright]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_MARK_UP</td>
     * <td>[markup]</td>
     * </tr>
     * <tr>
     * <td>MNEMONIC_MARK_DOWN</td>
     * <td>[markdown]</td>
     * </tr>
     *
     * </table>
     *
     * @param text
     *            The string of characters to be sent
     *
     * @see #sendAid
     *
     */

    public synchronized void sendKeys(String text) {
        if (isStatusErrorCode() && !resetRequired) {
            setCursorActive(false);
            simulateMnemonic(getMnemonicValue("[reset]"));
            setCursorActive(true);
        }

        if (oia.isKeyBoardLocked()) {
            if (text.equals("[reset]") || text.equals("[sysreq]")
                    || text.equals("[attn]")) {
                setCursorActive(false);
                simulateMnemonic(getMnemonicValue(text));
                setCursorActive(true);
            }
            else {
                if (isStatusErrorCode()) {
                    sessionVT.signalBell();
                    return;
                }

                oia.setKeysBuffered(true);

                if (bufferedKeys == null) bufferedKeys = text;
                else                      bufferedKeys += text;

                return;
            }
        }
        else {
            if (oia.isKeysBuffered()) {
                if (bufferedKeys != null) {
                    text = bufferedKeys + text;
                }

                oia.setKeysBuffered(false);
                bufferedKeys = null;
            }

            isInField();

            if (text.length() == 1 && !text.equals("[") && !text.equals("]")) {
                setCursorActive(false);
                simulateKeyStroke(text.charAt(0));
                setCursorActive(true);
            }
            else {
                strokenizer.setKeyStrokes(text);
                String s;
                boolean done = false;
                //            setCursorOff2();
                setCursorActive(false);

                while (!done) {
                    if (strokenizer.hasMoreKeyStrokes()) {
                        isInField();
                        s = strokenizer.nextKeyStroke();

                        if (s.length() == 1) {
                            simulateKeyStroke(s.charAt(0));
                        }
                        else {
                            simulateMnemonic(getMnemonicValue(s));
                        }

                        if (oia.isKeyBoardLocked()) {
                            bufferedKeys = strokenizer
                                           .getUnprocessedKeyStroked();

                            if (bufferedKeys != null) {
                                oia.setKeysBuffered(true);
                            }

                            done = true;
                        }
                    }
                    else {
                        done = true;
                    }
                }

                setCursorActive(true);
            }
        }
    }

    /**
     * The sendAid method sends an "aid" keystroke to the virtual screen. These
     * aid keys can be thought of as special keystrokes, like the Enter key,
     * PF1-24 keys or the Page Up key. All the valid special key values are
     * contained in the AID_ enumeration:
     *
     * @param aidKey
     *            The aid key to be sent to the host
     *
     * @see #sendKeys
     * @see TN5250jConstants#AID_CLEAR
     * @see #AID_ENTER
     * @see #AID_HELP
     * @see #AID_ROLL_UP
     * @see #AID_ROLL_DOWN
     * @see #AID_ROLL_LEFT
     * @see #AID_ROLL_RIGHT
     * @see #AID_PRINT
     * @see #AID_PF1
     * @see #AID_PF2
     * @see #AID_PF3
     * @see #AID_PF4
     * @see #AID_PF5
     * @see #AID_PF6
     * @see #AID_PF7
     * @see #AID_PF8
     * @see #AID_PF9
     * @see #AID_PF10
     * @see #AID_PF11
     * @see #AID_PF12
     * @see #AID_PF13
     * @see #AID_PF14
     * @see #AID_PF15
     * @see #AID_PF16
     * @see #AID_PF17
     * @see #AID_PF18
     * @see #AID_PF19
     * @see #AID_PF20
     * @see #AID_PF21
     * @see #AID_PF22
     * @see #AID_PF23
     * @see #AID_PF24
     */
    public void sendAid(int aidKey) {
        sessionVT.sendAidKey(aidKey);
    }

    /**
     * Restores the error line and sets the error mode off.
     *
     */
    protected void resetError() {
        restoreErrorLine();
        setStatus(STATUS_ERROR_CODE, STATUS_VALUE_OFF, "");
    }

    protected boolean simulateMnemonic(int mnem) {
        boolean simulated = false;

        switch (mnem) {
            case AID_CLEAR:
            case AID_ENTER:
            case AID_PF1:
            case AID_PF2:
            case AID_PF3:
            case AID_PF4:
            case AID_PF5:
            case AID_PF6:
            case AID_PF7:
            case AID_PF8:
            case AID_PF9:
            case AID_PF10:
            case AID_PF11:
            case AID_PF12:
            case AID_PF13:
            case AID_PF14:
            case AID_PF15:
            case AID_PF16:
            case AID_PF17:
            case AID_PF18:
            case AID_PF19:
            case AID_PF20:
            case AID_PF21:
            case AID_PF22:
            case AID_PF23:
            case AID_PF24:
            case AID_ROLL_DOWN:
            case AID_ROLL_UP:
            case AID_ROLL_LEFT:
            case AID_ROLL_RIGHT:
                if (!screenFields.isCanSendAid()) {
                    displayError(ERR_ENTER_NO_ALLOWED);
                }
                else
                    sendAid(mnem);

                simulated = true;
                break;

            case AID_HELP:
                sessionVT.sendHelpRequest();
                simulated = true;
                break;

            case AID_PRINT:
                sessionVT.hostPrint(1);
                simulated = true;
                break;

            case BACK_SPACE:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    if (screenFields.getCurrentField().startPos() == lastPos) {
                        if (backspaceError)
                            displayError(ERR_CURSOR_PROTECTED);
                        else {
                            gotoFieldPrev();
                            goto_XY(screenFields.getCurrentField().endPos());
                            updateDirty();
                        }
                    }
                    else {
                        screenFields.getCurrentField().getKeyPos(lastPos);
                        screenFields.getCurrentField().changePos(-1);
                        resetDirty(screenFields.getCurrentField().getCurrentPos());
                        shiftLeft(screenFields.getCurrentField().getCurrentPos());
                        updateDirty();
                        screenFields.setCurrentFieldMDT();
                        simulated = true;
                    }
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case BACK_TAB:
                if (screenFields.getCurrentField() != null
                        && screenFields.isCurrentFieldHighlightedEntry()) {
                    resetDirty(screenFields.getCurrentField().startPos);
                    gotoFieldPrev();
                    updateDirty();
                }
                else
                    gotoFieldPrev();

                if (screenFields.isCurrentFieldContinued()) {
                    do {
                        gotoFieldPrev();
                    }
                    while (screenFields.isCurrentFieldContinuedMiddle()
                            || screenFields.isCurrentFieldContinuedLast());
                }

                isInField();
                simulated = true;
                break;

            case UP:
            case MARK_UP:
                process_XY(lastPos - numCols);
                simulated = true;
                break;

            case DOWN:
            case MARK_DOWN:
                process_XY(lastPos + numCols);
                simulated = true;
                break;

            case LEFT:
            case MARK_LEFT:
                process_XY(lastPos - 1);
                simulated = true;
                break;

            case RIGHT:
            case MARK_RIGHT:
                process_XY(lastPos + 1);
                simulated = true;
                break;

            case NEXTWORD:
                gotoNextWord();
                simulated = true;
                break;

            case PREVWORD:
                gotoPrevWord();
                simulated = true;
                break;

            case DELETE:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    resetDirty(lastPos);
                    screenFields.getCurrentField().getKeyPos(lastPos);
                    shiftLeft(screenFields.getCurrentFieldPos());
                    screenFields.setCurrentFieldMDT();
                    updateDirty();
                    simulated = true;
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case TAB:
                if (screenFields.getCurrentField() != null
                        && !screenFields.isCurrentFieldContinued()) {
                    if (screenFields.isCurrentFieldHighlightedEntry()) {
                        resetDirty(screenFields.getCurrentField().startPos);
                        gotoFieldNext();
                        updateDirty();
                    }
                    else
                        gotoFieldNext();
                }
                else {
                    do {
                        gotoFieldNext();
                    }
                    while (screenFields.getCurrentField() != null
                            && (screenFields.isCurrentFieldContinuedMiddle() || screenFields
                                .isCurrentFieldContinuedLast()));
                }

                isInField();
                simulated = true;
                break;

            case EOF:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    int where = endOfField(screenFields.getCurrentField()
                                           .startPos(), true);

                    if (where > 0) {
                        setCursor((where / numCols) + 1, (where % numCols) + 1);
                    }

                    simulated = true;
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                resetDirty(lastPos);
                break;

            case ERASE_EOF:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    int where = lastPos;
                    resetDirty(lastPos);

                    if (fieldExit()) {
                        screenFields.setCurrentFieldMDT();

                        if (!screenFields.isCurrentFieldContinued()) {
                            gotoFieldNext();
                        }
                        else {
                            do {
                                gotoFieldNext();

                                if (screenFields.isCurrentFieldContinued())
                                    fieldExit();
                            }
                            while (screenFields.isCurrentFieldContinuedMiddle()
                                    || screenFields.isCurrentFieldContinuedLast());
                        }
                    }

                    updateDirty();
                    goto_XY(where);
                    simulated = true;
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case ERASE_FIELD:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    int where = lastPos;
                    lastPos = screenFields.getCurrentField().startPos();
                    resetDirty(lastPos);

                    if (fieldExit()) {
                        screenFields.setCurrentFieldMDT();

                        if (!screenFields.isCurrentFieldContinued()) {
                            gotoFieldNext();
                        }
                        else {
                            do {
                                gotoFieldNext();

                                if (screenFields.isCurrentFieldContinued())
                                    fieldExit();
                            }
                            while (screenFields.isCurrentFieldContinuedMiddle()
                                    || screenFields.isCurrentFieldContinuedLast());
                        }
                    }

                    updateDirty();
                    goto_XY(where);
                    simulated = true;
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case INSERT:
                // we toggle it
                oia.setInsertMode(oia.isInsertMode() ? false : true);
                break;

            case HOME:

                // position to the home position set
                if (lastPos + numCols + 1 != homePos) {
                    goto_XY(homePos - numCols - 1);
                    isInField();
                }
                else
                    gotoField(1);

                break;

            case KEYPAD_0:
                simulated = simulateKeyStroke('0');
                break;

            case KEYPAD_1:
                simulated = simulateKeyStroke('1');
                break;

            case KEYPAD_2:
                simulated = simulateKeyStroke('2');
                break;

            case KEYPAD_3:
                simulated = simulateKeyStroke('3');
                break;

            case KEYPAD_4:
                simulated = simulateKeyStroke('4');
                break;

            case KEYPAD_5:
                simulated = simulateKeyStroke('5');
                break;

            case KEYPAD_6:
                simulated = simulateKeyStroke('6');
                break;

            case KEYPAD_7:
                simulated = simulateKeyStroke('7');
                break;

            case KEYPAD_8:
                simulated = simulateKeyStroke('8');
                break;

            case KEYPAD_9:
                simulated = simulateKeyStroke('9');
                break;

            case KEYPAD_PERIOD:
                simulated = simulateKeyStroke('.');
                break;

            case KEYPAD_COMMA:
                simulated = simulateKeyStroke(',');
                break;

            case KEYPAD_MINUS:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    int s = screenFields.getCurrentField().getFieldShift();

                    if (s == 3 || s == 5 || s == 7) {
                        planes.setChar(lastPos, '-');
                        resetDirty(lastPos);
                        advancePos();

                        if (fieldExit()) {
                            screenFields.setCurrentFieldMDT();

                            if (!screenFields.isCurrentFieldContinued()) {
                                gotoFieldNext();
                            }
                            else {
                                do {
                                    gotoFieldNext();
                                }
                                while (screenFields
                                        .isCurrentFieldContinuedMiddle()
                                        || screenFields
                                        .isCurrentFieldContinuedLast());
                            }

                            simulated = true;
                            updateDirty();

                            if (screenFields.isCurrentFieldAutoEnter())
                                sendAid(AID_ENTER);
                        }
                    }
                    else {
                        displayError(ERR_FIELD_MINUS);
                    }
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case FIELD_EXIT:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    resetDirty(lastPos);
                    boolean autoFE = screenFields.isCurrentFieldAutoEnter();

                    if (fieldExit()) {
                        screenFields.setCurrentFieldMDT();

                        if (!screenFields.isCurrentFieldContinued() &&
                                !screenFields.isCurrentFieldAutoEnter()) {
                            gotoFieldNext();
                        }
                        else {
                            do {
                                gotoFieldNext();

                                if (screenFields.isCurrentFieldContinued())
                                    fieldExit();
                            }
                            while (screenFields.isCurrentFieldContinuedMiddle()
                                    || screenFields.isCurrentFieldContinuedLast());
                        }
                    }

                    updateDirty();
                    simulated = true;

                    if (autoFE)
                        sendAid(AID_ENTER);
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case FIELD_PLUS:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    resetDirty(lastPos);
                    boolean autoFE = screenFields.isCurrentFieldAutoEnter();

                    if (fieldExit()) {
                        screenFields.setCurrentFieldMDT();

                        if (!screenFields.isCurrentFieldContinued() &&
                                !screenFields.isCurrentFieldAutoEnter()) {
                            gotoFieldNext();
                        }
                        else {
                            do {
                                gotoFieldNext();
                            }
                            while (screenFields.isCurrentFieldContinuedMiddle()
                                    || screenFields.isCurrentFieldContinuedLast());
                        }
                    }

                    updateDirty();
                    simulated = true;

                    if (autoFE)
                        sendAid(AID_ENTER);
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case FIELD_MINUS:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    int s = screenFields.getCurrentField().getFieldShift();

                    if (s == 3 || s == 5 || s == 7) {
                        planes.setChar(lastPos, '-');
                        resetDirty(lastPos);
                        advancePos();
                        boolean autoFE = screenFields.isCurrentFieldAutoEnter();

                        if (fieldExit()) {
                            screenFields.setCurrentFieldMDT();

                            if (!screenFields.isCurrentFieldContinued()
                                    && !screenFields.isCurrentFieldAutoEnter()) {
                                gotoFieldNext();
                            }
                            else {
                                do {
                                    gotoFieldNext();
                                }
                                while (screenFields.isCurrentFieldContinuedMiddle()
                                        || screenFields.isCurrentFieldContinuedLast());
                            }
                        }

                        updateDirty();
                        simulated = true;

                        if (autoFE)
                            sendAid(AID_ENTER);
                    }
                    else {
                        displayError(ERR_FIELD_MINUS);
                    }
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case BOF:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    int where = screenFields.getCurrentField().startPos();

                    if (where > 0) {
                        goto_XY(where);
                    }

                    simulated = true;
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                resetDirty(lastPos);
                break;

            case SYSREQ:
                sessionVT.systemRequest();
                simulated = true;
                break;

            case RESET:
                if (isStatusErrorCode()) {
                    resetError();
                    isInField();
                    updateDirty();
                }
                else {
                    setPrehelpState(false, oia.isKeyBoardLocked(), false);
                }

                simulated = true;
                break;

            case ATTN:
                sessionVT.sendAttentionKey();
                simulated = true;
                break;

            case DUP_FIELD:
                if (screenFields.getCurrentField() != null
                        && screenFields.withinCurrentField(lastPos)
                        && !screenFields.isCurrentFieldBypassField()) {
                    if (screenFields.isCurrentFieldDupEnabled()) {
                        resetDirty(lastPos);
                        screenFields.getCurrentField().setFieldChar(lastPos,
                                (char) 0x1C);
                        screenFields.setCurrentFieldMDT();
                        gotoFieldNext();
                        updateDirty();
                        simulated = true;
                    }
                    else {
                        displayError(ERR_DUP_KEY_NOT_ALLOWED);
                    }
                }
                else {
                    displayError(ERR_CURSOR_PROTECTED);
                }

                break;

            case NEW_LINE:
                if (screenFields.getSize() > 0) {
                    int startRow = getRow(lastPos) + 1;
                    int startPos = lastPos;

                    if (startRow == getRows())
                        startRow = 0;

                    setCursor(++startRow, 1);

                    if (!isInField() && screenFields.getCurrentField() != null
                            && !screenFields.isCurrentFieldBypassField()) {
                        while (!isInField()
                                && screenFields.getCurrentField() != null
                                && !screenFields.isCurrentFieldBypassField()) {
                            // lets keep going
                            advancePos();

                            // Have we looped the screen?
                            if (lastPos == startPos) {
                                // if so then go back to starting point
                                goto_XY(startPos);
                                break;
                            }
                        }
                    }
                }

                simulated = true;
                break;

            case FAST_CURSOR_DOWN:
                int rowNow = (getCurrentRow() - 1) + 3;

                if (rowNow > getRows() - 1)
                    rowNow = rowNow - getRows();

                this.goto_XY(getPos(rowNow, getCurrentCol() - 1));
                simulated = true;
                break;

            case FAST_CURSOR_UP:
                rowNow = (getCurrentRow() - 1) - 3;

                if (rowNow < 0)
                    rowNow = (getRows()) + rowNow;

                this.goto_XY(getPos(rowNow, getCurrentCol() - 1));
                simulated = true;
                break;

            case FAST_CURSOR_LEFT:
                int colNow = (getCurrentCol() - 1) - 3;
                rowNow = getCurrentRow() - 1;

                if (colNow <= 0) {
                    colNow = getColumns() + colNow;
                    rowNow--;
                }

                if (rowNow < 0)
                    rowNow = getRows() - 1;

                process_XY(getPos(rowNow, colNow));
                simulated = true;
                break;

            case FAST_CURSOR_RIGHT:
                colNow = (getCurrentCol() - 1) + 3;
                rowNow = getCurrentRow() - 1;

                if (colNow >= getColumns()) {
                    colNow = colNow - getColumns();
                    rowNow++;
                }

                if (rowNow > getRows() - 1)
                    rowNow = getRows() - rowNow;

                process_XY(getPos(rowNow, colNow));
                simulated = true;
                break;

            default:
                Log.i(TAG, " Mnemonic not supported " + mnem);
                break;
        }

        return simulated;
    }

    protected boolean simulateKeyStroke(char c) {
        if (isStatusErrorCode() && !Character.isISOControl(c) && !keyProcessed) {
            if (resetRequired) return false;

            resetError();
        }

        boolean updateField = false;
        boolean numericError = false;
        boolean updatePos = false;
        boolean autoEnter = false;

        if (!Character.isISOControl(c)) {
            if (screenFields.getCurrentField() != null
                    && screenFields.withinCurrentField(lastPos)
                    && !screenFields.isCurrentFieldBypassField()) {
                if (screenFields.isCurrentFieldFER()
                        && !screenFields.withinCurrentField(screenFields
                                .getCurrentFieldPos())
                        && lastPos == screenFields.getCurrentField().endPos()
                        && screenFields.getCurrentFieldPos() > screenFields
                        .getCurrentField().endPos()) {
                    displayError(ERR_FIELD_EXIT_INVALID);
                    feError = true;
                    return false;
                }

                switch (screenFields.getCurrentFieldShift()) {
                    case 0: // Alpha shift
                    case 2: // Numeric Shift
                    case 4: // Kakana Shift
                        updateField = true;
                        break;

                    case 1: // Alpha Only
                        if (Character.isLetter(c) || c == ',' || c == '-'
                                || c == '.' || c == ' ')
                            updateField = true;

                        break;

                    case 3: // Numeric only
                        if (Character.isDigit(c) || c == '+' || c == ','
                                || c == '-' || c == '.' || c == ' ')
                            updateField = true;
                        else
                            numericError = true;

                        break;

                    case 5: // Digits only
                        if (Character.isDigit(c))
                            updateField = true;
                        else
                            displayError(ERR_NUMERIC_09);

                        break;

                    case 7: // Signed numeric
                        if (Character.isDigit(c) || c == '+' || c == '-')
                            if (lastPos == screenFields.getCurrentField().endPos()
                                    && (c != '+' && c != '-'))
                                displayError(ERR_INVALID_SIGN);
                            else
                                updateField = true;
                        else
                            displayError(ERR_NUMERIC_09);

                        break;
                }

                if (updateField) {
                    if (screenFields.isCurrentFieldToUpper())
                        c = Character.toUpperCase(c);

                    updatePos = true;
                    resetDirty(lastPos);

                    if (oia.isInsertMode()) {
                        if (endOfField(false) != screenFields.getCurrentField()
                                .endPos())
                            shiftRight(lastPos);
                        else {
                            displayError(ERR_NO_ROOM_INSERT);
                            updatePos = false;
                        }
                    }

                    if (updatePos) {
                        screenFields.getCurrentField().getKeyPos(
                            getRow(lastPos), getCol(lastPos));
                        screenFields.getCurrentField().changePos(1);
                        planes.setChar(lastPos, c);
                        screenFields.setCurrentFieldMDT();

                        // if we have gone passed the end of the field then goto
                        // the next field
                        if (!screenFields.withinCurrentField(screenFields
                                                             .getCurrentFieldPos())) {
                            if (screenFields.isCurrentFieldAutoEnter()) {
                                autoEnter = true;
                            }
                            else if (!screenFields.isCurrentFieldFER())
                                gotoFieldNext();
                            else {
                                //                        screenFields.getCurrentField().changePos(1);
                                //
                                //                        if (screenFields.
                                //                        cursorPos == endPos)
                                //                           System.out.println("end of field");
                                //
                                //                        feError != feError;
                                //                        if (feError)
                                //                           displayError(ERR_FIELD_EXIT_INVALID);
                            }
                        }
                        else
                            setCursor(screenFields.getCurrentField()
                                      .getCursorRow() + 1, screenFields
                                      .getCurrentField().getCursorCol() + 1);
                    }

                    fireScreenChanged();

                    if (autoEnter)
                        sendAid(AID_ENTER);
                }
                else {
                    if (numericError) {
                        displayError(ERR_NUMERIC_ONLY);
                    }
                }
            }
            else {
                displayError(ERR_CURSOR_PROTECTED);
            }
        }

        return updatePos;
    }

    /**
     * Method: endOfField
     * <p>
     *
     * convenience method that call endOfField with lastRow lastCol and passes
     * the posSpace to that method
     *
     * @param posSpace
     *            value of type boolean - specifying to return the position of
     *            the the last space or not
     * @return a value of type int - the screen postion (row * columns) + col
     *
     */
    private int endOfField(boolean posSpace) {
        return endOfField(lastPos, posSpace);
    }

    /**
     * Method: endOfField
     * <p>
     *
     * gets the position of the last character of the current field posSpace
     * parameter tells the routine whether to return the position of the last
     * space ( <= ' ') or the last non space posSpace == true last occurrence of
     * char <= ' ' posSpace == false last occurrence of char > ' '
     *
     * @param pos
     *            value of type int - position to start from
     * @param posSpace
     *            value of type boolean - specifying to return the position of
     *            the the last space or not
     * @return a value of type int - the screen postion (row * columns) + col
     *
     */
    private int endOfField(int pos, boolean posSpace) {
        int endPos = screenFields.getCurrentField().endPos();
        int fePos = endPos;
        // get the number of characters to the right
        int count = endPos - pos;

        // first lets get the real ending point without spaces and the such
        while (planes.getChar(endPos) <= ' ' && count-- > 0) {
            endPos--;
        }

        if (endPos == fePos) {
            return endPos;
        }

        screenFields.getCurrentField().getKeyPos(endPos);

        if (posSpace) screenFields.getCurrentField().changePos(+1);

        return screenFields.getCurrentFieldPos();
    }

    private boolean fieldExit() {
        int pos = lastPos;
        boolean mdt = false;
        int end = endOfField(false); // get the ending position of the first
        // non blank character in field
        ScreenField sf = screenFields.getCurrentField();

        if (sf.isMandatoryEnter() && end == sf.startPos()) {
            displayError(ERR_MANDATORY_ENTER);
            return false;
        }

        // save off the current pos of the field for checking field exit required
        //   positioning.  the getKeyPos resets this information so it is useless
        //   for comparing if we are positioned passed the end of field.
        //   Maybe this should be changed to not update the current cursor position
        //   of the field.
        int currentPos = sf.getCurrentPos();
        // get the number of characters to the right
        int count = (end - sf.startPos()) - sf.getKeyPos(pos);

        if (count == 0 && sf.isFER()) {
            if (currentPos > sf.endPos()) {
                mdt = true;
                return mdt;
            }
        }

        for (; count >= 0; count--) {
            planes.setChar(pos, initChar);
            setDirty(pos);
            pos++;
            mdt = true;
        }

        // This checks for a field minus because a field minus places
        // a negative sign and then advances a position. If it is the
        // end of the field where the minus is placed then this offset will
        //  place the count as -1.
        if (count == -1) {
            int s = sf.getFieldShift();

            if (s == 3 || s == 5 || s == 7) {
                mdt = true;
            }
        }

        int adj = sf.getAdjustment();

        if (adj != 0) {
            switch (adj) {
                case 5:
                    rightAdjustField('0');
                    sf.setRightAdjusted();
                    break;

                case 6:
                    rightAdjustField(' ');
                    sf.setRightAdjusted();
                    break;

                case 7:
                    sf.setMandatoryEntered();
                    break;
            }
        }
        else {
            // we need to right adjust signed numeric fields as well.
            if (sf.isSignedNumeric()) {
                rightAdjustField(' ');
            }
        }

        return mdt;
    }

    private void rightAdjustField(char fill) {
        int end = endOfField(false); // get the ending position of the first
        // non blank character in field
        // get the number of characters to the right
        int count = screenFields.getCurrentField().endPos() - end;

        // subtract 1 from count for signed numeric - note for later
        if (screenFields.getCurrentField().isSignedNumeric()) {
            if (planes.getChar(end - 1) != '-')
                count--;
        }

        int pos = screenFields.getCurrentField().startPos();

        while (count-- >= 0) {
            shiftRight(pos);
            planes.setChar(pos, fill);
            setDirty(pos);
        }
    }

    private void shiftLeft(int sPos) {
        int endPos = 0;
        int pos = sPos;
        int pPos = sPos;
        ScreenField sf = screenFields.getCurrentField();
        int end;
        int count;

        do {
            end = endOfField(pPos, false); // get the ending position of the
            // first
            // non blank character in field
            count = (end - screenFields.getCurrentField().startPos())
                    - screenFields.getCurrentField().getKeyPos(pPos);

            // now we loop through and shift the remaining characters to the
            // left
            while (count-- > 0) {
                pos++;
                planes.setChar(pPos, planes.getChar(pos));
                setDirty(pPos);
                pPos = pos;
            }

            if (screenFields.isCurrentFieldContinued()) {
                gotoFieldNext();

                if (screenFields.getCurrentField().isContinuedFirst())
                    break;

                pos = screenFields.getCurrentField().startPos();
                planes.setChar(pPos, planes.getChar(pos));
                setDirty(pPos);
                pPos = pos;
            }
        }
        while (screenFields.isCurrentFieldContinued()
                && !screenFields.getCurrentField().isContinuedFirst());

        if (end >= 0 && count >= -1) {
            endPos = end;
        }
        else {
            endPos = sPos;
        }

        screenFields.setCurrentField(sf);
        planes.setChar(endPos, initChar);
        setDirty(endPos);
        goto_XY(screenFields.getCurrentFieldPos());
        sf = null;
    }

    private void shiftRight(int sPos) {
        int end = endOfField(true); // get the ending position of the first
        // non blank character in field
        int pos = end;
        int pPos = end;
        int count = end - sPos;

        // now we loop through and shift the remaining characters to the right
        while (count-- > 0) {
            pos--;
            planes.setChar(pPos, planes.getChar(pos));
            setDirty(pPos);
            pPos = pos;
        }
    }

    public int getRow(int pos) {
        //      if (pos == 0)
        //         return 1;
        int row = pos / numCols;

        if (row < 0) {
            row = lastPos / numCols;
        }

        if (row > (lenScreen / numCols) - 1)
            row = (lenScreen / numCols) - 1;

        return row;
    }

    public int getCol(int pos) {
        int col = pos % (getColumns());

        if (col > 0) return col;

        return 0;
    }

    /**
     * This routine is 0 based offset. So to get row 20,1 then pass row 19,0
     *
     * @param row
     * @param col
     * @return
     */
    public int getPos(int row, int col) {
        return (row * numCols) + col;
    }

    /**
     * Current position is based on offsets of 1,1 not 0,0 of the current
     * position of the screen
     *
     * @return int
     */
    public int getCurrentPos() {
        //      return lastPos + numCols + 1;
        return lastPos + 1;
    }

    /**
     * I got this information from a tcp trace of each error. I could not find
     * any documenation for this. Maybe there is but I could not find it. If
     * anybody finds this documention could you please send me a copy. Please
     * note that I did not look that hard either.
     * <p>
     * 0000: 00 50 73 1D 89 81 00 50 DA 44 C8 45 08 00 45 00 .Ps....P.D.E..E.
     * </p>
     * <p>
     * 0010: 00 36 E9 1C 40 00 80 06 9B F9 C1 A8 33 58 C0 A8 .6..@...k....3X..
     * </p>
     * <p>
     * 0020: C0 02 06 0E 00 17 00 52 6E 88 73 40 DE CB 50 18 .......Rn.s@..P.
     * </p>
     * <p>
     * 0030: 20 12 3C 53 00 00 00 0C 12 A0 00 00 04 01 00 00 . <S............
     * </p>
     * <p>
     * 0040: 00 05 FF EF .... ----------|| The 00 XX is the code to be sent. I
     * found the following <table BORDER COLS=2 WIDTH="50%" >
     * <tr>
     * <td>ERR_CURSOR_PROTECTED</td>
     * <td>0x05</td>
     * </tr>
     * <tr>
     * <td>ERR_INVALID_SIGN</td>
     * <td>0x11</td>
     * </tr>
     * <tr>
     * <td>ERR_NO_ROOM_INSERT</td>
     * <td>0x12</td>
     * </tr>
     * <tr>
     * <td>ERR_NUMERIC_ONLY</td>
     * <td>0x09</td>
     * </tr>
     * <tr>
     * <td>ERR_NUMERIC_09</td>
     * <td>0x10</td>
     * </tr>
     * <tr>
     * <td>ERR_FIELD_MINUS</td>
     * <td>0x16</td>
     * </tr>
     * <tr>
     * <td>ERR_ENTER_NOT_ALLOWED</td>
     * <td>0x20</td>
     * </tr>
     * <tr>
     * <td>ERR_MANDATORY_ENTER</td>
     * <td>0x21</td>
     * </tr>
     * <tr>
     * <td>ERR_ENTER_NOT_ALLOWED</td>
     * <td>0x20</td>
     * </tr>
     * </table> I am tired of typing and they should be self explanitory. Finding
     * them in the first place was the pain.
     * </p>
     *
     * @param ec error code
     */
    private void displayError(int ec) {
        saveHomePos = homePos;
        homePos = lastPos + numCols + 1;
        pendingInsert = true;
        sessionVT.sendNegResponse2(ec);
    }

    private void process_XY(int pos) {
        if (pos < 0)
            pos = lenScreen + pos;

        if (pos > lenScreen - 1)
            pos = pos - lenScreen;

        // if there was a field exit error then we need to treat the movement
        //  of the cursor in a special way that equals that of Client Access.
        //    If the cursor is moved from the field then we need to reset the
        //       position within the field so that the last character can be typed
        //       over again instead of sending the field exit error again.
        //       We also need to reset the field exit error flag.
        //
        //    How we know we have a field exit error is when the field position is
        //    set beyond the end of the field and a character is then typed we can
        //    not position that character. To reset this we need to set the next
        //    position of the field to not be beyond the end of field but to the
        //    last character.
        //
        //    Now to make it work like Client Access if the cursor is a back space
        //    then do not move the cursor but place it on the last field. All
        //    other keys will reset the field position so that entering over the
        //    last character will not cause an error but replace that character or
        //    just plain move the cursor if the key was to do that.
        ScreenField sf = screenFields.getCurrentField();

        if (feError) {
            feError = false;
            sf.changePos(-1);
        }
        else {
            if (sf != null && sf.isFER()) {
                if ((sf.getCurrentPos()
                        > sf.endPos())) {
                    if (sf.withinField(pos)) {
                        sf.getKeyPos(pos);
                        return;
                    }

                    sf.getKeyPos(sf.endPos());
                }
            }

            goto_XY(pos);
        }
    }

    public boolean isUsingGuiInterface() {
        return guiInterface;
    }

    /**
     * Convinience class to return if the cursor is in a field or not.
     *
     * @return true or false
     */

    protected boolean isInField() {
        return isInField(lastPos, true);
    }

    /**
     *
     * Convinience class to return if the position that is passed is in a field
     * or not. If it is then the chgToField parameter will change the current
     * field to this field where the position indicates
     *
     * @param pos
     * @param chgToField
     * @return true or false
     */
    public boolean isInField(int pos, boolean chgToField) {
        return screenFields.isInField(pos, chgToField);
    }

    /**
     *
     * Convinience class to return if the position that is passed is in a field
     * or not. If it is then the field at this position becomes the current
     * working field
     *
     * @param pos
     * @return true or false
     */
    public boolean isInField(int pos) {
        return screenFields.isInField(pos, true);
    }

    /**
     * Convinience class to return if the position at row and column that is
     * passed is in a field or not. If it is then the field at this position
     * becomes the current working field.
     *
     * @param row
     * @param col
     * @return true or false
     */
    public boolean isInField(int row, int col) {
        return isInField(row, col, true);
    }

    /**
     *
     * Convinience class to return if the position at row and column that is
     * passed is in a field or not. If it is then the chgToField parameter will
     * change the current field to this field where the row and column
     * indicates.
     *
     * @param row
     * @param col
     * @param chgToField
     * @return true or false
     */
    public boolean isInField(int row, int col, boolean chgToField) {
        return screenFields.isInField((row * numCols) + col, chgToField);
    }

    /**
     * Gets the length of the screen - number of rows times number of columns
     *
     * @return int value of screen length
     */
    public int getScreenLength() {
        return lenScreen;
    }

    /**
     * Get the number or rows available.
     *
     * @return number of rows
     */
    public int getRows() {
        return numRows;
    }

    /**
     * Get the number of columns available.
     *
     * @return number of columns
     */
    public int getColumns() {
        return numCols;
    }

    /**
     * Get the current row where the cursor is
     *
     * @return the cursor current row position 1,1 based
     */
    public int getCurrentRow() {
        return (lastPos / numCols) + 1;
    }

    /**
     * Get the current column where the cursor is
     *
     * @return the cursor current column position 1,1 based
     */
    public int getCurrentCol() {
        return (lastPos % numCols) + 1;
    }

    /**
     * The last position of the cursor on the screen - Note - position is based
     * 0,0
     *
     * @return last position
     */
    protected int getLastPos() {
        return lastPos;
    }

    /**
     * Hotspot More... string
     *
     * @return string literal of More...
     */
    public StringBuffer getHSMore() {
        return hsMore;
    }

    /**
     * Hotspot Bottom string
     *
     * @return string literal of Bottom
     */
    public StringBuffer getHSBottom() {
        return hsBottom;
    }

    /**
     *
     * Return the screen represented as a character array
     *
     * @return character array containing the text
     */
    public char[] getScreenAsChars() {
        char[] sac = new char[lenScreen];
        char c;

        for (int x = 0; x < lenScreen; x++) {
            c = planes.getChar(x);

            // only draw printable characters (in this case >= ' ')
            if ((c >= ' ') && (!planes.isAttributePlace(x))) {
                sac[x] = c;
                // TODO: implement the underline check here
                //              if (screen[x].underLine && c <= ' ')
                //                  sac[x] = '_';
            }
            else
                sac[x] = ' ';
        }

        return sac;
    }

    public char[] getData(int startRow, int startCol, int endRow, int endCol, int plane) {
        try {
            int from = getPos(startRow, startCol);
            int to = getPos(endRow, endCol);

            if (from > to) {
                int f = from;
                to = f;
                from = f;
            }

            return planes.getPlaneData(from, to, plane);
        }
        catch (Exception oe) {
            return null;
        }
    }

    /**
     * <p>
     *  GetScreen retrieves the various planes associated with the presentation
     *  space. The data is returned as a linear array of character values in the
     *  array provided. The array is not terminated by a null character except
     *  when data is retrieved from the text plane, in which case a single null
     *  character is appended.
     *  </p>
     *  <p>
     *  The application must supply a buffer for the returned data and the length
     *  of the buffer. Data is returned starting from the beginning of the
     *  presentation space and continuing until the buffer is full or the entire
     *  plane has been copied. For text plane data, the buffer must include one
     *  extra position for the terminating null character.
     *  <p>
     *
     * @param buffer
     * @param bufferLength
     * @param plane
     * @return The number of characters copied to the buffer
     * @throws OhioException
     */

    public synchronized int GetScreen(char buffer[], int bufferLength, int plane)
    //                                       throws OhioException {
    {
        return GetScreen(buffer, bufferLength, 0, lenScreen, plane);
    }

    /**
     * <p>
     *  GetScreen retrieves the various planes associated with the presentation
     *  space. The data is returned as a linear array of character values in the
     *  array provided. The array is not terminated by a null character except
     *  when data is retrieved from the text plane, in which case a single null
     *  character is appended.
     * </p>
     * <p>
     * The application must supply a buffer for the returned data and the length
     * of the buffer. Data is returned starting from the given position and
     * continuing until the specified number of characters have been copied, the
     * buffer is full or the entire plane has been copied. For text plane data,
     * the buffer must include one extra position for the terminating null character.
     * </p>
     *
     * @param buffer
     * @param bufferLength
     * @param from
     * @param length
     * @param plane
     * @return The number of characters copied to the buffer
     * @throws OhioException
     */

    public synchronized int GetScreen(char buffer[], int bufferLength, int from,
                                      int length, int plane)
    //                                    throws OhioException {
    {
        return planes.GetScreen(buffer, bufferLength, from, length, plane);
    }

    /**
     * <p>
     *  GetScreen retrieves the various planes associated with the presentation
     *  space. The data is returned as a linear array of character values in the
     *  array provided. The array is not terminated by a null character except
     *  when data is retrieved from the text plane, in which case a single null
     *  character is appended.
     *  </p>
     *  <p>
     *  The application must supply a buffer for the returned data and the length
     *  of the buffer. Data is returned starting from the given coordinates and
     *  continuing until the specified number of characters have been copied,
     *  the buffer is full, or the entire plane has been copied. For text plane
     *  data, the buffer must include one extra position for the terminating null
     *  character.
     *  </p>
     *
     * @param buffer
     * @param bufferLength
     * @param row
     * @param col
     * @param length
     * @param plane
     * @return The number of characters copied to the buffer.
     * @throws OhioException
     */

    public synchronized int GetScreen(char buffer[], int bufferLength, int row,
                                      int col, int length, int plane)
    //                                       throws OhioException {
    {
        // Call GetScreen function after converting row and column to
        // a position.
        return planes.GetScreen(buffer, bufferLength, row, col, length, plane);
    }

    /**
     * <p>
     *  GetScreenRect retrieves data from the various planes associated with the
     *  presentation space. The data is returned as a linear array of character
     *  values in the buffer provided.
     *  </p>
     *
     * <p>
     * The application supplies two positions that represent opposing corners of
     * a rectangle within the presentation space. The starting and ending
     * positions can have any spatial relationship to each other. The data
     * returned starts from the row containing the upper-most point to the row
     * containing the lower-most point, and from the left-most column to the
     * right-most column.
     * </p>
     * <p>
     * The specified buffer must be at least large enough to contain the number
     * of characters in the rectangle. If the buffer is too small, no data is
     * copied and zero is returned by the method. Otherwise, the method returns
     * the number of characters copied.
     * </p>
     *
     * @param buffer
     * @param bufferLength
     * @param startPos
     * @param endPos
     * @param plane
     * @return The number of characters copied to the buffer
     * @throws OhioException
     */

    public synchronized int GetScreenRect(char buffer[], int bufferLength,
                                          int startPos, int endPos, int plane)
    //                                             throws OhioException {
    {
        return planes.GetScreenRect(buffer, bufferLength, startPos, endPos, plane);
    }

    /**
     * <p>
     *  GetScreenRect retrieves data from the various planes associated with the
     *  presentation space. The data is returned as a linear array of character
     *  values in the buffer provided. The buffer is not terminated by a null
     *  character.
     * </p>
     * <p>
     * The application supplies two coordinates that represent opposing corners
     * of a rectangle within the presentation space. The starting and ending
     * coordinates can have any spatial relationship to each other. The data
     * returned starts from the row containing the upper-most point to the row
     * containing the lower-most point, and from the left-most column to the
     * right-most column.
     * </p>
     * <p>
     * The specified buffer must be at least large enough to contain the number
     * of characters in the rectangle. If the buffer is too small, no data is
     * copied and zero is returned by the method. Otherwise, the method returns
     * the number of characters copied.
     * </p>
     *
     * @param buffer
     * @param bufferLength
     * @param startRow
     * @param startCol
     * @param endRow
     * @param endCol
     * @param plane
     * @return The number characters copied to the buffer
     * @throws OhioException
     */

    public synchronized int GetScreenRect(char buffer[], int bufferLength,
                                          int startRow, int startCol,
                                          int endRow, int endCol, int plane)
    //                                             throws OhioException {
    {
        return planes.GetScreenRect(buffer, bufferLength, startRow, startCol, endRow,
                                    endCol, plane);
    }

    public synchronized boolean[] getActiveAidKeys() {
        return sessionVT.getActiveAidKeys();
    }

    protected synchronized void setScreenData(String text, int location) {
        //                                             throws OhioException {
        if (location < 0 || location > lenScreen) {
            return;
            //         throw new OhioException(sessionVT.getSessionConfiguration(),
            //                      OhioScreen5250.class.getName(), "osohio.screen.ohio00300", 1);
        }

        int pos = location;
        int l = text.length();
        boolean updated = false;
        boolean flag = false;
        int x = 0;

        for (; x < l; x++) {
            if (isInField(pos + x, true)) {
                if (!screenFields.getCurrentField().isBypassField()) {
                    if (!flag) {
                        screenFields.getCurrentField().setMDT();
                        updated = true;
                        resetDirty(pos + x);
                        screenFields.setMasterMDT();
                        flag = true;
                    }

                    planes.screen[pos + x] = text.charAt(x);
                    setDirty(pos + x);
                }
            }
        }

        lastPos = pos + x;

        if (updated) {
            fireScreenChanged();
        }
    }

    /**
     * This routine is based on offset 1,1 not 0,0 it will translate to offset
     * 0,0 and call the goto_XY(int pos) it is mostly used from external classes
     * that use the 1,1 offset
     *
     * @param row
     * @param col
     */
    public void setCursor(int row, int col) {
        goto_XY(((row - 1) * numCols) + (col - 1));
    }

    // this routine is based on offset 0,0 not 1,1
    protected void goto_XY(int pos) {
        lastPos = pos;
        updateCursorLoc();
    }

    /*
     * set the content of the field at (l,c) to data
     * if l == -1, set the current field contents to data
     */
    public void setField(int l, int c, char [] data) {
        ScreenField cf;

        if (l >= 0) {
            lastPos = l * numCols + c;

            while (!isInField()) advancePos();

            setDirty(lastPos);
            fireCursorChanged();
        }

        if ((data != null) && (data.length > 0)) {
            cf = screenFields.getCurrentField();
            cf.setString(new String(data));
            lastPos = cf.getStartPos();
            setDirty(lastPos);
            setDirty(lastPos + cf.getLength());
            lastPos += data.length;

            if (!isInField()) {
                gotoFieldNext();
                isInField();
                cf = screenFields.getCurrentField();
                lastPos = cf.getStartPos();
            }

            setDirty(lastPos);
            fireCursorChanged();
        }

        updateDirty();
    }

    /**
     * Set the current working field to the field number specified.
     *
     * @param f -
     *            numeric field number on the screen
     * @return true or false whether it was sucessful
     */
    public boolean gotoField(int f) {
        int sizeFields = screenFields.getSize();

        if (f > sizeFields || f <= 0)
            return false;

        screenFields.setCurrentField(screenFields.getField(f - 1));

        while (screenFields.isCurrentFieldBypassField() && f < sizeFields) {
            screenFields.setCurrentField(screenFields.getField(f++));
        }

        return gotoField(screenFields.getCurrentField());
    }

    /**
     * Convenience method to set the field object passed as the currect working
     * screen field
     *
     * @param f
     * @return true or false whether it was sucessful
     * @see org.tn5250j.ScreenField
     */
    protected boolean gotoField(ScreenField f) {
        if (f != null) {
            goto_XY(f.startPos());
            return true;
        }

        return false;
    }

    /**
     * Convenience class to position the cursor to the next word on the screen
     *
     */
    private void gotoNextWord() {
        int pos = lastPos;

        if (planes.getChar(lastPos) > ' ') {
            advancePos();

            // get the next space character
            while (planes.getChar(lastPos) > ' ' && pos != lastPos) {
                advancePos();
            }
        }
        else
            advancePos();

        // now that we are positioned on the next space character get the
        // next none space character
        while (planes.getChar(lastPos) <= ' ' && pos != lastPos) {
            advancePos();
        }
    }

    /**
     * Convenience class to position the cursor to the previous word on the
     * screen
     *
     */
    private void gotoPrevWord() {
        int pos = lastPos;
        changePos(-1);

        // position previous white space character
        while (planes.getChar(lastPos) <= ' ') {
            changePos(-1);

            if (pos == lastPos)
                break;
        }

        changePos(-1);

        // get the previous space character
        while (planes.getChar(lastPos) > ' ' && pos != lastPos) {
            changePos(-1);
        }

        // and position one position more should give us the beginning of word
        advancePos();
    }

    /**
     * Convinience class to position to the next field on the screen.
     *
     * @see org.tn5250j.ScreenFields
     */
    private void gotoFieldNext() {
        if (screenFields.isCurrentFieldHighlightedEntry())
            unsetFieldHighlighted(screenFields.getCurrentField());

        screenFields.gotoFieldNext();

        if (screenFields.isCurrentFieldHighlightedEntry())
            setFieldHighlighted(screenFields.getCurrentField());
    }

    /**
     * Convinience class to position to the previous field on the screen.
     *
     * @see org.tn5250j.ScreenFields
     */
    private void gotoFieldPrev() {
        if (screenFields.isCurrentFieldHighlightedEntry())
            unsetFieldHighlighted(screenFields.getCurrentField());

        screenFields.gotoFieldPrev();

        if (screenFields.isCurrentFieldHighlightedEntry())
            setFieldHighlighted(screenFields.getCurrentField());
    }

    /* *** NEVER USED LOCALLY ************************************************** */
    //  /**
    //   * Used to restrict the cursor to a particular position on the screen. Used
    //   * in combination with windows to restrict the cursor to the active window
    //   * show on the screen.
    //   *
    //   * Not supported yet. Please implement me :-(
    //   *
    //   * @param depth
    //   * @param width
    //   */
    //  protected void setRestrictCursor(int depth, int width) {
    //
    //      restrictCursor = true;
    //      //      restriction
    //
    //  }

    /**
     * Creates a window on the screen
     *
     * @param depth
     * @param width
     * @param type
     * @param gui
     * @param monoAttr
     * @param colorAttr
     * @param ul
     * @param upper
     * @param ur
     * @param left
     * @param right
     * @param ll
     * @param bottom
     * @param lr
     */
    protected void createWindow(int depth, int width, int type, boolean gui,
                                int monoAttr, int colorAttr, int ul, int upper, int ur, int left,
                                int right, int ll, int bottom, int lr) {
        int c = getCol(lastPos);
        int w = 0;
        width++;
        w = width;
        // set leading attribute byte
        //      screen[lastPos].setCharAndAttr(initChar, initAttr, true);
        planes.setScreenCharAndAttr(lastPos, initChar, initAttr, true);
        setDirty(lastPos);
        advancePos();
        // set upper left
        //      screen[lastPos].setCharAndAttr((char) ul, colorAttr, false);
        planes.setScreenCharAndAttr(lastPos, (char) ul, colorAttr, false);

        if (gui) {
            //          screen[lastPos].setUseGUI(UPPER_LEFT);
            planes.setUseGUI(lastPos, UPPER_LEFT);
        }

        setDirty(lastPos);
        advancePos();

        // draw top row

        while (w-- >= 0) {
            //          screen[lastPos].setCharAndAttr((char) upper, colorAttr, false);
            planes.setScreenCharAndAttr(lastPos, (char) upper, colorAttr, false);

            if (gui) {
                //              screen[lastPos].setUseGUI(UPPER);
                planes.setUseGUI(lastPos, UPPER);
            }

            setDirty(lastPos);
            advancePos();
        }

        // set upper right
        //      screen[lastPos].setCharAndAttr((char) ur, colorAttr, false);
        planes.setScreenCharAndAttr(lastPos, (char) ur, colorAttr, false);

        if (gui) {
            //          screen[lastPos].setUseGUI(UPPER_RIGHT);
            planes.setUseGUI(lastPos, UPPER_RIGHT);
        }

        setDirty(lastPos);
        advancePos();
        // set ending attribute byte
        planes.setScreenCharAndAttr(lastPos, initChar, initAttr, true);
        setDirty(lastPos);
        lastPos = ((getRow(lastPos) + 1) * numCols) + c;

        // now handle body of window
        while (depth-- > 0) {
            // set leading attribute byte
            planes.setScreenCharAndAttr(lastPos, initChar, initAttr, true);
            setDirty(lastPos);
            advancePos();
            // set left
            planes.setScreenCharAndAttr(lastPos, (char) left, colorAttr, false);

            if (gui) {
                planes.setUseGUI(lastPos, GUI_LEFT);
            }

            setDirty(lastPos);
            advancePos();
            w = width;

            // fill it in
            while (w-- >= 0) {
                //              screen[lastPos].setCharAndAttr(initChar, initAttr, true);
                planes.setScreenCharAndAttr(lastPos, initChar, initAttr, true);
                //              screen[lastPos].setUseGUI(NO_GUI);
                planes.setUseGUI(lastPos, NO_GUI);
                setDirty(lastPos);
                advancePos();
            }

            // set right
            //          screen[lastPos].setCharAndAttr((char) right, colorAttr, false);
            planes.setScreenCharAndAttr(lastPos, (char) right, colorAttr, false);

            if (gui) {
                //              screen[lastPos].setUseGUI(RIGHT);
                planes.setUseGUI(lastPos, GUI_RIGHT);
            }

            setDirty(lastPos);
            advancePos();
            // set ending attribute byte
            //          screen[lastPos].setCharAndAttr(initChar, initAttr, true);
            planes.setScreenCharAndAttr(lastPos, initChar, initAttr, true);
            setDirty(lastPos);
            lastPos = ((getRow(lastPos) + 1) * numCols) + c;
        }

        // set leading attribute byte
        //      screen[lastPos].setCharAndAttr(initChar, initAttr, true);
        planes.setScreenCharAndAttr(lastPos, initChar, initAttr, true);
        setDirty(lastPos);
        advancePos();
        // set lower left
        //      screen[lastPos].setCharAndAttr((char) ll, colorAttr, false);
        planes.setScreenCharAndAttr(lastPos, (char) ll, colorAttr, false);

        if (gui) {
            //          screen[lastPos].setUseGUI(LOWER_LEFT);
            planes.setUseGUI(lastPos, LOWER_LEFT);
        }

        setDirty(lastPos);
        advancePos();
        w = width;

        // draw bottom row
        while (w-- >= 0) {
            planes.setScreenCharAndAttr(lastPos, (char) bottom, colorAttr, false);

            if (gui) {
                planes.setUseGUI(lastPos, BOTTOM);
            }

            setDirty(lastPos);
            advancePos();
        }

        // set lower right
        planes.setScreenCharAndAttr(lastPos, (char) lr, colorAttr, false);

        if (gui) {
            planes.setUseGUI(lastPos, LOWER_RIGHT);
        }

        setDirty(lastPos);
        advancePos();
        // set ending attribute byte
        planes.setScreenCharAndAttr(lastPos, initChar, initAttr, true);
        setDirty(lastPos);
    }

    /**
     * Creates a scroll bar on the screen using the parameters provided.
     *  ** we only support vertical scroll bars at the time.
     *
     * @param flag -
     *            type to draw - vertical or horizontal
     * @param totalRowScrollable
     * @param totalColScrollable
     * @param sliderRowPos
     * @param sliderColPos
     * @param sbSize
     */
    protected void createScrollBar(int flag, int totalRowScrollable,
                                   int totalColScrollable, int sliderRowPos, int sliderColPos,
                                   int sbSize) {
        //      System.out.println("Scrollbar flag: " + flag +
        //                           " scrollable Rows: " + totalRowScrollable +
        //                           " scrollable Cols: " + totalColScrollable +
        //                           " thumb Row: " + sliderRowPos +
        //                           " thumb Col: " + sliderColPos +
        //                           " size: " + sbSize +
        //                           " row: " + getRow(lastPos) +
        //                           " col: " + getCol(lastPos));
        int sp = lastPos;
        int size = sbSize - 2;
        int thumbPos = (int)(size * ((float) sliderColPos / (float) totalColScrollable));
        //      System.out.println(thumbPos);
        planes.setScreenCharAndAttr(sp, ' ', 32, false);
        planes.setUseGUI(sp, BUTTON_SB_UP);
        int ctr = 0;

        while (ctr < size) {
            sp += numCols;
            planes.setScreenCharAndAttr(sp, ' ', 32, false);

            if (ctr == thumbPos)
                planes.setUseGUI(sp, BUTTON_SB_THUMB);
            else
                planes.setUseGUI(sp, BUTTON_SB_GUIDE);

            ctr++;
        }

        sp += numCols;
        planes.setScreenCharAndAttr(sp, ' ', 32, false);
        planes.setUseGUI(sp, BUTTON_SB_DN);
    }

    /**
     * Write the title of the window that is on the screen
     *
     * @param pos
     * @param depth
     * @param width
     * @param orientation
     * @param monoAttr
     * @param colorAttr
     * @param title
     */
    protected void writeWindowTitle(int pos, int depth, int width,
                                    byte orientation, int monoAttr, int colorAttr, StringBuffer title) {
        int len = title.length();

        // get bit 0 and 1 for interrogation
        switch (orientation & 0xc0) {
            case 0x40: // right
                pos += (4 + width - len);
                break;

            case 0x80: // left
                pos += 2;
                break;

            default: // center
                // this is to place the position to the first text position of the
                // window
                //    the position passed in is the first attribute position, the next
                //    is the border character and then there is another attribute after
                //    that.
                pos += (3 + ((width / 2) - (len / 2)));
                break;
        }

        //  if bit 2 is on then this is a footer
        if ((orientation & 0x20) == 0x20)
            pos += ((depth + 1) * numCols);

        //      System.out.println(pos + "," + width + "," + len+ "," + getRow(pos)
        //                              + "," + getCol(pos) + "," + ((orientation >> 6) & 0xf0));

        for (int x = 0; x < len; x++) {
            planes.setChar(pos, title.charAt(x));
            planes.setUseGUI(pos++, NO_GUI);
        }
    }

    /**
     * Roll the screen up or down.
     *
     * Byte 1: Bit 0 0 = Roll up 1 = Roll down Bits 1-2 Reserved Bits 3-7 Number
     * of lines that the designated area is to be rolled Byte 2: Bits 0-7 Line
     * number defining the top line of the area that will participate in the
     * roll. Byte 3: Bits 0-7 Line number defining the bottom line of the area
     * that will participate in the roll.
     *
     * @param direction
     * @param topLine
     * @param bottomLine
     */
    protected void rollScreen(int direction, int topLine, int bottomLine) {
        // get the number of lines which are the last 5 bits
        /* int lines = direction & 0x7F; */
        // get the direction of the roll which is the first bit
        //    0 - up
        //    1 - down
        int updown = direction & 0x80;
        final int lines = direction & 0x7F;
        // calculate the reference points for the move.
        int start = this.getPos(topLine - 1, 0);
        int end = this.getPos(bottomLine - 1, numCols - 1);
        int len = end - start;

        //      System.out.println(" starting roll");
        //      dumpScreen();
        switch (updown) {
            case 0:

                //  Now round em up and head em UP.
                for (int x = start; x < end + numCols; x++) {
                    if (x + lines * numCols >= lenScreen) {
                        //Clear at the end
                        planes.setChar(x, ' ');
                    }
                    else {
                        planes.setChar(x, planes.getChar(x + lines * numCols));
                    }
                }

                break;

            case 1:

                //  Now round em up and head em DOWN.
                for (int x = end + numCols; x > 0; x--) {
                    if ((x - lines * numCols) < 0) {
                        //Do nothing ... tooo small!!!
                    }
                    else {
                        planes.setChar(x - lines  * numCols, planes.getChar(x));
                        //and clear
                        planes.setChar(x, ' ');
                    }
                }

                break;

            default:
                Log.w(TAG, " Invalid roll parameter - please report this");
        }

        //      System.out.println(" end roll");
        //      dumpScreen();
    }

    public void dumpScreen() {
        StringBuffer sb = new StringBuffer();
        char[] s = getScreenAsChars();
        int c = getColumns();
        int l = getRows() * c;
        int col = 0;

        for (int x = 0; x < l; x++, col++) {
            sb.append(s[x]);

            if (col == c) {
                sb.append('\n');
                col = 0;
            }
        }

        Log.i(TAG, sb.toString());
    }

    /**
     * Add a field to the field format table.
     *
     * @param attr - Field attribute
     * @param len - length of field
     * @param ffw1 - Field format word 1
     * @param ffw2 - Field format word 2
     * @param fcw1 - Field control word 1
     * @param fcw2 - Field control word 2
     */
    protected void addField(int attr, int len, int ffw1, int ffw2, int fcw1,
                            int fcw2) {
        lastAttr = attr;
        planes.setScreenCharAndAttr(lastPos, initChar, lastAttr, true);
        setDirty(lastPos);
        advancePos();
        ScreenField sf = null;

        // from 14.6.12 for Start of Field Order 5940 function manual
        //  examine the format table for an entry that begins at the current
        //  starting address plus 1.
        if (screenFields.existsAtPos(lastPos)) {
            screenFields.setCurrentFieldFFWs(ffw1, ffw2);
        }
        else {
            sf = screenFields.setField(attr, getRow(lastPos), getCol(lastPos),
                                       len, ffw1, ffw2, fcw1, fcw2);
            lastPos = sf.startPos();
            int x = len;
            boolean gui = guiInterface;

            if (sf.isBypassField())
                gui = false;

            while (x-- > 0) {
                if (planes.getChar(lastPos) == 0)
                    planes.setScreenCharAndAttr(lastPos, ' ', lastAttr, false);
                else
                    planes.setScreenAttr(lastPos, lastAttr);

                if (gui) {
                    planes.setUseGUI(lastPos, FIELD_MIDDLE);
                }

                // now we set the field plane attributes
                planes.setScreenFieldAttr(lastPos, ffw1);
                advancePos();
            }

            if (gui)
                if (len > 1) {
                    planes.setUseGUI(sf.startPos(), FIELD_LEFT);

                    if (lastPos > 0)
                        planes.setUseGUI(lastPos - 1, FIELD_RIGHT);
                    else
                        planes.setUseGUI(lastPos, FIELD_RIGHT);
                }
                else {
                    planes.setUseGUI(lastPos - 1, FIELD_ONE);
                }

            //         screen[lastPos].setCharAndAttr(initChar,initAttr,true);
            setEndingAttr(initAttr);
            lastPos = sf.startPos();
        }

        //      if (fcw1 != 0 || fcw2 != 0) {
        //         System.out.println("lr = " + lastRow + " lc = " + lastCol + " " +
        // sf.toString());
        //      }
        sf = null;
    }


    //      public void addChoiceField(int attr, int len, int ffw1, int ffw2, int
    // fcw1, int fcw2) {
    //
    //         lastAttr = attr;
    //
    //         screen[lastPos].setCharAndAttr(initChar,lastAttr,true);
    //         setDirty(lastPos);
    //
    //         advancePos();
    //
    //         boolean found = false;
    //         ScreenField sf = null;
    //
    //         // from 14.6.12 for Start of Field Order 5940 function manual
    //         // examine the format table for an entry that begins at the current
    //         // starting address plus 1.
    //         for (int x = 0;x < sizeFields; x++) {
    //            sf = screenFields[x];
    //
    //            if (lastPos == sf.startPos()) {
    //               screenFields.getCurrentField() = sf;
    //               screenFields.getCurrentField().setFFWs(ffw1,ffw2);
    //               found = true;
    //            }
    //
    //         }
    //
    //         if (!found) {
    //            sf =
    // setField(attr,getRow(lastPos),getCol(lastPos),len,ffw1,ffw2,fcw1,fcw2);
    //
    //            lastPos = sf.startPos();
    //            int x = len;
    //
    //            boolean gui = guiInterface;
    //            if (sf.isBypassField())
    //               gui = false;
    //
    //            while (x-- > 0) {
    //
    //               if (screen[lastPos].getChar() == 0)
    //                  screen[lastPos].setCharAndAttr(' ',lastAttr,false);
    //               else
    //                  screen[lastPos].setAttribute(lastAttr);
    //
    //               if (gui)
    //                  screen[lastPos].setUseGUI(FIELD_MIDDLE);
    //
    //               advancePos();
    //
    //            }
    //
    //            if (gui)
    //               if (len > 1) {
    //                  screen[sf.startPos()].setUseGUI(FIELD_LEFT);
    //                  if (lastPos > 0)
    //                     screen[lastPos-1].setUseGUI(FIELD_RIGHT);
    //                  else
    //                     screen[lastPos].setUseGUI(FIELD_RIGHT);
    //
    //               }
    //               else
    //                  screen[lastPos-1].setUseGUI(FIELD_ONE);
    //
    //            setEndingAttr(initAttr);
    //
    //            lastPos = sf.startPos();
    //         }
    //
    //   // if (fcw1 != 0 || fcw2 != 0) {
    //   //
    //   // System.out.println("lr = " + lastRow + " lc = " + lastCol + " " +
    // sf.toString());
    //   // }
    //         sf = null;
    //
    //      }

    /**
     * Return the fields that are contained in the Field Format Table
     *
     * @return ScreenFields object
     * @see org.tn5250j.ScreenFields
     */
    public ScreenFields getScreenFields() {
        return screenFields;
    }

    /**
     * Redraw the fields on the screen. Used for gui enhancement to redraw the
     * fields when toggling
     *
     */
    protected void drawFields() {
        ScreenField sf;
        int sizeFields = screenFields.getSize();

        for (int x = 0; x < sizeFields; x++) {
            sf = screenFields.getField(x);

            if (!sf.isBypassField()) {
                int pos = sf.startPos();
                int l = sf.length;
                boolean f = true;

                if (l >= lenScreen)
                    l = lenScreen - 1;

                if (l > 1) {
                    while (l-- > 0) {
                        if (guiInterface && f) {
                            planes.setUseGUI(pos, FIELD_LEFT);
                            f = false;
                        }
                        else {
                            planes.setUseGUI(pos, FIELD_MIDDLE);
                        }

                        if (guiInterface && l == 0) {
                            planes.setUseGUI(pos, FIELD_RIGHT);
                        }

                        setDirty(pos++);
                    }
                }
                else {
                    planes.setUseGUI(pos, FIELD_ONE);
                }
            }
        }

        //updateDirty();
    }

    /**
     * Draws the field on the screen. Used to redraw or change the attributes of
     * the field.
     *
     * @param sf -
     *            Field to be redrawn
     * @see org.tn5250j.ScreenField.java
     */
    protected void drawField(ScreenField sf) {
        int pos = sf.startPos();
        int x = sf.length;

        while (x-- > 0) {
            setDirty(pos++);
        }

        updateDirty();
    }

    /**
     * Set the field to be displayed as highlighted.
     *
     * @param sf -
     *            Field to be highlighted
     */
    protected void setFieldHighlighted(ScreenField sf) {
        int pos = sf.startPos();
        int x = sf.length;
        int na = sf.getHighlightedAttr();

        while (x-- > 0) {
            planes.setScreenAttr(pos, na);
            setDirty(pos++);
        }

        fireScreenChanged();
    }

    /**
     * Draw the field as un higlighted. This is used to reset the field
     * presentation on the screen after the field is exited.
     *
     * @param sf -
     *            Field to be unhighlighted
     */
    protected void unsetFieldHighlighted(ScreenField sf) {
        int pos = sf.startPos();
        int x = sf.length;
        int na = sf.getAttr();

        while (x-- > 0) {
            planes.setScreenAttr(pos, na);
            setDirty(pos++);
        }

        fireScreenChanged();
    }

    protected void setChar(int cByte) {
        if (lastPos > 0) {
            lastAttr = planes.getCharAttr(lastPos - 1);
        }

        if (cByte > 0 && (char)cByte < ' ') {
            planes.setScreenCharAndAttr(lastPos, (char) 0x00, 33, false);
            setDirty(lastPos);
            advancePos();
        }
        else {
            planes.setScreenCharAndAttr(lastPos, (char) cByte, lastAttr, false);
            setDirty(lastPos);

            if (guiInterface && !isInField(lastPos, false)) {
                planes.setUseGUI(lastPos, NO_GUI);
            }

            advancePos();
        }
    }

    protected void setEndingAttr(int cByte) {
        int attr = lastAttr;
        setAttr(cByte);
        lastAttr = attr;
    }

    protected void setAttr(int cByte) {
        lastAttr = cByte;
        //      int sattr = screen[lastPos].getCharAttr();
        //         System.out.println("changing from " + sattr + " to attr " + lastAttr
        // +
        //                     " at " + (this.getRow(lastPos) + 1) + "," + (this.getCol(lastPos) +
        // 1));
        planes.setScreenCharAndAttr(lastPos, initChar, lastAttr, true);
        setDirty(lastPos);
        advancePos();
        int pos = lastPos;
        int times = 0;
        //      sattr = screen[lastPos].getCharAttr();
        //         System.out.println(" next position after change " + sattr + " last
        // attr " + lastAttr +
        //                     " at " + (this.getRow(lastPos) + 1) + "," + (this.getCol(lastPos) +
        // 1) +
        //                     " attr place " + screen[lastPos].isAttributePlace());

        while (planes.getCharAttr(lastPos) != lastAttr
                && !planes.isAttributePlace(lastPos)) {
            planes.setScreenAttr(lastPos, lastAttr);

            if (guiInterface && !isInField(lastPos, false)) {
                int g = planes.getWhichGUI(lastPos);

                if (g >= FIELD_LEFT && g <= FIELD_ONE)
                    planes.setUseGUI(lastPos, NO_GUI);
            }

            setDirty(lastPos);
            times++;
            advancePos();
        }

        // sanity check for right now
        //      if (times > 200)
        //         System.out.println(" setAttr = " + times + " start = " + (sr + 1) +
        // "," + (sc + 1));
        lastPos = pos;
    }

    protected void setScreenCharAndAttr(char right, int colorAttr, boolean isAttr) {
        planes.setScreenCharAndAttr(lastPos, right, colorAttr, isAttr);
        setDirty(lastPos);
        advancePos();
    }

    protected void setScreenCharAndAttr(char right, int colorAttr,
                                        int whichGui, boolean isAttr) {
        planes.setScreenCharAndAttr(lastPos, right, colorAttr, isAttr);
        planes.setUseGUI(lastPos, whichGui);
        setDirty(lastPos);
        advancePos();
    }

    /**
     * Draw or redraw the dirty parts of the screen and display them.
     *
     * Rectangle dirty holds the dirty area of the screen to be updated.
     *
     * If you want to change the screen in anyway you need to set the screen
     * attributes before calling this routine.
     */
    protected void updateDirty() {
        fireScreenChanged();
    }

    protected void setDirty(int pos) {
        int minr = Math.min(getRow(pos), getRow(dirtyScreen.x));
        int minc = Math.min(getCol(pos), getCol(dirtyScreen.x));
        int maxr = Math.max(getRow(pos), getRow(dirtyScreen.y));
        int maxc = Math.max(getCol(pos), getCol(dirtyScreen.y));
        int x1 = getPos(minr, minc);
        int x2 = getPos(maxr, maxc);
        dirtyScreen.setBounds(x1, x2, 0, 0);
    }

    private void resetDirty(int pos) {
        dirtyScreen.setBounds(pos, pos, 0, 0);
    }

    /**
     * Change the screen position by one column
     */
    protected void advancePos() {
        changePos(1);
    }

    /**
     * Change position of the screen by the increment of parameter passed.
     *
     * If the position change is under the minimum of the first screen position
     * then the position is moved to the last row and column of the screen.
     *
     * If the position change is over the last row and column of the screen then
     * cursor is moved to first position of the screen.
     *
     * @param i
     */
    protected void changePos(int i) {
        lastPos += i;

        while (lastPos < 0)          lastPos += lenScreen;

        while (lastPos >= lenScreen) lastPos -= lenScreen;
    }


    protected void goHome() {
        //  now we try to move to first input field according to
        //  14.6 WRITE TO DISPLAY Command
        //    ? If the WTD command is valid, after the command is processed,
        //          the cursor moves to one of three locations:
        //    - The location set by an insert cursor order (unless control
        //          character byte 1, bit 1 is equal to B'1'.)
        //    - The start of the first non-bypass input field defined in the
        //          format table
        //    - A default starting address of row 1 column 1.
        if (pendingInsert && homePos > 0) {
            setCursor(getRow(homePos), getCol(homePos));
            isInField();
        }
        else {
            if (!gotoField(1)) {
                homePos = getPos(1, 1);
                setCursor(1, 1);
                isInField(0, 0);
            }
            else {
                homePos = getPos(getCurrentRow(), getCurrentCol());
            }
        }
    }

    protected void setPendingInsert(boolean flag, int icX, int icY) {
        pendingInsert = flag;

        if (pendingInsert) {
            homePos = getPos(icX, icY);
        }

        if (!isStatusErrorCode()) {
            setCursor(icX, icY);
        }
    }

    protected void setPendingInsert(boolean flag) {
        if (homePos != -1)
            pendingInsert = flag;
    }

    /**
     * Set the error line number to that of number passed.
     *
     * @param line
     */
    protected void setErrorLine(int line) {
        planes.setErrorLine(line);
    }

    /**
     * Returns the current error line number
     *
     * @return current error line number
     */
    protected int getErrorLine() {
        return planes.getErrorLine();
    }

    /**
     * Saves off the current error line characters to be used later.
     *
     */
    protected void saveErrorLine() {
        planes.saveErrorLine();
    }

    /**
     * Restores the error line characters from the save buffer.
     *
     * @see #saveErrorLine()
     */
    protected void restoreErrorLine() {
        if (planes.isErrorLineSaved()) {
            planes.restoreErrorLine();
            fireScreenChanged(planes.getErrorLine() - 1, 0, planes.getErrorLine() - 1, numCols - 1);
        }
    }

    protected void setStatus(byte attr, byte value, String s) {
        // set the status area
        switch (attr) {
            case STATUS_SYSTEM:
                if (value == STATUS_VALUE_ON) {
                    oia.setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT, ScreenOIA.OIA_LEVEL_INPUT_INHIBITED, s);
                }
                else {
                    oia.setInputInhibited(ScreenOIA.INPUTINHIBITED_NOTINHIBITED, ScreenOIA.OIA_LEVEL_NOT_INHIBITED, s);
                }

                break;

            case STATUS_ERROR_CODE:
                if (value == STATUS_VALUE_ON) {
                    setPrehelpState(true, true, false);
                    oia.setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT,
                                          ScreenOIA.OIA_LEVEL_INPUT_ERROR, s);
                    sessionVT.signalBell();
                }
                else {
                    oia.setInputInhibited(ScreenOIA.INPUTINHIBITED_NOTINHIBITED,
                                          ScreenOIA.OIA_LEVEL_NOT_INHIBITED);
                    setPrehelpState(false, true, true);
                    homePos = saveHomePos;
                    saveHomePos = 0;
                    pendingInsert = false;
                }

                break;
        }
    }

    protected boolean isStatusErrorCode() {
        return oia.getLevel() == ScreenOIA.OIA_LEVEL_INPUT_ERROR;
    }

    /**
     * This routine clears the screen, resets row and column to 0, resets the
     * last attribute to 32, clears the fields, turns insert mode off,
     * clears/initializes the screen character array.
     */
    protected void clearAll() {
        lastAttr = 32;
        lastPos = 0;
        clearTable();
        clearScreen();
        planes.setScreenAttr(0, initAttr);
        oia.setInsertMode(false);
    }

    /**
     * Clear the fields table
     */
    protected void clearTable() {
        oia.setKeyBoardLocked(true);
        screenFields.clearFFT();
        planes.initalizeFieldPlanes();
        pendingInsert = false;
        homePos = -1;
    }

    /**
     * Clear the gui constructs
     *
     */
    protected void clearGuiStuff() {
        for (int x = 0; x < lenScreen; x++) {
            planes.setUseGUI(x, NO_GUI);
        }

        dirtyScreen.setBounds(0, lenScreen - 1, 0, 0);
    }

    /**
     * Clear the screen by setting the initial character and initial attribute
     * to all the positions on the screen
     */
    protected void clearScreen() {
        planes.initalizePlanes();
        dirtyScreen.setBounds(0, lenScreen - 1, 0, 0);
        oia.clearScreen();
    }

    protected void restoreScreen() {
        lastAttr = 32;
        dirtyScreen.setBounds(0, lenScreen - 1, 0, 0);
        updateDirty();
    }

    public void onFontSizeChanged(float size) {
        fireScreenChanged(0, 0, numRows - 1, numCols - 1);
    }

    /**
     * repaint part of the screen
     *
     */
    private void fireScreenChanged(int startRow, int startCol, int endRow, int endCol) {
        int [] vt320color = {0x0, // black
                             0x4, // blue
                             0x2, // green
                             0x6, // cyan
                             0x1, // red
                             0x5, // magenta/purple
                             0xb, // yellow
                             0x7, // light gray/white
                             0x8, // dark gray
                             0xc, // light blue
                             0xa, // light green
                             0xe, // light cyan
                             0x9, // light red
                             0xd, // light magenta/purple
                             0x3, // brown
                             0xf  // bright white
                            };

        for (int r = startRow; r <= endRow; r++) {
            for (int c = startCol; c <= endCol; c++) {
                int p = getPos(r, c);
                char ch = planes.getChar(p);
                char co = planes.getCharColor(p);
                char at = planes.getCharExtended(p);
                boolean ia = planes.isAttributePlace(p);

                if (ch < ' ') ch = ' ';

                int bg = vt320color[(co >> 8) & 0x0f] + 1;
                int fg = vt320color[co        & 0x0f] + 1;
                int ul = at & EXTENDED_5250_UNDERLINE;
                int nd = at & EXTENDED_5250_NON_DSP;
                int vt_attr = (fg << VDUBuffer.COLOR_FG_SHIFT) + (bg << VDUBuffer.COLOR_BG_SHIFT);

                if (ul > 0) vt_attr |= VDUBuffer.UNDERLINE;

                if (ia || (nd > 0)) vt_attr |= VDUBuffer.INVISIBLE;

                buffer.putChar(c, r, ch, vt_attr);
            }
        }

        buffer.redrawPassthru();
        dirtyScreen.setBounds(lenScreen, 0, 0, 0);
    }

    /**
     * repaint the dirty part of the screen
     *
     */

    private synchronized void fireScreenChanged() {
        if (dirtyScreen.x > dirtyScreen.y) return;
        fireScreenChanged(getRow(dirtyScreen.x), getCol(dirtyScreen.x),
                          getRow(dirtyScreen.y), getCol(dirtyScreen.y));
    }

    /**
     * update the cursor position
     *
     */

    private synchronized void fireCursorChanged() {
        int l = getRow(lastPos);
        int c = getCol(lastPos);
        buffer.setCursorPosition(c, l);
    }

    /**
     * update the screen size.
     */
    private void fireScreenSizeChanged() {
        buffer.setScreenSize(numCols, numRows, true);
    }

    /**
     * This method does a complete refresh of the screen.
     */
    public final void updateScreen() {
        repaintScreen();
        setCursorActive(false);
        setCursorActive(true);
    }

    /**
     * Utility method to share the repaint behaviour between setBounds() and
     * updateScreen.
     */
    public void repaintScreen() {
        setCursorOff();
        dirtyScreen.setBounds(0, lenScreen - 1, 0, 0);
        updateDirty();

        // restore statuses that were on the screen before resize
        if (oia.getLevel() == ScreenOIA.OIA_LEVEL_INPUT_ERROR) {
            oia.setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT,
                                  ScreenOIA.OIA_LEVEL_INPUT_ERROR);
        }

        if (oia.getLevel() == ScreenOIA.OIA_LEVEL_INPUT_INHIBITED) {
            oia.setInputInhibited(ScreenOIA.INPUTINHIBITED_SYSTEM_WAIT,
                                  ScreenOIA.OIA_LEVEL_INPUT_INHIBITED);
        }

        if (oia.isMessageWait())
            oia.setMessageLightOn();

        setCursorOn();
    }

    // ADDED BY BARRY - changed by Kenneth to use the character plane
    //  This should be replaced with the getPlane methods when they are implemented
    public char[] getCharacters() {
        return planes.screen;
    }

}