Fix some CBZ cover/fake cover issues
[nikiroo-utils.git] / src / be / nikiroo / fanfix / reader / GuiReaderFrame.java
index 59663fe3e8369d41162e0fc276aacb86b3dce43d..99091d376ed03de370002f75fceb4538c52a9f26 100644 (file)
@@ -13,6 +13,7 @@ import java.awt.event.WindowEvent;
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -22,6 +23,7 @@ import java.util.Map.Entry;
 import javax.swing.BoxLayout;
 import javax.swing.JFileChooser;
 import javax.swing.JFrame;
+import javax.swing.JLabel;
 import javax.swing.JMenu;
 import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
@@ -29,6 +31,7 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JScrollPane;
+import javax.swing.SwingConstants;
 import javax.swing.SwingUtilities;
 import javax.swing.filechooser.FileFilter;
 import javax.swing.filechooser.FileNameExtensionFilter;
@@ -38,6 +41,8 @@ import be.nikiroo.fanfix.bundles.Config;
 import be.nikiroo.fanfix.bundles.UiConfig;
 import be.nikiroo.fanfix.data.MetaData;
 import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.fanfix.library.BasicLibrary;
+import be.nikiroo.fanfix.library.BasicLibrary.Status;
 import be.nikiroo.fanfix.library.LocalLibrary;
 import be.nikiroo.fanfix.output.BasicOutput.OutputType;
 import be.nikiroo.fanfix.reader.GuiReaderBook.BookActionListener;
@@ -66,6 +71,21 @@ class GuiReaderFrame extends JFrame {
        private GuiReaderBook selectedBook;
        private boolean words; // words or authors (secondary info on books)
 
+       /**
+        * A {@link Runnable} with a {@link Story} parameter.
+        * 
+        * @author niki
+        */
+       private interface StoryRunnable {
+               /**
+                * Run the action.
+                * 
+                * @param story
+                *            the story
+                */
+               public void run(Story story);
+       }
+
        /**
         * Create a new {@link GuiReaderFrame}.
         * 
@@ -97,10 +117,17 @@ class GuiReaderFrame extends JFrame {
                scroll.getVerticalScrollBar().setUnitIncrement(16);
                add(scroll, BorderLayout.CENTER);
 
+               String message = reader.getLibrary().getLibraryName();
+               if (!message.isEmpty()) {
+                       JLabel name = new JLabel(message, SwingConstants.CENTER);
+                       add(name, BorderLayout.NORTH);
+               }
+
                pgBar = new ProgressBar();
                add(pgBar, BorderLayout.SOUTH);
 
                pgBar.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                invalidate();
                                pgBar.setProgress(null);
@@ -110,6 +137,7 @@ class GuiReaderFrame extends JFrame {
                });
 
                pgBar.addUpdateListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                invalidate();
                                validate();
@@ -124,20 +152,94 @@ class GuiReaderFrame extends JFrame {
                final Progress pg = new Progress();
                final String typeF = type;
                outOfUi(pg, new Runnable() {
+                       @Override
                        public void run() {
-                               GuiReaderFrame.this.reader.getLibrary().refresh(false, pg);
-                               invalidate();
-                               setJMenuBar(createMenu());
-                               addBookPane(typeF, true);
-                               refreshBooks();
-                               validate();
-                               pane.setVisible(true);
+                               BasicLibrary lib = GuiReaderFrame.this.reader.getLibrary();
+                               Status status = lib.getStatus();
+
+                               if (status == Status.READY) {
+                                       lib.refresh(pg);
+                                       invalidate();
+                                       setJMenuBar(createMenu(true));
+                                       addBookPane(typeF, true);
+                                       refreshBooks();
+                                       validate();
+                                       pane.setVisible(true);
+                               } else {
+                                       invalidate();
+                                       setJMenuBar(createMenu(false));
+                                       validate();
+
+                                       String err = lib.getLibraryName() + "\n";
+                                       switch (status) {
+                                       case INVALID:
+                                               err += "Library not valid";
+                                               break;
+
+                                       case UNAUTORIZED:
+                                               err += "You are not allowed to access this library";
+                                               break;
+
+                                       case UNAVAILABLE:
+                                               err += "Library currently unavilable";
+                                               break;
+
+                                       default:
+                                               err += "An error occured when contacting the library";
+                                               break;
+                                       }
+
+                                       error(err, "Library error", null);
+                               }
                        }
                });
 
                setVisible(true);
        }
 
+       private void addSourcePanes() {
+               // Sources -> i18n
+               GuiReaderGroup bookPane = new GuiReaderGroup(reader, "Sources", color);
+
+               List<MetaData> sources = new ArrayList<MetaData>();
+               for (String source : reader.getLibrary().getSources()) {
+                       MetaData mSource = new MetaData();
+                       mSource.setLuid(null);
+                       mSource.setTitle(source);
+                       mSource.setSource(source);
+                       sources.add(mSource);
+               }
+
+               bookPane.refreshBooks(sources, false);
+
+               this.invalidate();
+               pane.invalidate();
+               pane.add(bookPane);
+               pane.validate();
+               this.validate();
+
+               bookPane.setActionListener(new BookActionListener() {
+                       @Override
+                       public void select(GuiReaderBook book) {
+                               selectedBook = book;
+                       }
+
+                       @Override
+                       public void popupRequested(GuiReaderBook book, MouseEvent e) {
+                               JPopupMenu popup = new JPopupMenu();
+                               popup.add(createMenuItemOpenBook());
+                               popup.show(e.getComponent(), e.getX(), e.getY());
+                       }
+
+                       @Override
+                       public void action(final GuiReaderBook book) {
+                               removeBookPanes();
+                               addBookPane(book.getMeta().getSource(), true);
+                               refreshBooks();
+                       }
+               });
+       }
+
        /**
         * Add a new {@link GuiReaderGroup} on the frame to display the books of the
         * selected type or author.
@@ -151,9 +253,14 @@ class GuiReaderFrame extends JFrame {
        private void addBookPane(String value, boolean type) {
                if (value == null) {
                        if (type) {
-                               for (String tt : reader.getLibrary().getSources()) {
-                                       if (tt != null) {
-                                               addBookPane(tt, type);
+                               if (Instance.getUiConfig().getBoolean(UiConfig.SOURCE_PAGE,
+                                               false)) {
+                                       addSourcePanes();
+                               } else {
+                                       for (String tt : reader.getLibrary().getSources()) {
+                                               if (tt != null) {
+                                                       addBookPane(tt, type);
+                                               }
                                        }
                                }
                        } else {
@@ -181,16 +288,19 @@ class GuiReaderFrame extends JFrame {
                this.validate();
 
                bookPane.setActionListener(new BookActionListener() {
+                       @Override
                        public void select(GuiReaderBook book) {
                                selectedBook = book;
                        }
 
+                       @Override
                        public void popupRequested(GuiReaderBook book, MouseEvent e) {
                                JPopupMenu popup = new JPopupMenu();
                                popup.add(createMenuItemOpenBook());
                                popup.addSeparator();
                                popup.add(createMenuItemExport());
-                               popup.add(createMenuItemMove());
+                               popup.add(createMenuItemMove(true));
+                               popup.add(createMenuItemSetCover());
                                popup.add(createMenuItemClearCache());
                                popup.add(createMenuItemRedownload());
                                popup.addSeparator();
@@ -198,6 +308,7 @@ class GuiReaderFrame extends JFrame {
                                popup.show(e.getComponent(), e.getX(), e.getY());
                        }
 
+                       @Override
                        public void action(final GuiReaderBook book) {
                                openBook(book);
                        }
@@ -238,9 +349,12 @@ class GuiReaderFrame extends JFrame {
        /**
         * Create the main menu bar.
         * 
+        * @param libOk
+        *            the library can be queried
+        * 
         * @return the bar
         */
-       private JMenuBar createMenu() {
+       private JMenuBar createMenu(boolean libOk) {
                bar = new JMenuBar();
 
                JMenu file = new JMenu("File");
@@ -248,18 +362,21 @@ class GuiReaderFrame extends JFrame {
 
                JMenuItem imprt = new JMenuItem("Import URL...", KeyEvent.VK_U);
                imprt.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                imprt(true);
                        }
                });
                JMenuItem imprtF = new JMenuItem("Import File...", KeyEvent.VK_F);
                imprtF.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                imprt(false);
                        }
                });
                JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X);
                exit.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                GuiReaderFrame.this.dispatchEvent(new WindowEvent(
                                                GuiReaderFrame.this, WindowEvent.WINDOW_CLOSING));
@@ -268,7 +385,7 @@ class GuiReaderFrame extends JFrame {
 
                file.add(createMenuItemOpenBook());
                file.add(createMenuItemExport());
-               file.add(createMenuItemMove());
+               file.add(createMenuItemMove(libOk));
                file.addSeparator();
                file.add(imprt);
                file.add(imprtF);
@@ -292,6 +409,7 @@ class GuiReaderFrame extends JFrame {
                JMenuItem vauthors = new JMenuItem("Author");
                vauthors.setMnemonic(KeyEvent.VK_A);
                vauthors.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                words = false;
                                refreshBooks();
@@ -301,6 +419,7 @@ class GuiReaderFrame extends JFrame {
                JMenuItem vwords = new JMenuItem("Word count");
                vwords.setMnemonic(KeyEvent.VK_W);
                vwords.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                words = true;
                                refreshBooks();
@@ -312,11 +431,16 @@ class GuiReaderFrame extends JFrame {
                JMenu sources = new JMenu("Sources");
                sources.setMnemonic(KeyEvent.VK_S);
 
-               List<String> tt = reader.getLibrary().getSources();
+               List<String> tt = new ArrayList<String>();
+               if (libOk) {
+                       tt.addAll(reader.getLibrary().getSources());
+               }
                tt.add(0, null);
+
                for (final String type : tt) {
                        JMenuItem item = new JMenuItem(type == null ? "All" : type);
                        item.addActionListener(new ActionListener() {
+                               @Override
                                public void actionPerformed(ActionEvent e) {
                                        removeBookPanes();
                                        addBookPane(type, true);
@@ -335,12 +459,16 @@ class GuiReaderFrame extends JFrame {
                JMenu authors = new JMenu("Authors");
                authors.setMnemonic(KeyEvent.VK_A);
 
-               List<String> aa = reader.getLibrary().getAuthors();
+               List<String> aa = new ArrayList<String>();
+               if (libOk) {
+                       aa.addAll(reader.getLibrary().getAuthors());
+               }
                aa.add(0, null);
                for (final String author : aa) {
                        JMenuItem item = new JMenuItem(author == null ? "All"
                                        : author.isEmpty() ? "[unknown]" : author);
                        item.addActionListener(new ActionListener() {
+                               @Override
                                public void actionPerformed(ActionEvent e) {
                                        removeBookPanes();
                                        addBookPane(author, false);
@@ -376,6 +504,7 @@ class GuiReaderFrame extends JFrame {
                item.setMnemonic(KeyEvent.VK_F);
 
                item.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                ConfigEditor<Config> ed = new ConfigEditor<Config>(
                                                Config.class, Instance.getConfig(),
@@ -401,6 +530,7 @@ class GuiReaderFrame extends JFrame {
                item.setMnemonic(KeyEvent.VK_U);
 
                item.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                ConfigEditor<UiConfig> ed = new ConfigEditor<UiConfig>(
                                                UiConfig.class, Instance.getUiConfig(),
@@ -451,6 +581,7 @@ class GuiReaderFrame extends JFrame {
 
                JMenuItem export = new JMenuItem("Save as...", KeyEvent.VK_S);
                export.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                if (selectedBook != null) {
                                        fc.showDialog(GuiReaderFrame.this, "Save");
@@ -461,13 +592,14 @@ class GuiReaderFrame extends JFrame {
                                                                + type.getDefaultExtension(false);
                                                final Progress pg = new Progress();
                                                outOfUi(pg, new Runnable() {
+                                                       @Override
                                                        public void run() {
                                                                try {
                                                                        reader.getLibrary().export(
                                                                                        selectedBook.getMeta().getLuid(),
                                                                                        type, path, pg);
                                                                } catch (IOException e) {
-                                                                       Instance.syserr(e);
+                                                                       Instance.getTraceHandler().error(e);
                                                                }
                                                        }
                                                });
@@ -510,14 +642,18 @@ class GuiReaderFrame extends JFrame {
        private JMenuItem createMenuItemClearCache() {
                JMenuItem refresh = new JMenuItem("Clear cache", KeyEvent.VK_C);
                refresh.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                if (selectedBook != null) {
                                        outOfUi(null, new Runnable() {
+                                               @Override
                                                public void run() {
                                                        reader.clearLocalReaderCache(selectedBook.getMeta()
                                                                        .getLuid());
                                                        selectedBook.setCached(false);
+                                                       GuiReaderBook.clearIcon(selectedBook.getMeta());
                                                        SwingUtilities.invokeLater(new Runnable() {
+                                                               @Override
                                                                public void run() {
                                                                        selectedBook.repaint();
                                                                }
@@ -534,15 +670,20 @@ class GuiReaderFrame extends JFrame {
        /**
         * Create the delete menu item.
         * 
+        * @param libOk
+        *            the library can be queried
+        * 
         * @return the item
         */
-       private JMenuItem createMenuItemMove() {
+       private JMenuItem createMenuItemMove(boolean libOk) {
                JMenu moveTo = new JMenu("Move to...");
                moveTo.setMnemonic(KeyEvent.VK_M);
 
                List<String> types = new ArrayList<String>();
                types.add(null);
-               types.addAll(reader.getLibrary().getSources());
+               if (libOk) {
+                       types.addAll(reader.getLibrary().getSources());
+               }
 
                for (String type : types) {
                        JMenuItem item = new JMenuItem(type == null ? "New type..." : type);
@@ -554,6 +695,7 @@ class GuiReaderFrame extends JFrame {
 
                        final String ftype = type;
                        item.addActionListener(new ActionListener() {
+                               @Override
                                public void actionPerformed(ActionEvent e) {
                                        if (selectedBook != null) {
                                                String type = ftype;
@@ -563,15 +705,17 @@ class GuiReaderFrame extends JFrame {
                                                                        "Moving story",
                                                                        JOptionPane.QUESTION_MESSAGE, null, null,
                                                                        selectedBook.getMeta().getSource());
+
                                                        if (rep == null) {
                                                                return;
-                                                       } else {
-                                                               type = rep.toString();
                                                        }
+
+                                                       type = rep.toString();
                                                }
 
                                                final String ftype = type;
                                                outOfUi(null, new Runnable() {
+                                                       @Override
                                                        public void run() {
                                                                reader.changeType(selectedBook.getMeta()
                                                                                .getLuid(), ftype);
@@ -579,8 +723,9 @@ class GuiReaderFrame extends JFrame {
                                                                selectedBook = null;
 
                                                                SwingUtilities.invokeLater(new Runnable() {
+                                                                       @Override
                                                                        public void run() {
-                                                                               setJMenuBar(createMenu());
+                                                                               setJMenuBar(createMenu(true));
                                                                        }
                                                                });
                                                        }
@@ -601,13 +746,20 @@ class GuiReaderFrame extends JFrame {
        private JMenuItem createMenuItemRedownload() {
                JMenuItem refresh = new JMenuItem("Redownload", KeyEvent.VK_R);
                refresh.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                if (selectedBook != null) {
                                        final MetaData meta = selectedBook.getMeta();
-                                       imprt(meta.getUrl(), new Runnable() {
-                                               public void run() {
+                                       imprt(meta.getUrl(), new StoryRunnable() {
+                                               @Override
+                                               public void run(Story story) {
                                                        reader.delete(meta.getLuid());
                                                        GuiReaderFrame.this.selectedBook = null;
+                                                       MetaData newMeta = story.getMeta();
+                                                       if (!newMeta.getSource().equals(meta.getSource())) {
+                                                               reader.changeType(newMeta.getLuid(),
+                                                                               meta.getSource());
+                                                       }
                                                }
                                        }, "Removing old copy");
                                }
@@ -625,9 +777,11 @@ class GuiReaderFrame extends JFrame {
        private JMenuItem createMenuItemDelete() {
                JMenuItem delete = new JMenuItem("Delete", KeyEvent.VK_D);
                delete.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                if (selectedBook != null) {
                                        outOfUi(null, new Runnable() {
+                                               @Override
                                                public void run() {
                                                        reader.delete(selectedBook.getMeta().getLuid());
                                                        selectedBook = null;
@@ -641,16 +795,48 @@ class GuiReaderFrame extends JFrame {
        }
 
        /**
-        * Create the open menu item.
+        * Create the open menu item for a book or a source (no LUID).
         * 
         * @return the item
         */
        private JMenuItem createMenuItemOpenBook() {
                JMenuItem open = new JMenuItem("Open", KeyEvent.VK_O);
                open.addActionListener(new ActionListener() {
+                       @Override
                        public void actionPerformed(ActionEvent e) {
                                if (selectedBook != null) {
-                                       openBook(selectedBook);
+                                       if (selectedBook.getMeta().getLuid() == null) {
+                                               removeBookPanes();
+                                               addBookPane(selectedBook.getMeta().getSource(), true);
+                                               refreshBooks();
+                                       } else {
+                                               openBook(selectedBook);
+                                       }
+                               }
+                       }
+               });
+
+               return open;
+       }
+
+       /**
+        * Create the SetCover menu item for a book to change the linked source
+        * cover.
+        * 
+        * @return the item
+        */
+       private JMenuItem createMenuItemSetCover() {
+               JMenuItem open = new JMenuItem("Set as cover for source", KeyEvent.VK_C);
+               open.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (selectedBook != null) {
+                                       reader.getLibrary().setSourceCover(
+                                                       selectedBook.getMeta().getSource(),
+                                                       selectedBook.getMeta().getLuid());
+                                       MetaData source = selectedBook.getMeta().clone();
+                                       source.setLuid(null);
+                                       GuiReaderBook.clearIcon(source);
                                }
                        }
                });
@@ -667,17 +853,19 @@ class GuiReaderFrame extends JFrame {
        private void openBook(final GuiReaderBook book) {
                final Progress pg = new Progress();
                outOfUi(pg, new Runnable() {
+                       @Override
                        public void run() {
                                try {
                                        reader.read(book.getMeta().getLuid(), pg);
                                        SwingUtilities.invokeLater(new Runnable() {
+                                               @Override
                                                public void run() {
                                                        book.setCached(true);
                                                }
                                        });
                                } catch (IOException e) {
                                        // TODO: error message?
-                                       Instance.syserr(e);
+                                       Instance.getTraceHandler().error(e);
                                }
                        }
                });
@@ -711,13 +899,17 @@ class GuiReaderFrame extends JFrame {
                setEnabled(false);
 
                new Thread(new Runnable() {
+                       @Override
                        public void run() {
-                               run.run();
-                               refreshBooks();
-                               reload.done();
-                               if (!pg.isDone()) {
-                                       // will trigger pgBar ActionListener:
-                                       pg.done();
+                               try {
+                                       run.run();
+                                       refreshBooks();
+                               } finally {
+                                       reload.done();
+                                       if (!pg.isDone()) {
+                                               // will trigger pgBar ActionListener:
+                                               pg.done();
+                                       }
                                }
                        }
                }, "outOfUi thread").start();
@@ -773,7 +965,7 @@ class GuiReaderFrame extends JFrame {
         * @param onSuccess
         *            Action to execute on success
         */
-       private void imprt(final String url, final Runnable onSuccess,
+       private void imprt(final String url, final StoryRunnable onSuccess,
                        String onSuccessPgName) {
                final Progress pg = new Progress();
                final Progress pgImprt = new Progress();
@@ -782,10 +974,13 @@ class GuiReaderFrame extends JFrame {
                pg.addProgress(pgOnSuccess, 5);
 
                outOfUi(pg, new Runnable() {
+                       @Override
                        public void run() {
                                Exception ex = null;
+                               Story story = null;
                                try {
-                                       reader.getLibrary().imprt(BasicReader.getUrl(url), pgImprt);
+                                       story = reader.getLibrary().imprt(BasicReader.getUrl(url),
+                                                       pgImprt);
                                } catch (IOException e) {
                                        ex = e;
                                }
@@ -796,17 +991,16 @@ class GuiReaderFrame extends JFrame {
 
                                pgOnSuccess.setProgress(0);
                                if (!ok) {
-                                       Instance.syserr(e);
-                                       SwingUtilities.invokeLater(new Runnable() {
-                                               public void run() {
-                                                       JOptionPane.showMessageDialog(GuiReaderFrame.this,
-                                                                       "Cannot import: " + url, e.getMessage(),
-                                                                       JOptionPane.ERROR_MESSAGE);
-                                               }
-                                       });
+                                       if (e instanceof UnknownHostException) {
+                                               error("URL not supported: " + url, "Cannot import URL",
+                                                               null);
+                                       } else {
+                                               error("Failed to import " + url + ": \n"
+                                                               + e.getMessage(), "Cannot import URL", e);
+                                       }
                                } else {
                                        if (onSuccess != null) {
-                                               onSuccess.run();
+                                               onSuccess.run(story);
                                        }
                                }
                                pgOnSuccess.done();
@@ -840,4 +1034,29 @@ class GuiReaderFrame extends JFrame {
                super.setEnabled(b);
                repaint();
        }
+
+       /**
+        * Display an error message and log the linked {@link Exception}.
+        * 
+        * @param message
+        *            the message
+        * @param title
+        *            the title of the error message
+        * @param e
+        *            the exception to log if any
+        */
+       private void error(final String message, final String title, Exception e) {
+               Instance.getTraceHandler().error(title + ": " + message);
+               if (e != null) {
+                       Instance.getTraceHandler().error(e);
+               }
+
+               SwingUtilities.invokeLater(new Runnable() {
+                       @Override
+                       public void run() {
+                               JOptionPane.showMessageDialog(GuiReaderFrame.this, message,
+                                               title, JOptionPane.ERROR_MESSAGE);
+                       }
+               });
+       }
 }