CLI search, step 1
[fanfix.git] / src / be / nikiroo / fanfix / reader / tui / TuiReaderApplication.java
index 6fa969b50da925f0ffe1994fa0f25dbc76faec8f..177c12ab867094cbd61292d88314662a569b0ecc 100644 (file)
@@ -4,13 +4,18 @@ import java.awt.Toolkit;
 import java.awt.datatransfer.DataFlavor;
 import java.io.IOException;
 import java.net.URL;
+import java.net.UnknownHostException;
 
 import jexer.TApplication;
 import jexer.TCommand;
 import jexer.TKeypress;
 import jexer.TMessageBox;
+import jexer.TMessageBox.Result;
+import jexer.TMessageBox.Type;
 import jexer.TStatusBar;
+import jexer.TWidget;
 import jexer.TWindow;
+import jexer.event.TCommandEvent;
 import jexer.event.TMenuEvent;
 import jexer.menu.TMenu;
 import be.nikiroo.fanfix.Instance;
@@ -19,6 +24,8 @@ import be.nikiroo.fanfix.data.Story;
 import be.nikiroo.fanfix.library.BasicLibrary;
 import be.nikiroo.fanfix.reader.BasicReader;
 import be.nikiroo.fanfix.reader.Reader;
+import be.nikiroo.fanfix.reader.tui.TuiReaderMainWindow.Mode;
+import be.nikiroo.fanfix.supported.SupportType;
 import be.nikiroo.utils.Progress;
 
 /**
@@ -34,25 +41,25 @@ class TuiReaderApplication extends TApplication implements Reader {
        public static final int MENU_IMPORT_URL = 1026;
        public static final int MENU_IMPORT_FILE = 1027;
        public static final int MENU_EXPORT = 1028;
-       public static final int MENU_LIBRARY = 1029;
-       public static final int MENU_EXIT = 1030;
+       public static final int MENU_DELETE = 1029;
+       public static final int MENU_LIBRARY = 1030;
+       public static final int MENU_EXIT = 1031;
+
+       public static final TCommand CMD_EXIT = new TCommand(MENU_EXIT) {
+       };
 
        private Reader reader;
        private TuiReaderMainWindow main;
 
-       private MetaData meta;
-       private String source;
-       private boolean useMeta;
-
        // start reading if meta present
        public TuiReaderApplication(Reader reader, BackendType backend)
                        throws Exception {
                super(backend);
                init(reader);
 
-               MetaData meta = getMeta();
-
-               showMain(meta, null, true);
+               if (getMeta() != null) {
+                       read(false);
+               }
        }
 
        public TuiReaderApplication(Reader reader, String source,
@@ -60,55 +67,13 @@ class TuiReaderApplication extends TApplication implements Reader {
                super(backend);
                init(reader);
 
-               showMain(null, source, false);
-       }
-
-       private void showMain(MetaData meta, String source, boolean useMeta)
-                       throws IOException {
-               // TODO: thread-safety
-               this.meta = meta;
-               this.source = source;
-               this.useMeta = useMeta;
-
-               if (main != null && main.isVisible()) {
-                       main.activate();
-               } else {
-                       if (main != null) {
-                               main.close();
-                       }
-                       main = new TuiReaderMainWindow(this);
-                       if (useMeta) {
-                               main.setMeta(meta);
-                               if (meta != null) {
-                                       read();
-                               }
-                       } else {
-                               main.setSource(source);
-                       }
-               }
+               showMain();
+               main.setMode(Mode.SOURCE, source);
        }
 
        @Override
-       public void read() throws IOException {
-               MetaData meta = getMeta();
-
-               if (meta == null) {
-                       throw new IOException("No story to read");
-               }
-
-               // TODO: open in editor + external option
-               if (!meta.isImageDocument()) {
-                       @SuppressWarnings("unused")
-                       Object discard = new TuiReaderStoryWindow(this, getLibrary(), meta,
-                                       getChapter());
-               } else {
-                       try {
-                               openExternal(getLibrary(), meta.getLuid());
-                       } catch (IOException e) {
-                               messageBox("Error when trying to open the story",
-                                               e.getMessage(), TMessageBox.Type.OK);
-                       }
-               }
+       public void read(boolean sync) throws IOException {
+               read(getStory(null), sync);
        }
 
        @Override
@@ -160,6 +125,80 @@ class TuiReaderApplication extends TApplication implements Reader {
        public void setChapter(int chapter) {
                reader.setChapter(chapter);
        }
+       
+       @Override
+       public void search(SupportType searchOn, String keywords, int page, int item) {
+               reader.search(searchOn, keywords, page,item);   
+       }
+       
+       @Override
+       public void searchTag(SupportType searchOn, int page, int item, String... tags) {
+               reader.searchTag(searchOn, page, item, tags);
+       }
+
+       /**
+        * Open the given {@link Story} for reading. This may or may not start an
+        * external program to read said {@link Story}.
+        * 
+        * @param story
+        *            the {@link Story} to read
+        * @param sync
+        *            execute the process synchronously (wait until it is terminated
+        *            before returning)
+        * 
+        * @throws IOException
+        *             in case of I/O errors
+        */
+       public void read(Story story, boolean sync) throws IOException {
+               if (story == null) {
+                       throw new IOException("No story to read");
+               }
+
+               // TODO: open in editor + external option
+               if (!story.getMeta().isImageDocument()) {
+                       TWindow window = new TuiReaderStoryWindow(this, story, getChapter());
+                       window.maximize();
+               } else {
+                       try {
+                               openExternal(getLibrary(), story.getMeta().getLuid(), sync);
+                       } catch (IOException e) {
+                               messageBox("Error when trying to open the story",
+                                               e.getMessage(), TMessageBox.Type.OK);
+                       }
+               }
+       }
+
+       /**
+        * Set the default status bar when this window appear.
+        * <p>
+        * Some shortcuts are always visible, and will be put here.
+        * <p>
+        * Note that shortcuts placed this way on menu won't work unless the menu
+        * also implement them.
+        * 
+        * @param window
+        *            the new window or menu on screen
+        * @param description
+        *            the description to show on the status ba
+        */
+       public TStatusBar setStatusBar(TWindow window, String description) {
+               TStatusBar statusBar = window.newStatusBar(description);
+               statusBar.addShortcutKeypress(TKeypress.kbF10, CMD_EXIT, "Exit");
+               return statusBar;
+
+       }
+
+       private void showMain() {
+               if (main != null && main.isVisible()) {
+                       main.activate();
+               } else {
+                       if (main != null) {
+                               main.close();
+                       }
+                       main = new TuiReaderMainWindow(this);
+                       main.maximize();
+               }
+       }
 
        private void init(Reader reader) {
                this.reader = reader;
@@ -169,8 +208,9 @@ class TuiReaderApplication extends TApplication implements Reader {
 
                // Add the menus TODO: i18n
                TMenu fileMenu = addMenu("&File");
-               fileMenu.addItem(MENU_OPEN, "&Open");
+               fileMenu.addItem(MENU_OPEN, "&Open...");
                fileMenu.addItem(MENU_EXPORT, "&Save as...");
+               fileMenu.addItem(MENU_DELETE, "&Delete...");
                // TODO: Move to...
                fileMenu.addSeparator();
                fileMenu.addItem(MENU_IMPORT_URL, "Import &URL...");
@@ -180,10 +220,8 @@ class TuiReaderApplication extends TApplication implements Reader {
                fileMenu.addSeparator();
                fileMenu.addItem(MENU_EXIT, "E&xit");
 
-               TStatusBar statusBar = fileMenu.newStatusBar("File-management "
+               setStatusBar(fileMenu, "File-management "
                                + "commands (Open, Save, Print, etc.)");
-               // TODO: doesn't actually work:
-               statusBar.addShortcutKeypress(TKeypress.kbF10, TCommand.cmExit, "Exit");
 
                // TODO: Edit: re-download, delete
 
@@ -194,14 +232,62 @@ class TuiReaderApplication extends TApplication implements Reader {
                getBackend().setTitle("Fanfix");
        }
 
+       @Override
+       protected boolean onCommand(TCommandEvent command) {
+               if (command.getCmd().equals(TuiReaderMainWindow.CMD_SEARCH)) {
+                       messageBox("title", "caption");
+                       return true;
+               }
+               return super.onCommand(command);
+       }
+
        @Override
        protected boolean onMenu(TMenuEvent menu) {
                // TODO: i18n
                switch (menu.getId()) {
                case MENU_EXIT:
-                       if (messageBox("Confirmation", "(TODO: i18n) Exit application?",
-                                       TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
-                               exit(); // TODO: exit(false) for "no confirm box"
+                       close(this);
+                       return true;
+               case MENU_OPEN:
+                       String openfile = null;
+                       try {
+                               openfile = fileOpenBox(".");
+                               reader.setMeta(BasicReader.getUrl(openfile), null);
+                               read(false);
+                       } catch (IOException e) {
+                               // TODO: i18n
+                               error("Fail to open file"
+                                               + (openfile == null ? "" : ": " + openfile),
+                                               "Import error", e);
+                       }
+
+                       return true;
+               case MENU_DELETE:
+                       String luid = null;
+                       String story = null;
+                       MetaData meta = null;
+                       if (main != null) {
+                               meta = main.getSelectedMeta();
+                       }
+                       if (meta != null) {
+                               luid = meta.getLuid();
+                               story = luid + ": " + meta.getTitle();
+                       }
+
+                       // TODO: i18n
+                       TMessageBox mbox = messageBox("Delete story", "Delete story \""
+                                       + story + "\"", Type.OKCANCEL);
+                       if (mbox.getResult() == Result.OK) {
+                               try {
+                                       reader.getLibrary().delete(luid);
+                                       if (main != null) {
+                                               main.refreshStories();
+                                       }
+                               } catch (IOException e) {
+                                       // TODO: i18n
+                                       error("Fail to delete the story: \"" + story + "\"",
+                                                       "Error", e);
+                               }
                        }
 
                        return true;
@@ -222,48 +308,131 @@ class TuiReaderApplication extends TApplication implements Reader {
                        String url = inputBox("Import story", "URL to import", clipboard)
                                        .getText();
 
-                       if (!imprt(url)) {
-                               // TODO: bad import
+                       try {
+                               if (!imprt(url)) {
+                                       // TODO: i18n
+                                       error("URK not supported: " + url, "Import error");
+                               }
+                       } catch (IOException e) {
+                               // TODO: i18n
+                               error("Fail to import URL: " + url, "Import error", e);
                        }
 
                        return true;
                case MENU_IMPORT_FILE:
+                       String filename = null;
                        try {
-                               String filename = fileOpenBox(".");
+                               filename = fileOpenBox(".");
                                if (!imprt(filename)) {
-                                       // TODO: bad import
+                                       // TODO: i18n
+                                       error("File not supported: " + filename, "Import error");
                                }
                        } catch (IOException e) {
-                               // TODO: bad file
-                               e.printStackTrace();
+                               // TODO: i18n
+                               error("Fail to import file"
+                                               + (filename == null ? "" : ": " + filename),
+                                               "Import error", e);
                        }
-
                        return true;
                case MENU_LIBRARY:
-                       try {
-                               showMain(meta, source, useMeta);
-                       } catch (IOException e) {
-                               e.printStackTrace();
-                       }
-
+                       showMain();
                        return true;
                }
 
                return super.onMenu(menu);
        }
 
-       private boolean imprt(String url) {
+       /**
+        * Import the given url.
+        * <p>
+        * Can fail if the host is not supported.
+        * 
+        * @param url
+        * 
+        * @return TRUE in case of success, FALSE if the host is not supported
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       private boolean imprt(String url) throws IOException {
                try {
                        reader.getLibrary().imprt(BasicReader.getUrl(url), null);
                        main.refreshStories();
                        return true;
-               } catch (IOException e) {
+               } catch (UnknownHostException e) {
                        return false;
                }
        }
 
        @Override
-       public void openExternal(BasicLibrary lib, String luid) throws IOException {
-               reader.openExternal(lib, luid);
+       public void openExternal(BasicLibrary lib, String luid, boolean sync)
+                       throws IOException {
+               reader.openExternal(lib, luid, sync);
+       }
+
+       /**
+        * Display an error message and log it.
+        * 
+        * @param message
+        *            the message
+        * @param title
+        *            the title of the error message
+        */
+       private void error(String message, String title) {
+               error(message, title, null);
+       }
+
+       /**
+        * Display an error message and log it, including the linked
+        * {@link Exception}.
+        * 
+        * @param message
+        *            the message
+        * @param title
+        *            the title of the error message
+        * @param e
+        *            the exception to log if any (can be NULL)
+        */
+       private void error(String message, String title, Exception e) {
+               Instance.getTraceHandler().error(title + ": " + message);
+               if (e != null) {
+                       Instance.getTraceHandler().error(e);
+               }
+
+               if (e != null) {
+                       messageBox(title, message //
+                                       + "\n" + e.getMessage());
+               } else {
+                       messageBox(title, message);
+               }
+       }
+
+       /**
+        * Ask the user and, if confirmed, close the {@link TApplication} this
+        * {@link TWidget} is running on.
+        * <p>
+        * This should result in the program terminating.
+        * 
+        * @param widget
+        *            the {@link TWidget}
+        */
+       static public void close(TWidget widget) {
+               close(widget.getApplication());
+       }
+
+       /**
+        * Ask the user and, if confirmed, close the {@link TApplication}.
+        * <p>
+        * This should result in the program terminating.
+        * 
+        * @param app
+        *            the {@link TApplication}
+        */
+       static void close(TApplication app) {
+               // TODO: i18n
+               if (app.messageBox("Confirmation", "(TODO: i18n) Exit application?",
+                               TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
+                       app.exit();
+               }
        }
 }