X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Ffanfix%2Freader%2Fui%2FGuiReaderMainPanel.java;fp=src%2Fbe%2Fnikiroo%2Ffanfix%2Freader%2Fui%2FGuiReaderMainPanel.java;h=0000000000000000000000000000000000000000;hb=d7367179b645781b4e0e4fc24893cb1e6725c14c;hp=a5eb6916f5972b281d7b7237a3464e2f50ee7c55;hpb=1387a30ab59dbf4071f2c5e5e0e08ca98c75b726;p=fanfix.git diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderMainPanel.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderMainPanel.java deleted file mode 100644 index a5eb691..0000000 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderMainPanel.java +++ /dev/null @@ -1,790 +0,0 @@ -package be.nikiroo.fanfix.reader.ui; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.EventQueue; -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.FocusAdapter; -import java.awt.event.FocusEvent; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import javax.swing.BoxLayout; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JMenuBar; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; - -import be.nikiroo.fanfix.Instance; -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.library.LocalLibrary; -import be.nikiroo.fanfix.reader.BasicReader; -import be.nikiroo.fanfix.reader.ui.GuiReaderBook.BookActionListener; -import be.nikiroo.fanfix.reader.ui.GuiReaderBookInfo.Type; -import be.nikiroo.utils.Progress; -import be.nikiroo.utils.ui.ProgressBar; - -/** - * A {@link Frame} that will show a {@link GuiReaderBook} item for each - * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a - * way to copy them to the {@link GuiReader} cache ( - * {@link BasicReader#getLibrary()}), read them, delete them... - * - * @author niki - */ -class GuiReaderMainPanel extends JPanel { - private static final long serialVersionUID = 1L; - private FrameHelper helper; - private Map books; - private GuiReaderGroup bookPane; // for more "All" - 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 boolean currentType; // type/source or author mode (All and Listing) - - /** - * An object that offers some helper methods to access the frame that host - * it and the Fanfix-related functions. - * - * @author niki - */ - public interface FrameHelper { - /** - * Return the reader associated to this {@link FrameHelper}. - * - * @return the reader - */ - public GuiReader getReader(); - - /** - * Create the main menu bar. - *

- * Will invalidate the layout. - * - * @param status - * the library status, must not be NULL - */ - public void createMenu(Status status); - - /** - * Create a popup menu for a {@link GuiReaderBook} that represents a - * story. - * - * @return the popup menu to display - */ - public JPopupMenu createBookPopup(); - - /** - * Create a popup menu for a {@link GuiReaderBook} that represents a - * source/type or an author. - * - * @return the popup menu to display - */ - public JPopupMenu createSourceAuthorPopup(); - } - - /** - * A {@link Runnable} with a {@link MetaData} parameter. - * - * @author niki - */ - public interface MetaDataRunnable { - /** - * Run the action. - * - * @param meta - * the meta of the story - */ - public void run(MetaData meta); - } - - /** - * Create a new {@link GuiReaderMainPanel}. - * - * @param parent - * the associated {@link FrameHelper} to forward some commands - * and access its {@link LocalLibrary} - * @param type - * the type of {@link Story} to load, or NULL for all types - */ - public GuiReaderMainPanel(FrameHelper parent, String type) { - super(new BorderLayout(), true); - - this.helper = parent; - - pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); - JScrollPane scroll = new JScrollPane(pane); - - Integer icolor = Instance.getInstance().getUiConfig().getColor(UiConfig.BACKGROUND_COLOR); - if (icolor != null) { - color = new Color(icolor); - setBackground(color); - pane.setBackground(color); - scroll.setBackground(color); - } - - scroll.getVerticalScrollBar().setUnitIncrement(16); - add(scroll, BorderLayout.CENTER); - - String message = parent.getReader().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) { - pgBar.invalidate(); - pgBar.setProgress(null); - setEnabled(true); - validate(); - } - }); - - pgBar.addUpdateListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - pgBar.invalidate(); - validate(); - repaint(); - } - }); - - books = new TreeMap(); - - addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - focus(); - } - }); - - pane.setVisible(false); - final Progress pg = new Progress(); - final String typeF = type; - outOfUi(pg, true, new Runnable() { - @Override - public void run() { - final BasicLibrary lib = helper.getReader().getLibrary(); - final Status status = lib.getStatus(); - - if (status == Status.READ_WRITE) { - lib.refresh(pg); - } - - inUi(new Runnable() { - @Override - public void run() { - if (status.isReady()) { - helper.createMenu(status); - pane.setVisible(true); - if (typeF == null) { - try { - addBookPane(true, false); - } catch (IOException e) { - error(e.getLocalizedMessage(), - "IOException", e); - } - } else { - addBookPane(typeF, true); - } - } else { - helper.createMenu(status); - validate(); - - String desc = Instance.getInstance().getTransGui().getStringX(StringIdGui.ERROR_LIB_STATUS, - status.toString()); - if (desc == null) { - desc = GuiReader - .trans(StringIdGui.ERROR_LIB_STATUS); - } - - String err = lib.getLibraryName() + "\n" + desc; - error(err, GuiReader - .trans(StringIdGui.TITLE_ERROR_LIBRARY), - null); - } - } - }); - } - }); - } - - public boolean getCurrentType() { - return currentType; - } - - /** - * Add a new {@link GuiReaderGroup} on the frame to display all the - * sources/types or all the authors, or a listing of all the books sorted - * either by source or author. - *

- * A display of all the sources/types or all the authors will show one icon - * per source/type or author. - *

- * A listing of all the books sorted by source/type or author will display - * all the books. - * - * @param type - * TRUE for type/source, FALSE for author - * @param listMode - * TRUE to get a listing of all the sources or authors, FALSE to - * get one icon per source or author - * - * @throws IOException - * in case of I/O error - */ - public void addBookPane(boolean type, boolean listMode) throws IOException { - this.currentType = type; - BasicLibrary lib = helper.getReader().getLibrary(); - if (type) { - if (!listMode) { - addListPane(GuiReader.trans(StringIdGui.MENU_SOURCES), - lib.getSources(), type); - } else { - for (String tt : lib.getSources()) { - if (tt != null) { - addBookPane(tt, type); - } - } - } - } else { - if (!listMode) { - addListPane(GuiReader.trans(StringIdGui.MENU_AUTHORS), - lib.getAuthors(), type); - } else { - for (String tt : lib.getAuthors()) { - if (tt != null) { - addBookPane(tt, type); - } - } - } - } - } - - /** - * Add a new {@link GuiReaderGroup} on the frame to display the books of the - * selected type or author. - *

- * Will invalidate the layout. - * - * @param value - * the author or the type, or NULL to get all the - * authors-or-types - * @param type - * TRUE for type/source, FALSE for author - */ - public void addBookPane(String value, boolean type) { - this.currentType = type; - - GuiReaderGroup bookPane = new GuiReaderGroup(helper.getReader(), value, - color); - - books.put(value, bookPane); - - pane.invalidate(); - pane.add(bookPane); - - bookPane.setActionListener(new BookActionListener() { - @Override - public void select(GuiReaderBook book) { - selectedBook = book; - } - - @Override - public void popupRequested(GuiReaderBook book, Component target, - int x, int y) { - JPopupMenu popup = helper.createBookPopup(); - popup.show(target, x, y); - } - - @Override - public void action(final GuiReaderBook book) { - openBook(book); - } - }); - - focus(); - } - - /** - * Clear the pane from any book that may be present, usually prior to adding - * new ones. - *

- * Will invalidate the layout. - */ - public void removeBookPanes() { - books.clear(); - pane.invalidate(); - pane.removeAll(); - } - - /** - * Refresh the list of {@link GuiReaderBook}s from disk. - *

- * Will validate the layout, as it is a "refresh" operation. - */ - public void refreshBooks() { - BasicLibrary lib = helper.getReader().getLibrary(); - for (String value : books.keySet()) { - List infos = new ArrayList(); - - List metas; - try { - if (currentType) { - metas = lib.getList().filter(value, null, null); - } else { - metas = lib.getList().filter(null, value, null); - } - } catch (IOException e) { - error(e.getLocalizedMessage(), "IOException", e); - metas = new ArrayList(); - } - - for (MetaData meta : metas) { - infos.add(GuiReaderBookInfo.fromMeta(meta)); - } - - books.get(value).refreshBooks(infos, words); - } - - if (bookPane != null) { - bookPane.refreshBooks(words); - } - - this.validate(); - } - - /** - * Open a {@link GuiReaderBook} item. - * - * @param book - * the {@link GuiReaderBook} to open - */ - public void openBook(final GuiReaderBook book) { - final Progress pg = new Progress(); - outOfUi(pg, false, new Runnable() { - @Override - public void run() { - try { - helper.getReader().read(book.getInfo().getMeta().getLuid(), - false, pg); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - book.setCached(true); - } - }); - } catch (IOException e) { - Instance.getInstance().getTraceHandler().error(e); - error(GuiReader.trans(StringIdGui.ERROR_CANNOT_OPEN), - GuiReader.trans(StringIdGui.TITLE_ERROR), e); - } - } - }); - } - - /** - * Prefetch a {@link GuiReaderBook} item (which can be a group, in which - * case we prefetch all its members). - * - * @param book - * the {@link GuiReaderBook} to open - */ - public void prefetchBook(final GuiReaderBook book) { - final List luids = new LinkedList(); - try { - switch (book.getInfo().getType()) { - case STORY: - luids.add(book.getInfo().getMeta().getLuid()); - break; - case SOURCE: - for (MetaData meta : helper.getReader().getLibrary() - .getList().filter(book.getInfo().getMainInfo(), null, null)) { - luids.add(meta.getLuid()); - } - break; - case AUTHOR: - for (MetaData meta : helper.getReader().getLibrary() - .getList().filter(null, book.getInfo().getMainInfo(), null)) { - luids.add(meta.getLuid()); - } - break; - } - } catch (IOException e) { - Instance.getInstance().getTraceHandler().error(e); - } - - final Progress pg = new Progress(); - pg.setMax(luids.size()); - - outOfUi(pg, false, new Runnable() { - @Override - public void run() { - try { - for (String luid : luids) { - Progress pgStep = new Progress(); - pg.addProgress(pgStep, 1); - - helper.getReader().prefetch(luid, pgStep); - } - - // TODO: also set the green button on sources/authors? - // requires to do the same when all stories inside are green - if (book.getInfo().getType() == Type.STORY) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - book.setCached(true); - } - }); - } - } catch (IOException e) { - Instance.getInstance().getTraceHandler().error(e); - error(GuiReader.trans(StringIdGui.ERROR_CANNOT_OPEN), - GuiReader.trans(StringIdGui.TITLE_ERROR), e); - } - } - }); - } - - /** - * Process the given action out of the Swing UI thread and link the given - * {@link ProgressBar} to the action. - *

- * 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 refreshBooks - * TRUE to refresh the books after - * @param run - * the action to run - */ - public void outOfUi(Progress progress, final boolean refreshBooks, - final Runnable run) { - final Progress pg = new Progress(); - final Progress reload = new Progress( - GuiReader.trans(StringIdGui.PROGRESS_OUT_OF_UI_RELOAD_BOOKS)); - - if (progress == null) { - progress = new Progress(); - } - - if (refreshBooks) { - pg.addProgress(progress, 100); - } else { - 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(); - if (refreshBooks) { - refreshBooks(); - } - } finally { - reload.done(); - if (!pg.isDone()) { - // will trigger pgBar ActionListener: - pg.done(); - } - } - } - }, "outOfUi thread").start(); - } - - /** - * Process the given action in the main Swing UI thread. - *

- * The code will make sure the current thread is the main UI thread and, if - * not, will switch to it before executing the runnable. - *

- * Synchronous operation. - * - * @param run - * the action to run - */ - public void inUi(final Runnable run) { - if (EventQueue.isDispatchThread()) { - run.run(); - } else { - try { - EventQueue.invokeAndWait(run); - } catch (InterruptedException e) { - Instance.getInstance().getTraceHandler().error(e); - } catch (InvocationTargetException e) { - Instance.getInstance().getTraceHandler().error(e); - } - } - } - - /** - * Import a {@link Story} into the main {@link LocalLibrary}. - *

- * Should be called inside the UI thread. - * - * @param askUrl - * TRUE for an {@link URL}, false for a {@link File} - */ - public 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.startsWith("https://"))) { - clipboard = ""; - } - - url = JOptionPane.showInputDialog(GuiReaderMainPanel.this, - GuiReader.trans(StringIdGui.SUBTITLE_IMPORT_URL), - GuiReader.trans(StringIdGui.TITLE_IMPORT_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); - } - } - - /** - * Actually import the {@link Story} into the main {@link LocalLibrary}. - *

- * Should be called inside the UI thread. - * - * @param url - * the {@link Story} to import by {@link URL} - * @param onSuccess - * Action to execute on success - * @param onSuccessPgName - * the name to use for the onSuccess progress bar - */ - public void imprt(final String url, final MetaDataRunnable 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, true, new Runnable() { - @Override - public void run() { - Exception ex = null; - MetaData meta = null; - try { - meta = helper.getReader().getLibrary() - .imprt(BasicReader.getUrl(url), pgImprt); - } catch (IOException e) { - ex = e; - } - - final Exception e = ex; - - final boolean ok = (e == null); - - pgOnSuccess.setProgress(0); - if (!ok) { - if (e instanceof UnknownHostException) { - error(GuiReader.trans( - StringIdGui.ERROR_URL_NOT_SUPPORTED, url), - GuiReader.trans(StringIdGui.TITLE_ERROR), null); - } else { - error(GuiReader.trans( - StringIdGui.ERROR_URL_IMPORT_FAILED, url, - e.getMessage()), GuiReader - .trans(StringIdGui.TITLE_ERROR), e); - } - } else { - if (onSuccess != null) { - onSuccess.run(meta); - } - } - pgOnSuccess.done(); - } - }); - } - - /** - * Enables or disables this component, depending on the value of the - * parameter b. An enabled component can respond to user input - * and generate events. Components are enabled initially by default. - *

- * Enabling or disabling this component will also affect its - * children. - * - * @param b - * If true, this component is enabled; otherwise - * this component is disabled - */ - @Override - public void setEnabled(boolean b) { - if (bar != null) { - bar.setEnabled(b); - } - - for (GuiReaderGroup group : books.values()) { - group.setEnabled(b); - } - super.setEnabled(b); - repaint(); - } - - public void setWords(boolean words) { - this.words = words; - } - - public GuiReaderBook getSelectedBook() { - return selectedBook; - } - - public void unsetSelectedBook() { - selectedBook = null; - } - - private void addListPane(String name, List values, - final boolean type) { - GuiReader reader = helper.getReader(); - BasicLibrary lib = reader.getLibrary(); - - bookPane = new GuiReaderGroup(reader, name, color); - - List infos = new ArrayList(); - for (String value : values) { - if (type) { - infos.add(GuiReaderBookInfo.fromSource(lib, value)); - } else { - infos.add(GuiReaderBookInfo.fromAuthor(lib, value)); - } - } - - bookPane.refreshBooks(infos, words); - - 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, Component target, - int x, int y) { - JPopupMenu popup = helper.createSourceAuthorPopup(); - popup.show(target, x, y); - } - - @Override - public void action(final GuiReaderBook book) { - removeBookPanes(); - addBookPane(book.getInfo().getMainInfo(), type); - refreshBooks(); - } - }); - - focus(); - } - - /** - * Focus the first {@link GuiReaderGroup} we find. - */ - private void focus() { - GuiReaderGroup group = null; - Map books = this.books; - if (books.size() > 0) { - group = books.values().iterator().next(); - } - - if (group == null) { - group = bookPane; - } - - if (group != null) { - group.requestFocusInWindow(); - } - } - - /** - * Display an error message and log the linked {@link Exception}. - * - * @param message - * the message - * @param title - * the title of the error message - * @param e - * the exception to log if any - */ - private void error(final String message, final String title, Exception e) { - Instance.getInstance().getTraceHandler().error(title + ": " + message); - if (e != null) { - Instance.getInstance().getTraceHandler().error(e); - } - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - JOptionPane.showMessageDialog(GuiReaderMainPanel.this, message, - title, JOptionPane.ERROR_MESSAGE); - } - }); - } -}