Empyreus / lanterna

Automatically exported from code.google.com/p/lanterna
GNU Lesser General Public License v3.0
0 stars 0 forks source link

File Chooser Dialog #97

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
It would be handy to have a standard file chooser dialog. Below is an 
implementation from one of my projects.

--------------------------------------------------------------------------------
----------
package assessin.ui.lanterna;

import com.googlecode.lanterna.TerminalFacade;
import com.googlecode.lanterna.gui.*;
import com.googlecode.lanterna.gui.GUIScreen.Position;
import com.googlecode.lanterna.gui.Theme.Category;
import com.googlecode.lanterna.gui.Theme.Definition;
import com.googlecode.lanterna.gui.component.*;
import com.googlecode.lanterna.gui.component.Panel.Orientation;
import com.googlecode.lanterna.gui.dialog.DialogButtons;
import com.googlecode.lanterna.gui.dialog.DialogResult;
import com.googlecode.lanterna.gui.dialog.MessageBox;
import com.googlecode.lanterna.gui.layout.BorderLayout;
import com.googlecode.lanterna.gui.layout.HorisontalLayout;
import com.googlecode.lanterna.gui.layout.LinearLayout;
import com.googlecode.lanterna.input.Key;
import com.googlecode.lanterna.terminal.TerminalSize;

import java.io.File;

// TODO: Localization

/**
 * Created by dboitnot on 12/6/13.
 */
public class FileChooser extends Window {
    private static final int PATH_LABEL_WIDTH = 60;
    private static final TerminalSize LIST_SIZE = new TerminalSize(30, 10);

    // mode-specific behavior
    private static enum Mode {
        OPEN("Open", false) {
            @Override
            public boolean checkFile(File f, GUIScreen owner) {
                if (!f.exists()) {
                    showFileNotFoundMessage(owner);
                    return false;
                }
                if (!f.isFile()) {
                    MessageBox.showMessageBox(owner, "Invalid Selection", "The file path you entered is not a regular file.");
                    return false;
                }
                return true;
            }

            @Override
            public Interactable defaultFocusedComponent(FileChooser chooser) {
                return chooser.fileList;
            }
        },

        SAVE("Save", true) {
            @Override
            public boolean checkFile(File f, GUIScreen owner) {
                if (f.isDirectory()) {
                    MessageBox.showMessageBox(owner, "Invalid Selection", "The file path you entered is a directory.");
                    return false;
                }

                File parent = f.getParentFile();
                if (parent == null || !parent.exists() || !parent.isDirectory()) {
                    MessageBox.showMessageBox(owner, "Invalid Selection", "You cannot save files to this location.");
                    return false;
                }

                if (f.exists() && MessageBox.showMessageBox(owner, "Overwrite File?",
                        "Are you sure you want to overwrite this file:\n\n" + f.getName(), DialogButtons.YES_NO) != DialogResult.YES)
                {
                    return false;
                }

                return true;
            }

            @Override
            public Interactable defaultFocusedComponent(FileChooser chooser) {
                return chooser.selectionTextBox;
            }
        };

        private final String confirmButtonText;
        private final boolean allowNotExists;

        Mode(String confirmButtonText, boolean allowNotExists) {
            this.confirmButtonText = confirmButtonText;
            this.allowNotExists = allowNotExists;
        }

        // Mode-specific file check, return true if file is acceptable.
        public abstract boolean checkFile(File f, GUIScreen owner);

        public abstract Interactable defaultFocusedComponent(FileChooser chooser);
    }

    private final Label pathLabel;
    private final ActionListBox dirList;
    private final ActionListBox fileList;
    private final TextBox selectionTextBox;
    private final Button confirmButton;

    private final Mode mode;

    private File dir;
    private boolean showHidden = false;
    private File confirmedSelection = null;

    private FileChooser(String title, Mode mode) {
        super(title);

        this.mode = mode;

        // Put some vertical space between components
        Panel rootPanel = new Panel(Orientation.VERTICAL);
        ((LinearLayout) rootPanel.getLayoutManager()).setPadding(1);
        addComponent(rootPanel);

        // Directory list on the left, file list on the right
        Panel listPanel = new Panel(Panel.Orientation.HORISONTAL);
        dirList = styledListBox(listPanel);
        fileList = styledListBox(listPanel);
        rootPanel.addComponent(listPanel);

        // Path label shows current directory
        pathLabel = new Label("", PATH_LABEL_WIDTH);
        rootPanel.addComponent(pathLabel);

        // The selection text box needs to handle the Enter key differently.
        selectionTextBox = new TextBox("") {
            @Override
            public Result keyboardInteraction(Key key) {
                if (key.getKind() == Key.Kind.Enter) {
                    browseTo(getSelection());
                    return Result.EVENT_HANDLED;
                }

                return super.keyboardInteraction(key);
            }
        };
        rootPanel.addComponent(selectionTextBox, HorisontalLayout.GROWS_HORIZONTALLY);

        // Buttons
        Panel bottomPanel = new Panel(Orientation.HORISONTAL);
        bottomPanel.setLayoutManager(new BorderLayout());
        rootPanel.addComponent(bottomPanel, HorisontalLayout.GROWS_HORIZONTALLY);
        Panel buttonPanel = new Panel(Orientation.HORISONTAL);
        confirmButton = new Button(mode.confirmButtonText, new Action() {
            @Override
            public void doAction() {
                confirm();
            }
        });
        buttonPanel.addComponent(confirmButton);
        buttonPanel.addComponent(new Button("Cancel", new Action() {
            @Override
            public void doAction() {
                close();
            }
        }));
        bottomPanel.addComponent(buttonPanel, BorderLayout.RIGHT);
    }

    private ActionListBox styledListBox(Panel panel) {
        ActionListBox ret = new ActionListBox(LIST_SIZE) {
            @Override
            protected Definition getListItemThemeDefinition(Theme theme) {
                return theme.getDefinition(Category.TEXTBOX_FOCUSED);
            }

            @Override
            protected Definition getSelectedListItemThemeDefinition(Theme theme) {
                return theme.getDefinition(Category.BUTTON_INACTIVE);
            }
        };
        panel.addComponent(ret);
        return ret;
    }

    private void setDir(File d) {
        dir = d;

        String txt = d.getAbsolutePath() + File.separator;

        if (txt.length() > PATH_LABEL_WIDTH)
            txt = "..." + txt.substring(txt.length() - PATH_LABEL_WIDTH + 3);

        pathLabel.setText(txt);
        refreshLists();
    }

    private void setSelection(File f) {
        if (f == null) {
            selectionTextBox.setText("");
            return;
        }

        File parent = f.getParentFile();
        String txt;
        if (parent == null) {
            // This shouldn't happen.
            txt = f.getAbsolutePath();
        } else {
            setDir(parent);
            txt = f.getName();
        }

        selectionTextBox.setText(txt);
        setFocus(confirmButton);
    }

    private File getSelection() {
        String txt = selectionTextBox.getText();
        if (txt.length() < 1)
            return null;

        File f = new File(txt);
        if (!f.isAbsolute())
            f = new File(dir.getAbsolutePath() + File.separator + f.getPath());

        // Try expanding tilde
        if (!f.exists() && txt.startsWith("~")) {
            f = new File(System.getProperty("user.home") + File.separator + txt.substring(1));
        }

        return f;
    }

    private void browseTo(File f) {
        if (f == null)
            return;

        if (f.isDirectory()) {
            setDir(f);
            setSelection(null);
        } else if (!f.exists()) {
            File parent = f.getParentFile();
            if (parent != null && parent.isDirectory() && mode.allowNotExists) {
                setSelection(f);
            } else {
                showFileNotFoundMessage(getOwner());
            }
        } else {
            setSelection(f);
        }
    }

    private void refreshLists() {
        dirList.clearItems();
        fileList.clearItems();

        File parent = dir.getParentFile();
        if (parent != null)
            dirList.addAction("..", browseAction(parent));

        File[] files = dir.listFiles();
        if (files == null)
            return;

        for (File f: files) {
            if (f.isHidden() && !showHidden)
                continue;
            if (f.isDirectory()) {
                dirList.addAction(f.getName(), browseAction(f));
            } else {
                fileList.addAction(f.getName(), browseAction(f));
            }
        }
    }

    private void confirm() {
        File f = getSelection();
        if (f == null)
            return;
        if (!mode.checkFile(f, getOwner()))
            return;
        confirmedSelection = f;
        close();
    }

    private Action browseAction(final File f) {
        return new Action() {
            @Override
            public void doAction() {
                browseTo(f);
            }
        };
    }

    private static void showFileNotFoundMessage(GUIScreen owner) {
        MessageBox.showMessageBox(owner, "File Not Found", "The file path you entered could not be found.");
    }

    private static File chooseFile(GUIScreen owner, String title, Mode mode, File initialSelection) {
        FileChooser chooser = new FileChooser(title, mode);

        Interactable focus = mode.defaultFocusedComponent(chooser);
        if (initialSelection == null) {
            chooser.setDir(new File("").getAbsoluteFile());
        } else if (initialSelection.isDirectory()) {
            chooser.setDir(initialSelection);
        } else {
            chooser.setSelection(initialSelection.getAbsoluteFile());
            focus = chooser.confirmButton;
        }
        chooser.setFocus(focus);

        owner.showWindow(chooser, Position.CENTER);
        return chooser.confirmedSelection;
    }

    public static File chooseFileForOpen(GUIScreen screen, String title, File defaultFile) {
        return chooseFile(screen, title, Mode.OPEN, defaultFile);
    }

    public static File chooseFileForOpen(GUIScreen screen, File defaultFile) {
        return chooseFileForOpen(screen, "Open File", defaultFile);
    }

    public static File chooseFileForOpen(GUIScreen screen, String title) {
        return chooseFileForOpen(screen, title, null);
    }

    public static File chooseFileForOpen(GUIScreen screen) {
        return chooseFileForOpen(screen, (File)null);
    }

    public static File chooseFileForSave(GUIScreen screen, String title, File defaultFile) {
        return chooseFile(screen, title, Mode.SAVE, defaultFile);
    }

    public static File chooseFileForSave(GUIScreen screen, File defaultFile) {
        return chooseFileForSave(screen, "Save As", defaultFile);
    }

    public static File chooseFileForSave(GUIScreen screen, String title) {
        return chooseFileForSave(screen, title, null);
    }

    public static File chooseFileForSave(GUIScreen screen) {
        return chooseFileForSave(screen, (File) null);
    }

    public static void main(String[] args) {
        GUIScreen gui = TerminalFacade.createGUIScreen();
        gui.getScreen().startScreen();

        System.out.println("Chose: " + chooseFileForSave(gui));

        gui.getScreen().stopScreen();
    }
}
--------------------------------------------------------------------------------
----------

Original issue reported on code.google.com by dboit...@gmail.com on 17 Dec 2013 at 1:07

GoogleCodeExporter commented 9 years ago
Adding a simple file dialog to 2.1.x-branch, just need to adjust the colors a 
little bit.

Original comment by mab...@gmail.com on 8 Feb 2014 at 1:35