1 package be
.nikiroo
.fanfix_swing
.gui
.book
;
3 import java
.awt
.event
.ActionEvent
;
4 import java
.awt
.event
.ActionListener
;
5 import java
.awt
.event
.KeyEvent
;
7 import java
.io
.IOException
;
8 import java
.util
.HashMap
;
9 import java
.util
.LinkedList
;
10 import java
.util
.List
;
12 import java
.util
.Map
.Entry
;
14 import javax
.swing
.JFileChooser
;
15 import javax
.swing
.JFrame
;
16 import javax
.swing
.JMenu
;
17 import javax
.swing
.JMenuItem
;
18 import javax
.swing
.JOptionPane
;
19 import javax
.swing
.JPopupMenu
;
20 import javax
.swing
.SwingWorker
;
21 import javax
.swing
.filechooser
.FileFilter
;
22 import javax
.swing
.filechooser
.FileNameExtensionFilter
;
24 import be
.nikiroo
.fanfix
.Instance
;
25 import be
.nikiroo
.fanfix
.bundles
.Config
;
26 import be
.nikiroo
.fanfix
.bundles
.StringIdGui
;
27 import be
.nikiroo
.fanfix
.bundles
.UiConfig
;
28 import be
.nikiroo
.fanfix
.data
.MetaData
;
29 import be
.nikiroo
.fanfix
.data
.Story
;
30 import be
.nikiroo
.fanfix
.library
.BasicLibrary
;
31 import be
.nikiroo
.fanfix
.library
.BasicLibrary
.Status
;
32 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
33 import be
.nikiroo
.fanfix_swing
.Actions
;
34 import be
.nikiroo
.fanfix_swing
.gui
.utils
.UiHelper
;
35 import be
.nikiroo
.utils
.Progress
;
36 import be
.nikiroo
.utils
.ui
.ConfigEditor
;
38 public class BookPopup
extends JPopupMenu
{
39 public abstract interface Informer
{
42 public List
<BookInfo
> getSelected();
44 public void setCached(BookInfo book
, boolean cached
);
46 public BookInfo
getUniqueSelected();
48 public void fireElementChanged(BookInfo book
);
50 public void invalidateCache();
54 * The different modification actions you can use on {@link Story} items.
58 private enum ChangeAction
{
59 /** Change the source/type, that is, move it to another source. */
61 /** Change its name. */
63 /** Change its author. */
67 // be careful with that
68 private BasicLibrary lib
;
70 private Informer informer
;
72 private Object object
;
74 private Object object2
;
76 private Object object3
;
78 public BookPopup(BasicLibrary lib
, Informer informer
) {
80 this.informer
= informer
;
82 Status status
= lib
.getStatus();
83 add(createMenuItemOpenBook());
85 add(createMenuItemExport());
86 if (status
.isWritable()) {
87 add(createMenuItemMoveTo());
88 add(createMenuItemSetCoverForSource());
89 add(createMenuItemSetCoverForAuthor());
91 add(createMenuItemDownloadToCache());
92 add(createMenuItemClearCache());
93 if (status
.isWritable()) {
94 add(createMenuItemRedownload());
96 add(createMenuItemRename());
97 add(createMenuItemSetAuthor());
99 add(createMenuItemDelete());
102 add(createMenuItemProperties());
105 private String
trans(StringIdGui id
) {
106 return Instance
.getInstance().getTransGui().getString(id
);
110 * Create the Fanfix Configuration menu item.
114 private JMenuItem
createMenuItemConfig() {
115 final String title
= trans(StringIdGui
.TITLE_CONFIG
);
116 JMenuItem item
= new JMenuItem(title
);
117 item
.setMnemonic(KeyEvent
.VK_F
);
119 item
.addActionListener(new ActionListener() {
121 public void actionPerformed(ActionEvent e
) {
122 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(Config
.class, Instance
.getInstance().getConfig(),
123 trans(StringIdGui
.SUBTITLE_CONFIG
));
124 JFrame frame
= new JFrame(title
);
126 frame
.setSize(850, 600);
127 frame
.setVisible(true);
135 * Create the UI Configuration menu item.
139 private JMenuItem
createMenuItemUiConfig() {
140 final String title
= trans(StringIdGui
.TITLE_CONFIG_UI
);
141 JMenuItem item
= new JMenuItem(title
);
142 item
.setMnemonic(KeyEvent
.VK_U
);
144 item
.addActionListener(new ActionListener() {
146 public void actionPerformed(ActionEvent e
) {
147 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(UiConfig
.class,
148 Instance
.getInstance().getUiConfig(), trans(StringIdGui
.SUBTITLE_CONFIG_UI
));
149 JFrame frame
= new JFrame(title
);
151 frame
.setSize(800, 600);
152 frame
.setVisible(true);
160 * Create the export menu item.
164 private JMenuItem
createMenuItemExport() {
166 // TODO: allow dir for multiple selection?
168 final JFileChooser fc
= new JFileChooser();
169 fc
.setAcceptAllFileFilterUsed(false);
171 // Add the "ALL" filters first, then the others
172 final Map
<FileFilter
, OutputType
> otherFilters
= new HashMap
<FileFilter
, OutputType
>();
173 for (OutputType type
: OutputType
.values()) {
174 String ext
= type
.getDefaultExtension(false);
175 String desc
= type
.getDesc(false);
177 if (ext
== null || ext
.isEmpty()) {
178 fc
.addChoosableFileFilter(createAllFilter(desc
));
180 otherFilters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
184 for (Entry
<FileFilter
, OutputType
> entry
: otherFilters
.entrySet()) {
185 fc
.addChoosableFileFilter(entry
.getKey());
189 JMenuItem export
= new JMenuItem(trans(StringIdGui
.MENU_FILE_EXPORT
), KeyEvent
.VK_S
);
190 export
.addActionListener(new ActionListener() {
192 public void actionPerformed(ActionEvent e
) {
193 final BookInfo book
= informer
.getUniqueSelected();
195 fc
.showDialog(BookPopup
.this.getParent(), trans(StringIdGui
.TITLE_SAVE
));
196 if (fc
.getSelectedFile() != null) {
197 final OutputType type
= otherFilters
.get(fc
.getFileFilter());
198 final String path
= fc
.getSelectedFile().getAbsolutePath() + type
.getDefaultExtension(false);
199 final Progress pg
= new Progress();
201 new SwingWorker
<Void
, Void
>() {
203 protected Void
doInBackground() throws Exception
{
204 lib
.export(book
.getMeta().getLuid(), type
, path
, pg
);
209 protected void done() {
212 } catch (Exception e
) {
213 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException",
227 * Create a {@link FileFilter} that accepts all files and return the given
230 * @param desc the description
234 private FileFilter
createAllFilter(final String desc
) {
235 return new FileFilter() {
237 public String
getDescription() {
242 public boolean accept(File f
) {
249 * Create the refresh (delete cache) menu item.
253 private JMenuItem
createMenuItemClearCache() {
254 JMenuItem refresh
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_CLEAR_CACHE
), KeyEvent
.VK_C
);
255 refresh
.addActionListener(new ActionListener() {
257 public void actionPerformed(ActionEvent e
) {
258 final List
<BookInfo
> selected
= informer
.getSelected();
259 if (!selected
.isEmpty()) {
260 new SwingWorker
<Void
, Void
>() {
262 protected Void
doInBackground() throws Exception
{
263 for (BookInfo book
: selected
) {
264 lib
.clearFromCache(book
.getMeta().getLuid());
265 BookCoverImager
.clearIcon(book
);
271 protected void done() {
274 for (BookInfo book
: selected
) {
275 informer
.setCached(book
, false);
277 } catch (Exception e
) {
278 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
290 * Create the "move to" menu item.
294 private JMenuItem
createMenuItemMoveTo() {
295 JMenu changeTo
= new JMenu(trans(StringIdGui
.MENU_FILE_MOVE_TO
));
296 changeTo
.setMnemonic(KeyEvent
.VK_M
);
298 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
300 groupedSources
= lib
.getSourcesGrouped();
301 } catch (IOException e
) {
302 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
305 JMenuItem item
= new JMenuItem(trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_TYPE
));
306 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, null));
308 changeTo
.addSeparator();
310 for (final String type
: groupedSources
.keySet()) {
311 List
<String
> list
= groupedSources
.get(type
);
312 if (list
.size() == 1 && list
.get(0).isEmpty()) {
313 item
= new JMenuItem(type
);
314 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, type
));
317 JMenu dir
= new JMenu(type
);
318 for (String sub
: list
) {
319 // " " instead of "" for the visual height
320 String itemName
= sub
.isEmpty() ?
" " : sub
;
321 String actualType
= type
;
322 if (!sub
.isEmpty()) {
323 actualType
+= "/" + sub
;
326 item
= new JMenuItem(itemName
);
327 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, actualType
));
338 * Create the "set author" menu item.
342 private JMenuItem
createMenuItemSetAuthor() {
343 JMenu changeTo
= new JMenu(trans(StringIdGui
.MENU_FILE_SET_AUTHOR
));
344 changeTo
.setMnemonic(KeyEvent
.VK_A
);
347 JMenuItem newItem
= new JMenuItem(trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_AUTHOR
));
348 changeTo
.add(newItem
);
349 changeTo
.addSeparator();
350 newItem
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, null));
353 Map
<String
, List
<String
>> groupedAuthors
;
356 groupedAuthors
= lib
.getAuthorsGrouped();
357 } catch (IOException e
) {
358 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
359 groupedAuthors
= new HashMap
<String
, List
<String
>>();
363 if (groupedAuthors
.size() > 1) {
364 for (String key
: groupedAuthors
.keySet()) {
365 JMenu group
= new JMenu(key
);
366 for (String value
: groupedAuthors
.get(key
)) {
367 JMenuItem item
= new JMenuItem(value
.isEmpty() ?
trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
368 item
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, value
));
373 } else if (groupedAuthors
.size() == 1) {
374 for (String value
: groupedAuthors
.values().iterator().next()) {
375 JMenuItem item
= new JMenuItem(value
.isEmpty() ?
trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
376 item
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, value
));
385 * Create the "rename" menu item.
389 private JMenuItem
createMenuItemRename() {
390 JMenuItem changeTo
= new JMenuItem(trans(StringIdGui
.MENU_FILE_RENAME
));
391 changeTo
.setMnemonic(KeyEvent
.VK_R
);
392 changeTo
.addActionListener(createMoveAction(ChangeAction
.TITLE
, null));
396 private ActionListener
createMoveAction(final ChangeAction what
, final String type
) {
397 return new ActionListener() {
399 public void actionPerformed(ActionEvent e
) {
400 final List
<BookInfo
> selected
= informer
.getSelected();
401 if (!selected
.isEmpty()) {
402 String changeTo
= type
;
406 if (selected
.size() == 1) {
407 MetaData meta
= selected
.get(0).getMeta();
408 if (what
== ChangeAction
.SOURCE
) {
409 init
= meta
.getSource();
410 } else if (what
== ChangeAction
.TITLE
) {
411 init
= meta
.getTitle();
412 } else if (what
== ChangeAction
.AUTHOR
) {
413 init
= meta
.getAuthor();
417 Object rep
= JOptionPane
.showInputDialog(BookPopup
.this.getParent(),
418 trans(StringIdGui
.SUBTITLE_MOVE_TO
), trans(StringIdGui
.TITLE_MOVE_TO
),
419 JOptionPane
.QUESTION_MESSAGE
, null, null, init
);
425 changeTo
= rep
.toString();
428 final String fChangeTo
= changeTo
;
429 new SwingWorker
<Void
, Void
>() {
431 protected Void
doInBackground() throws Exception
{
432 for (BookInfo book
: selected
) {
433 String luid
= book
.getMeta().getLuid();
434 if (what
== ChangeAction
.SOURCE
) {
435 lib
.changeSource(luid
, fChangeTo
, null);
436 } else if (what
== ChangeAction
.TITLE
) {
437 lib
.changeTitle(luid
, fChangeTo
, null);
438 } else if (what
== ChangeAction
.AUTHOR
) {
439 lib
.changeAuthor(luid
, fChangeTo
, null);
447 protected void done() {
449 // this can create new sources/authors, so a simple fireElementChanged is not
450 // enough, we need to clear the whole cache (for BrowserPanel for instance)
451 informer
.invalidateCache();
453 // TODO: not enough!!
454 // after move, item disappears in the list, probably caused by the Library
457 // Even if problems occurred, still invalidate the cache
459 } catch (Exception e
) {
460 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
470 * Create the re-download (then delete original) menu item.
474 private JMenuItem
createMenuItemRedownload() {
475 JMenuItem refresh
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_REDOWNLOAD
), KeyEvent
.VK_R
);
476 refresh
.addActionListener(new ActionListener() {
478 public void actionPerformed(ActionEvent e
) {
479 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
480 // if (selectedBook != null) {
481 // final MetaData meta = selectedBook.getInfo().getMeta();
482 // mainPanel.imprt(meta.getUrl(), new MetaDataRunnable() {
484 // public void run(MetaData newMeta) {
485 // if (!newMeta.getSource().equals(meta.getSource())) {
486 // reader.changeSource(newMeta.getLuid(), meta.getSource());
489 // }, trans(StringIdGui.PROGRESS_CHANGE_SOURCE));
498 * Create the download to cache menu item.
502 private JMenuItem
createMenuItemDownloadToCache() {
503 JMenuItem refresh
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_DOWNLOAD_TO_CACHE
), KeyEvent
.VK_T
);
504 refresh
.addActionListener(new ActionListener() {
506 public void actionPerformed(ActionEvent e
) {
507 final List
<BookInfo
> selected
= informer
.getSelected();
509 new SwingWorker
<Void
, Void
>() {
511 protected Void
doInBackground() throws Exception
{
513 final List
<String
> luids
= new LinkedList
<String
>();
514 for (BookInfo book
: selected
) {
515 switch (book
.getType()) {
517 luids
.add(book
.getMeta().getLuid());
520 for (MetaData meta
: lib
.getListBySource(book
.getMainInfo())) {
521 luids
.add(meta
.getLuid());
525 for (MetaData meta
: lib
.getListByAuthor(book
.getMainInfo())) {
526 luids
.add(meta
.getLuid());
530 for (MetaData meta
: lib
.getList(null).filter(null, null, book
.getMainInfo())) {
531 luids
.add(meta
.getLuid());
537 // TODO: do something with pg?
538 final Progress pg
= new Progress();
539 pg
.setMax(luids
.size());
540 for (String luid
: luids
) {
541 Progress pgStep
= new Progress();
542 pg
.addProgress(pgStep
, 1);
544 lib
.getFile(luid
, pgStep
);
551 protected void done() {
554 for (BookInfo book
: selected
) {
555 informer
.setCached(book
, true);
557 } catch (Exception e
) {
558 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
569 * Create the delete menu item.
573 private JMenuItem
createMenuItemDelete() {
574 JMenuItem delete
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_DELETE
), KeyEvent
.VK_D
);
575 delete
.addActionListener(new ActionListener() {
577 public void actionPerformed(ActionEvent e
) {
578 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
579 // if (selectedBook != null && selectedBook.getInfo().getMeta() != null) {
581 // final MetaData meta = selectedBook.getInfo().getMeta();
582 // int rep = JOptionPane.showConfirmDialog(GuiReaderFrame.this,
583 // trans(StringIdGui.SUBTITLE_DELETE, meta.getLuid(), meta.getTitle()),
584 // trans(StringIdGui.TITLE_DELETE), JOptionPane.OK_CANCEL_OPTION);
586 // if (rep == JOptionPane.OK_OPTION) {
587 // mainPanel.outOfUi(null, true, new Runnable() {
589 // public void run() {
590 // reader.delete(meta.getLuid());
591 // mainPanel.unsetSelectedBook();
603 * Create the properties menu item.
607 private JMenuItem
createMenuItemProperties() {
608 JMenuItem delete
= new JMenuItem(trans(StringIdGui
.MENU_FILE_PROPERTIES
), KeyEvent
.VK_P
);
609 delete
.addActionListener(new ActionListener() {
611 public void actionPerformed(ActionEvent e
) {
612 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
613 // if (selectedBook != null) {
614 // mainPanel.outOfUi(null, false, new Runnable() {
616 // public void run() {
617 // new GuiReaderPropertiesFrame(lib, selectedBook.getInfo().getMeta())
618 // .setVisible(true);
629 * Create the open menu item for a book, a source/type or an author.
633 public JMenuItem
createMenuItemOpenBook() {
634 JMenuItem open
= new JMenuItem(trans(StringIdGui
.MENU_FILE_OPEN
), KeyEvent
.VK_O
);
635 open
.addActionListener(new ActionListener() {
637 public void actionPerformed(ActionEvent e
) {
638 final BookInfo book
= informer
.getUniqueSelected();
640 Actions
.openExternal(lib
, book
.getMeta(), BookPopup
.this.getParent(), new Runnable() {
643 informer
.setCached(book
, true);
654 * Create the SetCover menu item for a book to change the linked source cover.
658 private JMenuItem
createMenuItemSetCoverForSource() {
659 JMenuItem open
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_SOURCE
), KeyEvent
.VK_C
);
660 open
.addActionListener(new ActionListener() {
662 public void actionPerformed(ActionEvent ae
) {
663 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
664 // if (selectedBook != null) {
665 // BasicLibrary lib = lib;
666 // String luid = selectedBook.getInfo().getMeta().getLuid();
667 // String source = selectedBook.getInfo().getMeta().getSource();
670 // lib.setSourceCover(source, luid);
671 // } catch (IOException e) {
672 // error(e.getLocalizedMessage(), "IOException", e);
675 // GuiReaderBookInfo sourceInfo = GuiReaderBookInfo.fromSource(lib, source);
676 // GuiReaderCoverImager.clearIcon(sourceInfo);
685 * Create the SetCover menu item for a book to change the linked source cover.
689 private JMenuItem
createMenuItemSetCoverForAuthor() {
690 JMenuItem open
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_AUTHOR
), KeyEvent
.VK_A
);
691 open
.addActionListener(new ActionListener() {
693 public void actionPerformed(ActionEvent ae
) {
694 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
695 // if (selectedBook != null) {
696 // String luid = selectedBook.getInfo().getMeta().getLuid();
697 // String author = selectedBook.getInfo().getMeta().getAuthor();
700 // lib.setAuthorCover(author, luid);
701 // } catch (IOException e) {
702 // error(e.getLocalizedMessage(), "IOException", e);
705 // GuiReaderBookInfo authorInfo = GuiReaderBookInfo.fromAuthor(lib, author);
706 // GuiReaderCoverImager.clearIcon(authorInfo);