reader: sync/async work
authorNiki Roo <niki@nikiroo.be>
Thu, 14 Mar 2019 08:39:26 +0000 (09:39 +0100)
committerNiki Roo <niki@nikiroo.be>
Thu, 14 Mar 2019 08:39:26 +0000 (09:39 +0100)
TODO.md
src/be/nikiroo/fanfix/Main.java
src/be/nikiroo/fanfix/reader/BasicReader.java
src/be/nikiroo/fanfix/reader/Reader.java
src/be/nikiroo/fanfix/reader/android/AndroidReader.java
src/be/nikiroo/fanfix/reader/cli/CliReader.java
src/be/nikiroo/fanfix/reader/tui/TuiReader.java
src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java
src/be/nikiroo/fanfix/reader/tui/TuiReaderMainWindow.java
src/be/nikiroo/fanfix/reader/ui/GuiReader.java
src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java

diff --git a/TODO.md b/TODO.md
index ff3396ba86fdf7730320c3af853a69c89d27750a..7843c81263cc3e5aafd5fd89bbbc72d11658b5f2 100644 (file)
--- a/TODO.md
+++ b/TODO.md
@@ -6,8 +6,9 @@ My current planning for Fanfix (but not everything appears on this list):
   - [x] New API on FimFiction.net (faster)
   - [ ] Others? Any ideas? I'm open for requests
     - [x] [e-Hentai](https://e-hentai.org/) requested
-    - [ ] Find some FR comics/manga websites
-- [x] A GUI library
+    - [x] Find some FR comics/manga websites
+    - [ ] Find more FR thingies
+- [ ] A GUI library
   - [x] Make one
   - [x] Make it run when no args passed
   - [x] Fix the UI, it is ugly
@@ -19,6 +20,8 @@ My current planning for Fanfix (but not everything appears on this list):
     - [x] ..as a screen view
   - [x] options screen
   - [x] support progress events
+  - [x] Real menus
+    - [ ] Store the long lists in [A-B], [BA-BB], ...
 - [ ] A TUI library
   - [x] Choose an output (Jexer)
   - [x] Implement it from --set-reader to the actual window
index 53c4a8629f952075f4519131f5541363f5701f45..f83186c1d89bffdd01b1c837a1b29b6047cb5f33 100644 (file)
@@ -354,18 +354,12 @@ public class Main {
                        }
                }
 
-               // We cannot do it when in GUI mode, because it is async...
-               // So if we close the temp files before it is actually used,
-               // we have a problem...
-               // TODO: close it at the correct time (for now, finalize try to do it)
-               if (false) {
-                       try {
-                               Instance.getTempFiles().close();
-                       } catch (IOException e) {
-                               Instance.getTraceHandler().error(
-                                               new IOException(
-                                                               "Cannot dispose of the temporary files", e));
-                       }
+               try {
+                       Instance.getTempFiles().close();
+               } catch (IOException e) {
+                       Instance.getTraceHandler()
+                                       .error(new IOException(
+                                                       "Cannot dispose of the temporary files", e));
                }
 
                if (exitCode == 255) {
@@ -462,7 +456,7 @@ public class Main {
        }
 
        /**
-        * Start the CLI reader for this {@link Story}.
+        * Start the current reader for this {@link Story}.
         * 
         * @param story
         *            the LUID of the {@link Story} in the {@link LocalLibrary}
@@ -488,7 +482,7 @@ public class Main {
                        if (chapString != null) {
                                try {
                                        reader.setChapter(Integer.parseInt(chapString));
-                                       reader.read();
+                                       reader.read(true);
                                } catch (NumberFormatException e) {
                                        Instance.getTraceHandler().error(
                                                        new IOException("Chapter number cannot be parsed: "
@@ -496,7 +490,7 @@ public class Main {
                                        return 2;
                                }
                        } else {
-                               reader.read();
+                               reader.read(true);
                        }
                } catch (IOException e) {
                        Instance.getTraceHandler().error(e);
@@ -548,7 +542,8 @@ public class Main {
                                        BasicSupport support = BasicSupport.getSupport(source);
 
                                        if (support != null) {
-                                               Instance.getTraceHandler().trace("Support found: " + support.getClass());
+                                               Instance.getTraceHandler().trace(
+                                                               "Support found: " + support.getClass());
                                                Progress pgIn = new Progress();
                                                Progress pgOut = new Progress();
                                                if (pg != null) {
index 778b6338b108f847bea745fbe8fe04c77a0454ec..71f19a101bc8b5b3216b66bc2fea24bcde24f7ee 100644 (file)
@@ -91,8 +91,7 @@ public abstract class BasicReader implements Reader {
        }
 
        @Override
-       public synchronized void setMeta(URL url, Progress pg)
-                       throws IOException {
+       public synchronized void setMeta(URL url, Progress pg) throws IOException {
                BasicSupport support = BasicSupport.getSupport(url);
                if (support == null) {
                        throw new IOException("URL not supported: " + url.toString());
@@ -208,16 +207,20 @@ public abstract class BasicReader implements Reader {
         *            the {@link BasicLibrary} to select the {@link Story} from
         * @param luid
         *            the {@link Story} LUID
+        * @param sync
+        *            execute the process synchronously (wait until it is terminated
+        *            before returning)
         * 
         * @throws IOException
         *             in case of I/O error
         */
        @Override
-       public void openExternal(BasicLibrary lib, String luid) throws IOException {
+       public void openExternal(BasicLibrary lib, String luid, boolean sync)
+                       throws IOException {
                MetaData meta = lib.getInfo(luid);
                File target = lib.getFile(luid, null);
 
-               openExternal(meta, target);
+               openExternal(meta, target, sync);
        }
 
        /**
@@ -228,11 +231,15 @@ public abstract class BasicReader implements Reader {
         *            the {@link Story} to load
         * @param target
         *            the target {@link File}
+        * @param sync
+        *            execute the process synchronously (wait until it is terminated
+        *            before returning)
         * 
         * @throws IOException
         *             in case of I/O error
         */
-       protected void openExternal(MetaData meta, File target) throws IOException {
+       protected void openExternal(MetaData meta, File target, boolean sync)
+                       throws IOException {
                String program = null;
                if (meta.isImageDocument()) {
                        program = Instance.getUiConfig().getString(
@@ -246,7 +253,7 @@ public abstract class BasicReader implements Reader {
                        program = null;
                }
 
-               start(target, program);
+               start(target, program, sync);
        }
 
        /**
@@ -257,11 +264,17 @@ public abstract class BasicReader implements Reader {
         *            the target to open
         * @param program
         *            the program to use or NULL for the default system starter
+        * @param sync
+        *            execute the process synchronously (wait until it is terminated
+        *            before returning)
         * 
         * @throws IOException
         *             in case of I/O error
         */
-       protected void start(File target, String program) throws IOException {
+       protected void start(File target, String program, boolean sync)
+                       throws IOException {
+
+               Process proc = null;
                if (program == null) {
                        boolean ok = false;
                        for (String starter : new String[] { "xdg-open", "open", "see",
@@ -269,7 +282,7 @@ public abstract class BasicReader implements Reader {
                                try {
                                        Instance.getTraceHandler().trace(
                                                        "starting external program");
-                                       Runtime.getRuntime().exec(
+                                       proc = Runtime.getRuntime().exec(
                                                        new String[] { starter, target.getAbsolutePath() });
                                        ok = true;
                                        break;
@@ -281,8 +294,17 @@ public abstract class BasicReader implements Reader {
                        }
                } else {
                        Instance.getTraceHandler().trace("starting external program");
-                       Runtime.getRuntime().exec(
+                       proc = Runtime.getRuntime().exec(
                                        new String[] { program, target.getAbsolutePath() });
                }
+
+               if (proc != null && sync) {
+                       while (proc.isAlive()) {
+                               try {
+                                       Thread.sleep(100);
+                               } catch (InterruptedException e) {
+                               }
+                       }
+               }
        }
 }
index 5fe1c5d67d55bef4993bcd6e55ce7f6c95d8bb6d..b001e304842f4956095050160d5bf65b8795d3f3 100644 (file)
@@ -130,11 +130,15 @@ public interface Reader {
        /**
         * Start the {@link Story} Reading.
         * 
+        * @param sync
+        *            execute the process synchronously (wait until it is terminated
+        *            before returning)
+        * 
         * @throws IOException
         *             in case of I/O error or if the {@link Story} was not
         *             previously set
         */
-       public void read() throws IOException;
+       public void read(boolean sync) throws IOException;
 
        /**
         * The selected chapter to start reading at (starting at 1, 0 = description,
@@ -156,6 +160,8 @@ public interface Reader {
        /**
         * Start the reader in browse mode for the given source (or pass NULL for
         * all sources).
+        * <p>
+        * Note that this must be a <b>synchronous</b> action.
         * 
         * @param source
         *            the type of {@link Story} to take into account, or NULL for
@@ -164,16 +170,20 @@ public interface Reader {
        public void browse(String source);
 
        /**
-        * Open the {@link Story} with an external reader (the program will be
+        * Open the {@link Story} with an external reader (the program should be
         * passed the main file associated with this {@link Story}).
         * 
         * @param lib
         *            the {@link BasicLibrary} to select the {@link Story} from
         * @param luid
         *            the {@link Story} LUID
+        * @param sync
+        *            execute the process synchronously (wait until it is terminated
+        *            before returning)
         * 
         * @throws IOException
         *             in case of I/O error
         */
-       public void openExternal(BasicLibrary lib, String luid) throws IOException;
+       public void openExternal(BasicLibrary lib, String luid, boolean sync)
+                       throws IOException;
 }
index 85f07f25bd6c935e53b9ea17050f41656315761c..7dcdd04565fd4c72b3f381c03623cdc77cd42ec4 100644 (file)
@@ -24,7 +24,7 @@ public class AndroidReader extends BasicReader {
        }
 
        @Override
-       public void read() throws IOException {
+       public void read(boolean sync) throws IOException {
        }
 
        @Override
@@ -32,7 +32,7 @@ public class AndroidReader extends BasicReader {
        }
 
        @Override
-       protected void start(File target, String program) throws IOException {
+       protected void start(File target, String program, boolean sync) throws IOException {
                if (program == null) {
                        try {
                                Intent[] intents = new Intent[] { //
@@ -53,10 +53,10 @@ public class AndroidReader extends BasicReader {
 
                                app.startActivity(chooserIntent);
                        } catch (UnsupportedOperationException e) {
-                               super.start(target, program);
+                               super.start(target, program, sync);
                        }
                } else {
-                       super.start(target, program);
+                       super.start(target, program, sync);
                }
        }
 }
index b28d4d4e8987479b7936699e7db14368c947d691..9ec37a583d983cee46333f32f793ea1dc04dc3b1 100644 (file)
@@ -20,7 +20,7 @@ import be.nikiroo.fanfix.reader.BasicReader;
  */
 class CliReader extends BasicReader {
        @Override
-       public void read() throws IOException {
+       public void read(boolean sync) throws IOException {
                MetaData meta = getMeta();
 
                if (meta == null) {
index 7192854ea6fed7f07978427a9c6534fbeb07e74a..f94f7838c2552e41d7061cd59e3c5bb3328ca7a9 100644 (file)
@@ -50,7 +50,7 @@ class TuiReader extends BasicReader {
        }
 
        @Override
-       public void read() throws IOException {
+       public void read(boolean sync) throws IOException {
                try {
                        TuiReaderApplication app = new TuiReaderApplication(this,
                                        guessBackendType());
index 8cd5c0b90ce99ed820ba16725e45fb9ced3530ad..f08b84ccbbf17328283b059fae0ec960fe29cee1 100644 (file)
@@ -56,9 +56,8 @@ class TuiReaderApplication extends TApplication implements Reader {
                super(backend);
                init(reader);
 
-               MetaData meta = getMeta();
-               if (meta != null) {
-                       read();
+               if (getMeta() != null) {
+                       read(false);
                }
        }
 
@@ -72,8 +71,8 @@ class TuiReaderApplication extends TApplication implements Reader {
        }
 
        @Override
-       public void read() throws IOException {
-               read(getStory(null));
+       public void read(boolean sync) throws IOException {
+               read(getStory(null), sync);
        }
 
        @Override
@@ -126,7 +125,20 @@ class TuiReaderApplication extends TApplication implements Reader {
                reader.setChapter(chapter);
        }
 
-       public void read(Story story) throws IOException {
+       /**
+        * 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");
                }
@@ -137,7 +149,7 @@ class TuiReaderApplication extends TApplication implements Reader {
                        window.maximize();
                } else {
                        try {
-                               openExternal(getLibrary(), story.getMeta().getLuid());
+                               openExternal(getLibrary(), story.getMeta().getLuid(), sync);
                        } catch (IOException e) {
                                messageBox("Error when trying to open the story",
                                                e.getMessage(), TMessageBox.Type.OK);
@@ -230,7 +242,7 @@ class TuiReaderApplication extends TApplication implements Reader {
                        try {
                                openfile = fileOpenBox(".");
                                reader.setMeta(BasicReader.getUrl(openfile), null);
-                               read();
+                               read(false);
                        } catch (IOException e) {
                                // TODO: i18n
                                error("Fail to open file"
@@ -342,8 +354,9 @@ class TuiReaderApplication extends TApplication implements Reader {
        }
 
        @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);
        }
 
        /**
index cbca857d01fc79770bfb04de9b35b96f1b224878..932cbcbb03fb33a5bf1a3721282c1866a41e801e 100644 (file)
@@ -22,7 +22,6 @@ import be.nikiroo.fanfix.Instance;
 import be.nikiroo.fanfix.data.MetaData;
 import be.nikiroo.fanfix.library.BasicLibrary;
 import be.nikiroo.fanfix.output.BasicOutput.OutputType;
-import be.nikiroo.fanfix.reader.Reader;
 import be.nikiroo.jexer.TSizeConstraint;
 
 /**
@@ -43,7 +42,7 @@ class TuiReaderMainWindow extends TWindow {
        private TList list;
        private List<MetaData> listKeys;
        private List<String> listItems;
-       private Reader reader;
+       private TuiReaderApplication reader;
 
        private Mode mode = Mode.SOURCE;
        private String target = null;
@@ -300,7 +299,7 @@ class TuiReaderMainWindow extends TWindow {
                try {
                        reader.setChapter(-1);
                        reader.setMeta(meta);
-                       reader.read();
+                       reader.read(false);
                } catch (IOException e) {
                        Instance.getTraceHandler().error(e);
                }
index 519a507ea7d0fc1d080d3c04f602736a26afb45a..02f153cdf82d47dc8f2486f7e3a72cff94a75903 100644 (file)
@@ -2,6 +2,8 @@ package be.nikiroo.fanfix.reader.ui;
 
 import java.awt.Desktop;
 import java.awt.EventQueue;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
 import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
@@ -79,14 +81,14 @@ class GuiReader extends BasicReader {
        }
 
        @Override
-       public void read() throws IOException {
+       public void read(boolean sync) throws IOException {
                MetaData meta = getMeta();
 
                if (meta == null) {
                        throw new IOException("No story to read");
                }
 
-               read(meta.getLuid(), null);
+               read(meta.getLuid(), sync, null);
        }
 
        /**
@@ -107,6 +109,7 @@ class GuiReader extends BasicReader {
                // TODO: improve presentation of update message
                final VersionCheck updates = VersionCheck.check();
                StringBuilder builder = new StringBuilder();
+               final Boolean[] done = new Boolean[] { false };
 
                final JEditorPane updateMessage = new JEditorPane("text/html", "");
                if (updates.isNewVersionAvailable()) {
@@ -162,21 +165,48 @@ class GuiReader extends BasicReader {
                                        }
                                }
 
-                               new GuiReaderFrame(GuiReader.this, typeFinal).setVisible(true);
+                               try {
+                                       GuiReaderFrame gui = new GuiReaderFrame(GuiReader.this,
+                                                       typeFinal);
+                                       gui.setVisible(true);
+                                       gui.addWindowListener(new WindowAdapter() {
+                                               @Override
+                                               public void windowClosed(WindowEvent e) {
+                                                       super.windowClosed(e);
+                                                       done[0] = true;
+                                               }
+                                       });
+                               } catch (Exception e) {
+                                       Instance.getTraceHandler().error(e);
+                                       done[0] = true;
+                               }
                        }
                });
+
+               // This action must be synchronous, so wait until the frame is closed
+               while (!done[0]) {
+                       try {
+                               Thread.sleep(100);
+                       } catch (InterruptedException e) {
+                       }
+               }
        }
 
        @Override
-       public void start(File target, String program) throws IOException {
-               if (program == null) {
+       public void start(File target, String program, boolean sync)
+                       throws IOException {
+
+               boolean handled = false;
+               if (program == null && !sync) {
                        try {
                                Desktop.getDesktop().browse(target.toURI());
+                               handled = true;
                        } catch (UnsupportedOperationException e) {
-                               super.start(target, program);
                        }
-               } else {
-                       super.start(target, program);
+               }
+
+               if (!handled) {
+                       super.start(target, program, sync);
                }
        }
 
@@ -218,9 +248,14 @@ class GuiReader extends BasicReader {
        /**
         * "Open" the given {@link Story}. It usually involves starting an external
         * program adapted to the given file type.
+        * <p>
+        * Asynchronous method.
         * 
         * @param luid
         *            the luid of the {@link Story} to open
+        * @param sync
+        *            execute the process synchronously (wait until it is terminated
+        *            before returning)
         * @param pg
         *            the optional progress (we may need to prepare the
         *            {@link Story} for reading
@@ -228,13 +263,13 @@ class GuiReader extends BasicReader {
         * @throws IOException
         *             in case of I/O errors
         */
-       void read(String luid, Progress pg) throws IOException {
+       void read(String luid, boolean sync, Progress pg) throws IOException {
                File file = cacheLib.getFile(luid, pg);
 
                // TODO: show a special page for the chapter?
                // We could also implement an internal viewer, both for image and
                // non-image documents
-               openExternal(getLibrary().getInfo(luid), file);
+               openExternal(getLibrary().getInfo(luid), file, sync);
        }
 
        /**
index 39187b968b39a970849294f4fe1ac1f046f0199e..b8db8f747e420de4e89884aeb47be5a71105d10c 100644 (file)
@@ -858,7 +858,7 @@ class GuiReaderFrame extends JFrame {
                        @Override
                        public void run() {
                                try {
-                                       reader.read(book.getMeta().getLuid(), pg);
+                                       reader.read(book.getMeta().getLuid(), false, pg);
                                        SwingUtilities.invokeLater(new Runnable() {
                                                @Override
                                                public void run() {