Merge branch 'subtree'
[fanfix.git] / src / jexer / TFileOpenBox.java
index 96085b8cb336c124a641d14602dd7d434668431b..a2cc0cf7e48f2e68efebfefec3c66d14775796c9 100644 (file)
@@ -1,29 +1,27 @@
 /*
  * Jexer - Java Text User Interface
  *
- * License: LGPLv3 or later
+ * The MIT License (MIT)
  *
- * This module is licensed under the GNU Lesser General Public License
- * Version 3.  Please see the file "COPYING" in this directory for more
- * information about the GNU Lesser General Public License Version 3.
+ * Copyright (C) 2019 Kevin Lamonte
  *
- *     Copyright (C) 2015  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:
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of
- * the License, or (at your option) any later version.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see
- * http://www.gnu.org/licenses/, or write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
+ * 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
@@ -32,19 +30,24 @@ package jexer;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
+import java.util.ResourceBundle;
 
+import jexer.backend.SwingTerminal;
 import jexer.bits.GraphicsChars;
 import jexer.event.TKeypressEvent;
+import jexer.ttree.TDirectoryTreeItem;
+import jexer.ttree.TTreeItem;
+import jexer.ttree.TTreeViewWidget;
 import static jexer.TKeypress.*;
 
 /**
  * TFileOpenBox is a system-modal dialog for selecting a file to open.  Call
  * it like:
  *
- * <p>
  * <pre>
  * {@code
- *     filename = application.fileOpenBox("/path/to/file.ext",
+ *     filename = fileOpenBox("/path/to/file.ext",
  *         TFileOpenBox.Type.OPEN);
  *     if (filename != null) {
  *         ... the user selected a file, go open it ...
@@ -53,34 +56,50 @@ import static jexer.TKeypress.*;
  * </pre>
  *
  */
-public final class TFileOpenBox extends TWindow {
+public class TFileOpenBox extends TWindow {
+
+    /**
+     * Translated strings.
+     */
+    private static final ResourceBundle i18n = ResourceBundle.getBundle(TFileOpenBox.class.getName());
+
+    // ------------------------------------------------------------------------
+    // Constants --------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * TFileOpenBox can be called for either Open or Save actions.
      */
     public enum Type {
+        /**
+         * Button will be labeled "Open".
+         */
         OPEN,
-        SAVE
+
+        /**
+         * Button will be labeled "Save".
+         */
+        SAVE,
+
+        /**
+         * Button will be labeled "Select".
+         */
+        SELECT
     }
 
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * String to return, or null if the user canceled.
      */
     private String filename = null;
 
-    /**
-     * Get the return string.
-     *
-     * @return the filename the user selected, or null if they canceled.
-     */
-    public String getFilename() {
-        return filename;
-    }
-
     /**
      * The left-side tree view pane.
      */
-    private TTreeView treeView;
+    private TTreeViewWidget treeView;
 
     /**
      * The data behind treeView.
@@ -103,28 +122,26 @@ public final class TFileOpenBox extends TWindow {
     private TButton openButton;
 
     /**
-     * See if there is a valid filename to return.  If the filename is a
-     * directory, then
+     * The type of box this is (OPEN, SAVE, or SELECT).
+     */
+    private Type type = Type.OPEN;
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Public constructor.  The file open box will be centered on screen.
      *
-     * @param newFilename the filename to check and return
+     * @param application the TApplication that manages this window
+     * @param path path of selected file
+     * @param type one of the Type constants
+     * @throws IOException of a java.io operation throws
      */
-    private void checkFilename(final String newFilename) throws IOException {
-        File newFile = new File(newFilename);
-        if (newFile.exists()) {
-            if (newFile.isFile()) {
-                filename = newFilename;
-                getApplication().closeWindow(this);
-                return;
-            }
-            if (newFile.isDirectory()) {
-                treeViewRoot = new TDirectoryTreeItem(treeView, newFilename,
-                    true);
-                treeView.setTreeRoot(treeViewRoot, true);
-                treeView.reflow();
-                openButton.setEnabled(false);
-                directoryList.setPath(newFilename);
-            }
-        }
+    public TFileOpenBox(final TApplication application, final String path,
+        final Type type) throws IOException {
+
+        this(application, path, type, null);
     }
 
     /**
@@ -133,9 +150,11 @@ public final class TFileOpenBox extends TWindow {
      * @param application the TApplication that manages this window
      * @param path path of selected file
      * @param type one of the Type constants
+     * @param filters a list of strings that files must match to be displayed
+     * @throws IOException of a java.io operation throws
      */
     public TFileOpenBox(final TApplication application, final String path,
-        final Type type) throws IOException {
+        final Type type, final List<String> filters) throws IOException {
 
         // Register with the TApplication
         super(application, "", 0, 0, 76, 22, MODAL);
@@ -148,24 +167,35 @@ public final class TFileOpenBox extends TWindow {
                     try {
                         checkFilename(entryField.getText());
                     } catch (IOException e) {
-                        e.printStackTrace();
+                        // If the backend is Swing, we can emit the stack
+                        // trace to stderr.  Otherwise, just squash it.
+                        if (getScreen() instanceof SwingTerminal) {
+                            e.printStackTrace();
+                        }
                     }
                 }
             }, null);
         entryField.onKeypress(new TKeypressEvent(kbEnd));
 
         // Add directory treeView
-        treeView = addTreeView(1, 3, 30, getHeight() - 6,
+        treeView = addTreeViewWidget(1, 3, 30, getHeight() - 6,
             new TAction() {
                 public void DO() {
                     TTreeItem item = treeView.getSelected();
                     File selectedDir = ((TDirectoryTreeItem) item).getFile();
                     try {
                         directoryList.setPath(selectedDir.getCanonicalPath());
-                        openButton.setEnabled(false);
-                        activate(directoryList);
+                        entryField.setText(selectedDir.getCanonicalPath());
+                        if (type == Type.OPEN) {
+                            openButton.setEnabled(false);
+                        }
+                        activate(treeView);
                     } catch (IOException e) {
-                        e.printStackTrace();
+                        // If the backend is Swing, we can emit the stack
+                        // trace to stderr.  Otherwise, just squash it.
+                        if (getScreen() instanceof SwingTerminal) {
+                            e.printStackTrace();
+                        }
                     }
                 }
             }
@@ -182,26 +212,53 @@ public final class TFileOpenBox extends TWindow {
                         entryField.onKeypress(new TKeypressEvent(kbEnd));
                         openButton.setEnabled(true);
                         activate(entryField);
+                        checkFilename(entryField.getText());
                     } catch (IOException e) {
-                        e.printStackTrace();
+                        // If the backend is Swing, we can emit the stack
+                        // trace to stderr.  Otherwise, just squash it.
+                        if (getScreen() instanceof SwingTerminal) {
+                            e.printStackTrace();
+                        }
                     }
                 }
-            }
-        );
+            },
+            new TAction() {
+                public void DO() {
+                    try {
+                        File newPath = directoryList.getPath();
+                        entryField.setText(newPath.getCanonicalPath());
+                        entryField.onKeypress(new TKeypressEvent(kbEnd));
+                        openButton.setEnabled(true);
+                        activate(entryField);
+                    } catch (IOException e) {
+                        // If the backend is Swing, we can emit the stack
+                        // trace to stderr.  Otherwise, just squash it.
+                        if (getScreen() instanceof SwingTerminal) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            },
+            filters);
 
         String openLabel = "";
         switch (type) {
         case OPEN:
-            openLabel = " &Open ";
-            setTitle("Open File...");
+            openLabel = i18n.getString("openButton");
+            setTitle(i18n.getString("openTitle"));
             break;
         case SAVE:
-            openLabel = " &Save ";
-            setTitle("Save File...");
+            openLabel = i18n.getString("saveButton");
+            setTitle(i18n.getString("saveTitle"));
+            break;
+        case SELECT:
+            openLabel = i18n.getString("selectButton");
+            setTitle(i18n.getString("selectTitle"));
             break;
         default:
             throw new IllegalArgumentException("Invalid type: " + type);
         }
+        this.type = type;
 
         // Setup button actions
         openButton = addButton(openLabel, this.getWidth() - 12, 3,
@@ -210,14 +267,20 @@ public final class TFileOpenBox extends TWindow {
                     try {
                         checkFilename(entryField.getText());
                     } catch (IOException e) {
-                        e.printStackTrace();
+                        // If the backend is Swing, we can emit the stack
+                        // trace to stderr.  Otherwise, just squash it.
+                        if (getScreen() instanceof SwingTerminal) {
+                            e.printStackTrace();
+                        }
                     }
                 }
             }
         );
-        openButton.setEnabled(false);
+        if (type == Type.OPEN) {
+            openButton.setEnabled(false);
+        }
 
-        addButton("&Cancel", getWidth() - 12, 5,
+        addButton(i18n.getString("cancelButton"), getWidth() - 12, 5,
             new TAction() {
                 public void DO() {
                     filename = null;
@@ -237,15 +300,9 @@ public final class TFileOpenBox extends TWindow {
         getApplication().yield();
     }
 
-    /**
-     * Draw me on screen.
-     */
-    @Override
-    public void draw() {
-        super.draw();
-        getScreen().vLineXY(33, 4, getHeight() - 6, GraphicsChars.WINDOW_SIDE,
-            getBackground());
-    }
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Handle keystrokes.
@@ -262,8 +319,98 @@ public final class TFileOpenBox extends TWindow {
             return;
         }
 
+        if (treeView.isActive()) {
+            if ((keypress.equals(kbEnter))
+                || (keypress.equals(kbUp))
+                || (keypress.equals(kbDown))
+                || (keypress.equals(kbPgUp))
+                || (keypress.equals(kbPgDn))
+                || (keypress.equals(kbHome))
+                || (keypress.equals(kbEnd))
+            ) {
+                // Tree view will be changing, update the directory list.
+                super.onKeypress(keypress);
+
+                // This is the same action as treeView's enter.
+                TTreeItem item = treeView.getSelected();
+                File selectedDir = ((TDirectoryTreeItem) item).getFile();
+                try {
+                    directoryList.setPath(selectedDir.getCanonicalPath());
+                    if (type == Type.OPEN) {
+                        openButton.setEnabled(false);
+                    }
+                    activate(treeView);
+                } catch (IOException e) {
+                    // If the backend is Swing, we can emit the stack trace
+                    // to stderr.  Otherwise, just squash it.
+                    if (getScreen() instanceof SwingTerminal) {
+                        e.printStackTrace();
+                    }
+                }
+                return;
+            }
+        }
+
         // Pass to my parent
         super.onKeypress(keypress);
     }
 
+    // ------------------------------------------------------------------------
+    // TWidget ----------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Draw me on screen.
+     */
+    @Override
+    public void draw() {
+        super.draw();
+        vLineXY(33, 4, getHeight() - 6, GraphicsChars.WINDOW_SIDE,
+            getBackground());
+    }
+
+    // ------------------------------------------------------------------------
+    // TFileOpenBox -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get the return string.
+     *
+     * @return the filename the user selected, or null if they canceled.
+     */
+    public String getFilename() {
+        return filename;
+    }
+
+    /**
+     * See if there is a valid filename to return.  If the filename is a
+     * directory, then
+     *
+     * @param newFilename the filename to check and return
+     * @throws IOException of a java.io operation throws
+     */
+    private void checkFilename(final String newFilename) throws IOException {
+        File newFile = new File(newFilename);
+        if (newFile.exists()) {
+            if (newFile.isFile() || (type == Type.SELECT)) {
+                filename = newFilename;
+                getApplication().closeWindow(this);
+                return;
+            }
+            if (newFile.isDirectory()) {
+                treeViewRoot = new TDirectoryTreeItem(treeView,
+                    newFilename, true);
+                treeView.setTreeRoot(treeViewRoot, true);
+                if (type == Type.OPEN) {
+                    openButton.setEnabled(false);
+                }
+                directoryList.setPath(newFilename);
+            }
+        } else if (type != Type.OPEN) {
+            filename = newFilename;
+            getApplication().closeWindow(this);
+            return;
+        }
+    }
+
 }