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
);
53 * The different modification actions you can use on {@link Story} items.
57 private enum ChangeAction
{
58 /** Change the source/type, that is, move it to another source. */
60 /** Change its name. */
62 /** Change its author. */
66 // be careful with that
67 private BasicLibrary lib
;
69 private Informer informer
;
71 private Object object
;
73 private Object object2
;
75 private Object object3
;
77 public BookPopup(BasicLibrary lib
, Informer informer
) {
79 this.informer
= informer
;
81 Status status
= lib
.getStatus();
82 add(createMenuItemOpenBook());
84 add(createMenuItemExport());
85 if (status
.isWritable()) {
86 add(createMenuItemMoveTo());
87 add(createMenuItemSetCoverForSource());
88 add(createMenuItemSetCoverForAuthor());
90 add(createMenuItemDownloadToCache());
91 add(createMenuItemClearCache());
92 if (status
.isWritable()) {
93 add(createMenuItemRedownload());
95 add(createMenuItemRename());
96 add(createMenuItemSetAuthor());
98 add(createMenuItemDelete());
101 add(createMenuItemProperties());
104 private String
trans(StringIdGui id
) {
105 return Instance
.getInstance().getTransGui().getString(id
);
109 * Create the Fanfix Configuration menu item.
113 private JMenuItem
createMenuItemConfig() {
114 final String title
= trans(StringIdGui
.TITLE_CONFIG
);
115 JMenuItem item
= new JMenuItem(title
);
116 item
.setMnemonic(KeyEvent
.VK_F
);
118 item
.addActionListener(new ActionListener() {
120 public void actionPerformed(ActionEvent e
) {
121 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(Config
.class, Instance
.getInstance().getConfig(),
122 trans(StringIdGui
.SUBTITLE_CONFIG
));
123 JFrame frame
= new JFrame(title
);
125 frame
.setSize(850, 600);
126 frame
.setVisible(true);
134 * Create the UI Configuration menu item.
138 private JMenuItem
createMenuItemUiConfig() {
139 final String title
= trans(StringIdGui
.TITLE_CONFIG_UI
);
140 JMenuItem item
= new JMenuItem(title
);
141 item
.setMnemonic(KeyEvent
.VK_U
);
143 item
.addActionListener(new ActionListener() {
145 public void actionPerformed(ActionEvent e
) {
146 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(UiConfig
.class,
147 Instance
.getInstance().getUiConfig(), trans(StringIdGui
.SUBTITLE_CONFIG_UI
));
148 JFrame frame
= new JFrame(title
);
150 frame
.setSize(800, 600);
151 frame
.setVisible(true);
159 * Create the export menu item.
163 private JMenuItem
createMenuItemExport() {
165 // TODO: allow dir for multiple selection?
167 final JFileChooser fc
= new JFileChooser();
168 fc
.setAcceptAllFileFilterUsed(false);
170 // Add the "ALL" filters first, then the others
171 final Map
<FileFilter
, OutputType
> otherFilters
= new HashMap
<FileFilter
, OutputType
>();
172 for (OutputType type
: OutputType
.values()) {
173 String ext
= type
.getDefaultExtension(false);
174 String desc
= type
.getDesc(false);
176 if (ext
== null || ext
.isEmpty()) {
177 fc
.addChoosableFileFilter(createAllFilter(desc
));
179 otherFilters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
183 for (Entry
<FileFilter
, OutputType
> entry
: otherFilters
.entrySet()) {
184 fc
.addChoosableFileFilter(entry
.getKey());
188 JMenuItem export
= new JMenuItem(trans(StringIdGui
.MENU_FILE_EXPORT
), KeyEvent
.VK_S
);
189 export
.addActionListener(new ActionListener() {
191 public void actionPerformed(ActionEvent e
) {
192 final BookInfo book
= informer
.getUniqueSelected();
194 fc
.showDialog(BookPopup
.this.getParent(), trans(StringIdGui
.TITLE_SAVE
));
195 if (fc
.getSelectedFile() != null) {
196 final OutputType type
= otherFilters
.get(fc
.getFileFilter());
197 final String path
= fc
.getSelectedFile().getAbsolutePath() + type
.getDefaultExtension(false);
198 final Progress pg
= new Progress();
200 new SwingWorker
<Void
, Void
>() {
202 protected Void
doInBackground() throws Exception
{
203 lib
.export(book
.getMeta().getLuid(), type
, path
, pg
);
208 protected void done() {
211 } catch (Exception e
) {
212 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException",
226 * Create a {@link FileFilter} that accepts all files and return the given
229 * @param desc the description
233 private FileFilter
createAllFilter(final String desc
) {
234 return new FileFilter() {
236 public String
getDescription() {
241 public boolean accept(File f
) {
248 * Create the refresh (delete cache) menu item.
252 private JMenuItem
createMenuItemClearCache() {
253 JMenuItem refresh
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_CLEAR_CACHE
), KeyEvent
.VK_C
);
254 refresh
.addActionListener(new ActionListener() {
256 public void actionPerformed(ActionEvent e
) {
257 final List
<BookInfo
> selected
= informer
.getSelected();
258 if (!selected
.isEmpty()) {
259 new SwingWorker
<Void
, Void
>() {
261 protected Void
doInBackground() throws Exception
{
262 for (BookInfo book
: selected
) {
263 lib
.clearFromCache(book
.getMeta().getLuid());
264 BookCoverImager
.clearIcon(book
);
270 protected void done() {
273 for (BookInfo book
: selected
) {
274 informer
.setCached(book
, false);
276 } catch (Exception e
) {
277 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
289 * Create the "move to" menu item.
293 private JMenuItem
createMenuItemMoveTo() {
294 JMenu changeTo
= new JMenu(trans(StringIdGui
.MENU_FILE_MOVE_TO
));
295 changeTo
.setMnemonic(KeyEvent
.VK_M
);
297 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
299 groupedSources
= lib
.getSourcesGrouped();
300 } catch (IOException e
) {
301 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
304 JMenuItem item
= new JMenuItem(trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_TYPE
));
305 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, null));
307 changeTo
.addSeparator();
309 for (final String type
: groupedSources
.keySet()) {
310 List
<String
> list
= groupedSources
.get(type
);
311 if (list
.size() == 1 && list
.get(0).isEmpty()) {
312 item
= new JMenuItem(type
);
313 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, type
));
316 JMenu dir
= new JMenu(type
);
317 for (String sub
: list
) {
318 // " " instead of "" for the visual height
319 String itemName
= sub
.isEmpty() ?
" " : sub
;
320 String actualType
= type
;
321 if (!sub
.isEmpty()) {
322 actualType
+= "/" + sub
;
325 item
= new JMenuItem(itemName
);
326 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, actualType
));
337 * Create the "set author" menu item.
341 private JMenuItem
createMenuItemSetAuthor() {
342 JMenu changeTo
= new JMenu(trans(StringIdGui
.MENU_FILE_SET_AUTHOR
));
343 changeTo
.setMnemonic(KeyEvent
.VK_A
);
346 JMenuItem newItem
= new JMenuItem(trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_AUTHOR
));
347 changeTo
.add(newItem
);
348 changeTo
.addSeparator();
349 newItem
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, null));
352 Map
<String
, List
<String
>> groupedAuthors
;
355 groupedAuthors
= lib
.getAuthorsGrouped();
356 } catch (IOException e
) {
357 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
358 groupedAuthors
= new HashMap
<String
, List
<String
>>();
362 if (groupedAuthors
.size() > 1) {
363 for (String key
: groupedAuthors
.keySet()) {
364 JMenu group
= new JMenu(key
);
365 for (String value
: groupedAuthors
.get(key
)) {
366 JMenuItem item
= new JMenuItem(value
.isEmpty() ?
trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
367 item
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, value
));
372 } else if (groupedAuthors
.size() == 1) {
373 for (String value
: groupedAuthors
.values().iterator().next()) {
374 JMenuItem item
= new JMenuItem(value
.isEmpty() ?
trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
375 item
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, value
));
384 * Create the "rename" menu item.
388 private JMenuItem
createMenuItemRename() {
389 JMenuItem changeTo
= new JMenuItem(trans(StringIdGui
.MENU_FILE_RENAME
));
390 changeTo
.setMnemonic(KeyEvent
.VK_R
);
391 changeTo
.addActionListener(createMoveAction(ChangeAction
.TITLE
, null));
395 private ActionListener
createMoveAction(final ChangeAction what
, final String type
) {
396 return new ActionListener() {
398 public void actionPerformed(ActionEvent e
) {
399 final List
<BookInfo
> selected
= informer
.getSelected();
400 if (!selected
.isEmpty()) {
401 String changeTo
= type
;
405 if (selected
.size() == 1) {
406 MetaData meta
= selected
.get(0).getMeta();
407 if (what
== ChangeAction
.SOURCE
) {
408 init
= meta
.getSource();
409 } else if (what
== ChangeAction
.TITLE
) {
410 init
= meta
.getTitle();
411 } else if (what
== ChangeAction
.AUTHOR
) {
412 init
= meta
.getAuthor();
416 Object rep
= JOptionPane
.showInputDialog(BookPopup
.this.getParent(),
417 trans(StringIdGui
.SUBTITLE_MOVE_TO
), trans(StringIdGui
.TITLE_MOVE_TO
),
418 JOptionPane
.QUESTION_MESSAGE
, null, null, init
);
424 changeTo
= rep
.toString();
427 final String fChangeTo
= changeTo
;
428 new SwingWorker
<Void
, Void
>() {
430 protected Void
doInBackground() throws Exception
{
431 for (BookInfo book
: selected
) {
432 String luid
= book
.getMeta().getLuid();
433 if (what
== ChangeAction
.SOURCE
) {
434 lib
.changeSource(luid
, fChangeTo
, null);
435 } else if (what
== ChangeAction
.TITLE
) {
436 lib
.changeTitle(luid
, fChangeTo
, null);
437 } else if (what
== ChangeAction
.AUTHOR
) {
438 lib
.changeAuthor(luid
, fChangeTo
, null);
441 // TODO: ^-- this can create new sources/authors, update maybe required?
447 protected void done() {
450 for (BookInfo book
: selected
) {
451 informer
.fireElementChanged(book
);
455 } catch (Exception e
) {
456 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
466 * Create the re-download (then delete original) menu item.
470 private JMenuItem
createMenuItemRedownload() {
471 JMenuItem refresh
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_REDOWNLOAD
), KeyEvent
.VK_R
);
472 refresh
.addActionListener(new ActionListener() {
474 public void actionPerformed(ActionEvent e
) {
475 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
476 // if (selectedBook != null) {
477 // final MetaData meta = selectedBook.getInfo().getMeta();
478 // mainPanel.imprt(meta.getUrl(), new MetaDataRunnable() {
480 // public void run(MetaData newMeta) {
481 // if (!newMeta.getSource().equals(meta.getSource())) {
482 // reader.changeSource(newMeta.getLuid(), meta.getSource());
485 // }, trans(StringIdGui.PROGRESS_CHANGE_SOURCE));
494 * Create the download to cache menu item.
498 private JMenuItem
createMenuItemDownloadToCache() {
499 JMenuItem refresh
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_DOWNLOAD_TO_CACHE
), KeyEvent
.VK_T
);
500 refresh
.addActionListener(new ActionListener() {
502 public void actionPerformed(ActionEvent e
) {
503 final List
<BookInfo
> selected
= informer
.getSelected();
505 new SwingWorker
<Void
, Void
>() {
507 protected Void
doInBackground() throws Exception
{
509 final List
<String
> luids
= new LinkedList
<String
>();
510 for (BookInfo book
: selected
) {
511 switch (book
.getType()) {
513 luids
.add(book
.getMeta().getLuid());
516 for (MetaData meta
: lib
.getListBySource(book
.getMainInfo())) {
517 luids
.add(meta
.getLuid());
521 for (MetaData meta
: lib
.getListByAuthor(book
.getMainInfo())) {
522 luids
.add(meta
.getLuid());
526 for (MetaData meta
: lib
.getList(null).filter(null, null, book
.getMainInfo())) {
527 luids
.add(meta
.getLuid());
533 // TODO: do something with pg?
534 final Progress pg
= new Progress();
535 pg
.setMax(luids
.size());
536 for (String luid
: luids
) {
537 Progress pgStep
= new Progress();
538 pg
.addProgress(pgStep
, 1);
540 lib
.getFile(luid
, pgStep
);
547 protected void done() {
550 for (BookInfo book
: selected
) {
551 informer
.setCached(book
, true);
553 } catch (Exception e
) {
554 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(), "IOException", e
);
565 * Create the delete menu item.
569 private JMenuItem
createMenuItemDelete() {
570 JMenuItem delete
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_DELETE
), KeyEvent
.VK_D
);
571 delete
.addActionListener(new ActionListener() {
573 public void actionPerformed(ActionEvent e
) {
574 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
575 // if (selectedBook != null && selectedBook.getInfo().getMeta() != null) {
577 // final MetaData meta = selectedBook.getInfo().getMeta();
578 // int rep = JOptionPane.showConfirmDialog(GuiReaderFrame.this,
579 // trans(StringIdGui.SUBTITLE_DELETE, meta.getLuid(), meta.getTitle()),
580 // trans(StringIdGui.TITLE_DELETE), JOptionPane.OK_CANCEL_OPTION);
582 // if (rep == JOptionPane.OK_OPTION) {
583 // mainPanel.outOfUi(null, true, new Runnable() {
585 // public void run() {
586 // reader.delete(meta.getLuid());
587 // mainPanel.unsetSelectedBook();
599 * Create the properties menu item.
603 private JMenuItem
createMenuItemProperties() {
604 JMenuItem delete
= new JMenuItem(trans(StringIdGui
.MENU_FILE_PROPERTIES
), KeyEvent
.VK_P
);
605 delete
.addActionListener(new ActionListener() {
607 public void actionPerformed(ActionEvent e
) {
608 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
609 // if (selectedBook != null) {
610 // mainPanel.outOfUi(null, false, new Runnable() {
612 // public void run() {
613 // new GuiReaderPropertiesFrame(lib, selectedBook.getInfo().getMeta())
614 // .setVisible(true);
625 * Create the open menu item for a book, a source/type or an author.
629 public JMenuItem
createMenuItemOpenBook() {
630 JMenuItem open
= new JMenuItem(trans(StringIdGui
.MENU_FILE_OPEN
), KeyEvent
.VK_O
);
631 open
.addActionListener(new ActionListener() {
633 public void actionPerformed(ActionEvent e
) {
634 final BookInfo book
= informer
.getUniqueSelected();
636 Actions
.openExternal(lib
, book
.getMeta(), BookPopup
.this.getParent(), new Runnable() {
639 informer
.setCached(book
, true);
650 * Create the SetCover menu item for a book to change the linked source cover.
654 private JMenuItem
createMenuItemSetCoverForSource() {
655 JMenuItem open
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_SOURCE
), KeyEvent
.VK_C
);
656 open
.addActionListener(new ActionListener() {
658 public void actionPerformed(ActionEvent ae
) {
659 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
660 // if (selectedBook != null) {
661 // BasicLibrary lib = lib;
662 // String luid = selectedBook.getInfo().getMeta().getLuid();
663 // String source = selectedBook.getInfo().getMeta().getSource();
666 // lib.setSourceCover(source, luid);
667 // } catch (IOException e) {
668 // error(e.getLocalizedMessage(), "IOException", e);
671 // GuiReaderBookInfo sourceInfo = GuiReaderBookInfo.fromSource(lib, source);
672 // GuiReaderCoverImager.clearIcon(sourceInfo);
681 * Create the SetCover menu item for a book to change the linked source cover.
685 private JMenuItem
createMenuItemSetCoverForAuthor() {
686 JMenuItem open
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_AUTHOR
), KeyEvent
.VK_A
);
687 open
.addActionListener(new ActionListener() {
689 public void actionPerformed(ActionEvent ae
) {
690 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
691 // if (selectedBook != null) {
692 // String luid = selectedBook.getInfo().getMeta().getLuid();
693 // String author = selectedBook.getInfo().getMeta().getAuthor();
696 // lib.setAuthorCover(author, luid);
697 // } catch (IOException e) {
698 // error(e.getLocalizedMessage(), "IOException", e);
701 // GuiReaderBookInfo authorInfo = GuiReaderBookInfo.fromAuthor(lib, author);
702 // GuiReaderCoverImager.clearIcon(authorInfo);