misc fixes
authorKevin Lamonte <kevin.lamonte@gmail.com>
Thu, 22 Nov 2018 23:06:48 +0000 (17:06 -0600)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Thu, 22 Nov 2018 23:06:48 +0000 (17:06 -0600)
22 files changed:
src/jexer/TApplication.java
src/jexer/TApplication.properties
src/jexer/TButton.java
src/jexer/TComboBox.java
src/jexer/TEditColorThemeWindow.java
src/jexer/TExceptionDialog.java [new file with mode: 0644]
src/jexer/TExceptionDialog.properties [new file with mode: 0644]
src/jexer/TFileOpenBox.java
src/jexer/TFontChooserWindow.java [new file with mode: 0644]
src/jexer/TFontChooserWindow.properties [new file with mode: 0644]
src/jexer/TSpinner.java
src/jexer/TWidget.java
src/jexer/backend/Backend.java
src/jexer/backend/ECMA48Terminal.java
src/jexer/backend/GenericBackend.java
src/jexer/backend/MultiBackend.java
src/jexer/backend/SwingTerminal.java
src/jexer/backend/TWindowBackend.java
src/jexer/backend/TerminalReader.java
src/jexer/demos/DemoApplication.java
src/jexer/menu/TMenu.java
src/jexer/menu/TMenu.properties

index 96f91f2943696d38a7f6e4d72b8fab2036da3482..41981daf3c3ca0e3943223534de125e0c943d835 100644 (file)
  */
 package jexer;
 
+import java.io.File;
 import java.io.InputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.UnsupportedEncodingException;
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
@@ -812,11 +814,23 @@ public class TApplication implements Runnable {
             closeAllWindows();
             return true;
         }
+        if (menu.getId() == TMenu.MID_ABOUT) {
+            showAboutDialog();
+            return true;
+        }
         if (menu.getId() == TMenu.MID_REPAINT) {
             getScreen().clearPhysical();
             doRepaint();
             return true;
         }
+        if (menu.getId() == TMenu.MID_VIEW_IMAGE) {
+            openImage();
+            return true;
+        }
+        if (menu.getId() == TMenu.MID_CHANGE_FONT) {
+            new TFontChooserWindow(this);
+            return true;
+        }
         return false;
     }
 
@@ -969,7 +983,10 @@ public class TApplication implements Runnable {
                 mouseX = mouse.getX();
                 mouseY = mouse.getY();
             } else {
-                if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
+                if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN)
+                    && (!mouse.isMouseWheelUp())
+                    && (!mouse.isMouseWheelDown())
+                ) {
                     if ((mouse.getTime().getTime() - lastMouseUpTime) <
                         doubleClickTime) {
 
@@ -1151,7 +1168,10 @@ public class TApplication implements Runnable {
                 mouseX = mouse.getX();
                 mouseY = mouse.getY();
             } else {
-                if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
+                if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN)
+                    && (!mouse.isMouseWheelUp())
+                    && (!mouse.isMouseWheelDown())
+                ) {
                     if ((mouse.getTime().getTime() - lastMouseUpTime) <
                         doubleClickTime) {
 
@@ -1314,6 +1334,7 @@ public class TApplication implements Runnable {
         synchronized (invokeLaters) {
             invokeLaters.add(command);
         }
+        doRepaint();
     }
 
     /**
@@ -1452,6 +1473,41 @@ public class TApplication implements Runnable {
         this.focusFollowsMouse = focusFollowsMouse;
     }
 
+    /**
+     * Display the about dialog.
+     */
+    protected void showAboutDialog() {
+        String version = getClass().getPackage().getImplementationVersion();
+        if (version == null) {
+            // This is Java 9+, use a hardcoded string here.
+            version = "0.3.0";
+        }
+        messageBox(i18n.getString("aboutDialogTitle"),
+            MessageFormat.format(i18n.getString("aboutDialogText"), version),
+            TMessageBox.Type.OK);
+    }
+
+    /**
+     * Handle the Tool | Open image menu item.
+     */
+    private void openImage() {
+        try {
+            List<String> filters = new ArrayList<String>();
+            filters.add("^.*\\.[Jj][Pp][Gg]$");
+            filters.add("^.*\\.[Jj][Pp][Ee][Gg]$");
+            filters.add("^.*\\.[Pp][Nn][Gg]$");
+            filters.add("^.*\\.[Gg][Ii][Ff]$");
+            filters.add("^.*\\.[Bb][Mm][Pp]$");
+            String filename = fileOpenBox(".", TFileOpenBox.Type.OPEN, filters);
+            if (filename != null) {
+                new TImageWindow(this, new File(filename));
+            }
+        } catch (IOException e) {
+            // Show this exception to the user.
+            new TExceptionDialog(this, e);
+        }
+    }
+
     // ------------------------------------------------------------------------
     // Screen refresh loop ----------------------------------------------------
     // ------------------------------------------------------------------------
@@ -1959,29 +2015,33 @@ public class TApplication implements Runnable {
             int z = window.getZ();
             window.setZ(-1);
             window.onUnfocus();
+            windows.remove(window);
             Collections.sort(windows);
-            windows.remove(0);
             activeWindow = null;
+            int newZ = 0;
+            boolean foundNextWindow = false;
+
             for (TWindow w: windows) {
+                w.setZ(newZ);
+                newZ++;
 
                 // Do not activate a hidden window.
                 if (w.isHidden()) {
                     continue;
                 }
 
-                if (w.getZ() > z) {
-                    w.setZ(w.getZ() - 1);
-                    if (w.getZ() == 0) {
-                        w.setActive(true);
-                        w.onFocus();
-                        assert (activeWindow == null);
-                        activeWindow = w;
-                    } else {
-                        if (w.isActive()) {
-                            w.setActive(false);
-                            w.onUnfocus();
-                        }
-                    }
+                if (foundNextWindow == false) {
+                    foundNextWindow = true;
+                    w.setActive(true);
+                    w.onFocus();
+                    assert (activeWindow == null);
+                    activeWindow = w;
+                    continue;
+                }
+
+                if (w.isActive()) {
+                    w.setActive(false);
+                    w.onUnfocus();
                 }
             }
         }
@@ -2557,7 +2617,7 @@ public class TApplication implements Runnable {
 
         if (((focusFollowsMouse == true)
                 && (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION))
-            || (mouse.getType() == TMouseEvent.Type.MOUSE_UP)
+            || (mouse.getType() == TMouseEvent.Type.MOUSE_DOWN)
         ) {
             synchronized (windows) {
                 Collections.sort(windows);
@@ -2873,6 +2933,22 @@ public class TApplication implements Runnable {
         return menu;
     }
 
+    /**
+     * Convenience function to add a default tools (hamburger) menu.
+     *
+     * @return the new menu
+     */
+    public final TMenu addToolMenu() {
+        TMenu toolMenu = addMenu(i18n.getString("toolMenuTitle"));
+        toolMenu.addDefaultItem(TMenu.MID_REPAINT);
+        toolMenu.addDefaultItem(TMenu.MID_VIEW_IMAGE);
+        toolMenu.addDefaultItem(TMenu.MID_CHANGE_FONT);
+        TStatusBar toolStatusBar = toolMenu.newStatusBar(i18n.
+            getString("toolMenuStatus"));
+        toolStatusBar.addShortcutKeypress(kbF1, cmHelp, i18n.getString("Help"));
+        return toolMenu;
+    }
+
     /**
      * Convenience function to add a default "File" menu.
      *
index bf9bcbea01c544e9475c536fa4263ad8566e5535..c4d183e2c09531da6a6d548861802d4517b8efdf 100644 (file)
@@ -1,4 +1,7 @@
 Help=Help
+
+toolMenuTitle=&\u2261
+toolMenuStatus=Additional tools
 fileMenuTitle=&File
 fileMenuStatus=File-management commands (Open, Save, Print, etc.)
 editMenuTitle=&Edit
@@ -10,3 +13,6 @@ helpMenuStatus=Access online help
 
 exitDialogTitle=Confirmation
 exitDialogText=Exit application?
+
+aboutDialogTitle=About
+aboutDialogText=Jexer Version {0}
index 29f37432826cf7fbc3556df35462117cc557ced8..9c0e98be0a799765a013f278f1575f5562389a35 100644 (file)
@@ -68,6 +68,11 @@ public class TButton extends TWidget {
      */
     private TAction action;
 
+    /**
+     * The background color used for the button "shadow".
+     */
+    private CellAttributes shadowColor;
+
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -92,6 +97,11 @@ public class TButton extends TWidget {
         setY(y);
         setHeight(2);
         setWidth(mnemonic.getRawLabel().length() + 3);
+
+        shadowColor = new CellAttributes();
+        shadowColor.setTo(getWindow().getBackground());
+        shadowColor.setForeColor(Color.BLACK);
+        shadowColor.setBold(false);
     }
 
     /**
@@ -209,10 +219,6 @@ public class TButton extends TWidget {
     public void draw() {
         CellAttributes buttonColor;
         CellAttributes menuMnemonicColor;
-        CellAttributes shadowColor = new CellAttributes();
-        shadowColor.setTo(getWindow().getBackground());
-        shadowColor.setForeColor(Color.BLACK);
-        shadowColor.setBold(false);
 
         if (!isEnabled()) {
             buttonColor = getTheme().getColor("tbutton.disabled");
@@ -275,4 +281,16 @@ public class TButton extends TWidget {
         }
     }
 
+    /**
+     * Set the background color used for the button "shadow".
+     *
+     * @param color the new background color
+     */
+    public void setShadowColor(final CellAttributes color) {
+        shadowColor = new CellAttributes();
+        shadowColor.setTo(color);
+        shadowColor.setForeColor(Color.BLACK);
+        shadowColor.setBold(false);
+    }
+
 }
index bb223c3a12aa4b6b3340bdaf544858a6025fde25..5748c36fd2819524b572bb529b4a8340e6b3eded 100644 (file)
@@ -249,7 +249,7 @@ public class TComboBox extends TWidget {
             }
         }
 
-        if (isAbsoluteActive() && (limitToListValue == false)) {
+        if (isAbsoluteActive()) {
             comboBoxColor = getTheme().getColor("tcombobox.active");
         } else {
             comboBoxColor = getTheme().getColor("tcombobox.inactive");
index 55be8aa61b18e45c50deba26720104ff04926e6a..5d1c0412c81963a75eefaef10d407f0bbde1be84 100644 (file)
@@ -627,7 +627,7 @@ public class TEditColorThemeWindow extends TWindow {
     // ------------------------------------------------------------------------
 
     /**
-     * Public constructor.  The file open box will be centered on screen.
+     * Public constructor.  The window will be centered on screen.
      *
      * @param application the TApplication that manages this window
      */
diff --git a/src/jexer/TExceptionDialog.java b/src/jexer/TExceptionDialog.java
new file mode 100644 (file)
index 0000000..0e06756
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.ResourceBundle;
+
+import jexer.bits.CellAttributes;
+
+/**
+ * TExceptionDialog displays an exception and its stack trace to the user,
+ * and provides a means to save a troubleshooting report for support.
+ */
+public class TExceptionDialog extends TWindow {
+
+    /**
+     * Translated strings.
+     */
+    private static ResourceBundle i18n = ResourceBundle.getBundle(TExceptionDialog.class.getName());
+
+    // ------------------------------------------------------------------------
+    // Constants --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * The exception.  We will actually make it Throwable, for the unlikely
+     * event we catch an Error rather than an Exception.
+     */
+    private Throwable exception;
+
+    /**
+     * The exception's stack trace.
+     */
+    private TList stackTrace;
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Public constructor.
+     *
+     * @param application TApplication that manages this window
+     * @param exception the exception to display
+     */
+    public TExceptionDialog(final TApplication application,
+        final Throwable exception) {
+
+        super(application, i18n.getString("windowTitle"),
+            1, 1, 70, 20, CENTERED | MODAL);
+
+        this.exception = exception;
+
+        addLabel(i18n.getString("captionLine1"), 1, 1,
+            "twindow.background.modal");
+        addLabel(i18n.getString("captionLine2"), 1, 2,
+            "twindow.background.modal");
+        addLabel(i18n.getString("captionLine3"), 1, 3,
+            "twindow.background.modal");
+        addLabel(i18n.getString("captionLine4"), 1, 4,
+            "twindow.background.modal");
+
+        addLabel(MessageFormat.format(i18n.getString("exceptionString"),
+                exception.getClass().getName(), exception.getMessage()),
+            2, 6, "ttext", false);
+
+        ArrayList<String> stackTraceStrings = new ArrayList<String>();
+        StackTraceElement [] stack = exception.getStackTrace();
+        for (int i = 0; i < stack.length; i++) {
+            stackTraceStrings.add(stack[i].toString());
+        }
+        stackTrace = addList(stackTraceStrings, 2, 7, getWidth() - 6, 8);
+
+        // Buttons
+        addButton(i18n.getString("saveButton"), 19, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    saveToFile();
+                }
+            });
+
+        TButton closeButton = addButton(i18n.getString("closeButton"),
+            35, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    // Don't do anything, just close the window.
+                    TExceptionDialog.this.close();
+                }
+            });
+
+        // Save this for last: make the close button default action.
+        activate(closeButton);
+    }
+
+    // ------------------------------------------------------------------------
+    // TWindow ----------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Draw the exception message background.
+     */
+    @Override
+    public void draw() {
+        // Draw window and border.
+        super.draw();
+
+        CellAttributes boxColor = getTheme().getColor("ttext");
+        hLineXY(3, 7, getWidth() - 6, ' ', boxColor);
+    }
+
+    // ------------------------------------------------------------------------
+    // TExceptionDialog -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Save a troubleshooting report to file.  Note that we do NOT translate
+     * the strings within the error report.
+     */
+    private void saveToFile() {
+        // Prompt for filename.
+        PrintWriter writer = null;
+        try {
+            String filename = fileSaveBox(".");
+            if (filename == null) {
+                // User cancelled, bail out.
+                return;
+            }
+            writer = new PrintWriter(new FileWriter(filename));
+            writer.write("Date: " + new Date(System.currentTimeMillis())
+                + "\n");
+
+            // System properties
+            writer.write("System properties:\n");
+            writer.write("-----------------------------------\n");
+            System.getProperties().store(writer, null);
+            writer.write("-----------------------------------\n");
+            writer.write("\n");
+
+            // The exception we caught
+            writer.write("Caught exception:\n");
+            writer.write("-----------------------------------\n");
+            exception.printStackTrace(writer);
+            writer.write("-----------------------------------\n");
+            writer.write("\n");
+            // The exception's cause, if it was set
+            if (exception.getCause() != null) {
+                writer.write("Caught exception's cause:\n");
+                writer.write("-----------------------------------\n");
+                exception.getCause().printStackTrace(writer);
+                writer.write("-----------------------------------\n");
+            }
+            writer.write("\n");
+
+            // The UI stack trace
+            writer.write("UI stack trace:\n");
+            writer.write("-----------------------------------\n");
+            (new Throwable("UI Thread")).printStackTrace(writer);
+            writer.write("-----------------------------------\n");
+            writer.write("\n");
+            writer.close();
+        } catch (IOException e) {
+            messageBox(i18n.getString("errorDialogTitle"),
+                MessageFormat.format(i18n.
+                    getString("errorSavingFile"), e.getMessage()));
+        } finally {
+            if (writer != null) {
+                writer.close();
+                writer = null;
+            }
+        }
+    }
+}
diff --git a/src/jexer/TExceptionDialog.properties b/src/jexer/TExceptionDialog.properties
new file mode 100644 (file)
index 0000000..d07998c
--- /dev/null
@@ -0,0 +1,15 @@
+windowTitle=Java Exception Caught
+statusBar=Exception
+
+captionLine1=An error has occurred.  This may be due to a programming bug, but
+captionLine2=could also be a correctable or temporary issue.  The stack trace
+captionLine3=is reported below.  If you wish to submit a bug report, please
+captionLine4=use the Save button to create a more detailed error log.
+
+exceptionString={0}: {1}
+
+saveButton=&Save Report
+closeButton=\ \ \ &Close\ \ \ 
+
+errorDialogTitle=Error
+errorSavingFile=Error saving file: {0}
index ac23cfd13557e58100bee15a9a878ecb55157b6d..eaa38ceea99cea1c3a4ab5e9239e77963ab8ec44 100644 (file)
@@ -185,6 +185,7 @@ public class TFileOpenBox extends TWindow {
                     File selectedDir = ((TDirectoryTreeItem) item).getFile();
                     try {
                         directoryList.setPath(selectedDir.getCanonicalPath());
+                        entryField.setText(selectedDir.getCanonicalPath());
                         if (type == Type.OPEN) {
                             openButton.setEnabled(false);
                         }
@@ -391,7 +392,7 @@ public class TFileOpenBox extends TWindow {
     private void checkFilename(final String newFilename) throws IOException {
         File newFile = new File(newFilename);
         if (newFile.exists()) {
-            if (newFile.isFile()) {
+            if (newFile.isFile() || (type == Type.SELECT)) {
                 filename = newFilename;
                 getApplication().closeWindow(this);
                 return;
diff --git a/src/jexer/TFontChooserWindow.java b/src/jexer/TFontChooserWindow.java
new file mode 100644 (file)
index 0000000..5878b59
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ * 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.awt.Font;
+import java.awt.GraphicsEnvironment;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import jexer.backend.SwingTerminal;
+import jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+import jexer.event.TKeypressEvent;
+import static jexer.TKeypress.*;
+
+/**
+ * TFontChooserWindow provides an easy UI for users to alter the running
+ * font.
+ *
+ */
+public class TFontChooserWindow extends TWindow {
+
+    /**
+     * Translated strings.
+     */
+    private static final ResourceBundle i18n = ResourceBundle.getBundle(TFontChooserWindow.class.getName());
+
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * The Swing screen.
+     */
+    private SwingTerminal terminal = null;
+
+    /**
+     * The font name.
+     */
+    private TComboBox fontName;
+
+    /**
+     * The font size.
+     */
+    private TField fontSize;
+
+    /**
+     * The X text adjustment.
+     */
+    private TField textAdjustX;
+
+    /**
+     * The Y text adjustment.
+     */
+    private TField textAdjustY;
+
+    /**
+     * The height text adjustment.
+     */
+    private TField textAdjustHeight;
+
+    /**
+     * The width text adjustment.
+     */
+    private TField textAdjustWidth;
+
+    /**
+     * The original font size.
+     */
+    private int oldFontSize = 20;
+
+    /**
+     * The original font.
+     */
+    private Font oldFont = null;
+
+    /**
+     * The original text adjust X value.
+     */
+    private int oldTextAdjustX = 0;
+
+    /**
+     * The original text adjust Y value.
+     */
+    private int oldTextAdjustY = 0;
+
+    /**
+     * The original text adjust height value.
+     */
+    private int oldTextAdjustHeight = 0;
+
+    /**
+     * The original text adjust width value.
+     */
+    private int oldTextAdjustWidth = 0;
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Public constructor.  The window will be centered on screen.
+     *
+     * @param application the TApplication that manages this window
+     */
+    public TFontChooserWindow(final TApplication application) {
+
+        // Register with the TApplication
+        super(application, i18n.getString("windowTitle"), 0, 0, 60, 18, MODAL);
+
+        // Add shortcut text
+        newStatusBar(i18n.getString("statusBar"));
+
+        if (getScreen() instanceof SwingTerminal) {
+            terminal = (SwingTerminal) getScreen();
+        }
+
+        addLabel(i18n.getString("fontName"), 1, 1, "ttext", false);
+        addLabel(i18n.getString("fontSize"), 1, 2, "ttext", false);
+        addLabel(i18n.getString("textAdjustX"), 1, 4, "ttext", false);
+        addLabel(i18n.getString("textAdjustY"), 1, 5, "ttext", false);
+        addLabel(i18n.getString("textAdjustHeight"), 1, 6, "ttext", false);
+        addLabel(i18n.getString("textAdjustWidth"), 1, 7, "ttext", false);
+
+        int col = 18;
+        if (terminal == null) {
+            // Non-Swing case: we can't change anything
+            addLabel(i18n.getString("unavailable"), col, 1);
+            addLabel(i18n.getString("unavailable"), col, 2);
+            addLabel(i18n.getString("unavailable"), col, 4);
+            addLabel(i18n.getString("unavailable"), col, 5);
+            addLabel(i18n.getString("unavailable"), col, 6);
+            addLabel(i18n.getString("unavailable"), col, 7);
+        } else {
+            oldFont = terminal.getFont();
+            oldFontSize = terminal.getFontSize();
+            oldTextAdjustX = terminal.getTextAdjustX();
+            oldTextAdjustY = terminal.getTextAdjustY();
+            oldTextAdjustHeight = terminal.getTextAdjustHeight();
+            oldTextAdjustWidth = terminal.getTextAdjustWidth();
+
+            String [] fontNames = GraphicsEnvironment.
+                getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+            List<String> fonts = new ArrayList<String>();
+            fonts.add(0, i18n.getString("builtInTerminus"));
+            fonts.addAll(Arrays.asList(fontNames));
+            fontName = addComboBox(col, 1, 25, fonts, 0, 10,
+                new TAction() {
+                    public void DO() {
+                        if (fontName.getText().equals(i18n.
+                                getString("builtInTerminus"))) {
+
+                            terminal.setDefaultFont();
+                        } else {
+                            terminal.setFont(new Font(fontName.getText(),
+                                    Font.PLAIN, terminal.getFontSize()));
+                            fontSize.setText(Integer.toString(
+                                terminal.getFontSize()));
+                            textAdjustX.setText(Integer.toString(
+                                terminal.getTextAdjustX()));
+                            textAdjustY.setText(Integer.toString(
+                                terminal.getTextAdjustY()));
+                            textAdjustHeight.setText(Integer.toString(
+                                terminal.getTextAdjustHeight()));
+                            textAdjustWidth.setText(Integer.toString(
+                                terminal.getTextAdjustWidth()));
+                        }
+                    }
+                }
+            );
+
+            // Font size
+            fontSize = addField(col, 2, 3, true,
+                Integer.toString(terminal.getFontSize()),
+                new TAction() {
+                    public void DO() {
+                        int currentSize = terminal.getFontSize();
+                        int newSize = currentSize;
+                        try {
+                            newSize = Integer.parseInt(fontSize.getText());
+                        } catch (NumberFormatException e) {
+                            fontSize.setText(Integer.toString(currentSize));
+                        }
+                        if (newSize != currentSize) {
+                            terminal.setFontSize(newSize);
+                            textAdjustX.setText(Integer.toString(
+                                terminal.getTextAdjustX()));
+                            textAdjustY.setText(Integer.toString(
+                                terminal.getTextAdjustY()));
+                            textAdjustHeight.setText(Integer.toString(
+                                terminal.getTextAdjustHeight()));
+                            textAdjustWidth.setText(Integer.toString(
+                                terminal.getTextAdjustWidth()));
+                        }
+                    }
+                },
+                null);
+
+            addSpinner(col + 3, 2,
+                new TAction() {
+                    public void DO() {
+                        int currentSize = terminal.getFontSize();
+                        int newSize = currentSize;
+                        try {
+                            newSize = Integer.parseInt(fontSize.getText());
+                            newSize++;
+                        } catch (NumberFormatException e) {
+                            fontSize.setText(Integer.toString(currentSize));
+                        }
+                        fontSize.setText(Integer.toString(newSize));
+                        if (newSize != currentSize) {
+                            terminal.setFontSize(newSize);
+                            textAdjustX.setText(Integer.toString(
+                                terminal.getTextAdjustX()));
+                            textAdjustY.setText(Integer.toString(
+                                terminal.getTextAdjustY()));
+                            textAdjustHeight.setText(Integer.toString(
+                                terminal.getTextAdjustHeight()));
+                            textAdjustWidth.setText(Integer.toString(
+                                terminal.getTextAdjustWidth()));
+                        }
+                    }
+                },
+                new TAction() {
+                    public void DO() {
+                        int currentSize = terminal.getFontSize();
+                        int newSize = currentSize;
+                        try {
+                            newSize = Integer.parseInt(fontSize.getText());
+                            newSize--;
+                        } catch (NumberFormatException e) {
+                            fontSize.setText(Integer.toString(currentSize));
+                        }
+                        fontSize.setText(Integer.toString(newSize));
+                        if (newSize != currentSize) {
+                            terminal.setFontSize(newSize);
+                            textAdjustX.setText(Integer.toString(
+                                terminal.getTextAdjustX()));
+                            textAdjustY.setText(Integer.toString(
+                                terminal.getTextAdjustY()));
+                            textAdjustHeight.setText(Integer.toString(
+                                terminal.getTextAdjustHeight()));
+                            textAdjustWidth.setText(Integer.toString(
+                                terminal.getTextAdjustWidth()));
+                        }
+                    }
+                }
+            );
+
+            // textAdjustX
+            textAdjustX = addField(col, 4, 3, true,
+                Integer.toString(terminal.getTextAdjustX()),
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustX();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustX.getText());
+                        } catch (NumberFormatException e) {
+                            textAdjustX.setText(Integer.toString(currentAdjust));
+                        }
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustX(newAdjust);
+                        }
+                    }
+                },
+                null);
+
+            addSpinner(col + 3, 4,
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustX();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustX.getText());
+                            newAdjust++;
+                        } catch (NumberFormatException e) {
+                            textAdjustX.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustX.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustX(newAdjust);
+                        }
+                    }
+                },
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustX();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustX.getText());
+                            newAdjust--;
+                        } catch (NumberFormatException e) {
+                            textAdjustX.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustX.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustX(newAdjust);
+                        }
+                    }
+                }
+            );
+
+            // textAdjustY
+            textAdjustY = addField(col, 5, 3, true,
+                Integer.toString(terminal.getTextAdjustY()),
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustY();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustY.getText());
+                        } catch (NumberFormatException e) {
+                            textAdjustY.setText(Integer.toString(currentAdjust));
+                        }
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustY(newAdjust);
+                        }
+                    }
+                },
+                null);
+
+            addSpinner(col + 3, 5,
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustY();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustY.getText());
+                            newAdjust++;
+                        } catch (NumberFormatException e) {
+                            textAdjustY.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustY.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustY(newAdjust);
+                        }
+                    }
+                },
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustY();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustY.getText());
+                            newAdjust--;
+                        } catch (NumberFormatException e) {
+                            textAdjustY.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustY.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustY(newAdjust);
+                        }
+                    }
+                }
+            );
+
+            // textAdjustHeight
+            textAdjustHeight = addField(col, 6, 3, true,
+                Integer.toString(terminal.getTextAdjustHeight()),
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustHeight();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustHeight.getText());
+                        } catch (NumberFormatException e) {
+                            textAdjustHeight.setText(Integer.toString(currentAdjust));
+                        }
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustHeight(newAdjust);
+                        }
+                    }
+                },
+                null);
+
+            addSpinner(col + 3, 6,
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustHeight();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustHeight.getText());
+                            newAdjust++;
+                        } catch (NumberFormatException e) {
+                            textAdjustHeight.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustHeight.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustHeight(newAdjust);
+                        }
+                    }
+                },
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustHeight();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustHeight.getText());
+                            newAdjust--;
+                        } catch (NumberFormatException e) {
+                            textAdjustHeight.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustHeight.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustHeight(newAdjust);
+                        }
+                    }
+                }
+            );
+
+            // textAdjustWidth
+            textAdjustWidth = addField(col, 7, 3, true,
+                Integer.toString(terminal.getTextAdjustWidth()),
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustWidth();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustWidth.getText());
+                        } catch (NumberFormatException e) {
+                            textAdjustWidth.setText(Integer.toString(currentAdjust));
+                        }
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustWidth(newAdjust);
+                        }
+                    }
+                },
+                null);
+
+            addSpinner(col + 3, 7,
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustWidth();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustWidth.getText());
+                            newAdjust++;
+                        } catch (NumberFormatException e) {
+                            textAdjustWidth.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustWidth.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustWidth(newAdjust);
+                        }
+                    }
+                },
+                new TAction() {
+                    public void DO() {
+                        int currentAdjust = terminal.getTextAdjustWidth();
+                        int newAdjust = currentAdjust;
+                        try {
+                            newAdjust = Integer.parseInt(textAdjustWidth.getText());
+                            newAdjust--;
+                        } catch (NumberFormatException e) {
+                            textAdjustWidth.setText(Integer.toString(currentAdjust));
+                        }
+                        textAdjustWidth.setText(Integer.toString(newAdjust));
+                        if (newAdjust != currentAdjust) {
+                            terminal.setTextAdjustWidth(newAdjust);
+                        }
+                    }
+                }
+            );
+
+        }
+
+        addButton(i18n.getString("okButton"), 18, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    // Close window.
+                    TFontChooserWindow.this.close();
+                }
+            });
+
+        TButton cancelButton = addButton(i18n.getString("cancelButton"),
+            30, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    // Restore old values, then close the window.
+                    if (terminal != null) {
+                        terminal.setFont(oldFont);
+                        terminal.setFontSize(oldFontSize);
+                        terminal.setTextAdjustX(oldTextAdjustX);
+                        terminal.setTextAdjustY(oldTextAdjustY);
+                        terminal.setTextAdjustHeight(oldTextAdjustHeight);
+                        terminal.setTextAdjustWidth(oldTextAdjustWidth);
+                    }
+                    TFontChooserWindow.this.close();
+                }
+            });
+
+        // Save this for last: make the cancel button default action.
+        activate(cancelButton);
+
+    }
+
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Handle keystrokes.
+     *
+     * @param keypress keystroke event
+     */
+    @Override
+    public void onKeypress(final TKeypressEvent keypress) {
+        // Escape - behave like cancel
+        if (keypress.equals(kbEsc)) {
+            // Restore old values, then close the window.
+            if (terminal != null) {
+                terminal.setFont(oldFont);
+                terminal.setFontSize(oldFontSize);
+            }
+            getApplication().closeWindow(this);
+            return;
+        }
+
+        // Pass to my parent
+        super.onKeypress(keypress);
+    }
+
+    // ------------------------------------------------------------------------
+    // TWindow ----------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Draw me on screen.
+     */
+    @Override
+    public void draw() {
+        super.draw();
+
+        int left = 34;
+        CellAttributes color = getTheme().getColor("ttext");
+        drawBox(left, 6, left + 24, 14, color, color, 3, false);
+        putStringXY(left + 2, 6, i18n.getString("sample"), color);
+        for (int i = 7; i < 13; i++) {
+            hLineXY(left + 1, i, 22, GraphicsChars.HATCH, color);
+        }
+
+    }
+
+    // ------------------------------------------------------------------------
+    // TFontChooserWindow -----------------------------------------------------
+    // ------------------------------------------------------------------------
+
+}
diff --git a/src/jexer/TFontChooserWindow.properties b/src/jexer/TFontChooserWindow.properties
new file mode 100644 (file)
index 0000000..de30c1a
--- /dev/null
@@ -0,0 +1,15 @@
+windowTitle=Font
+okButton=\ \ &OK\ \ 
+cancelButton=&Cancel
+statusBar=Select Font Options
+
+fontName=Font name:
+fontSize=Font size:
+textAdjustX=X adjust:
+textAdjustY=Y adjust:
+textAdjustHeight=Height adjust:
+textAdjustWidth=Width adjust:
+
+unavailable=Unavailable
+builtInTerminus=Built-In Terminus
+sample=\ Sample Window\ 
index 881e1a7d8cddf08ee6fc448aae7a56d6acb93da0..ba45f6a64794b2d638a08f8a6fe3b1cf6d0036c9 100644 (file)
@@ -89,7 +89,7 @@ public class TSpinner extends TWidget {
      */
     private boolean mouseOnUpArrow(final TMouseEvent mouse) {
         if ((mouse.getY() == 0)
-            && (mouse.getX() == getWidth() - 1)
+            && (mouse.getX() == getWidth() - 2)
         ) {
             return true;
         }
@@ -104,7 +104,7 @@ public class TSpinner extends TWidget {
      */
     private boolean mouseOnDownArrow(final TMouseEvent mouse) {
         if ((mouse.getY() == 0)
-            && (mouse.getX() == getWidth() - 2)
+            && (mouse.getX() == getWidth() - 1)
         ) {
             return true;
         }
index 4a4ba2ce18190f361ba1830d4983b66a62565ec3..e8cd1a0e7e68a79f83d6de41f685a39aad379b81 100644 (file)
@@ -1098,12 +1098,19 @@ public abstract class TWidget implements Comparable<TWidget> {
             return;
         }
 
-        if (child != activeChild) {
-            if (activeChild != null) {
-                activeChild.active = false;
+        if (children.size() == 1) {
+            if (children.get(0).enabled == true) {
+                child.active = true;
+                activeChild = child;
+            }
+        } else {
+            if (child != activeChild) {
+                if (activeChild != null) {
+                    activeChild.active = false;
+                }
+                child.active = true;
+                activeChild = child;
             }
-            child.active = true;
-            activeChild = child;
         }
     }
 
@@ -1114,6 +1121,14 @@ public abstract class TWidget implements Comparable<TWidget> {
      * isn't enabled, then the next enabled child will be activated.
      */
     public final void activate(final int tabOrder) {
+        if (children.size() == 1) {
+            if (children.get(0).enabled == true) {
+                children.get(0).active = true;
+                activeChild = children.get(0);
+            }
+            return;
+        }
+
         if (activeChild == null) {
             return;
         }
@@ -1144,11 +1159,25 @@ public abstract class TWidget implements Comparable<TWidget> {
      */
     public final void switchWidget(final boolean forward) {
 
-        // Only switch if there are multiple enabled widgets
-        if ((children.size() < 2) || (activeChild == null)) {
+        // No children: do nothing.
+        if (children.size() == 0) {
+            return;
+        }
+
+        // If there is only one child, make it active if it is enabled.
+        if (children.size() == 1) {
+            if (children.get(0).enabled == true) {
+                activeChild = children.get(0);
+                activeChild.active = true;
+            } else {
+                children.get(0).active = false;
+                activeChild = null;
+            }
             return;
         }
 
+        // Two or more children: go forward or backward to the next enabled
+        // child.
         int tabOrder = activeChild.tabOrder;
         do {
             if (forward) {
index 8bd1816cd55053b461c0187896db966ac5aa492c..eaed7e64966228e6386d21349c757ae80efaa5e2 100644 (file)
@@ -96,4 +96,9 @@ public interface Backend {
      */
     public void setListener(final Object listener);
 
+    /**
+     * Reload backend options from System properties.
+     */
+    public void reloadOptions();
+
 }
index 1dc3957d75b534deec2b318a9c2bd501db8d7f5f..2afa81a35500e2a99c17c6500c0b748a5f922926 100644 (file)
@@ -1085,23 +1085,7 @@ public class ECMA48Terminal extends LogicalScreen
         windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
             sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
 
-        // Permit RGB colors only if externally requested.
-        if (System.getProperty("jexer.ECMA48.rgbColor") != null) {
-            if (System.getProperty("jexer.ECMA48.rgbColor").equals("true")) {
-                doRgbColor = true;
-            } else {
-                doRgbColor = false;
-            }
-        }
-
-        // Pull the system properties for sixel output.
-        if (System.getProperty("jexer.ECMA48.sixel") != null) {
-            if (System.getProperty("jexer.ECMA48.sixel").equals("true")) {
-                sixel = true;
-            } else {
-                sixel = false;
-            }
-        }
+        reloadOptions();
 
         // Spin up the input reader
         eventQueue = new LinkedList<TInputEvent>();
@@ -1187,23 +1171,7 @@ public class ECMA48Terminal extends LogicalScreen
         windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
             sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
 
-        // Permit RGB colors only if externally requested
-        if (System.getProperty("jexer.ECMA48.rgbColor") != null) {
-            if (System.getProperty("jexer.ECMA48.rgbColor").equals("true")) {
-                doRgbColor = true;
-            } else {
-                doRgbColor = false;
-            }
-        }
-
-        // Pull the system properties for sixel output.
-        if (System.getProperty("jexer.ECMA48.sixel") != null) {
-            if (System.getProperty("jexer.ECMA48.sixel").equals("true")) {
-                sixel = true;
-            } else {
-                sixel = false;
-            }
-        }
+        reloadOptions();
 
         // Spin up the input reader
         eventQueue = new LinkedList<TInputEvent>();
@@ -1357,6 +1325,27 @@ public class ECMA48Terminal extends LogicalScreen
         this.listener = listener;
     }
 
+    /**
+     * Reload options from System properties.
+     */
+    public void reloadOptions() {
+        // Permit RGB colors only if externally requested.
+        if (System.getProperty("jexer.ECMA48.rgbColor",
+                "false").equals("true")
+        ) {
+            doRgbColor = true;
+        } else {
+            doRgbColor = false;
+        }
+
+        // Pull the system properties for sixel output.
+        if (System.getProperty("jexer.ECMA48.sixel", "true").equals("true")) {
+            sixel = true;
+        } else {
+            sixel = false;
+        }
+    }
+
     // ------------------------------------------------------------------------
     // Runnable ---------------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -2745,11 +2734,19 @@ public class ECMA48Terminal extends LogicalScreen
 
         StringBuilder sb = new StringBuilder();
 
-        assert (sixel == true);
         assert (cells != null);
         assert (cells.size() > 0);
         assert (cells.get(0).getImage() != null);
 
+        if (sixel == false) {
+            sb.append(normal());
+            sb.append(gotoXY(x, y));
+            for (int i = 0; i < cells.size(); i++) {
+                sb.append(' ');
+            }
+            return sb.toString();
+        }
+
         if (sixelCache == null) {
             sixelCache = new SixelCache(height * 10);
         }
index 908be1ef8c60400c5f3db36ef34bc62ef8b032b5..fa72956c8e27c6c247005816ff8f3489b505d070 100644 (file)
@@ -137,4 +137,11 @@ public abstract class GenericBackend implements Backend {
         terminal.setListener(listener);
     }
 
+    /**
+     * Reload backend options from System properties.
+     */
+    public void reloadOptions() {
+        terminal.reloadOptions();
+    }
+
 }
index 5e4d3ca11224a274bdcb33a6fd5d3c338bf36ef4..08591ed44b8b90fc651978581ab052d68ce0d63e 100644 (file)
@@ -162,6 +162,15 @@ public class MultiBackend implements Backend {
         }
     }
 
+    /**
+     * Reload backend options from System properties.
+     */
+    public void reloadOptions() {
+        for (Backend backend: backends) {
+            backend.reloadOptions();
+        }
+    }
+
     // ------------------------------------------------------------------------
     // MultiBackend -----------------------------------------------------------
     // ------------------------------------------------------------------------
index bab8f820df9ed168a8ff348235706ad6d317eb9b..6a3b203dbff2d5f4d8ad59ed992cf62b549ff75c 100644 (file)
@@ -164,11 +164,6 @@ public class SwingTerminal extends LogicalScreen
      */
     private Map<Cell, BufferedImage> glyphCache;
 
-    /**
-     * If true, we were successful getting Terminus.
-     */
-    private boolean gotTerminus = false;
-
     /**
      * If true, we were successful at getting the font dimensions.
      */
@@ -194,6 +189,16 @@ public class SwingTerminal extends LogicalScreen
      */
     private int textHeight = 1;
 
+    /**
+     * Width of a character cell in pixels, as reported by font.
+     */
+    private int fontTextWidth = 1;
+
+    /**
+     * Height of a character cell in pixels, as reported by font.
+     */
+    private int fontTextHeight = 1;
+
     /**
      * Descent of a character cell in pixels.
      */
@@ -209,6 +214,16 @@ public class SwingTerminal extends LogicalScreen
      */
     private int textAdjustX = 0;
 
+    /**
+     * System-dependent height adjustment for text in the character cell.
+     */
+    private int textAdjustHeight = 0;
+
+    /**
+     * System-dependent width adjustment for text in the character cell.
+     */
+    private int textAdjustWidth = 0;
+
     /**
      * Top pixel absolute location.
      */
@@ -302,26 +317,7 @@ public class SwingTerminal extends LogicalScreen
         this.fontSize = fontSize;
 
         setDOSColors();
-
-        // Figure out my cursor style.
-        String cursorStyleString = System.getProperty(
-            "jexer.Swing.cursorStyle", "underline").toLowerCase();
-        if (cursorStyleString.equals("underline")) {
-            cursorStyle = CursorStyle.UNDERLINE;
-        } else if (cursorStyleString.equals("outline")) {
-            cursorStyle = CursorStyle.OUTLINE;
-        } else if (cursorStyleString.equals("block")) {
-            cursorStyle = CursorStyle.BLOCK;
-        }
-
-        // Pull the system property for triple buffering.
-        if (System.getProperty("jexer.Swing.tripleBuffer") != null) {
-            if (System.getProperty("jexer.Swing.tripleBuffer").equals("true")) {
-                SwingComponent.tripleBuffer = true;
-            } else {
-                SwingComponent.tripleBuffer = false;
-            }
-        }
+        reloadOptions();
 
         try {
             SwingUtilities.invokeAndWait(new Runnable() {
@@ -388,7 +384,7 @@ public class SwingTerminal extends LogicalScreen
                     SwingTerminal.this.top = insets.top;
 
                     // Load the font so that we can set sessionInfo.
-                    getDefaultFont();
+                    setDefaultFont();
 
                     // Get the default cols x rows and set component size
                     // accordingly.
@@ -443,17 +439,7 @@ public class SwingTerminal extends LogicalScreen
         this.fontSize = fontSize;
 
         setDOSColors();
-
-        // Figure out my cursor style.
-        String cursorStyleString = System.getProperty(
-            "jexer.Swing.cursorStyle", "underline").toLowerCase();
-        if (cursorStyleString.equals("underline")) {
-            cursorStyle = CursorStyle.UNDERLINE;
-        } else if (cursorStyleString.equals("outline")) {
-            cursorStyle = CursorStyle.OUTLINE;
-        } else if (cursorStyleString.equals("block")) {
-            cursorStyle = CursorStyle.BLOCK;
-        }
+        reloadOptions();
 
         try {
             SwingUtilities.invokeAndWait(new Runnable() {
@@ -519,7 +505,7 @@ public class SwingTerminal extends LogicalScreen
                     SwingTerminal.this.top = insets.top;
 
                     // Load the font so that we can set sessionInfo.
-                    getDefaultFont();
+                    setDefaultFont();
 
                     // Get the default cols x rows and set component size
                     // accordingly.
@@ -643,6 +629,31 @@ public class SwingTerminal extends LogicalScreen
         this.listener = listener;
     }
 
+    /**
+     * Reload options from System properties.
+     */
+    public void reloadOptions() {
+        // Figure out my cursor style.
+        String cursorStyleString = System.getProperty(
+            "jexer.Swing.cursorStyle", "underline").toLowerCase();
+        if (cursorStyleString.equals("underline")) {
+            cursorStyle = CursorStyle.UNDERLINE;
+        } else if (cursorStyleString.equals("outline")) {
+            cursorStyle = CursorStyle.OUTLINE;
+        } else if (cursorStyleString.equals("block")) {
+            cursorStyle = CursorStyle.BLOCK;
+        }
+
+        // Pull the system property for triple buffering.
+        if (System.getProperty("jexer.Swing.tripleBuffer",
+                "true").equals("true")
+        ) {
+            SwingComponent.tripleBuffer = true;
+        } else {
+            SwingComponent.tripleBuffer = false;
+        }
+    }
+
     // ------------------------------------------------------------------------
     // SwingTerminal ----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -729,25 +740,35 @@ public class SwingTerminal extends LogicalScreen
      * @param font the new font
      */
     public void setFont(final Font font) {
-        this.font = font;
-        getFontDimensions();
-        swing.setFont(font);
-        glyphCacheBlink = new HashMap<Cell, BufferedImage>();
-        glyphCache = new HashMap<Cell, BufferedImage>();
-        resizeToScreen();
+        synchronized (this) {
+            this.font = font;
+            getFontDimensions();
+            swing.setFont(font);
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            resizeToScreen();
+        }
+    }
+
+    /**
+     * Get the font this screen was last set to.
+     *
+     * @return the font
+     */
+    public Font getFont() {
+        return font;
     }
 
     /**
      * Set the font to Terminus, the best all-around font for both CP437 and
      * ISO8859-1.
      */
-    public void getDefaultFont() {
+    public void setDefaultFont() {
         try {
             ClassLoader loader = Thread.currentThread().getContextClassLoader();
             InputStream in = loader.getResourceAsStream(FONTFILE);
             Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in);
             Font terminus = terminusRoot.deriveFont(Font.PLAIN, fontSize);
-            gotTerminus = true;
             font = terminus;
         } catch (java.awt.FontFormatException e) {
             e.printStackTrace();
@@ -760,6 +781,100 @@ public class SwingTerminal extends LogicalScreen
         setFont(font);
     }
 
+    /**
+     * Get the X text adjustment.
+     *
+     * @return X text adjustment
+     */
+    public int getTextAdjustX() {
+        return textAdjustX;
+    }
+
+    /**
+     * Set the X text adjustment.
+     *
+     * @param textAdjustX the X text adjustment
+     */
+    public void setTextAdjustX(final int textAdjustX) {
+        synchronized (this) {
+            this.textAdjustX = textAdjustX;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
+    /**
+     * Get the Y text adjustment.
+     *
+     * @return Y text adjustment
+     */
+    public int getTextAdjustY() {
+        return textAdjustY;
+    }
+
+    /**
+     * Set the Y text adjustment.
+     *
+     * @param textAdjustY the Y text adjustment
+     */
+    public void setTextAdjustY(final int textAdjustY) {
+        synchronized (this) {
+            this.textAdjustY = textAdjustY;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
+    /**
+     * Get the height text adjustment.
+     *
+     * @return height text adjustment
+     */
+    public int getTextAdjustHeight() {
+        return textAdjustHeight;
+    }
+
+    /**
+     * Set the height text adjustment.
+     *
+     * @param textAdjustHeight the height text adjustment
+     */
+    public void setTextAdjustHeight(final int textAdjustHeight) {
+        synchronized (this) {
+            this.textAdjustHeight = textAdjustHeight;
+            textHeight = fontTextHeight + textAdjustHeight;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
+    /**
+     * Get the width text adjustment.
+     *
+     * @return width text adjustment
+     */
+    public int getTextAdjustWidth() {
+        return textAdjustWidth;
+    }
+
+    /**
+     * Set the width text adjustment.
+     *
+     * @param textAdjustWidth the width text adjustment
+     */
+    public void setTextAdjustWidth(final int textAdjustWidth) {
+        synchronized (this) {
+            this.textAdjustWidth = textAdjustWidth;
+            textWidth = fontTextWidth + textAdjustWidth;
+            glyphCacheBlink = new HashMap<Cell, BufferedImage>();
+            glyphCache = new HashMap<Cell, BufferedImage>();
+            clearPhysical();
+        }
+    }
+
     /**
      * Convert a CellAttributes foreground color to an Swing Color.
      *
@@ -855,13 +970,11 @@ public class SwingTerminal extends LogicalScreen
     }
 
     /**
-     * Figure out what textAdjustX and textAdjustY should be, based on the
-     * location of a vertical bar (to find textAdjustY) and a horizontal bar
-     * (to find textAdjustX).
-     *
-     * @return true if textAdjustX and textAdjustY were guessed at correctly
+     * Figure out what textAdjustX, textAdjustY, textAdjustHeight, and
+     * textAdjustWidth should be, based on the location of a vertical bar and
+     * a horizontal bar.
      */
-    private boolean getFontAdjustments() {
+    private void getFontAdjustments() {
         BufferedImage image = null;
 
         // What SHOULD happen is that the topmost/leftmost white pixel is at
@@ -871,66 +984,73 @@ public class SwingTerminal extends LogicalScreen
         Graphics2D gr2 = null;
         int gr2x = 3;
         int gr2y = 3;
-        image = new BufferedImage(textWidth * 2, textHeight * 2,
+        image = new BufferedImage(fontTextWidth * 2, fontTextHeight * 2,
             BufferedImage.TYPE_INT_ARGB);
 
         gr2 = image.createGraphics();
         gr2.setFont(swing.getFont());
         gr2.setColor(java.awt.Color.BLACK);
-        gr2.fillRect(0, 0, textWidth * 2, textHeight * 2);
+        gr2.fillRect(0, 0, fontTextWidth * 2, fontTextHeight * 2);
         gr2.setColor(java.awt.Color.WHITE);
         char [] chars = new char[1];
+        chars[0] = jexer.bits.GraphicsChars.SINGLE_BAR;
+        gr2.drawChars(chars, 0, 1, gr2x, gr2y + fontTextHeight - maxDescent);
         chars[0] = jexer.bits.GraphicsChars.VERTICAL_BAR;
-        gr2.drawChars(chars, 0, 1, gr2x, gr2y + textHeight - maxDescent);
+        gr2.drawChars(chars, 0, 1, gr2x, gr2y + fontTextHeight - maxDescent);
         gr2.dispose();
 
-        for (int x = 0; x < textWidth; x++) {
-            for (int y = 0; y < textHeight; y++) {
+        int top = fontTextHeight * 2;
+        int bottom = -1;
+        int left = fontTextWidth * 2;
+        int right = -1;
+        textAdjustX = 0;
+        textAdjustY = 0;
+        textAdjustHeight = 0;
+        textAdjustWidth = 0;
 
-                /*
-                System.err.println("X: " + x + " Y: " + y + " " +
-                    image.getRGB(x, y));
-                 */
-
-                if ((image.getRGB(x, y) & 0xFFFFFF) != 0) {
-                    textAdjustY = (gr2y - y);
-
-                    // System.err.println("textAdjustY: " + textAdjustY);
-                    x = textWidth;
-                    break;
-                }
-            }
-        }
-
-        gr2 = image.createGraphics();
-        gr2.setFont(swing.getFont());
-        gr2.setColor(java.awt.Color.BLACK);
-        gr2.fillRect(0, 0, textWidth * 2, textHeight * 2);
-        gr2.setColor(java.awt.Color.WHITE);
-        chars[0] = jexer.bits.GraphicsChars.SINGLE_BAR;
-        gr2.drawChars(chars, 0, 1, gr2x, gr2y + textHeight - maxDescent);
-        gr2.dispose();
-
-        for (int x = 0; x < textWidth; x++) {
-            for (int y = 0; y < textHeight; y++) {
+        for (int x = 0; x < fontTextWidth * 2; x++) {
+            for (int y = 0; y < fontTextHeight * 2; y++) {
 
                 /*
-                System.err.println("X: " + x + " Y: " + y + " " +
+                System.err.println("X: " + x + " Y: " + y + " " +
                     image.getRGB(x, y));
-                 */
+                */
 
                 if ((image.getRGB(x, y) & 0xFFFFFF) != 0) {
-                    textAdjustX = (gr2x - x);
-
-                    // System.err.println("textAdjustX: " + textAdjustX);
-                    return true;
+                    // Pixel is present.
+                    if (y < top) {
+                        top = y;
+                    }
+                    if (y > bottom) {
+                        bottom = y;
+                    }
+                    if (x < left) {
+                        left = x;
+                    }
+                    if (x > right) {
+                        right = x;
+                    }
                 }
             }
         }
+        if (left < right) {
+            textAdjustX = (gr2x - left);
+            textAdjustWidth = fontTextWidth - (right - left + 1);
+        }
+        if (top < bottom) {
+            textAdjustY = (gr2y - top);
+            textAdjustHeight = fontTextHeight - (bottom - top + 1);
+        }
+        // System.err.println("top " + top + " bottom " + bottom);
+        // System.err.println("left " + left + " right " + right);
 
-        // Something weird happened, don't rely on this function.
-        // System.err.println("getFontAdjustments: false");
-        return false;
+        // Special case: do not believe fonts that claim to be wider than
+        // they are tall.
+        if (fontTextWidth >= fontTextHeight) {
+            textAdjustX = 0;
+            textAdjustWidth = 0;
+            fontTextWidth = fontTextHeight / 2;
+        }
     }
 
     /**
@@ -958,26 +1078,16 @@ public class SwingTerminal extends LogicalScreen
         maxDescent = fm.getMaxDescent();
         Rectangle2D bounds = fm.getMaxCharBounds(gr);
         int leading = fm.getLeading();
-        textWidth = (int)Math.round(bounds.getWidth());
-        // textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
+        fontTextWidth = (int)Math.round(bounds.getWidth());
+        // fontTextHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
 
         // This produces the same number, but works better for ugly
         // monospace.
-        textHeight = fm.getMaxAscent() + maxDescent - leading;
+        fontTextHeight = fm.getMaxAscent() + maxDescent - leading;
 
-        if (gotTerminus == true) {
-            textHeight++;
-        }
-
-        if (getFontAdjustments() == false) {
-            // We were unable to programmatically determine textAdjustX and
-            // textAdjustY, so try some guesses based on VM vendor.
-            String runtime = System.getProperty("java.runtime.name");
-            if ((runtime != null) && (runtime.contains("Java(TM)"))) {
-                textAdjustY = -1;
-                textAdjustX = 0;
-            }
-        }
+        getFontAdjustments();
+        textHeight = fontTextHeight + textAdjustHeight;
+        textWidth = fontTextWidth + textAdjustWidth;
 
         if (sessionInfo != null) {
             sessionInfo.setTextCellDimensions(textWidth, textHeight);
index 20b9d7d63e2dd3b33c91029d38a4be77353dcf79..0a042336dcda9bdecc092814d75e0454f1ac42c7 100644 (file)
@@ -427,6 +427,13 @@ public class TWindowBackend extends TWindow implements Backend {
         this.listener = listener;
     }
 
+    /**
+     * Reload backend options from System properties.
+     */
+    public void reloadOptions() {
+        // NOP
+    }
+
     // ------------------------------------------------------------------------
     // TWindowBackend ---------------------------------------------------------
     // ------------------------------------------------------------------------
index 8edadbf4c015ea104beded27c0134c80223ce0a3..32033e0b1943c12999621566f96e29f7465cff22 100644 (file)
@@ -66,4 +66,9 @@ public interface TerminalReader {
      */
     public void setListener(final Object listener);
 
+    /**
+     * Reload options from System properties.
+     */
+    public void reloadOptions();
+
 }
index 18d47f37e5a348002ecadd2df9e2755d7b920769..5eb89aee5a26942d7addb43e82e38008a281baba 100644 (file)
@@ -203,6 +203,7 @@ public class DemoApplication extends TApplication {
         new DemoMainWindow(this);
 
         // Add the menus
+        addToolMenu();
         addFileMenu();
         addEditMenu();
 
index bfda60247a2f81ffe20afcd8599a94a06181cfe7..dbbea82bd1a9509dcd2bdd6a4e576cec84d57176 100644 (file)
@@ -58,45 +58,48 @@ public class TMenu extends TWindow {
     // Reserved menu item IDs
     public static final int MID_UNUSED          = -1;
 
+    // Tools menu
+    public static final int MID_REPAINT         = 1;
+    public static final int MID_VIEW_IMAGE      = 2;
+    public static final int MID_CHANGE_FONT     = 3;
+
     // File menu
-    public static final int MID_EXIT            = 1;
+    public static final int MID_NEW             = 10;
+    public static final int MID_EXIT            = 11;
     public static final int MID_QUIT            = MID_EXIT;
-    public static final int MID_OPEN_FILE       = 2;
-    public static final int MID_SHELL           = 3;
+    public static final int MID_OPEN_FILE       = 12;
+    public static final int MID_SHELL           = 13;
 
     // Edit menu
-    public static final int MID_CUT             = 10;
-    public static final int MID_COPY            = 11;
-    public static final int MID_PASTE           = 12;
-    public static final int MID_CLEAR           = 13;
+    public static final int MID_CUT             = 20;
+    public static final int MID_COPY            = 21;
+    public static final int MID_PASTE           = 22;
+    public static final int MID_CLEAR           = 23;
 
     // Search menu
-    public static final int MID_FIND            = 20;
-    public static final int MID_REPLACE         = 21;
-    public static final int MID_SEARCH_AGAIN    = 22;
-    public static final int MID_GOTO_LINE       = 23;
+    public static final int MID_FIND            = 30;
+    public static final int MID_REPLACE         = 31;
+    public static final int MID_SEARCH_AGAIN    = 32;
+    public static final int MID_GOTO_LINE       = 33;
 
     // Window menu
-    public static final int MID_TILE            = 30;
-    public static final int MID_CASCADE         = 31;
-    public static final int MID_CLOSE_ALL       = 32;
-    public static final int MID_WINDOW_MOVE     = 33;
-    public static final int MID_WINDOW_ZOOM     = 34;
-    public static final int MID_WINDOW_NEXT     = 35;
-    public static final int MID_WINDOW_PREVIOUS = 36;
-    public static final int MID_WINDOW_CLOSE    = 37;
+    public static final int MID_TILE            = 40;
+    public static final int MID_CASCADE         = 41;
+    public static final int MID_CLOSE_ALL       = 42;
+    public static final int MID_WINDOW_MOVE     = 43;
+    public static final int MID_WINDOW_ZOOM     = 44;
+    public static final int MID_WINDOW_NEXT     = 45;
+    public static final int MID_WINDOW_PREVIOUS = 46;
+    public static final int MID_WINDOW_CLOSE    = 47;
 
     // Help menu
-    public static final int MID_HELP_CONTENTS           = 40;
-    public static final int MID_HELP_INDEX              = 41;
-    public static final int MID_HELP_SEARCH             = 42;
-    public static final int MID_HELP_PREVIOUS           = 43;
-    public static final int MID_HELP_HELP               = 44;
-    public static final int MID_HELP_ACTIVE_FILE        = 45;
-    public static final int MID_ABOUT                   = 46;
-
-    // Other
-    public static final int MID_REPAINT         = 50;
+    public static final int MID_HELP_CONTENTS           = 50;
+    public static final int MID_HELP_INDEX              = 51;
+    public static final int MID_HELP_SEARCH             = 52;
+    public static final int MID_HELP_PREVIOUS           = 53;
+    public static final int MID_HELP_HELP               = 54;
+    public static final int MID_HELP_ACTIVE_FILE        = 55;
+    public static final int MID_ABOUT                   = 56;
 
     // ------------------------------------------------------------------------
     // Variables --------------------------------------------------------------
@@ -508,6 +511,22 @@ public class TMenu extends TWindow {
 
         switch (id) {
 
+        case MID_REPAINT:
+            label = i18n.getString("menuRepaintDesktop");
+            break;
+
+        case MID_VIEW_IMAGE:
+            label = i18n.getString("menuViewImage");
+            break;
+
+        case MID_CHANGE_FONT:
+            label = i18n.getString("menuChangeFont");
+            break;
+
+        case MID_NEW:
+            label = i18n.getString("menuNew");
+            break;
+
         case MID_EXIT:
             label = i18n.getString("menuExit");
             key = kbAltX;
@@ -608,10 +627,6 @@ public class TMenu extends TWindow {
             label = i18n.getString("menuHelpAbout");
             break;
 
-        case MID_REPAINT:
-            label = i18n.getString("menuRepaintDesktop");
-            break;
-
         default:
             throw new IllegalArgumentException("Invalid menu ID: " + id);
         }
index d42157cb04a498d2d6c408a9657cf32342379bb4..0ce4cde9963cb54d040d4992c6637b2936d860f1 100644 (file)
@@ -1,3 +1,4 @@
+menuNew=&New
 menuExit=E&xit
 menuShell=O&S Shell
 menuOpen=&Open
@@ -26,3 +27,5 @@ menuHelpActive=Active &file...
 menuHelpAbout=&About...
 
 menuRepaintDesktop=&Repaint desktop
+menuViewImage=&Open image...
+menuChangeFont=Change &font...