package be.nikiroo.fanfix.reader.ui;
import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Font;
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;
-import java.awt.event.MouseEvent;
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.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
-import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
-import javax.swing.JPanel;
import javax.swing.JPopupMenu;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
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.BasicReader;
-import be.nikiroo.fanfix.reader.ui.GuiReaderBook.BookActionListener;
+import be.nikiroo.fanfix.reader.ui.GuiReaderMainPanel.FrameHelper;
+import be.nikiroo.fanfix.reader.ui.GuiReaderMainPanel.StoryRunnable;
import be.nikiroo.utils.Progress;
import be.nikiroo.utils.Version;
import be.nikiroo.utils.ui.ConfigEditor;
-import be.nikiroo.utils.ui.ProgressBar;
/**
* A {@link Frame} that will show a {@link GuiReaderBook} item for each
*
* @author niki
*/
-class GuiReaderFrame extends JFrame {
+class GuiReaderFrame extends JFrame implements FrameHelper {
private static final long serialVersionUID = 1L;
private GuiReader reader;
- private Map<GuiReaderGroup, String> booksByType;
- private Map<GuiReaderGroup, String> booksByAuthor;
- private JPanel pane;
- private Color color;
- private ProgressBar pgBar;
- private JMenuBar bar;
- private GuiReaderBook selectedBook;
- private boolean words; // words or authors (secondary info on books)
+ private GuiReaderMainPanel mainPanel;
/**
- * A {@link Runnable} with a {@link Story} parameter.
+ * The different modification actions you can use on {@link Story} items.
*
* @author niki
*/
- private interface StoryRunnable {
- /**
- * Run the action.
- *
- * @param story
- * the story
- */
- public void run(Story story);
+ private enum ChangeAction {
+ /** Change the source/type, that is, move it to another source. */
+ SOURCE,
+ /** Change its name. */
+ TITLE,
+ /** Change its author. */
+ AUTHOR
}
/**
this.reader = reader;
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ mainPanel = new GuiReaderMainPanel(this, type);
+
setSize(800, 600);
setLayout(new BorderLayout());
-
- pane = new JPanel();
- pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
-
- Integer icolor = Instance.getUiConfig().getColor(
- UiConfig.BACKGROUND_COLOR);
- if (icolor != null) {
- color = new Color(icolor);
- setBackground(color);
- pane.setBackground(color);
- }
-
- JScrollPane scroll = new JScrollPane(pane);
- 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);
- validate();
- setEnabled(true);
- }
- });
-
- pgBar.addUpdateListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- invalidate();
- validate();
- repaint();
- }
- });
-
- booksByType = new HashMap<GuiReaderGroup, String>();
- booksByAuthor = new HashMap<GuiReaderGroup, String>();
-
- pane.setVisible(false);
- final Progress pg = new Progress();
- final String typeF = type;
- outOfUi(pg, new Runnable() {
- @Override
- public void run() {
- 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 unavailable";
- break;
-
- default:
- err += "An error occured when contacting the library";
- break;
- }
-
- error(err, "Library error", null);
- }
- }
- });
-
- setVisible(true);
+ add(mainPanel, BorderLayout.CENTER);
}
- 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.
- *
- * @param value
- * the author or the type, or NULL to get all the
- * authors-or-types
- * @param type
- * TRUE for type, FALSE for author
- */
- private void addBookPane(String value, boolean type) {
- if (value == null) {
- if (type) {
- if (Instance.getUiConfig().getBoolean(UiConfig.SOURCE_PAGE,
- false)) {
- addSourcePanes();
- } else {
- for (String tt : reader.getLibrary().getSources()) {
- if (tt != null) {
- addBookPane(tt, type);
- }
- }
- }
- } else {
- for (String tt : reader.getLibrary().getAuthors()) {
- if (tt != null) {
- addBookPane(tt, type);
- }
- }
- }
-
- return;
- }
-
- GuiReaderGroup bookPane = new GuiReaderGroup(reader, value, color);
- if (type) {
- booksByType.put(bookPane, value);
- } else {
- booksByAuthor.put(bookPane, value);
- }
-
- 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.addSeparator();
- popup.add(createMenuItemExport());
- popup.add(createMenuItemMove(true));
- popup.add(createMenuItemSetCover());
- popup.add(createMenuItemClearCache());
- popup.add(createMenuItemRedownload());
- popup.addSeparator();
- popup.add(createMenuItemDelete());
- popup.addSeparator();
- popup.add(createMenuItemProperties());
- popup.show(e.getComponent(), e.getX(), e.getY());
- }
-
- @Override
- public void action(final GuiReaderBook book) {
- openBook(book);
- }
- });
+ @Override
+ public JPopupMenu createBookPopup() {
+ JPopupMenu popup = new JPopupMenu();
+ popup.add(createMenuItemOpenBook());
+ popup.addSeparator();
+ popup.add(createMenuItemExport());
+ popup.add(createMenuItemMoveTo(true));
+ popup.add(createMenuItemSetCoverForSource());
+ popup.add(createMenuItemSetCoverForAuthor());
+ popup.add(createMenuItemClearCache());
+ popup.add(createMenuItemRedownload());
+ popup.addSeparator();
+ popup.add(createMenuItemRename(true));
+ popup.add(createMenuItemSetAuthor(true));
+ popup.addSeparator();
+ popup.add(createMenuItemDelete());
+ popup.addSeparator();
+ popup.add(createMenuItemProperties());
+ return popup;
}
- private void removeBookPanes() {
- booksByType.clear();
- booksByAuthor.clear();
- pane.invalidate();
- this.invalidate();
- pane.removeAll();
- pane.validate();
- this.validate();
+ @Override
+ public JPopupMenu createSourceAuthorPopup() {
+ JPopupMenu popup = new JPopupMenu();
+ popup.add(createMenuItemOpenBook());
+ return popup;
}
- /**
- * Refresh the list of {@link GuiReaderBook}s from disk.
- */
- private void refreshBooks() {
- for (GuiReaderGroup group : booksByType.keySet()) {
- List<MetaData> stories = reader.getLibrary().getListBySource(
- booksByType.get(group));
- group.refreshBooks(stories, words);
- }
-
- for (GuiReaderGroup group : booksByAuthor.keySet()) {
- List<MetaData> stories = reader.getLibrary().getListByAuthor(
- booksByAuthor.get(group));
- group.refreshBooks(stories, words);
- }
+ @Override
+ public void createMenu(boolean libOk) {
+ invalidate();
- pane.repaint();
- this.repaint();
- }
-
- /**
- * Create the main menu bar.
- *
- * @param libOk
- * the library can be queried
- *
- * @return the bar
- */
- private JMenuBar createMenu(boolean libOk) {
- bar = new JMenuBar();
+ JMenuBar bar = new JMenuBar();
JMenu file = new JMenu("File");
file.setMnemonic(KeyEvent.VK_F);
imprt.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- imprt(true);
+ mainPanel.imprt(true);
}
});
JMenuItem imprtF = new JMenuItem("Import File...", KeyEvent.VK_F);
imprtF.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- imprt(false);
+ mainPanel.imprt(false);
}
});
JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X);
file.add(createMenuItemOpenBook());
file.add(createMenuItemExport());
- file.add(createMenuItemMove(libOk));
+ file.add(createMenuItemMoveTo(libOk));
file.addSeparator();
file.add(imprt);
file.add(imprtF);
file.addSeparator();
+ file.add(createMenuItemRename(libOk));
+ file.add(createMenuItemSetAuthor(libOk));
+ file.addSeparator();
file.add(exit);
bar.add(file);
vauthors.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- words = false;
- refreshBooks();
+ mainPanel.setWords(false);
+ mainPanel.refreshBooks();
}
});
view.add(vauthors);
vwords.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- words = true;
- refreshBooks();
+ mainPanel.setWords(true);
+ mainPanel.refreshBooks();
}
});
view.add(vwords);
bar.add(view);
+ Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
+ if (libOk) {
+ groupedSources = reader.getLibrary().getSourcesGrouped();
+ }
JMenu sources = new JMenu("Sources");
sources.setMnemonic(KeyEvent.VK_S);
+ populateMenuSA(sources, groupedSources, true);
+ bar.add(sources);
- List<String> tt = new ArrayList<String>();
+ Map<String, List<String>> goupedAuthors = new HashMap<String, List<String>>();
if (libOk) {
- tt.addAll(reader.getLibrary().getSources());
+ goupedAuthors = reader.getLibrary().getAuthorsGrouped();
}
- 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);
- refreshBooks();
- }
- });
- sources.add(item);
-
- if (type == null) {
- sources.addSeparator();
- }
- }
-
- bar.add(sources);
-
JMenu authors = new JMenu("Authors");
authors.setMnemonic(KeyEvent.VK_A);
-
- List<Entry<String, List<String>>> authorGroups = reader.getLibrary()
- .getAuthorsGrouped();
- if (authorGroups.size() > 1) {
- // Multiple groups
-
- // null -> "All" authors special item
- populateMenuAuthorList(authors, Arrays.asList((String) null));
-
- for (Entry<String, List<String>> group : authorGroups) {
- JMenu thisGroup = new JMenu(group.getKey());
- populateMenuAuthorList(thisGroup, group.getValue());
- authors.add(thisGroup);
- }
- } else {
- // Only one group
-
- // null -> "All" authors special item
- List<String> authorNames = new ArrayList<String>();
- authorNames.add(null);
- if (authorGroups.size() > 0) {
- authorNames.addAll(authorGroups.get(0).getValue());
- }
- populateMenuAuthorList(authors, authorNames);
- }
-
+ populateMenuSA(authors, goupedAuthors, false);
bar.add(authors);
JMenu options = new JMenu("Options");
options.add(createMenuItemUiConfig());
bar.add(options);
- return bar;
+ setJMenuBar(bar);
+ }
+
+ // "" = [unknown]
+ private void populateMenuSA(JMenu menu,
+ Map<String, List<String>> groupedValues, boolean type) {
+
+ // "All" and "Listing" special items
+ JMenuItem item = new JMenuItem("All");
+ item.addActionListener(getActionOpenList(type, false));
+ menu.add(item);
+ item = new JMenuItem("Listing");
+ item.addActionListener(getActionOpenList(type, true));
+ menu.add(item);
+ menu.addSeparator();
+
+ for (final String value : groupedValues.keySet()) {
+ List<String> list = groupedValues.get(value);
+ if (type && list.size() == 1 && list.get(0).isEmpty()) {
+ // leaf item source/type
+ item = new JMenuItem(value.isEmpty() ? "[unknown]" : value);
+ item.addActionListener(getActionOpen(value, type));
+ menu.add(item);
+ } else {
+ JMenu dir;
+ if (!type && groupedValues.size() == 1) {
+ // only one group of authors
+ dir = menu;
+ } else {
+ dir = new JMenu(value.isEmpty() ? "[unknown]" : value);
+ }
+
+ for (String sub : list) {
+ // " " instead of "" for the visual height
+ String itemName = sub;
+ if (itemName.isEmpty()) {
+ itemName = type ? " " : "[unknown]";
+ }
+
+ String actualValue = value;
+ if (type) {
+ if (!sub.isEmpty()) {
+ actualValue += "/" + sub;
+ }
+ } else {
+ actualValue = sub;
+ }
+
+ item = new JMenuItem(itemName);
+ item.addActionListener(getActionOpen(actualValue, type));
+ dir.add(item);
+ }
+
+ if (menu != dir) {
+ menu.add(dir);
+ }
+ }
+ }
}
/**
- * Populate a list of authors as {@link JMenuItem}s into the given
- * {@link JMenu}.
- * <p>
- * Each item will select the author when clicked.
+ * Return an {@link ActionListener} that will set the given source (type) as
+ * the selected/displayed one.
+ *
+ * @param type
+ * the type (source) to select, cannot be NULL
*
- * @param authors
- * the parent {@link JMenuItem}
- * @param names
- * the authors' names
+ * @return the {@link ActionListener}
*/
- private void populateMenuAuthorList(JMenu authors, List<String> names) {
- for (final String name : names) {
- JMenuItem item = new JMenuItem(name == null ? "All"
- : name.isEmpty() ? "[unknown]" : name);
- item.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- removeBookPanes();
- addBookPane(name, false);
- refreshBooks();
- }
- });
- authors.add(item);
+ private ActionListener getActionOpen(final String source, final boolean type) {
+ return new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mainPanel.removeBookPanes();
+ mainPanel.addBookPane(source, type);
+ mainPanel.refreshBooks();
+ }
+ };
+ }
- if (name == null || name.isEmpty()) {
- authors.addSeparator();
+ private ActionListener getActionOpenList(final boolean type,
+ final boolean listMode) {
+ return new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mainPanel.removeBookPanes();
+ mainPanel.addBookPane(type, listMode);
+ mainPanel.refreshBooks();
}
- }
+ };
}
/**
export.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
if (selectedBook != null) {
fc.showDialog(GuiReaderFrame.this, "Save");
if (fc.getSelectedFile() != null) {
.getAbsolutePath()
+ type.getDefaultExtension(false);
final Progress pg = new Progress();
- outOfUi(pg, new Runnable() {
+ mainPanel.outOfUi(pg, new Runnable() {
@Override
public void run() {
try {
reader.getLibrary().export(
- selectedBook.getMeta().getLuid(),
- type, path, pg);
+ selectedBook.getInfo().getMeta()
+ .getLuid(), type, path, pg);
} catch (IOException e) {
Instance.getTraceHandler().error(e);
}
refresh.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
if (selectedBook != null) {
- outOfUi(null, new Runnable() {
+ mainPanel.outOfUi(null, new Runnable() {
@Override
public void run() {
- reader.clearLocalReaderCache(selectedBook.getMeta()
- .getLuid());
+ reader.clearLocalReaderCache(selectedBook.getInfo()
+ .getMeta().getLuid());
selectedBook.setCached(false);
GuiReaderCoverImager.clearIcon(selectedBook
- .getMeta());
+ .getInfo());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
}
/**
- * Create the delete menu item.
+ * Create the "move to" menu item.
*
* @param libOk
* the library can be queried
*
* @return the item
*/
- private JMenuItem createMenuItemMove(boolean libOk) {
- JMenu moveTo = new JMenu("Move to...");
- moveTo.setMnemonic(KeyEvent.VK_M);
+ private JMenuItem createMenuItemMoveTo(boolean libOk) {
+ JMenu changeTo = new JMenu("Move to");
+ changeTo.setMnemonic(KeyEvent.VK_M);
- List<String> types = new ArrayList<String>();
- types.add(null);
+ Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
if (libOk) {
- types.addAll(reader.getLibrary().getSources());
+ groupedSources = reader.getLibrary().getSourcesGrouped();
}
- for (String type : types) {
- JMenuItem item = new JMenuItem(type == null ? "New type..." : type);
+ JMenuItem item = new JMenuItem("New type...");
+ item.addActionListener(createMoveAction(ChangeAction.SOURCE, null));
+ changeTo.add(item);
+ changeTo.addSeparator();
+
+ for (final String type : groupedSources.keySet()) {
+ List<String> 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;
+ }
- moveTo.add(item);
- if (type == null) {
- moveTo.addSeparator();
+ item = new JMenuItem(itemName);
+ item.addActionListener(createMoveAction(
+ ChangeAction.SOURCE, actualType));
+ dir.add(item);
+ }
+ changeTo.add(dir);
}
+ }
- final String ftype = type;
- item.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- if (selectedBook != null) {
- String type = ftype;
- if (type == null) {
- Object rep = JOptionPane.showInputDialog(
- GuiReaderFrame.this, "Move to:",
- "Moving story",
- JOptionPane.QUESTION_MESSAGE, null, null,
- selectedBook.getMeta().getSource());
-
- if (rep == null) {
- return;
- }
-
- type = rep.toString();
- }
+ return changeTo;
+ }
- final String ftype = type;
- outOfUi(null, new Runnable() {
- @Override
- public void run() {
- reader.changeSource(selectedBook.getMeta()
- .getLuid(), ftype);
+ /**
+ * Create the "set author" menu item.
+ *
+ * @param libOk
+ * the library can be queried
+ *
+ * @return the item
+ */
+ private JMenuItem createMenuItemSetAuthor(boolean libOk) {
+ JMenu changeTo = new JMenu("Set author");
+ changeTo.setMnemonic(KeyEvent.VK_A);
- selectedBook = null;
+ // New author
+ JMenuItem newItem = new JMenuItem("New author...");
+ changeTo.add(newItem);
+ changeTo.addSeparator();
+ newItem.addActionListener(createMoveAction(ChangeAction.AUTHOR, null));
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- setJMenuBar(createMenu(true));
- }
- });
- }
- });
+ // Existing authors
+ if (libOk) {
+ Map<String, List<String>> groupedAuthors = reader.getLibrary()
+ .getAuthorsGrouped();
+
+ 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() ? "[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() ? "[unknown]" : value);
+ item.addActionListener(createMoveAction(
+ ChangeAction.AUTHOR, value));
+ changeTo.add(item);
}
- });
+ }
}
- return moveTo;
+ return changeTo;
+ }
+
+ /**
+ * Create the "rename" menu item.
+ *
+ * @param libOk
+ * the library can be queried
+ *
+ * @return the item
+ */
+ private JMenuItem createMenuItemRename(
+ @SuppressWarnings("unused") boolean libOk) {
+ JMenuItem changeTo = new JMenuItem("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 GuiReaderBook selectedBook = mainPanel.getSelectedBook();
+ if (selectedBook != null) {
+ String changeTo = type;
+ if (type == null) {
+ MetaData meta = selectedBook.getInfo().getMeta();
+ String init = "";
+ 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(
+ GuiReaderFrame.this, "Move to:",
+ "Moving story", JOptionPane.QUESTION_MESSAGE,
+ null, null, init);
+
+ if (rep == null) {
+ return;
+ }
+
+ changeTo = rep.toString();
+ }
+
+ final String fChangeTo = changeTo;
+ mainPanel.outOfUi(null, new Runnable() {
+ @Override
+ public void run() {
+ String luid = selectedBook.getInfo().getMeta()
+ .getLuid();
+ if (what == ChangeAction.SOURCE) {
+ reader.changeSource(luid, fChangeTo);
+ } else if (what == ChangeAction.TITLE) {
+ reader.changeTitle(luid, fChangeTo);
+ } else if (what == ChangeAction.AUTHOR) {
+ reader.changeAuthor(luid, fChangeTo);
+ }
+
+ mainPanel.unsetSelectedBook();
+
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ createMenu(true);
+ }
+ });
+ }
+ });
+ }
+ }
+ };
}
/**
- * Create the redownload (then delete original) menu item.
+ * Create the re-download (then delete original) menu item.
*
* @return the item
*/
refresh.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
if (selectedBook != null) {
- final MetaData meta = selectedBook.getMeta();
- imprt(meta.getUrl(), new StoryRunnable() {
+ final MetaData meta = selectedBook.getInfo().getMeta();
+ mainPanel.imprt(meta.getUrl(), new StoryRunnable() {
@Override
public void run(Story story) {
reader.delete(meta.getLuid());
- GuiReaderFrame.this.selectedBook = null;
+ mainPanel.unsetSelectedBook();
MetaData newMeta = story.getMeta();
if (!newMeta.getSource().equals(meta.getSource())) {
reader.changeSource(newMeta.getLuid(),
delete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
if (selectedBook != null) {
- outOfUi(null, new Runnable() {
+ mainPanel.outOfUi(null, new Runnable() {
@Override
public void run() {
- reader.delete(selectedBook.getMeta().getLuid());
- selectedBook = null;
+ reader.delete(selectedBook.getInfo().getMeta()
+ .getLuid());
+ mainPanel.unsetSelectedBook();
}
});
}
delete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
if (selectedBook != null) {
- outOfUi(null, new Runnable() {
+ mainPanel.outOfUi(null, new Runnable() {
@Override
public void run() {
- final MetaData meta = selectedBook.getMeta();
- new JFrame() {
- private static final long serialVersionUID = 1L;
- @SuppressWarnings("unused")
- private Object init = init();
-
- private Object init() {
- // Borders
- int top = 20;
- int space = 10;
-
- // Image
- ImageIcon img = GuiReaderCoverImager
- .generateCoverIcon(
- reader.getLibrary(), meta);
-
- // frame
- setTitle(meta.getLuid() + ": "
- + meta.getTitle());
-
- setSize(800, img.getIconHeight() + 2 * top);
- setLayout(new BorderLayout());
-
- // Main panel
- JPanel mainPanel = new JPanel(
- new BorderLayout());
- JPanel mainPanelKeys = new JPanel();
- mainPanelKeys.setLayout(new BoxLayout(
- mainPanelKeys, BoxLayout.Y_AXIS));
- JPanel mainPanelValues = new JPanel();
- mainPanelValues.setLayout(new BoxLayout(
- mainPanelValues, BoxLayout.Y_AXIS));
-
- mainPanel.add(mainPanelKeys,
- BorderLayout.WEST);
- mainPanel.add(mainPanelValues,
- BorderLayout.CENTER);
-
- List<Entry<String, String>> infos = BasicReader
- .getMetaDesc(meta);
-
- Color trans = new Color(0, 0, 0, 1);
- for (Entry<String, String> info : infos) {
- JTextArea key = new JTextArea(info
- .getKey());
- key.setFont(new Font(key.getFont()
- .getFontName(), Font.BOLD, key
- .getFont().getSize()));
- key.setEditable(false);
- key.setLineWrap(false);
- key.setBackground(trans);
- mainPanelKeys.add(key);
-
- JTextArea value = new JTextArea(info
- .getValue());
- value.setEditable(false);
- value.setLineWrap(false);
- value.setBackground(trans);
- mainPanelValues.add(value);
- }
-
- // Image
- JLabel imgLabel = new JLabel(img);
- imgLabel.setVerticalAlignment(JLabel.TOP);
-
- // Borders
- mainPanelKeys.setBorder(BorderFactory
- .createEmptyBorder(top, space, 0, 0));
- mainPanelValues.setBorder(BorderFactory
- .createEmptyBorder(top, space, 0, 0));
- imgLabel.setBorder(BorderFactory
- .createEmptyBorder(0, space, 0, 0));
-
- // Add all
- add(imgLabel, BorderLayout.WEST);
- add(mainPanel, BorderLayout.CENTER);
-
- return null;
- }
-
- }.setVisible(true);
+ new GuiReaderPropertiesFrame(reader.getLibrary(),
+ selectedBook.getInfo().getMeta())
+ .setVisible(true);
}
});
}
}
/**
- * Create the open menu item for a book or a source (no LUID).
+ * Create the open menu item for a book, a source/type or an author.
*
* @return the item
*/
- private JMenuItem createMenuItemOpenBook() {
+ public JMenuItem createMenuItemOpenBook() {
JMenuItem open = new JMenuItem("Open", KeyEvent.VK_O);
open.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
if (selectedBook != null) {
- if (selectedBook.getMeta().getLuid() == null) {
- removeBookPanes();
- addBookPane(selectedBook.getMeta().getSource(), true);
- refreshBooks();
+ if (selectedBook.getInfo().getMeta() == null) {
+ mainPanel.removeBookPanes();
+ mainPanel.addBookPane(selectedBook.getInfo()
+ .getMainInfo(), mainPanel.getCurrentType());
+ mainPanel.refreshBooks();
} else {
- openBook(selectedBook);
+ mainPanel.openBook(selectedBook);
}
}
}
*
* @return the item
*/
- private JMenuItem createMenuItemSetCover() {
+ private JMenuItem createMenuItemSetCoverForSource() {
JMenuItem open = new JMenuItem("Set as cover for source", KeyEvent.VK_C);
open.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
if (selectedBook != null) {
- reader.getLibrary().setSourceCover(
- selectedBook.getMeta().getSource(),
- selectedBook.getMeta().getLuid());
- MetaData source = selectedBook.getMeta().clone();
- source.setLuid(null);
- GuiReaderCoverImager.clearIcon(source);
- }
- }
- });
+ BasicLibrary lib = reader.getLibrary();
+ String luid = selectedBook.getInfo().getMeta().getLuid();
+ String source = selectedBook.getInfo().getMeta()
+ .getSource();
- return open;
- }
+ lib.setSourceCover(source, luid);
- /**
- * Open a {@link GuiReaderBook} item.
- *
- * @param book
- * the {@link GuiReaderBook} to open
- */
- 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(), false, pg);
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- book.setCached(true);
- }
- });
- } catch (IOException e) {
- // TODO: error message?
- Instance.getTraceHandler().error(e);
+ GuiReaderBookInfo sourceInfo = GuiReaderBookInfo
+ .fromSource(lib, source);
+ GuiReaderCoverImager.clearIcon(sourceInfo);
}
}
});
- }
-
- /**
- * Process the given action out of the Swing UI thread and link the given
- * {@link ProgressBar} to the action.
- * <p>
- * The code will make sure that the {@link ProgressBar} (if not NULL) is set
- * to done when the action is done.
- *
- * @param progress
- * the {@link ProgressBar} or NULL
- * @param run
- * the action to run
- */
- private void outOfUi(Progress progress, final Runnable run) {
- final Progress pg = new Progress();
- final Progress reload = new Progress("Reload books");
- if (progress == null) {
- progress = new Progress();
- }
-
- pg.addProgress(progress, 90);
- pg.addProgress(reload, 10);
-
- invalidate();
- pgBar.setProgress(pg);
- validate();
- setEnabled(false);
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- run.run();
- refreshBooks();
- } finally {
- reload.done();
- if (!pg.isDone()) {
- // will trigger pgBar ActionListener:
- pg.done();
- }
- }
- }
- }, "outOfUi thread").start();
- }
-
- /**
- * Import a {@link Story} into the main {@link LocalLibrary}.
- * <p>
- * Should be called inside the UI thread.
- *
- * @param askUrl
- * TRUE for an {@link URL}, false for a {@link File}
- */
- private void imprt(boolean askUrl) {
- JFileChooser fc = new JFileChooser();
-
- 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(GuiReaderFrame.this,
- "url of the story to import?", "Importing from URL",
- 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.toString().isEmpty()) {
- imprt(url.toString(), null, null);
- }
+ return open;
}
/**
- * Actually import the {@link Story} into the main {@link LocalLibrary}.
- * <p>
- * Should be called inside the UI thread.
+ * Create the SetCover menu item for a book to change the linked source
+ * cover.
*
- * @param url
- * the {@link Story} to import by {@link URL}
- * @param onSuccess
- * Action to execute on success
+ * @return the item
*/
- private void imprt(final String url, final StoryRunnable onSuccess,
- String onSuccessPgName) {
- final Progress pg = new Progress();
- final Progress pgImprt = new Progress();
- final Progress pgOnSuccess = new Progress(onSuccessPgName);
- pg.addProgress(pgImprt, 95);
- pg.addProgress(pgOnSuccess, 5);
-
- outOfUi(pg, new Runnable() {
+ private JMenuItem createMenuItemSetCoverForAuthor() {
+ JMenuItem open = new JMenuItem("Set as cover for author", KeyEvent.VK_A);
+ open.addActionListener(new ActionListener() {
@Override
- public void run() {
- Exception ex = null;
- Story story = null;
- try {
- story = reader.getLibrary().imprt(BasicReader.getUrl(url),
- pgImprt);
- } catch (IOException e) {
- ex = e;
- }
-
- final Exception e = ex;
+ public void actionPerformed(ActionEvent e) {
+ final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
+ if (selectedBook != null) {
+ BasicLibrary lib = reader.getLibrary();
+ String luid = selectedBook.getInfo().getMeta().getLuid();
+ String author = selectedBook.getInfo().getMeta()
+ .getAuthor();
- final boolean ok = (e == null);
+ lib.setAuthorCover(author, luid);
- pgOnSuccess.setProgress(0);
- if (!ok) {
- 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(story);
- }
+ GuiReaderBookInfo authorInfo = GuiReaderBookInfo
+ .fromAuthor(lib, author);
+ GuiReaderCoverImager.clearIcon(authorInfo);
}
- pgOnSuccess.done();
}
});
- }
-
- /**
- * Enables or disables this component, depending on the value of the
- * parameter <code>b</code>. An enabled component can respond to user input
- * and generate events. Components are enabled initially by default.
- * <p>
- * Disabling this component will also affect its children.
- *
- * @param b
- * If <code>true</code>, this component is enabled; otherwise
- * this component is disabled
- */
- @Override
- public void setEnabled(boolean b) {
- if (bar != null) {
- bar.setEnabled(b);
- }
- for (GuiReaderGroup group : booksByType.keySet()) {
- group.setEnabled(b);
- }
- for (GuiReaderGroup group : booksByAuthor.keySet()) {
- group.setEnabled(b);
- }
- super.setEnabled(b);
- repaint();
+ return open;
}
/**
* @param e
* the exception to log if any
*/
- private void error(final String message, final String title, Exception e) {
+ public void error(final String message, final String title, Exception e) {
Instance.getTraceHandler().error(title + ": " + message);
if (e != null) {
Instance.getTraceHandler().error(e);
}
});
}
+
+ @Override
+ public GuiReader getReader() {
+ return reader;
+ }
}