From 8a570f38228c9d5610cae5f6569d7c60ff704cf2 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sat, 18 Apr 2020 17:01:38 +0200 Subject: [PATCH] ImporterFrame usage --- src/be/nikiroo/fanfix_swing/Actions.java | 12 +- .../nikiroo/fanfix_swing/gui/BooksPanel.java | 36 +- .../fanfix_swing/gui/ImporterFrame.java | 85 -- .../nikiroo/fanfix_swing/gui/MainFrame.java | 8 +- .../nikiroo/fanfix_swing/gui/SearchBar.java | 9 +- .../gui/book/BookCoverImager.java | 2 +- .../fanfix_swing/gui/book/BookLine.java | 2 +- .../fanfix_swing/gui/book/BookPopup.java | 772 ------------------ .../gui/importer/ImporterFrame.java | 253 ++++++ .../gui/importer/ImporterItem.java | 171 ++++ 10 files changed, 466 insertions(+), 884 deletions(-) delete mode 100644 src/be/nikiroo/fanfix_swing/gui/ImporterFrame.java delete mode 100644 src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java create mode 100644 src/be/nikiroo/fanfix_swing/gui/importer/ImporterFrame.java create mode 100644 src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java diff --git a/src/be/nikiroo/fanfix_swing/Actions.java b/src/be/nikiroo/fanfix_swing/Actions.java index ff635b7a..4c26e08b 100644 --- a/src/be/nikiroo/fanfix_swing/Actions.java +++ b/src/be/nikiroo/fanfix_swing/Actions.java @@ -148,8 +148,8 @@ public class Actions { Process proc = null; if (program == null) { boolean ok = false; - for (String starter : new String[] { "xdg-open", "see", - "start", "run", "open" }) { + for (String starter : new String[] { "xdg-open", "see", "start", + "run", "open" }) { try { Instance.getInstance().getTraceHandler() .trace("starting external program: " + starter); @@ -195,7 +195,7 @@ public class Actions { * Action to execute on success */ static public void imprt(final Container parent, final String url, - final Progress pg, final Runnable onSuccess) { + Progress pg, final Runnable onSuccess) { final Progress fpg = pg; new SwingWorker() { @Override @@ -206,14 +206,14 @@ public class Actions { try { Instance.getInstance().getLibrary() - .imprt(BasicReader.getUrl(url), fpg); + .imprt(BasicReader.getUrl(url), pg); - fpg.done(); + pg.done(); if (onSuccess != null) { onSuccess.run(); } } catch (IOException e) { - fpg.done(); + pg.done(); if (e instanceof UnknownHostException) { UiHelper.error(parent, Instance.getInstance().getTransGui().getString( diff --git a/src/be/nikiroo/fanfix_swing/gui/BooksPanel.java b/src/be/nikiroo/fanfix_swing/gui/BooksPanel.java index 20e28af1..3f4094bd 100644 --- a/src/be/nikiroo/fanfix_swing/gui/BooksPanel.java +++ b/src/be/nikiroo/fanfix_swing/gui/BooksPanel.java @@ -36,10 +36,16 @@ import be.nikiroo.fanfix_swing.gui.utils.UiHelper; public class BooksPanel extends ListenerPanel { private class ListModel extends DefaultListModel { + public void fireElementChanged(int index) { + if (index >= 0) { + fireContentsChanged(this, index, index); + } + } + public void fireElementChanged(BookInfo element) { int index = indexOf(element); if (index >= 0) { - fireContentsChanged(element, index, index); + fireContentsChanged(this, index, index); } } } @@ -55,19 +61,19 @@ public class BooksPanel extends ListenerPanel { private int hoveredIndex = -1; private ListModel data = new ListModel(); private DelayWorker bookCoverUpdater; - - private SearchBar searchBar; + private String filter = ""; public BooksPanel(boolean listMode) { setLayout(new BorderLayout()); - searchBar = new SearchBar(); - add(searchBar, BorderLayout.NORTH); + final SearchBar search = new SearchBar(); + add(search, BorderLayout.NORTH); - searchBar.addActionListener(new ActionListener() { + search.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - filter(searchBar.getText()); + filter = search.getText(); + filter(); } }); @@ -113,11 +119,10 @@ public class BooksPanel extends ListenerPanel { this.bookInfos.addAll(bookInfos); bookCoverUpdater.clear(); - filter(searchBar.getText()); + filter(); } - // cannot be NULL - private void filter(String filter) { + private void filter() { data.clear(); for (BookInfo bookInfo : bookInfos) { if (bookInfo.getMainInfo() == null || filter.isEmpty() @@ -166,10 +171,16 @@ public class BooksPanel extends ListenerPanel { book.setCached(cached); fireElementChanged(book); } - + + @Override public void fireElementChanged(BookInfo book) { data.fireElementChanged(book); } + + @Override + public void removeElement(BookInfo book) { + data.removeElement(book); + } @Override public List getSelected() { @@ -228,8 +239,9 @@ public class BooksPanel extends ListenerPanel { return; if (hoveredIndex > -1) { + int index = hoveredIndex; hoveredIndex = -1; - list.repaint(); + data.fireElementChanged(index); } } diff --git a/src/be/nikiroo/fanfix_swing/gui/ImporterFrame.java b/src/be/nikiroo/fanfix_swing/gui/ImporterFrame.java deleted file mode 100644 index d4ab526c..00000000 --- a/src/be/nikiroo/fanfix_swing/gui/ImporterFrame.java +++ /dev/null @@ -1,85 +0,0 @@ -package be.nikiroo.fanfix_swing.gui; - -import java.awt.Container; -import java.awt.Toolkit; -import java.awt.datatransfer.DataFlavor; -import java.io.File; -import java.net.URL; - -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JOptionPane; - -import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.bundles.StringIdGui; -import be.nikiroo.fanfix.library.LocalLibrary; -import be.nikiroo.fanfix_swing.Actions; -import be.nikiroo.utils.Progress; - -public class ImporterFrame extends JFrame { - public ImporterFrame() { - - } - - /** - * Ask for and import an {@link URL} into the main {@link LocalLibrary}. - *

- * Should be called inside the UI thread. - * - * @param parent - * a container we can use to display the {@link URL} chooser and - * to show error messages if any - * @param onSuccess - * Action to execute on success - */ - public void imprtUrl(final Container parent, final Runnable onSuccess) { - 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.startsWith("https://"))) { - clipboard = ""; - } - - Object url = JOptionPane.showInputDialog(parent, - Instance.getInstance().getTransGui() - .getString(StringIdGui.SUBTITLE_IMPORT_URL), - Instance.getInstance().getTransGui() - .getString(StringIdGui.TITLE_IMPORT_URL), - JOptionPane.QUESTION_MESSAGE, null, null, clipboard); - - Progress pg = null; - if (url != null && !url.toString().isEmpty()) { - Actions.imprt(parent, url.toString(), pg, onSuccess); - } - } - - /** - * Ask for and import a {@link File} into the main {@link LocalLibrary}. - *

- * Should be called inside the UI thread. - * - * @param parent - * a container we can use to display the {@link File} chooser and - * to show error messages if any - * @param onSuccess - * Action to execute on success - */ - - public void imprtFile(final Container parent, final Runnable onSuccess) { - JFileChooser fc = new JFileChooser(); - - Progress pg = null; - if (fc.showOpenDialog(parent) != JFileChooser.CANCEL_OPTION) { - Object url = fc.getSelectedFile().getAbsolutePath(); - if (url != null && !url.toString().isEmpty()) { - Actions.imprt(parent, url.toString(), pg, onSuccess); - } - } - } -} diff --git a/src/be/nikiroo/fanfix_swing/gui/MainFrame.java b/src/be/nikiroo/fanfix_swing/gui/MainFrame.java index 313f086e..948f3ccd 100644 --- a/src/be/nikiroo/fanfix_swing/gui/MainFrame.java +++ b/src/be/nikiroo/fanfix_swing/gui/MainFrame.java @@ -12,12 +12,14 @@ import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JSplitPane; +import be.nikiroo.fanfix_swing.gui.importer.ImporterFrame; import be.nikiroo.utils.Version; public class MainFrame extends JFrame { private BooksPanel books; private DetailsPanel details; private BrowserPanel browser; + private ImporterFrame importer = new ImporterFrame(); public MainFrame(boolean sidePanel, boolean detailsPanel) { super("Fanfix " + Version.getCurrentVersion()); @@ -94,8 +96,7 @@ public class MainFrame extends JFrame { item1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - // TODO: correctly use the importer (wip) - new ImporterFrame().imprtUrl(MainFrame.this, new Runnable() { + importer.imprtUrl(MainFrame.this, new Runnable() { @Override public void run() { browser.reloadData(); @@ -112,8 +113,7 @@ public class MainFrame extends JFrame { item2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - // TODO: correctly use the importer (wip) - new ImporterFrame().imprtFile(MainFrame.this, new Runnable() { + importer.imprtFile(MainFrame.this, new Runnable() { @Override public void run() { browser.reloadData(); diff --git a/src/be/nikiroo/fanfix_swing/gui/SearchBar.java b/src/be/nikiroo/fanfix_swing/gui/SearchBar.java index eb0c5876..0be2f080 100644 --- a/src/be/nikiroo/fanfix_swing/gui/SearchBar.java +++ b/src/be/nikiroo/fanfix_swing/gui/SearchBar.java @@ -102,11 +102,14 @@ public class SearchBar extends ListenerPanel { } /** - * Return the current text displayed by this {@link SearchBar}. + * Return the current text displayed by this {@link SearchBar}, or an empty + * {@link String} if none. * - * @return the text + * @return the text, cannot be NULL */ public String getText() { - return text.getText(); + // Should usually not be NULL, but not impossible + String text = this.text.getText(); + return text == null ? "" : text; } } diff --git a/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java b/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java index 100b79f0..2b0795f6 100644 --- a/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java +++ b/src/be/nikiroo/fanfix_swing/gui/book/BookCoverImager.java @@ -79,7 +79,7 @@ class BookCoverImager { static public void paintOverlay(Graphics g, boolean enabled, boolean selected, boolean hovered, boolean cached) { Rectangle clip = g.getClipBounds(); - if (clip.getWidth() <= 0 || clip.getHeight() <= 0) { + if (clip == null || clip.getWidth() <= 0 || clip.getHeight() <= 0) { return; } diff --git a/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java b/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java index d601dd6c..4e2e3f4c 100644 --- a/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java +++ b/src/be/nikiroo/fanfix_swing/gui/book/BookLine.java @@ -23,7 +23,7 @@ public class BookLine extends JPanel { private static final int MAX_DISPLAY_SIZE = 40; - /** Colour used for the seconday item (author/word count). */ + /** Colour used for the secondary item (author/word count). */ protected static final Color AUTHOR_COLOR = new Color(128, 128, 128); private boolean selected; diff --git a/src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java b/src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java deleted file mode 100644 index 80827c07..00000000 --- a/src/be/nikiroo/fanfix_swing/gui/book/BookPopup.java +++ /dev/null @@ -1,772 +0,0 @@ -package be.nikiroo.fanfix_swing.gui.book; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPopupMenu; -import javax.swing.SwingWorker; -import javax.swing.filechooser.FileFilter; -import javax.swing.filechooser.FileNameExtensionFilter; - -import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.bundles.Config; -import be.nikiroo.fanfix.bundles.StringIdGui; -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.output.BasicOutput.OutputType; -import be.nikiroo.fanfix_swing.Actions; -import be.nikiroo.fanfix_swing.gui.utils.UiHelper; -import be.nikiroo.utils.Progress; -import be.nikiroo.utils.ui.ConfigEditor; - -public class BookPopup extends JPopupMenu { - public abstract interface Informer { - - // not null - public List getSelected(); - - public void setCached(BookInfo book, boolean cached); - - public BookInfo getUniqueSelected(); - - public void fireElementChanged(BookInfo book); - - public void invalidateCache(); - } - - /** - * The different modification actions you can use on {@link Story} items. - * - * @author niki - */ - private enum ChangeAction { - /** Change the source/type, that is, move it to another source. */ - SOURCE, - /** Change its name. */ - TITLE, - /** Change its author. */ - AUTHOR - } - - // be careful with that - private BasicLibrary lib; - - private Informer informer; - - public BookPopup(BasicLibrary lib, Informer informer) { - this.lib = lib; - this.informer = informer; - - Status status = lib.getStatus(); - add(createMenuItemOpenBook()); - addSeparator(); - add(createMenuItemExport()); - if (status.isWritable()) { - add(createMenuItemMoveTo()); - add(createMenuItemSetCoverForSource()); - add(createMenuItemSetCoverForAuthor()); - } - add(createMenuItemDownloadToCache()); - add(createMenuItemClearCache()); - if (status.isWritable()) { - add(createMenuItemRedownload()); - addSeparator(); - add(createMenuItemRename()); - add(createMenuItemSetAuthor()); - addSeparator(); - add(createMenuItemDelete()); - } - addSeparator(); - add(createMenuItemProperties()); - } - - private String trans(StringIdGui id) { - return Instance.getInstance().getTransGui().getString(id); - } - - /** - * Create the Fanfix Configuration menu item. - * - * @return the item - */ - private JMenuItem createMenuItemConfig() { - final String title = trans(StringIdGui.TITLE_CONFIG); - JMenuItem item = new JMenuItem(title); - item.setMnemonic(KeyEvent.VK_F); - - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ConfigEditor ed = new ConfigEditor(Config.class, - Instance.getInstance().getConfig(), - trans(StringIdGui.SUBTITLE_CONFIG)); - JFrame frame = new JFrame(title); - frame.add(ed); - frame.setSize(850, 600); - frame.setVisible(true); - } - }); - - return item; - } - - /** - * Create the UI Configuration menu item. - * - * @return the item - */ - private JMenuItem createMenuItemUiConfig() { - final String title = trans(StringIdGui.TITLE_CONFIG_UI); - JMenuItem item = new JMenuItem(title); - item.setMnemonic(KeyEvent.VK_U); - - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ConfigEditor ed = new ConfigEditor( - UiConfig.class, Instance.getInstance().getUiConfig(), - trans(StringIdGui.SUBTITLE_CONFIG_UI)); - JFrame frame = new JFrame(title); - frame.add(ed); - frame.setSize(800, 600); - frame.setVisible(true); - } - }); - - return item; - } - - /** - * Create the export menu item. - * - * @return the item - */ - private JMenuItem createMenuItemExport() { - - // TODO: allow dir for multiple selection? - - final JFileChooser fc = new JFileChooser(); - fc.setAcceptAllFileFilterUsed(false); - - // Add the "ALL" filters first, then the others - final Map otherFilters = new HashMap(); - for (OutputType type : OutputType.values()) { - String ext = type.getDefaultExtension(false); - String desc = type.getDesc(false); - - if (ext == null || ext.isEmpty()) { - fc.addChoosableFileFilter(createAllFilter(desc)); - } else { - otherFilters.put(new FileNameExtensionFilter(desc, ext), type); - } - } - - for (Entry entry : otherFilters.entrySet()) { - fc.addChoosableFileFilter(entry.getKey()); - } - // - - JMenuItem export = new JMenuItem(trans(StringIdGui.MENU_FILE_EXPORT), - KeyEvent.VK_S); - export.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final BookInfo book = informer.getUniqueSelected(); - if (book != null) { - fc.showDialog(BookPopup.this.getParent(), - trans(StringIdGui.TITLE_SAVE)); - if (fc.getSelectedFile() != null) { - final OutputType type = otherFilters - .get(fc.getFileFilter()); - final String path = fc.getSelectedFile() - .getAbsolutePath() - + type.getDefaultExtension(false); - final Progress pg = new Progress(); - - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - lib.export(book.getMeta().getLuid(), type, path, - pg); - return null; - } - - @Override - protected void done() { - try { - get(); - } catch (Exception e) { - UiHelper.error(BookPopup.this.getParent(), - e.getLocalizedMessage(), - "IOException", e); - } - } - }.execute(); - } - } - } - }); - - return export; - } - - /** - * Create a {@link FileFilter} that accepts all files and return the given - * description. - * - * @param desc - * the description - * - * @return the filter - */ - private FileFilter createAllFilter(final String desc) { - return new FileFilter() { - @Override - public String getDescription() { - return desc; - } - - @Override - public boolean accept(File f) { - return true; - } - }; - } - - /** - * Create the refresh (delete cache) menu item. - * - * @return the item - */ - private JMenuItem createMenuItemClearCache() { - JMenuItem refresh = new JMenuItem( - trans(StringIdGui.MENU_EDIT_CLEAR_CACHE), KeyEvent.VK_C); - refresh.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final List selected = informer.getSelected(); - if (!selected.isEmpty()) { - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - for (BookInfo book : selected) { - lib.clearFromCache(book.getMeta().getLuid()); - BookCoverImager.clearIcon(book); - } - return null; - } - - @Override - protected void done() { - try { - get(); - for (BookInfo book : selected) { - informer.setCached(book, false); - } - } catch (Exception e) { - UiHelper.error(BookPopup.this.getParent(), - e.getLocalizedMessage(), "IOException", - e); - } - } - }.execute(); - } - } - }); - - return refresh; - } - - /** - * Create the "move to" menu item. - * - * @return the item - */ - private JMenuItem createMenuItemMoveTo() { - JMenu changeTo = new JMenu(trans(StringIdGui.MENU_FILE_MOVE_TO)); - changeTo.setMnemonic(KeyEvent.VK_M); - - Map> groupedSources = new HashMap>(); - try { - groupedSources = lib.getSourcesGrouped(); - } catch (IOException e) { - UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), - "IOException", e); - } - - JMenuItem item = new JMenuItem( - trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_TYPE)); - item.addActionListener(createMoveAction(ChangeAction.SOURCE, null)); - changeTo.add(item); - changeTo.addSeparator(); - - for (final String type : groupedSources.keySet()) { - List list = groupedSources.get(type); - if (list.size() == 1 && list.get(0).isEmpty()) { - item = new JMenuItem(type); - item.addActionListener( - createMoveAction(ChangeAction.SOURCE, type)); - changeTo.add(item); - } else { - JMenu dir = new JMenu(type); - for (String sub : list) { - // " " instead of "" for the visual height - String itemName = sub.isEmpty() ? " " : sub; - String actualType = type; - if (!sub.isEmpty()) { - actualType += "/" + sub; - } - - item = new JMenuItem(itemName); - item.addActionListener( - createMoveAction(ChangeAction.SOURCE, actualType)); - dir.add(item); - } - changeTo.add(dir); - } - } - - return changeTo; - } - - /** - * Create the "set author" menu item. - * - * @return the item - */ - private JMenuItem createMenuItemSetAuthor() { - JMenu changeTo = new JMenu(trans(StringIdGui.MENU_FILE_SET_AUTHOR)); - changeTo.setMnemonic(KeyEvent.VK_A); - - // New author - JMenuItem newItem = new JMenuItem( - trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_AUTHOR)); - changeTo.add(newItem); - changeTo.addSeparator(); - newItem.addActionListener(createMoveAction(ChangeAction.AUTHOR, null)); - - // Existing authors - Map> groupedAuthors; - - try { - groupedAuthors = lib.getAuthorsGrouped(); - } catch (IOException e) { - UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), - "IOException", e); - groupedAuthors = new HashMap>(); - - } - - if (groupedAuthors.size() > 1) { - for (String key : groupedAuthors.keySet()) { - JMenu group = new JMenu(key); - for (String value : groupedAuthors.get(key)) { - JMenuItem item = new JMenuItem(value.isEmpty() - ? trans(StringIdGui.MENU_AUTHORS_UNKNOWN) - : value); - item.addActionListener( - createMoveAction(ChangeAction.AUTHOR, value)); - group.add(item); - } - changeTo.add(group); - } - } else if (groupedAuthors.size() == 1) { - for (String value : groupedAuthors.values().iterator().next()) { - JMenuItem item = new JMenuItem(value.isEmpty() - ? trans(StringIdGui.MENU_AUTHORS_UNKNOWN) - : value); - item.addActionListener( - createMoveAction(ChangeAction.AUTHOR, value)); - changeTo.add(item); - } - } - - return changeTo; - } - - /** - * Create the "rename" menu item. - * - * @return the item - */ - private JMenuItem createMenuItemRename() { - JMenuItem changeTo = new JMenuItem(trans(StringIdGui.MENU_FILE_RENAME)); - changeTo.setMnemonic(KeyEvent.VK_R); - changeTo.addActionListener(createMoveAction(ChangeAction.TITLE, null)); - return changeTo; - } - - private ActionListener createMoveAction(final ChangeAction what, - final String type) { - return new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final List selected = informer.getSelected(); - if (!selected.isEmpty()) { - String changeTo = type; - if (type == null) { - String init = ""; - - if (selected.size() == 1) { - MetaData meta = selected.get(0).getMeta(); - if (what == ChangeAction.SOURCE) { - init = meta.getSource(); - } else if (what == ChangeAction.TITLE) { - init = meta.getTitle(); - } else if (what == ChangeAction.AUTHOR) { - init = meta.getAuthor(); - } - } - - Object rep = JOptionPane.showInputDialog( - BookPopup.this.getParent(), - trans(StringIdGui.SUBTITLE_MOVE_TO), - trans(StringIdGui.TITLE_MOVE_TO), - JOptionPane.QUESTION_MESSAGE, null, null, init); - - if (rep == null) { - return; - } - - changeTo = rep.toString(); - } - - final String fChangeTo = changeTo; - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - for (BookInfo book : selected) { - String luid = book.getMeta().getLuid(); - if (what == ChangeAction.SOURCE) { - lib.changeSource(luid, fChangeTo, null); - } else if (what == ChangeAction.TITLE) { - lib.changeTitle(luid, fChangeTo, null); - } else if (what == ChangeAction.AUTHOR) { - lib.changeAuthor(luid, fChangeTo, null); - } - } - - return null; - } - - @Override - protected void done() { - try { - // this can create new sources/authors, so a - // simple fireElementChanged is not - // enough, we need to clear the whole cache (for - // BrowserPanel for instance) - informer.invalidateCache(); - - // But we ALSO fire those, because they appear - // before the whole refresh... - for (BookInfo book : selected) { - informer.fireElementChanged(book); - } - - // TODO: also refresh the - // Sources/Authors(/Tags?) list - - // Even if problems occurred, still invalidate - // the cache - get(); - } catch (Exception e) { - UiHelper.error(BookPopup.this.getParent(), - e.getLocalizedMessage(), "IOException", - e); - } - } - }.execute(); - } - } - }; - } - - /** - * Create the re-download (then delete original) menu item. - * - * @return the item - */ - private JMenuItem createMenuItemRedownload() { - JMenuItem refresh = new JMenuItem( - trans(StringIdGui.MENU_EDIT_REDOWNLOAD), KeyEvent.VK_R); - refresh.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // final GuiReaderBook selectedBook = - // mainPanel.getSelectedBook(); - // if (selectedBook != null) { - // final MetaData meta = selectedBook.getInfo().getMeta(); - // mainPanel.imprt(meta.getUrl(), new MetaDataRunnable() { - // @Override - // public void run(MetaData newMeta) { - // if (!newMeta.getSource().equals(meta.getSource())) { - // reader.changeSource(newMeta.getLuid(), meta.getSource()); - // } - // } - // }, trans(StringIdGui.PROGRESS_CHANGE_SOURCE)); - // } - } - }); - - return refresh; - } - - /** - * Create the download to cache menu item. - * - * @return the item - */ - private JMenuItem createMenuItemDownloadToCache() { - JMenuItem refresh = new JMenuItem( - trans(StringIdGui.MENU_EDIT_DOWNLOAD_TO_CACHE), KeyEvent.VK_T); - refresh.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final List selected = informer.getSelected(); - - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - - final List luids = new LinkedList(); - for (BookInfo book : selected) { - switch (book.getType()) { - case STORY: - luids.add(book.getMeta().getLuid()); - break; - case SOURCE: - for (MetaData meta : lib.getList().filter( - book.getMainInfo(), null, null)) { - luids.add(meta.getLuid()); - } - break; - case AUTHOR: - for (MetaData meta : lib.getList().filter(null, - book.getMainInfo(), null)) { - luids.add(meta.getLuid()); - } - break; - case TAG: - for (MetaData meta : lib.getList().filter(null, - null, book.getMainInfo())) { - luids.add(meta.getLuid()); - } - break; - } - } - - // TODO: do something with pg? - final Progress pg = new Progress(); - pg.setMax(luids.size()); - for (String luid : luids) { - Progress pgStep = new Progress(); - pg.addProgress(pgStep, 1); - - lib.getFile(luid, pgStep); - } - - return null; - } - - @Override - protected void done() { - try { - get(); - for (BookInfo book : selected) { - informer.setCached(book, true); - } - } catch (Exception e) { - UiHelper.error(BookPopup.this.getParent(), - e.getLocalizedMessage(), "IOException", e); - } - } - }.execute(); - } - }); - - return refresh; - } - - /** - * Create the delete menu item. - * - * @return the item - */ - private JMenuItem createMenuItemDelete() { - JMenuItem delete = new JMenuItem(trans(StringIdGui.MENU_EDIT_DELETE), - KeyEvent.VK_D); - delete.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // final GuiReaderBook selectedBook = - // mainPanel.getSelectedBook(); - // if (selectedBook != null && selectedBook.getInfo().getMeta() - // != null) { - // - // final MetaData meta = selectedBook.getInfo().getMeta(); - // int rep = JOptionPane.showConfirmDialog(GuiReaderFrame.this, - // trans(StringIdGui.SUBTITLE_DELETE, meta.getLuid(), - // meta.getTitle()), - // trans(StringIdGui.TITLE_DELETE), - // JOptionPane.OK_CANCEL_OPTION); - // - // if (rep == JOptionPane.OK_OPTION) { - // mainPanel.outOfUi(null, true, new Runnable() { - // @Override - // public void run() { - // reader.delete(meta.getLuid()); - // mainPanel.unsetSelectedBook(); - // } - // }); - // } - // } - } - }); - - return delete; - } - - /** - * Create the properties menu item. - * - * @return the item - */ - private JMenuItem createMenuItemProperties() { - JMenuItem delete = new JMenuItem( - trans(StringIdGui.MENU_FILE_PROPERTIES), KeyEvent.VK_P); - delete.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // final GuiReaderBook selectedBook = - // mainPanel.getSelectedBook(); - // if (selectedBook != null) { - // mainPanel.outOfUi(null, false, new Runnable() { - // @Override - // public void run() { - // new GuiReaderPropertiesFrame(lib, - // selectedBook.getInfo().getMeta()) - // .setVisible(true); - // } - // }); - // } - } - }); - - return delete; - } - - /** - * Create the open menu item for a book, a source/type or an author. - * - * @return the item - */ - public JMenuItem createMenuItemOpenBook() { - JMenuItem open = new JMenuItem(trans(StringIdGui.MENU_FILE_OPEN), - KeyEvent.VK_O); - open.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final BookInfo book = informer.getUniqueSelected(); - if (book != null) { - Actions.openExternal(lib, book.getMeta(), - BookPopup.this.getParent(), new Runnable() { - @Override - public void run() { - informer.setCached(book, true); - } - }); - } - } - }); - - return open; - } - - /** - * Create the SetCover menu item for a book to change the linked source - * cover. - * - * @return the item - */ - private JMenuItem createMenuItemSetCoverForSource() { - JMenuItem open = new JMenuItem( - trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_SOURCE), - KeyEvent.VK_C); - open.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent ae) { - // final GuiReaderBook selectedBook = - // mainPanel.getSelectedBook(); - // if (selectedBook != null) { - // BasicLibrary lib = lib; - // String luid = selectedBook.getInfo().getMeta().getLuid(); - // String source = selectedBook.getInfo().getMeta().getSource(); - // - // try { - // lib.setSourceCover(source, luid); - // } catch (IOException e) { - // error(e.getLocalizedMessage(), "IOException", e); - // } - // - // GuiReaderBookInfo sourceInfo = - // GuiReaderBookInfo.fromSource(lib, source); - // GuiReaderCoverImager.clearIcon(sourceInfo); - // } - } - }); - - return open; - } - - /** - * Create the SetCover menu item for a book to change the linked source - * cover. - * - * @return the item - */ - private JMenuItem createMenuItemSetCoverForAuthor() { - JMenuItem open = new JMenuItem( - trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_AUTHOR), - KeyEvent.VK_A); - open.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent ae) { - // final GuiReaderBook selectedBook = - // mainPanel.getSelectedBook(); - // if (selectedBook != null) { - // String luid = selectedBook.getInfo().getMeta().getLuid(); - // String author = selectedBook.getInfo().getMeta().getAuthor(); - // - // try { - // lib.setAuthorCover(author, luid); - // } catch (IOException e) { - // error(e.getLocalizedMessage(), "IOException", e); - // } - // - // GuiReaderBookInfo authorInfo = - // GuiReaderBookInfo.fromAuthor(lib, author); - // GuiReaderCoverImager.clearIcon(authorInfo); - // } - } - }); - - return open; - } -} diff --git a/src/be/nikiroo/fanfix_swing/gui/importer/ImporterFrame.java b/src/be/nikiroo/fanfix_swing/gui/importer/ImporterFrame.java new file mode 100644 index 00000000..b59215ba --- /dev/null +++ b/src/be/nikiroo/fanfix_swing/gui/importer/ImporterFrame.java @@ -0,0 +1,253 @@ +package be.nikiroo.fanfix_swing.gui.importer; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; + +import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.bundles.StringIdGui; +import be.nikiroo.fanfix.library.LocalLibrary; +import be.nikiroo.fanfix.reader.BasicReader; +import be.nikiroo.fanfix.supported.BasicSupport; +import be.nikiroo.fanfix_swing.Actions; +import be.nikiroo.fanfix_swing.gui.SearchBar; +import be.nikiroo.fanfix_swing.gui.book.BookBlock; +import be.nikiroo.fanfix_swing.gui.book.BookInfo; +import be.nikiroo.fanfix_swing.gui.book.BookLine; +import be.nikiroo.utils.Progress; +import be.nikiroo.utils.Progress.ProgressListener; + +public class ImporterFrame extends JFrame { + private class ListModel extends DefaultListModel { + public void fireElementChanged(int index) { + if (index >= 0) { + fireContentsChanged(this, index, index); + } + } + + public void fireElementChanged(ImporterItem element) { + int index = indexOf(element); + if (index >= 0) { + fireContentsChanged(this, index, index); + } + } + } + + private JList list; + private ListModel data = new ListModel(); + private List items = new ArrayList(); + private String filter = ""; + private int hoveredIndex = -1; + + public ImporterFrame() { + setLayout(new BorderLayout()); + + list = new JList(data); + this.add(list, BorderLayout.CENTER); + + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.setSelectedIndex(0); + list.setCellRenderer(generateRenderer()); + list.setVisibleRowCount(5); + + list.addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseMoved(MouseEvent me) { + Point p = new Point(me.getX(), me.getY()); + int index = list.locationToIndex(p); + if (index != hoveredIndex) { + int oldIndex = hoveredIndex; + hoveredIndex = index; + data.fireElementChanged(oldIndex); + data.fireElementChanged(index); + } + } + }); + list.addMouseListener(new MouseAdapter() { + @Override + public void mouseExited(MouseEvent e) { + if (hoveredIndex > -1) { + int oldIndex = hoveredIndex; + hoveredIndex = -1; + data.fireElementChanged(oldIndex); + } + } + }); + + JPanel top = new JPanel(); + top.setLayout(new BorderLayout()); + + final SearchBar search = new SearchBar(); + top.add(search, BorderLayout.CENTER); + + search.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + filter = search.getText(); + filter(); + } + }); + + JButton clear = new JButton("Clear"); + top.add(clear, BorderLayout.EAST); + clear.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + boolean changed = false; + for (int i = 0; i < items.size(); i++) { + if (items.get(i).isDone()) { + items.remove(i--); + changed = true; + } + } + + if (changed) { + filter(); + } + } + }); + + this.add(top, BorderLayout.NORTH); + + setSize(800, 600); + } + + /** + * Ask for and import an {@link URL} into the main {@link LocalLibrary}. + *

+ * Should be called inside the UI thread. + * + * @param parent + * a container we can use to display the {@link URL} chooser and + * to show error messages if any + * @param onSuccess + * Action to execute on success + */ + public void imprtUrl(final Container parent, final Runnable onSuccess) { + 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.startsWith("https://"))) { + clipboard = ""; + } + + Object url = JOptionPane.showInputDialog(parent, + Instance.getInstance().getTransGui() + .getString(StringIdGui.SUBTITLE_IMPORT_URL), + Instance.getInstance().getTransGui() + .getString(StringIdGui.TITLE_IMPORT_URL), + JOptionPane.QUESTION_MESSAGE, null, null, clipboard); + + Progress pg = new Progress(); + String basename = null; + try { + BasicSupport support = BasicSupport + .getSupport(BasicReader.getUrl(url.toString())); + basename = support.getType().getSourceName(); + } catch (Exception e) { + } + + add(pg, basename); // TODO: what when null? + + if (url != null && !url.toString().isEmpty()) { + Actions.imprt(parent, url.toString(), pg, onSuccess); + } + // TODO what when not ok? + + setVisible(true); + } + + /** + * Ask for and import a {@link File} into the main {@link LocalLibrary}. + *

+ * Should be called inside the UI thread. + * + * @param parent + * a container we can use to display the {@link File} chooser and + * to show error messages if any + * @param onSuccess + * Action to execute on success + */ + + public void imprtFile(final Container parent, final Runnable onSuccess) { + JFileChooser fc = new JFileChooser(); + + Progress pg = new Progress(); + add(pg, "File"); + + if (fc.showOpenDialog(parent) != JFileChooser.CANCEL_OPTION) { + Object url = fc.getSelectedFile().getAbsolutePath(); + if (url != null && !url.toString().isEmpty()) { + Actions.imprt(parent, url.toString(), pg, onSuccess); + } + } + + setVisible(true); + } + + private void add(Progress pg, final String basename) { + final ImporterItem item = new ImporterItem(pg, basename); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + data.fireElementChanged(item); + } + }); + + items.add(item); + filter(); + } + + private void filter() { + data.clear(); + for (ImporterItem item : items) { + String text = item.getStoryName() + " " + item.getAction(); + if (filter.isEmpty() || text.isEmpty() + || text.toLowerCase().contains(filter.toLowerCase())) { + data.addElement(item); + } + } + list.repaint(); + } + + private ListCellRenderer generateRenderer() { + return new ListCellRenderer() { + @Override + public Component getListCellRendererComponent( + JList list, ImporterItem item, + int index, boolean isSelected, boolean cellHasFocus) { + item.setSelected(isSelected); + item.setHovered(index == hoveredIndex); + return item; + } + }; + } +} diff --git a/src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java b/src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java new file mode 100644 index 00000000..2e88c638 --- /dev/null +++ b/src/be/nikiroo/fanfix_swing/gui/importer/ImporterItem.java @@ -0,0 +1,171 @@ +package be.nikiroo.fanfix_swing.gui.importer; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.JLabel; +import javax.swing.SwingUtilities; + +import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel; +import be.nikiroo.utils.Progress; +import be.nikiroo.utils.Progress.ProgressListener; + +public class ImporterItem extends ListenerPanel { + static public final String CHANGE = "change"; + + private String basename = ""; + private String storyName = ""; + private String action = ""; + private double progress = -1; + + private boolean hovered; + private boolean selected; + private boolean done; + + private JLabel labelName; + private JLabel labelAction; + + public ImporterItem(Progress pg, String basename) { + this.basename = basename == null ? "" : basename; + + labelName = new JLabel(getStoryName()); + labelAction = new JLabel(getAction()); + + setDone(true); + setDone(false); // to trigger the colour change + + setLayout(new BorderLayout()); + add(labelName, BorderLayout.NORTH); + add(labelAction, BorderLayout.SOUTH); + + init(pg); + } + + static public Color getBackground(boolean enabled, boolean selected, + boolean hovered) { + Color color = new Color(255, 255, 255, 0); + if (!enabled) { + } else if (selected && !hovered) { + color = new Color(80, 80, 100, 40); + } else if (!selected && hovered) { + color = new Color(230, 230, 255, 100); + } else if (selected && hovered) { + color = new Color(200, 200, 255, 100); + } + + return color; + } + + public String getStoryName() { + return basename + ": " + storyName; + } + + public String getAction() { + // space is for the default size + if (done) { + return "Done"; + } + + return action.isEmpty() ? " " : action; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + if (this.selected != selected) { + this.selected = selected; + setBackground(getBackground(isEnabled(), selected, hovered)); + } + } + + public boolean isHovered() { + return hovered; + } + + public void setHovered(boolean hovered) { + if (this.hovered != hovered) { + this.hovered = hovered; + setBackground(getBackground(isEnabled(), selected, hovered)); + } + } + + @Override + public void setEnabled(boolean enabled) { + if (isEnabled() != enabled) { + super.setEnabled(enabled); + setBackground(getBackground(isEnabled(), selected, hovered)); + } + } + + public boolean isDone() { + return done; + } + + public void setDone(boolean done) { + if (this.done != done) { + this.done = done; + if (done) { + labelAction.setForeground(Color.green.darker()); + labelAction + .setFont(labelAction.getFont().deriveFont(Font.BOLD)); + } else { + labelAction.setForeground(Color.gray); + labelAction + .setFont(labelAction.getFont().deriveFont(Font.PLAIN)); + } + } + } + + private void init(final Progress pg) { + pg.addProgressListener(new ProgressListener() { + @Override + public void progress(Progress notUsed, String currentAction) { + // TODO: get/setSubject on Progress? + currentAction = currentAction == null ? "" : currentAction; + + if (storyName.isEmpty() + && !currentAction.equals("Initialising")) { + storyName = currentAction; + } + + if (storyName.equals(currentAction)) { + currentAction = ""; + } + + if (pg.getRelativeProgress() != progress + || !action.equals(currentAction)) { + progress = pg.getRelativeProgress(); + action = currentAction; + + // The rest must be done in the UI thead + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setDone(pg.isDone()); + labelName.setText(" " + getStoryName()); + labelAction.setText(" " + getAction()); + fireActionPerformed(CHANGE); + } + }); + } + } + }); + } + + @Override + public void paint(Graphics g) { + Rectangle clip = g.getClipBounds(); + if (!(clip == null || clip.getWidth() <= 0 || clip.getHeight() <= 0)) { + g.setColor(new Color(200, 200, 255, 128)); + g.fillRect(clip.x, clip.y, (int) Math.round(clip.width * progress), + clip.height); + } + + super.paint(g); + } +} \ No newline at end of file -- 2.27.0