stubs for TTableWidget
authorKevin Lamonte <kevin.lamonte@gmail.com>
Fri, 8 Mar 2019 14:08:21 +0000 (08:08 -0600)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Fri, 8 Mar 2019 14:08:21 +0000 (08:08 -0600)
20 files changed:
.gitignore
src/jexer/TApplication.java
src/jexer/TApplication.properties
src/jexer/TField.java
src/jexer/TKeypress.java
src/jexer/TLabel.java
src/jexer/TPasswordField.java
src/jexer/TTableWidget.java [new file with mode: 0644]
src/jexer/TTableWindow.java [new file with mode: 0644]
src/jexer/TTableWindow.properties [new file with mode: 0644]
src/jexer/bits/ColorTheme.java
src/jexer/demos/Demo2.java
src/jexer/demos/Demo2.properties
src/jexer/demos/DemoApplication.java
src/jexer/demos/DemoMainWindow.java
src/jexer/demos/DemoMainWindow.properties
src/jexer/menu/TMenu.java
src/jexer/menu/TMenu.properties
src/jexer/menu/TSubMenu.java
src/jexer/net/TelnetInputStream.java

index 7c3b76b823ecc9cf8c3726eb3bc407cabcfffdf5..9e8f22f5eda7f89dca21b941d75c292e79ae4323 100644 (file)
@@ -13,6 +13,9 @@ build/**
 # Generated docs
 docs/**
 
+# Maven artifacts
+target/**
+
 # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
 hs_err_pid*
 
index 264b8c8220e3fddb3b062e612a97ed74761f22c2..872266d7af9021513838db3a2f15aea94dd19981 100644 (file)
@@ -62,6 +62,7 @@ import jexer.backend.ECMA48Backend;
 import jexer.backend.TWindowBackend;
 import jexer.menu.TMenu;
 import jexer.menu.TMenuItem;
+import jexer.menu.TSubMenu;
 import static jexer.TCommand.*;
 import static jexer.TKeypress.*;
 
@@ -3094,6 +3095,50 @@ public class TApplication implements Runnable {
         return helpMenu;
     }
 
+    /**
+     * Convenience function to add a default "Table" menu.
+     *
+     * @return the new menu
+     */
+    public final TMenu addTableMenu() {
+        TMenu tableMenu = addMenu(i18n.getString("tableMenuTitle"));
+        TSubMenu borderMenu = tableMenu.addSubMenu(i18n.
+            getString("tableSubMenuBorders"));
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_NONE, false);
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_ALL, false);
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_RIGHT, false);
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_LEFT, false);
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_TOP, false);
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_BOTTOM, false);
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM, false);
+        borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_THICK_BOTTOM, false);
+        TSubMenu deleteMenu = tableMenu.addSubMenu(i18n.
+            getString("tableSubMenuDelete"));
+        deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_LEFT, false);
+        deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_UP, false);
+        deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_ROW, false);
+        deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_COLUMN, false);
+        TSubMenu insertMenu = tableMenu.addSubMenu(i18n.
+            getString("tableSubMenuInsert"));
+        insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_LEFT, false);
+        insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_RIGHT, false);
+        insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_ABOVE, false);
+        insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_BELOW, false);
+        TSubMenu columnMenu = tableMenu.addSubMenu(i18n.
+            getString("tableSubMenuColumn"));
+        columnMenu.addDefaultItem(TMenu.MID_TABLE_COLUMN_NARROW, false);
+        columnMenu.addDefaultItem(TMenu.MID_TABLE_COLUMN_WIDEN, false);
+        TSubMenu fileMenu = tableMenu.addSubMenu(i18n.
+            getString("tableSubMenuFile"));
+        fileMenu.addDefaultItem(TMenu.MID_TABLE_FILE_SAVE_CSV, false);
+        fileMenu.addDefaultItem(TMenu.MID_TABLE_FILE_SAVE_TEXT, false);
+
+        TStatusBar statusBar = tableMenu.newStatusBar(i18n.
+            getString("tableMenuStatus"));
+        statusBar.addShortcutKeypress(kbF1, cmHelp, i18n.getString("Help"));
+        return tableMenu;
+    }
+
     // ------------------------------------------------------------------------
     // TTimer management ------------------------------------------------------
     // ------------------------------------------------------------------------
index c4d183e2c09531da6a6d548861802d4517b8efdf..5c50457c72c4541c7d5122ad26cc37b2830fc645 100644 (file)
@@ -11,6 +11,14 @@ windowMenuStatus=Open, arrange, and list windows
 helpMenuTitle=&Help
 helpMenuStatus=Access online help
 
+tableMenuTitle=&Table
+tableSubMenuBorders=&Borders
+tableSubMenuDelete=&Delete
+tableSubMenuInsert=&Insert
+tableSubMenuColumn=&Column
+tableSubMenuFile=&File
+tableMenuStatus=Table manipulation commands
+
 exitDialogTitle=Confirmation
 exitDialogText=Exit application?
 
index b4330b4cb3c809210c4abf07b6592b0ac69bbd20..e8fd3defc8baeb8229f99f831760f9866d618730 100644 (file)
@@ -43,6 +43,11 @@ public class TField extends TWidget {
     // Variables --------------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Background character for unfilled-in text.
+     */
+    protected char backgroundChar = GraphicsChars.HATCH;
+
     /**
      * Field text.
      */
@@ -84,6 +89,16 @@ public class TField extends TWidget {
      */
     protected TAction updateAction;
 
+    /**
+     * The color to use when this field is active.
+     */
+    private String activeColorKey = "tfield.active";
+
+    /**
+     * The color to use when this field is not active.
+     */
+    private String inactiveColorKey = "tfield.inactive";
+
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -349,16 +364,16 @@ public class TField extends TWidget {
         CellAttributes fieldColor;
 
         if (isAbsoluteActive()) {
-            fieldColor = getTheme().getColor("tfield.active");
+            fieldColor = getTheme().getColor(activeColorKey);
         } else {
-            fieldColor = getTheme().getColor("tfield.inactive");
+            fieldColor = getTheme().getColor(inactiveColorKey);
         }
 
         int end = windowStart + getWidth();
         if (end > text.length()) {
             end = text.length();
         }
-        hLineXY(0, 0, getWidth(), GraphicsChars.HATCH, fieldColor);
+        hLineXY(0, 0, getWidth(), backgroundChar, fieldColor);
         putStringXY(0, 0, text.substring(windowStart, end), fieldColor);
 
         // Fix the cursor, it will be rendered by TApplication.drawAll().
@@ -369,6 +384,24 @@ public class TField extends TWidget {
     // TField -----------------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Get field background character.
+     *
+     * @return background character
+     */
+    public final char getBackgroundChar() {
+        return backgroundChar;
+    }
+
+    /**
+     * Set field background character.
+     *
+     * @param backgroundChar the background character
+     */
+    public void setBackgroundChar(final char backgroundChar) {
+        this.backgroundChar = backgroundChar;
+    }
+
     /**
      * Get field text.
      *
@@ -520,4 +553,25 @@ public class TField extends TWidget {
         normalizeWindowStart();
     }
 
+    /**
+     * Set the active color key.
+     *
+     * @param activeColorKey ColorTheme key color to use when this field is
+     * active
+     */
+    public void setActiveColorKey(final String activeColorKey) {
+        this.activeColorKey = activeColorKey;
+    }
+
+    /**
+     * Set the inactive color key.
+     *
+     * @param inactiveColorKey ColorTheme key color to use when this field is
+     * inactive
+     */
+    public void setInactiveColorKey(final String inactiveColorKey) {
+        this.inactiveColorKey = inactiveColorKey;
+    }
+
+
 }
index 9cb493282d82adcfab4c1d14e308b40d9d32cc03..7713e552529f49e0010bc81a627b523e75d097b1 100644 (file)
@@ -821,6 +821,13 @@ public class TKeypress {
             return "\u25C0\u2500\u2518";
         }
 
+        if (equals(kbShiftLeft)) {
+            return "Shift+\u2190";
+        }
+        if (equals(kbShiftRight)) {
+            return "Shift+\u2192";
+        }
+
         if (isFunctionKey) {
             switch (keyCode) {
             case F1:
index f33a8d75f51ac7f6045f3dbc6b8d97934157af2d..75623ffc11ad8ecee95e062d4d0b506a4f63afca 100644 (file)
@@ -222,6 +222,24 @@ public class TLabel extends TWidget {
         mnemonic = new MnemonicString(label);
     }
 
+    /**
+     * Get the label color.
+     *
+     * @param return the ColorTheme key color to use for foreground text
+     */
+    public String getColorKey() {
+        return colorKey;
+    }
+
+    /**
+     * Set the label color.
+     *
+     * @param colorKey ColorTheme key color to use for foreground text
+     */
+    public void setColorKey(final String colorKey) {
+        this.colorKey = colorKey;
+    }
+
     /**
      * Act as though the mnemonic shortcut was pressed.
      */
index ea88dfa45f96761e868fa65236895fb8f22dde98..1b78fc8b8db65256bf18b555004c078333da1b98 100644 (file)
@@ -115,7 +115,7 @@ public class TPasswordField extends TField {
             end = text.length();
         }
 
-        hLineXY(0, 0, getWidth(), GraphicsChars.HATCH, fieldColor);
+        hLineXY(0, 0, getWidth(), backgroundChar, fieldColor);
         if (showStars) {
             hLineXY(0, 0, getWidth() - 2, '*', fieldColor);
         } else {
diff --git a/src/jexer/TTableWidget.java b/src/jexer/TTableWidget.java
new file mode 100644 (file)
index 0000000..480e828
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2019 Kevin Lamonte
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import jexer.event.TKeypressEvent;
+import jexer.event.TMenuEvent;
+import jexer.menu.TMenu;
+import static jexer.TKeypress.*;
+
+/**
+ * TTableWidget is used to display and edit regular two-dimensional tables of
+ * cells.
+ *
+ * This class was inspired by a TTable implementation originally developed by
+ * David "Niki" ROULET [niki@nikiroo.be], made available under MIT at
+ * https://github.com/nikiroo/jexer/tree/ttable_pull.
+ */
+public class TTableWidget extends TWidget {
+
+    // ------------------------------------------------------------------------
+    // Constants --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Available borders for cells.
+     */
+    public enum Border {
+        /**
+         * No border.
+         */
+        NONE,
+
+        /**
+         * Single bar: \u2502 (vertical) and \u2500 (horizontal).
+         */
+        SINGLE,
+
+        /**
+         * Double bar: \u2551 (vertical) and \u2550 (horizontal).
+         */
+        DOUBLE,
+
+        /**
+         * Thick bar: \u258C (vertical, left half block) and \u2580
+         * (horizontal, upper block).
+         */
+        THICK,
+    }
+
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * The underlying data, organized as columns.
+     */
+    private ArrayList<Column> columns = new ArrayList<Column>();
+
+    /**
+     * The underlying data, organized as rows.
+     */
+    private ArrayList<Row> rows = new ArrayList<Row>();
+
+    /**
+     * The row in model corresponding to the top-left visible cell.
+     */
+    private int top = 0;
+
+    /**
+     * The column in model corresponding to the top-left visible cell.
+     */
+    private int left = 0;
+
+    /**
+     * The row in model corresponding to the currently selected cell.
+     */
+    private int selectedRow = 0;
+
+    /**
+     * The column in model corresponding to the currently selected cell.
+     */
+    private int selectedColumn = 0;
+
+    /**
+     * If true, highlight the entire row of the currently-selected cell.
+     */
+    private boolean highlightRow = false;
+
+    /**
+     * If true, highlight the entire column of the currently-selected cell.
+     */
+    private boolean highlightColumn = false;
+
+    /**
+     * Column represents a column of cells.
+     */
+    public class Column {
+
+        /**
+         * Width of column.
+         */
+        private int width = 8;
+
+        /**
+         * The cells of this column.
+         */
+        private ArrayList<Cell> cells = new ArrayList<Cell>();
+
+        /**
+         * Column label.
+         */
+        private String label = "";
+
+        /**
+         * The border for this column.
+         */
+        private Border border = Border.NONE;
+
+        /**
+         * Add an entry to this column.
+         *
+         * @param cell the cell to add
+         */
+        public void add(final Cell cell) {
+            cells.add(cell);
+        }
+
+        /**
+         * Get an entry from this column.
+         *
+         * @param row the entry index to get
+         * @return the cell at row
+         */
+        public Cell get(final int row) {
+            return cells.get(row);
+        }
+    }
+
+    /**
+     * Row represents a row of cells.
+     */
+    public class Row {
+
+        /**
+         * Height of row.
+         */
+        private int height = 1;
+
+        /**
+         * The cells of this row.
+         */
+        private ArrayList<Cell> cells = new ArrayList<Cell>();
+
+        /**
+         * Row label.
+         */
+        private String label = "";
+
+        /**
+         * The border for this row.
+         */
+        private Border border = Border.NONE;
+
+        /**
+         * Add an entry to this column.
+         *
+         * @param cell the cell to add
+         */
+        public void add(final Cell cell) {
+            cells.add(cell);
+        }
+
+        /**
+         * Get an entry from this row.
+         *
+         * @param column the entry index to get
+         * @return the cell at column
+         */
+        public Cell get(final int column) {
+            return cells.get(column);
+        }
+
+    }
+
+    /**
+     * Cell represents an editable cell in the table.  Normally, navigation
+     * to a cell only highlights it; pressing Enter or F2 will switch to
+     * editing mode.
+     */
+    public class Cell extends TWidget {
+
+        // --------------------------------------------------------------------
+        // Variables ----------------------------------------------------------
+        // --------------------------------------------------------------------
+
+        /**
+         * The field containing the cell's data.
+         */
+        private TField field;
+
+        /**
+         * The column of this cell.
+         */
+        private int column;
+
+        /**
+         * The row of this cell.
+         */
+        private int row;
+
+        /**
+         * If true, the cell is being edited.
+         */
+        private boolean isEditing = false;
+
+        /**
+         * Text of field before editing.
+         */
+        private String fieldText;
+
+        // --------------------------------------------------------------------
+        // Constructors -------------------------------------------------------
+        // --------------------------------------------------------------------
+
+        /**
+         * Public constructor.
+         *
+         * @param parent parent widget
+         * @param x column relative to parent
+         * @param y row relative to parent
+         * @param width width of widget
+         * @param height height of widget
+         * @param column column index of this cell
+         * @param row row index of this cell
+         */
+        public Cell(final TTableWidget parent, final int x, final int y,
+            final int width, final int height, final int column,
+            final int row) {
+
+            super(parent, x, y, width, height);
+            this.column = column;
+            this.row = row;
+
+            field = addField(0, 0, width - 1, false);
+            field.setEnabled(false);
+            field.setActiveColorKey("ttable.active");
+            field.setInactiveColorKey("ttable.inactive");
+            field.setBackgroundChar(' ');
+        }
+
+        // --------------------------------------------------------------------
+        // Event handlers -----------------------------------------------------
+        // --------------------------------------------------------------------
+
+        /**
+         * Handle keystrokes.
+         *
+         * @param keypress keystroke event
+         */
+        @Override
+        public void onKeypress(final TKeypressEvent keypress) {
+            // System.err.println("Cell onKeypress: " + keypress);
+
+            if (isEditing) {
+                if (keypress.equals(kbEsc)) {
+                    // ESC cancels the edit.
+                    field.setText(fieldText);
+                    isEditing = false;
+                    field.setEnabled(false);
+                    return;
+                }
+                if (keypress.equals(kbEnter)) {
+                    // Enter ends editing.
+                    fieldText = field.getText();
+                    isEditing = false;
+                    field.setEnabled(false);
+                    return;
+                }
+                // Pass down to field.
+                super.onKeypress(keypress);
+            }
+
+            if (keypress.equals(kbEnter) || keypress.equals(kbF2)) {
+                // Enter or F2 starts editing.
+                fieldText = field.getText();
+                isEditing = true;
+                field.setEnabled(true);
+                activate(field);
+                return;
+            }
+        }
+
+        // --------------------------------------------------------------------
+        // TWidget ------------------------------------------------------------
+        // --------------------------------------------------------------------
+
+        /**
+         * Draw this cell.
+         */
+        @Override
+        public void draw() {
+            TTableWidget table = (TTableWidget) getParent();
+
+            field.setActiveColorKey("ttable.active");
+            field.setInactiveColorKey("ttable.inactive");
+
+            if (isAbsoluteActive()) {
+                if (table.selectedColumn == column) {
+                    if ((table.selectedRow == row)
+                        || (table.highlightColumn == true)
+                    ) {
+                        field.setActiveColorKey("ttable.active");
+                        field.setInactiveColorKey("ttable.active");
+                    }
+                } else if (table.selectedRow == row) {
+                    if ((table.selectedColumn == column)
+                        || (table.highlightRow == true)
+                    ) {
+                        field.setActiveColorKey("ttable.active");
+                        field.setInactiveColorKey("ttable.active");
+                    }
+                }
+            }
+
+            super.draw();
+        }
+
+        // --------------------------------------------------------------------
+        // TTable.Cell --------------------------------------------------------
+        // --------------------------------------------------------------------
+
+        /**
+         * Get field text.
+         *
+         * @return field text
+         */
+        public final String getText() {
+            return field.getText();
+        }
+
+        /**
+         * Set field text.
+         *
+         * @param text the new field text
+         */
+        public void setText(final String text) {
+            field.setText(text);
+        }
+
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Public constructor.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of widget
+     * @param height height of widget
+     */
+    public TTableWidget(final TWidget parent, final int x, final int y,
+        final int width, final int height) {
+
+        super(parent, x, y, width, height);
+
+        // Initialize the starting row and column.
+        rows.add(new Row());
+        columns.add(new Column());
+
+        // Place a grid of cells that fit in this space.
+        int row = 0;
+        for (int i = 0; i < height; i += rows.get(0).height) {
+            int column = 0;
+            for (int j = 0; j < width; j += columns.get(0).width) {
+                Cell cell = new Cell(this, j, i, columns.get(0).width,
+                    rows.get(0).height, column, row);
+
+                cell.setText("" + row + " " + column);
+                rows.get(row).add(cell);
+                columns.get(column).add(cell);
+                if ((i == 0) && (j + columns.get(0).width < width)) {
+                    columns.add(new Column());
+                }
+                column++;
+            }
+            if (i + rows.get(0).height < height) {
+                rows.add(new Row());
+            }
+            row++;
+        }
+        activate(columns.get(selectedColumn).get(selectedRow));
+    }
+
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Handle keystrokes.
+     *
+     * @param keypress keystroke event
+     */
+    @Override
+    public void onKeypress(final TKeypressEvent keypress) {
+        if (keypress.equals(kbTab)
+            || keypress.equals(kbShiftTab)
+        ) {
+            // Squash tab and back-tab.  They don't make sense in the TTable
+            // grid context.
+            return;
+        }
+        
+        if (getSelectedCell().isEditing) {
+            super.onKeypress(keypress);
+            return;
+        }
+
+        if (keypress.equals(kbLeft)) {
+            if (selectedColumn > 0) {
+                selectedColumn--;
+            }
+            activate(columns.get(selectedColumn).get(selectedRow));
+        } else if (keypress.equals(kbRight)) {
+            if (selectedColumn < columns.size() - 1) {
+                selectedColumn++;
+            }
+            activate(columns.get(selectedColumn).get(selectedRow));
+        } else if (keypress.equals(kbUp)) {
+            if (selectedRow > 0) {
+                selectedRow--;
+            }
+            activate(columns.get(selectedColumn).get(selectedRow));
+        } else if (keypress.equals(kbDown)) {
+            if (selectedRow < rows.size() - 1) {
+                selectedRow++;
+            }
+            activate(columns.get(selectedColumn).get(selectedRow));
+        } else if (keypress.equals(kbHome)) {
+            selectedColumn = 0;
+            activate(columns.get(selectedColumn).get(selectedRow));
+        } else if (keypress.equals(kbEnd)) {
+            selectedColumn = columns.size() - 1;
+            activate(columns.get(selectedColumn).get(selectedRow));
+        } else if (keypress.equals(kbPgUp)) {
+            // TODO
+        } else if (keypress.equals(kbPgDn)) {
+            // TODO
+        } else if (keypress.equals(kbCtrlHome)) {
+            // TODO
+        } else if (keypress.equals(kbCtrlEnd)) {
+            // TODO
+        } else {
+            // Pass to the Cell.
+            super.onKeypress(keypress);
+        }
+    }
+
+    /**
+     * Handle posted menu events.
+     *
+     * @param menu menu event
+     */
+    @Override
+    public void onMenu(final TMenuEvent menu) {
+        switch (menu.getId()) {
+        case TMenu.MID_TABLE_BORDER_NONE:
+        case TMenu.MID_TABLE_BORDER_ALL:
+        case TMenu.MID_TABLE_BORDER_RIGHT:
+        case TMenu.MID_TABLE_BORDER_LEFT:
+        case TMenu.MID_TABLE_BORDER_TOP:
+        case TMenu.MID_TABLE_BORDER_BOTTOM:
+        case TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM:
+        case TMenu.MID_TABLE_BORDER_THICK_BOTTOM:
+        case TMenu.MID_TABLE_DELETE_LEFT:
+        case TMenu.MID_TABLE_DELETE_UP:
+        case TMenu.MID_TABLE_DELETE_ROW:
+        case TMenu.MID_TABLE_DELETE_COLUMN:
+        case TMenu.MID_TABLE_INSERT_LEFT:
+        case TMenu.MID_TABLE_INSERT_RIGHT:
+        case TMenu.MID_TABLE_INSERT_ABOVE:
+        case TMenu.MID_TABLE_INSERT_BELOW:
+            break;
+        case TMenu.MID_TABLE_COLUMN_NARROW:
+            columns.get(selectedColumn).width--;
+            for (Cell cell: getSelectedColumn().cells) {
+                cell.setWidth(columns.get(selectedColumn).width);
+                cell.field.setWidth(columns.get(selectedColumn).width - 1);
+            }
+            break;
+        case TMenu.MID_TABLE_COLUMN_WIDEN:
+            columns.get(selectedColumn).width++;
+            for (Cell cell: getSelectedColumn().cells) {
+                cell.setWidth(columns.get(selectedColumn).width);
+                cell.field.setWidth(columns.get(selectedColumn).width - 1);
+            }
+            break;
+        case TMenu.MID_TABLE_FILE_SAVE_CSV:
+        case TMenu.MID_TABLE_FILE_SAVE_TEXT:
+            break;
+        default:
+            super.onMenu(menu);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // TWidget ----------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    // ------------------------------------------------------------------------
+    // TTable -----------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get the currently-selected cell.
+     *
+     * @return the selected cell
+     */
+    public Cell getSelectedCell() {
+        assert (rows.get(selectedRow) != null);
+        assert (rows.get(selectedRow).get(selectedColumn) != null);
+        assert (columns.get(selectedColumn) != null);
+        assert (columns.get(selectedColumn).get(selectedRow) != null);
+        assert (rows.get(selectedRow).get(selectedColumn) ==
+            columns.get(selectedColumn).get(selectedRow));
+
+        return (columns.get(selectedColumn).get(selectedRow));
+    }
+
+    /**
+     * Get the currently-selected column.
+     *
+     * @return the selected column
+     */
+    public Column getSelectedColumn() {
+        assert (selectedColumn >= 0);
+        assert (columns.size() > selectedColumn);
+        assert (columns.get(selectedColumn) != null);
+        return columns.get(selectedColumn);
+    }
+
+    /**
+     * Get the currently-selected row.
+     *
+     * @return the selected row
+     */
+    public Row getSelectedRow() {
+        assert (selectedRow >= 0);
+        assert (rows.size() > selectedRow);
+        assert (rows.get(selectedRow) != null);
+        return rows.get(selectedRow);
+    }
+
+}
diff --git a/src/jexer/TTableWindow.java b/src/jexer/TTableWindow.java
new file mode 100644 (file)
index 0000000..0219cfc
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2019 Kevin Lamonte
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import java.util.ResourceBundle;
+
+import jexer.event.TCommandEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import jexer.event.TResizeEvent;
+import jexer.menu.TMenu;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
+
+/**
+ * TTableWindow is used to display and edit regular two-dimensional tables of
+ * cells.
+ */
+public class TTableWindow extends TScrollableWindow {
+
+    /**
+     * Translated strings.
+     */
+    private static final ResourceBundle i18n = ResourceBundle.getBundle(TTableWindow.class.getName());
+
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * The table widget.
+     */
+    private TTableWidget tableField;
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Public constructor sets window title.
+     *
+     * @param parent the main application
+     * @param title the window title
+     */
+    public TTableWindow(final TApplication parent, final String title) {
+
+        super(parent, title, 0, 0, parent.getScreen().getWidth() / 2,
+            parent.getScreen().getHeight() / 2 - 2, RESIZABLE | CENTERED);
+
+        tableField = new TTableWidget(this, 0, 0, getWidth() - 2, getHeight() - 2);
+        setupAfterTable();
+    }
+
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Called by application.switchWindow() when this window gets the
+     * focus, and also by application.addWindow().
+     */
+    public void onFocus() {
+        // Enable the table menu items.
+        getApplication().enableMenuItem(TMenu.MID_CUT);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_NONE);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_ALL);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_RIGHT);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_LEFT);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_TOP);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_BOTTOM);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_BORDER_THICK_BOTTOM);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_LEFT);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_UP);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_ROW);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_DELETE_COLUMN);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_LEFT);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_RIGHT);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_ABOVE);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_INSERT_BELOW);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_COLUMN_NARROW);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_COLUMN_WIDEN);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_FILE_SAVE_CSV);
+        getApplication().enableMenuItem(TMenu.MID_TABLE_FILE_SAVE_TEXT);
+    }
+
+    /**
+     * Called by application.switchWindow() when another window gets the
+     * focus.
+     */
+    public void onUnfocus() {
+        // Disable the table menu items.
+        getApplication().disableMenuItem(TMenu.MID_CUT);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_NONE);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_ALL);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_RIGHT);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_LEFT);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_TOP);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_BOTTOM);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_BORDER_THICK_BOTTOM);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_LEFT);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_UP);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_ROW);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_DELETE_COLUMN);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_LEFT);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_RIGHT);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_ABOVE);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_INSERT_BELOW);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_COLUMN_NARROW);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_COLUMN_WIDEN);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_FILE_SAVE_CSV);
+        getApplication().disableMenuItem(TMenu.MID_TABLE_FILE_SAVE_TEXT);
+    }
+
+    // ------------------------------------------------------------------------
+    // TWindow ----------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Draw the window.
+     */
+    @Override
+    public void draw() {
+        // Draw as normal.
+        super.draw();
+
+        // Add borders on rows and columns.
+        // TODO
+    }
+
+    /**
+     * Handle mouse press events.
+     *
+     * @param mouse mouse button press event
+     */
+    @Override
+    public void onMouseDown(final TMouseEvent mouse) {
+        // Use TWidget's code to pass the event to the children.
+        super.onMouseDown(mouse);
+
+        if (mouseOnTable(mouse)) {
+            // The table might have changed, update the scollbars.
+            // TODO
+            /*
+            setBottomValue(editField.getMaximumRowNumber());
+            setVerticalValue(editField.getVisibleRowNumber());
+            setRightValue(editField.getMaximumColumnNumber());
+            setHorizontalValue(editField.getEditingColumnNumber());
+            */
+        } else {
+            if (mouse.isMouseWheelUp() || mouse.isMouseWheelDown()) {
+                // Vertical scrollbar actions
+                // TODO
+                // editField.setVisibleRowNumber(getVerticalValue());
+            }
+        }
+    }
+
+    /**
+     * Handle mouse release events.
+     *
+     * @param mouse mouse button release event
+     */
+    @Override
+    public void onMouseUp(final TMouseEvent mouse) {
+        // Use TWidget's code to pass the event to the children.
+        super.onMouseUp(mouse);
+
+        if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
+            // Clicked on vertical scrollbar
+            // TODO
+            // editField.setVisibleRowNumber(getVerticalValue());
+        }
+
+        // TODO: horizontal scrolling
+    }
+
+    /**
+     * Method that subclasses can override to handle mouse movements.
+     *
+     * @param mouse mouse motion event
+     */
+    @Override
+    public void onMouseMotion(final TMouseEvent mouse) {
+        // Use TWidget's code to pass the event to the children.
+        super.onMouseMotion(mouse);
+
+        if (mouseOnTable(mouse) && mouse.isMouse1()) {
+            // The editor might have changed, update the scollbars.
+            // TODO
+            /*
+            setBottomValue(editField.getMaximumRowNumber());
+            setVerticalValue(editField.getVisibleRowNumber());
+            setRightValue(editField.getMaximumColumnNumber());
+            setHorizontalValue(editField.getEditingColumnNumber());
+            */
+        } else {
+            if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
+                // Clicked/dragged on vertical scrollbar
+                // TODO
+                // editField.setVisibleRowNumber(getVerticalValue());
+            }
+
+            // TODO: horizontal scrolling
+        }
+
+    }
+
+    /**
+     * Handle keystrokes.
+     *
+     * @param keypress keystroke event
+     */
+    @Override
+    public void onKeypress(final TKeypressEvent keypress) {
+        // Use TWidget's code to pass the event to the children.
+        super.onKeypress(keypress);
+
+        // The editor might have changed, update the scollbars.
+        // TODO
+        /*
+        setBottomValue(editField.getMaximumRowNumber());
+        setVerticalValue(editField.getVisibleRowNumber());
+        setRightValue(editField.getMaximumColumnNumber());
+        setHorizontalValue(editField.getEditingColumnNumber());
+         */
+    }
+
+    /**
+     * Handle window/screen resize events.
+     *
+     * @param event resize event
+     */
+    @Override
+    public void onResize(final TResizeEvent event) {
+        if (event.getType() == TResizeEvent.Type.WIDGET) {
+            // Resize the table
+            TResizeEvent tableSize = new TResizeEvent(TResizeEvent.Type.WIDGET,
+                event.getWidth() - 2, event.getHeight() - 2);
+            tableField.onResize(tableSize);
+
+            // Have TScrollableWindow handle the scrollbars
+            super.onResize(event);
+            return;
+        }
+
+        // Pass to children instead
+        for (TWidget widget: getChildren()) {
+            widget.onResize(event);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // TTableWindow -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Setup other fields after the table is created.
+     */
+    private void setupAfterTable() {
+        hScroller = new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
+        vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
+        setMinimumWindowWidth(25);
+        setMinimumWindowHeight(10);
+        setTopValue(1);
+        // setBottomValue(editField.getMaximumRowNumber());
+        setLeftValue(1);
+        // setRightValue(editField.getMaximumColumnNumber());
+
+        statusBar = newStatusBar(i18n.getString("statusBar"));
+        statusBar.addShortcutKeypress(kbF1, cmHelp,
+            i18n.getString("statusBarHelp"));
+        /*
+        statusBar.addShortcutKeypress(kbF2, cmSave,
+            i18n.getString("statusBarSave"));
+        statusBar.addShortcutKeypress(kbF3, cmOpen,
+            i18n.getString("statusBarOpen"));
+        */
+        statusBar.addShortcutKeypress(kbF10, cmMenu,
+            i18n.getString("statusBarMenu"));
+    }
+
+    /**
+     * Check if a mouse press/release/motion event coordinate is over the
+     * table.
+     *
+     * @param mouse a mouse-based event
+     * @return whether or not the mouse is on the table
+     */
+    private boolean mouseOnTable(final TMouseEvent mouse) {
+        if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
+            && (mouse.getAbsoluteX() <  getAbsoluteX() + getWidth() - 1)
+            && (mouse.getAbsoluteY() >= getAbsoluteY() + 1)
+            && (mouse.getAbsoluteY() <  getAbsoluteY() + getHeight() - 1)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/src/jexer/TTableWindow.properties b/src/jexer/TTableWindow.properties
new file mode 100644 (file)
index 0000000..a93ecdd
--- /dev/null
@@ -0,0 +1,3 @@
+statusBar=Editor
+statusBarHelp=Help
+statusBarMenu=Menu
index 9ac4986b8c46961d64f72560e799526bb0ee440c..5109dde72d4a6cc87d0c42c3047ecdbd62d979ba 100644 (file)
@@ -634,6 +634,18 @@ public class ColorTheme {
         color.setBold(false);
         colors.put("teditor", color);
 
+        // TTable
+        color = new CellAttributes();
+        color.setForeColor(Color.WHITE);
+        color.setBackColor(Color.BLUE);
+        color.setBold(false);
+        colors.put("ttable.inactive", color);
+        color = new CellAttributes();
+        color.setForeColor(Color.BLACK);
+        color.setBackColor(Color.CYAN);
+        color.setBold(false);
+        colors.put("ttable.active", color);
+
     }
 
     /**
index e6112f65da363cac3d2c515506d3ace66e92b6e5..2db03ceda44afad1db3a09bb0a216975374fcac0 100644 (file)
@@ -68,11 +68,20 @@ public class Demo2 {
                     format(i18n.getString("newConnection"), socket));
                 DemoApplication app = new DemoApplication(socket.getInputStream(),
                     socket.getOutputStream());
+                (new Thread(app)).start();
+                Thread.sleep(500);
+                System.out.println(MessageFormat.
+                    format(i18n.getString("terminal"),
+                    ((jexer.net.TelnetInputStream) socket.getInputStream()).
+                        getTerminalType()));
+                System.out.println(MessageFormat.
+                    format(i18n.getString("username"),
+                    ((jexer.net.TelnetInputStream) socket.getInputStream()).
+                        getUsername()));
                 System.out.println(MessageFormat.
                     format(i18n.getString("language"),
                     ((jexer.net.TelnetInputStream) socket.getInputStream()).
                         getLanguage()));
-                (new Thread(app)).start();
             }
         } catch (Exception e) {
             e.printStackTrace();
index 0d937476b7ab5bb75b967b25ea7b440a1dee102e..fa2b98fcddb8fda75177fc63cbc380e0afd43c9d 100644 (file)
@@ -1,3 +1,5 @@
 usageString=USAGE: java -cp jexer.jar jexer.demos.Demo2 port
 newConnection=New connection: {0}
+username=\ \ \ username: {0}
 language=\ \ \ language: {0}
+terminal=\ \ \ terminal: {0}
index 0e4f189c5760493622bb0ce9c17536de96fc3c13..85057037ec188e65184debba167945f5dc0a4d0a 100644 (file)
@@ -236,6 +236,7 @@ public class DemoApplication extends TApplication {
             item = swingMenu.addItem(3001, i18n.getString("smaller"));
         }
 
+        addTableMenu();
         addWindowMenu();
         addHelpMenu();
     }
index 1ae35f0b7dab058f42fdef8e7b6946241d00b6fe..5433d44924006ccdfad3b59e4ee96da9f41c9dc2 100644 (file)
@@ -42,6 +42,7 @@ import jexer.TEditColorThemeWindow;
 import jexer.TEditorWindow;
 import jexer.TLabel;
 import jexer.TProgressBar;
+import jexer.TTableWindow;
 import jexer.TTimer;
 import jexer.TWidget;
 import jexer.TWindow;
@@ -119,7 +120,7 @@ public class DemoMainWindow extends TWindow {
     private DemoMainWindow(final TApplication parent, final int flags) {
         // Construct a demo window.  X and Y don't matter because it will be
         // centered on screen.
-        super(parent, i18n.getString("windowTitle"), 0, 0, 64, 23, flags);
+        super(parent, i18n.getString("windowTitle"), 0, 0, 64, 25, flags);
 
         int row = 1;
 
@@ -191,6 +192,24 @@ public class DemoMainWindow extends TWindow {
         );
         row += 2;
 
+        addLabel(i18n.getString("ttableLabel"), 1, row);
+        addButton(i18n.getString("ttableButton1"), 35, row,
+            new TAction() {
+                public void DO() {
+                    // TODO
+                }
+            }
+        );
+        addButton(i18n.getString("ttableButton2"), 48, row,
+            new TAction() {
+                public void DO() {
+                    new TTableWindow(getApplication(),
+                        i18n.getString("tableDemo"));
+                }
+            }
+        );
+        row += 2;
+
         addLabel(i18n.getString("treeViewLabel"), 1, row);
         addButton(i18n.getString("treeViewButton"), 35, row,
             new TAction() {
index 4a512cb328c996a26a0ae283219b7d8e17dba054..31f276ca43af96cceda23faac6e1f9d6d80573dd 100644 (file)
@@ -17,6 +17,9 @@ radioButtonButton=&CheckBoxes
 editorLabel=Editor window
 editorButton1=&1 Widget
 editorButton2=&2 Window
+ttableLabel=TTable
+ttableButton1=&3 Widget
+ttableButton2=&4 Window
 textAreaLabel=Text areas
 textAreaButton=&Text
 treeViewLabel=Tree views
@@ -31,3 +34,5 @@ timerText=Timer: %d
 errorTitle=Error
 errorReadingFile=Error reading file: {0}
 errorOpeningFile=Error opening file dialog: {0}
+
+tableDemo=TTableWindow Demo
index 7c3121230a3539a8b7158ac7a3ff1ee4ecde927b..1bcc80055a63b70e79ac8f9f95da8cb9b05945b6 100644 (file)
@@ -101,6 +101,28 @@ public class TMenu extends TWindow {
     public static final int MID_HELP_ACTIVE_FILE        = 55;
     public static final int MID_ABOUT                   = 56;
 
+    // Table menu
+    public static final int MID_TABLE_BORDER_NONE               = 60;
+    public static final int MID_TABLE_BORDER_ALL                = 61;
+    public static final int MID_TABLE_BORDER_RIGHT              = 62;
+    public static final int MID_TABLE_BORDER_LEFT               = 63;
+    public static final int MID_TABLE_BORDER_TOP                = 64;
+    public static final int MID_TABLE_BORDER_BOTTOM             = 65;
+    public static final int MID_TABLE_BORDER_DOUBLE_BOTTOM      = 66;
+    public static final int MID_TABLE_BORDER_THICK_BOTTOM       = 67;
+    public static final int MID_TABLE_DELETE_LEFT               = 68;
+    public static final int MID_TABLE_DELETE_UP                 = 69;
+    public static final int MID_TABLE_DELETE_ROW                = 70;
+    public static final int MID_TABLE_DELETE_COLUMN             = 71;
+    public static final int MID_TABLE_INSERT_LEFT               = 72;
+    public static final int MID_TABLE_INSERT_RIGHT              = 73;
+    public static final int MID_TABLE_INSERT_ABOVE              = 74;
+    public static final int MID_TABLE_INSERT_BELOW              = 75;
+    public static final int MID_TABLE_COLUMN_NARROW             = 76;
+    public static final int MID_TABLE_COLUMN_WIDEN              = 77;
+    public static final int MID_TABLE_FILE_SAVE_CSV             = 78;
+    public static final int MID_TABLE_FILE_SAVE_TEXT            = 79;
+
     // ------------------------------------------------------------------------
     // Variables --------------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -627,6 +649,69 @@ public class TMenu extends TWindow {
             label = i18n.getString("menuHelpAbout");
             break;
 
+        case MID_TABLE_BORDER_NONE:
+            label = i18n.getString("menuTableBorderNone");
+            break;
+        case MID_TABLE_BORDER_ALL:
+            label = i18n.getString("menuTableBorderAll");
+            break;
+        case MID_TABLE_BORDER_RIGHT:
+            label = i18n.getString("menuTableBorderRight");
+            break;
+        case MID_TABLE_BORDER_LEFT:
+            label = i18n.getString("menuTableBorderLeft");
+            break;
+        case MID_TABLE_BORDER_TOP:
+            label = i18n.getString("menuTableBorderTop");
+            break;
+        case MID_TABLE_BORDER_BOTTOM:
+            label = i18n.getString("menuTableBorderBottom");
+            break;
+        case MID_TABLE_BORDER_DOUBLE_BOTTOM:
+            label = i18n.getString("menuTableBorderDoubleBottom");
+            break;
+        case MID_TABLE_BORDER_THICK_BOTTOM:
+            label = i18n.getString("menuTableBorderThickBottom");
+            break;
+        case MID_TABLE_DELETE_LEFT:
+            label = i18n.getString("menuTableDeleteLeft");
+            break;
+        case MID_TABLE_DELETE_UP:
+            label = i18n.getString("menuTableDeleteUp");
+            break;
+        case MID_TABLE_DELETE_ROW:
+            label = i18n.getString("menuTableDeleteRow");
+            break;
+        case MID_TABLE_DELETE_COLUMN:
+            label = i18n.getString("menuTableDeleteColumn");
+            break;
+        case MID_TABLE_INSERT_LEFT:
+            label = i18n.getString("menuTableInsertLeft");
+            break;
+        case MID_TABLE_INSERT_RIGHT:
+            label = i18n.getString("menuTableInsertRight");
+            break;
+        case MID_TABLE_INSERT_ABOVE:
+            label = i18n.getString("menuTableInsertAbove");
+            break;
+        case MID_TABLE_INSERT_BELOW:
+            label = i18n.getString("menuTableInsertBelow");
+            break;
+        case MID_TABLE_COLUMN_NARROW:
+            label = i18n.getString("menuTableColumnNarrow");
+            key = kbShiftLeft;
+            break;
+        case MID_TABLE_COLUMN_WIDEN:
+            label = i18n.getString("menuTableColumnWiden");
+            key = kbShiftRight;
+            break;
+        case MID_TABLE_FILE_SAVE_CSV:
+            label = i18n.getString("menuTableFileSaveCsv");
+            break;
+        case MID_TABLE_FILE_SAVE_TEXT:
+            label = i18n.getString("menuTableFileSaveText");
+            break;
+
         default:
             throw new IllegalArgumentException("Invalid menu ID: " + id);
         }
index 0ce4cde9963cb54d040d4992c6637b2936d860f1..c64da1876249bac2b42d18e5161f451488ca3d6f 100644 (file)
@@ -26,6 +26,27 @@ menuHelpHelp=&Help on help
 menuHelpActive=Active &file...
 menuHelpAbout=&About...
 
+menuTableBorderNone=&None
+menuTableBorderAll=&All
+menuTableBorderRight=&Right
+menuTableBorderLeft=&Left
+menuTableBorderTop=&Top
+menuTableBorderBottom=&Bottom
+menuTableBorderDoubleBottom=Bottom (&double)
+menuTableBorderThickBottom=Bottom (t&hick)
+menuTableDeleteLeft=Cell (Shift &Left)
+menuTableDeleteUp=Cell (Shift &Up)
+menuTableDeleteRow=Entire &Row
+menuTableDeleteColumn=Entire &Column
+menuTableInsertLeft=Column &Left
+menuTableInsertRight=Column &Right
+menuTableInsertAbove=Row &Above
+menuTableInsertBelow=Row &Below
+menuTableColumnNarrow=&Narrow
+menuTableColumnWiden=&Widen
+menuTableFileSaveCsv=Save As &CSV...
+menuTableFileSaveText=Save As &Text...
+
 menuRepaintDesktop=&Repaint desktop
 menuViewImage=&Open image...
 menuChangeFont=Change &font...
index 547711bcf5f4a39c3f261010f1be9ca0f960c174..e285c5ab6d78c485f67ed6e2597c4c17e31af189 100644 (file)
@@ -234,6 +234,18 @@ public class TSubMenu extends TMenuItem {
         return menu.addDefaultItem(id);
     }
 
+    /**
+     * Convenience function to add one of the default menu items.
+     *
+     * @param id menu item ID.  Must be between 0 (inclusive) and 1023
+     * (inclusive).
+     * @param enabled default state for enabled
+     * @return the new menu item
+     */
+    public TMenuItem addDefaultItem(final int id, final boolean enabled) {
+        return menu.addDefaultItem(id, enabled);
+    }
+
     /**
      * Convenience function to add a menu separator.
      */
index ac010e8f0ab3db06f9c5a038e0ae2464ec209216..3e74561a40c20e6ea9b166dea9de4d2e24e00f1c 100644 (file)
@@ -199,6 +199,15 @@ public class TelnetInputStream extends InputStream implements SessionInfo {
         this.language = language;
     }
 
+    /**
+     * Get the terminal type as reported by the telnet Terminal Type option.
+     *
+     * @return the terminal type
+     */
+    public String getTerminalType() {
+        return master.terminalType;
+    }
+
     /**
      * Text window width getter.
      *
@@ -311,6 +320,7 @@ public class TelnetInputStream extends InputStream implements SessionInfo {
 
         // If we got something, return it.
         if (rc > 0) {
+            readBufferEnd += rc;
             readBufferStart++;
             return readBuffer[readBufferStart - 1];
         }