X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Ffanfix%2Freader%2FLocalReaderFrame.java;h=a90360eed234bde1c99349844d687b02832bf70a;hb=793f1071fae48daed3b545a03a286c85e527d244;hp=16684f946aa5cf0744780cabc1caa2cc4a13b75e;hpb=edd4628984f5f06e955606651fc828ac839f7f43;p=fanfix.git diff --git a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java index 16684f9..a90360e 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java @@ -3,6 +3,8 @@ package be.nikiroo.fanfix.reader; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Frame; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -11,12 +13,12 @@ import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import java.net.URL; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import javax.swing.BoxLayout; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; @@ -38,8 +40,8 @@ import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListener; import be.nikiroo.utils.Progress; +import be.nikiroo.utils.Version; import be.nikiroo.utils.ui.ProgressBar; -import be.nikiroo.utils.ui.WrapLayout; /** * A {@link Frame} that will show a {@link LocalReaderBook} item for each @@ -52,14 +54,14 @@ import be.nikiroo.utils.ui.WrapLayout; class LocalReaderFrame extends JFrame { private static final long serialVersionUID = 1L; private LocalReader reader; - private List stories; - private List books; - private JPanel bookPane; - private String type; + private Map booksByType; + private Map booksByAuthor; + private JPanel pane; private Color color; private ProgressBar pgBar; private JMenuBar bar; private LocalReaderBook selectedBook; + private boolean words; // words or authors (secondary info on books) /** * Create a new {@link LocalReaderFrame}. @@ -71,7 +73,7 @@ class LocalReaderFrame extends JFrame { * the type of {@link Story} to load, or NULL for all types */ public LocalReaderFrame(LocalReader reader, String type) { - super("Fanfix Library"); + super(String.format("Fanfix %s Library", Version.getCurrentVersion())); this.reader = reader; @@ -79,79 +81,128 @@ class LocalReaderFrame extends JFrame { setSize(800, 600); setLayout(new BorderLayout()); - books = new ArrayList(); - bookPane = new JPanel(new WrapLayout(WrapLayout.LEADING, 5, 5)); + pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); color = Instance.getUiConfig().getColor(UiConfig.BACKGROUND_COLOR); - if (color != null) { setBackground(color); - bookPane.setBackground(color); + pane.setBackground(color); } - JScrollPane scroll = new JScrollPane(bookPane); + JScrollPane scroll = new JScrollPane(pane); scroll.getVerticalScrollBar().setUnitIncrement(16); add(scroll, BorderLayout.CENTER); pgBar = new ProgressBar(); add(pgBar, BorderLayout.SOUTH); - refreshBooks(type); setJMenuBar(createMenu()); + booksByType = new HashMap(); + booksByAuthor = new HashMap(); + + addBookPane(type, true); + refreshBooks(); + setVisible(true); } /** - * Refresh the list of {@link LocalReaderBook}s from disk. + * Add a new {@link LocalReaderGroup} on the frame to display the books of + * the selected type or author. * + * @param value + * the author or the type * @param type - * the type of {@link Story} to load, or NULL for all types + * TRUE for type, FALSE for author */ - private void refreshBooks(String type) { - this.type = type; - stories = Instance.getLibrary().getList(type); - books.clear(); - bookPane.invalidate(); - bookPane.removeAll(); - for (MetaData meta : stories) { - LocalReaderBook book = new LocalReaderBook(meta, - reader.isCached(meta.getLuid())); - if (color != null) { - book.setBackground(color); + private void addBookPane(String value, boolean type) { + if (value == null) { + if (type) { + for (String tt : Instance.getLibrary().getTypes()) { + if (tt != null) { + addBookPane(tt, type); + } + } + } else { + for (String tt : Instance.getLibrary().getAuthors()) { + if (tt != null) { + addBookPane(tt, type); + } + } } - books.add(book); + return; + } - book.addActionListener(new BookActionListener() { - public void select(LocalReaderBook book) { - selectedBook = book; - for (LocalReaderBook abook : books) { - abook.setSelected(abook == book); - } - } + LocalReaderGroup bookPane = new LocalReaderGroup(reader, value, color); + if (type) { + booksByType.put(bookPane, value); + } else { + booksByAuthor.put(bookPane, value); + } - public void popupRequested(LocalReaderBook book, MouseEvent e) { - JPopupMenu popup = new JPopupMenu(); - popup.add(createMenuItemOpenBook()); - popup.addSeparator(); - popup.add(createMenuItemExport()); - popup.add(createMenuItemRefresh()); - popup.addSeparator(); - popup.add(createMenuItemDelete()); - popup.show(e.getComponent(), e.getX(), e.getY()); - } + this.invalidate(); + pane.invalidate(); + pane.add(bookPane); + pane.validate(); + this.validate(); - public void action(final LocalReaderBook book) { - openBook(book); - } - }); + bookPane.setActionListener(new BookActionListener() { + public void select(LocalReaderBook book) { + selectedBook = book; + } - bookPane.add(book); + public void popupRequested(LocalReaderBook book, MouseEvent e) { + JPopupMenu popup = new JPopupMenu(); + popup.add(createMenuItemOpenBook()); + popup.addSeparator(); + popup.add(createMenuItemExport()); + popup.add(createMenuItemClearCache()); + popup.add(createMenuItemRedownload()); + popup.addSeparator(); + popup.add(createMenuItemDelete()); + popup.show(e.getComponent(), e.getX(), e.getY()); + } + + public void action(final LocalReaderBook book) { + openBook(book); + } + }); + } + + private void removeBookPanes() { + booksByType.clear(); + booksByAuthor.clear(); + pane.invalidate(); + this.invalidate(); + pane.removeAll(); + pane.validate(); + this.validate(); + } + + /** + * Refresh the list of {@link LocalReaderBook}s from disk. + * + * @param type + * the type of {@link Story} to load, or NULL for all types + */ + private void refreshBooks() { + for (LocalReaderGroup group : booksByType.keySet()) { + List stories = Instance.getLibrary().getListByType( + booksByType.get(group)); + group.refreshBooks(stories, words); + } + + for (LocalReaderGroup group : booksByAuthor.keySet()) { + List stories = Instance.getLibrary().getListByAuthor( + booksByAuthor.get(group)); + group.refreshBooks(stories, words); } - bookPane.validate(); - bookPane.repaint(); + pane.repaint(); + this.repaint(); } /** @@ -165,13 +216,13 @@ class LocalReaderFrame extends JFrame { JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); - JMenuItem imprt = new JMenuItem("Import URL", KeyEvent.VK_U); + JMenuItem imprt = new JMenuItem("Import URL...", KeyEvent.VK_U); imprt.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { imprt(true); } }); - JMenuItem imprtF = new JMenuItem("Import File", KeyEvent.VK_F); + JMenuItem imprtF = new JMenuItem("Import File...", KeyEvent.VK_F); imprtF.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { imprt(false); @@ -198,7 +249,8 @@ class LocalReaderFrame extends JFrame { JMenu edit = new JMenu("Edit"); edit.setMnemonic(KeyEvent.VK_E); - edit.add(createMenuItemRefresh()); + edit.add(createMenuItemClearCache()); + edit.add(createMenuItemRedownload()); edit.addSeparator(); edit.add(createMenuItemDelete()); @@ -206,24 +258,72 @@ class LocalReaderFrame extends JFrame { JMenu view = new JMenu("View"); view.setMnemonic(KeyEvent.VK_V); + JMenuItem vauthors = new JMenuItem("Author"); + vauthors.setMnemonic(KeyEvent.VK_A); + vauthors.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + words = false; + refreshBooks(); + } + }); + view.add(vauthors); + JMenuItem vwords = new JMenuItem("Word count"); + vwords.setMnemonic(KeyEvent.VK_W); + vwords.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + words = true; + refreshBooks(); + } + }); + view.add(vwords); + bar.add(view); + + JMenu sources = new JMenu("Sources"); + sources.setMnemonic(KeyEvent.VK_S); List tt = Instance.getLibrary().getTypes(); tt.add(0, null); for (final String type : tt) { - JMenuItem item = new JMenuItem(type == null ? "All books" : type); + JMenuItem item = new JMenuItem(type == null ? "All" : type); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - refreshBooks(type); + removeBookPanes(); + addBookPane(type, true); + refreshBooks(); } }); - view.add(item); + sources.add(item); if (type == null) { - view.addSeparator(); + sources.addSeparator(); } } - bar.add(view); + bar.add(sources); + + JMenu authors = new JMenu("Authors"); + authors.setMnemonic(KeyEvent.VK_A); + + List aa = Instance.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() { + public void actionPerformed(ActionEvent e) { + removeBookPanes(); + addBookPane(author, false); + refreshBooks(); + } + }); + authors.add(item); + + if (author == null || author.isEmpty()) { + authors.addSeparator(); + } + } + + bar.add(authors); return bar; } @@ -241,6 +341,7 @@ class LocalReaderFrame extends JFrame { for (OutputType type : OutputType.values()) { String ext = type.getDefaultExtension(false); String desc = type.getDesc(false); + if (ext == null || ext.isEmpty()) { filters.put(createAllFilter(desc), type); } else { @@ -266,20 +367,24 @@ class LocalReaderFrame extends JFrame { public void actionPerformed(ActionEvent e) { if (selectedBook != null) { fc.showDialog(LocalReaderFrame.this, "Save"); - final OutputType type = filters.get(fc.getFileFilter()); - final String path = fc.getSelectedFile().getAbsolutePath() - + type.getDefaultExtension(false); - final Progress pg = new Progress(); - outOfUi(pg, new Runnable() { - public void run() { - try { - Instance.getLibrary().export( - selectedBook.getLuid(), type, path, pg); - } catch (IOException e) { - Instance.syserr(e); + if (fc.getSelectedFile() != null) { + final OutputType type = filters.get(fc.getFileFilter()); + final String path = fc.getSelectedFile() + .getAbsolutePath() + + type.getDefaultExtension(false); + final Progress pg = new Progress(); + outOfUi(pg, new Runnable() { + public void run() { + try { + Instance.getLibrary().export( + selectedBook.getMeta().getLuid(), + type, path, pg); + } catch (IOException e) { + Instance.syserr(e); + } } - } - }); + }); + } } } }); @@ -315,14 +420,14 @@ class LocalReaderFrame extends JFrame { * * @return the item */ - private JMenuItem createMenuItemRefresh() { + private JMenuItem createMenuItemClearCache() { JMenuItem refresh = new JMenuItem("Clear cache", KeyEvent.VK_C); refresh.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (selectedBook != null) { outOfUi(null, new Runnable() { public void run() { - reader.refresh(selectedBook.getLuid()); + reader.refresh(selectedBook.getMeta().getLuid()); selectedBook.setCached(false); SwingUtilities.invokeLater(new Runnable() { public void run() { @@ -338,6 +443,29 @@ class LocalReaderFrame extends JFrame { return refresh; } + /** + * Create the redownload (then delete original) menu item. + * + * @return the item + */ + private JMenuItem createMenuItemRedownload() { + JMenuItem refresh = new JMenuItem("Redownload", KeyEvent.VK_R); + refresh.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (selectedBook != null) { + imprt(selectedBook.getMeta().getUrl(), new Runnable() { + public void run() { + reader.delete(selectedBook.getMeta().getLuid()); + selectedBook = null; + } + }); + } + } + }); + + return refresh; + } + /** * Create the delete menu item. * @@ -350,11 +478,11 @@ class LocalReaderFrame extends JFrame { if (selectedBook != null) { outOfUi(null, new Runnable() { public void run() { - reader.delete(selectedBook.getLuid()); + reader.delete(selectedBook.getMeta().getLuid()); selectedBook = null; SwingUtilities.invokeLater(new Runnable() { public void run() { - refreshBooks(type); + refreshBooks(); } }); } @@ -395,7 +523,7 @@ class LocalReaderFrame extends JFrame { outOfUi(pg, new Runnable() { public void run() { try { - reader.open(book.getLuid(), pg); + reader.open(book.getMeta().getLuid(), pg); SwingUtilities.invokeLater(new Runnable() { public void run() { book.setCached(true); @@ -424,15 +552,11 @@ class LocalReaderFrame extends JFrame { private void outOfUi(final Progress pg, final Runnable run) { pgBar.setProgress(pg); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - setEnabled(false); - pgBar.addActioListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - pgBar.setProgress(null); - setEnabled(true); - } - }); + setEnabled(false); + pgBar.addActioListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + pgBar.setProgress(null); + setEnabled(true); } }); @@ -454,6 +578,8 @@ class LocalReaderFrame extends JFrame { /** * Import a {@link Story} into the main {@link Library}. + *

+ * Should be called inside the UI thread. * * @param askUrl * TRUE for an {@link URL}, false for a {@link File} @@ -461,48 +587,80 @@ class LocalReaderFrame extends JFrame { private void imprt(boolean askUrl) { JFileChooser fc = new JFileChooser(); - final String url; + Object url; if (askUrl) { + String clipboard = ""; + try { + clipboard = ("" + Toolkit.getDefaultToolkit() + .getSystemClipboard().getData(DataFlavor.stringFlavor)) + .trim(); + } catch (Exception e) { + // No data will be handled + } + + if (clipboard == null || !clipboard.startsWith("http")) { + clipboard = ""; + } + url = JOptionPane.showInputDialog(LocalReaderFrame.this, "url of the story to import?", "Importing from URL", - JOptionPane.QUESTION_MESSAGE); + JOptionPane.QUESTION_MESSAGE, null, null, clipboard); } else if (fc.showOpenDialog(this) != JFileChooser.CANCEL_OPTION) { url = fc.getSelectedFile().getAbsolutePath(); } else { url = null; } - if (url != null && !url.isEmpty()) { - final Progress pg = new Progress("Importing " + url); - outOfUi(pg, new Runnable() { - public void run() { - Exception ex = null; - try { - Instance.getLibrary() - .imprt(BasicReader.getUrl(url), pg); - } catch (IOException e) { - ex = e; - } + if (url != null && !url.toString().isEmpty()) { + imprt(url.toString(), null); + } + } - final Exception e = ex; + /** + * Actually import the {@link Story} into the main {@link Library}. + *

+ * Should be called inside the UI thread. + * + * @param url + * the {@link Story} to import by {@link URL} + * @param onSuccess + * Action to execute on success + */ + private void imprt(final String url, final Runnable onSuccess) { + final Progress pg = new Progress("Importing " + url); + outOfUi(pg, new Runnable() { + public void run() { + Exception ex = null; + try { + Instance.getLibrary().imprt(BasicReader.getUrl(url), pg); + } catch (IOException e) { + ex = e; + } - final boolean ok = (e == null); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - if (!ok) { - JOptionPane.showMessageDialog( - LocalReaderFrame.this, - "Cannot import: " + url, - e.getMessage(), - JOptionPane.ERROR_MESSAGE); - } else { - refreshBooks(type); + final Exception e = ex; + + final boolean ok = (e == null); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (!ok) { + Instance.syserr(e); + JOptionPane.showMessageDialog( + LocalReaderFrame.this, "Cannot import: " + + url, e.getMessage(), + JOptionPane.ERROR_MESSAGE); + + setEnabled(true); + } else { + refreshBooks(); + if (onSuccess != null) { + onSuccess.run(); + refreshBooks(); } } - }); - } - }); - } + } + }); + } + }); } /** @@ -518,15 +676,13 @@ class LocalReaderFrame extends JFrame { */ @Override public void setEnabled(boolean b) { - for (LocalReaderBook book : books) { - book.setEnabled(b); - book.repaint(); - } - bar.setEnabled(b); - bookPane.setEnabled(b); - bookPane.repaint(); - + for (LocalReaderGroup group : booksByType.keySet()) { + group.setEnabled(b); + } + for (LocalReaderGroup group : booksByAuthor.keySet()) { + group.setEnabled(b); + } super.setEnabled(b); repaint(); }