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 public BookPopup(BasicLibrary lib
, Informer informer
) {
74 this.informer
= informer
;
76 Status status
= lib
.getStatus();
77 add(createMenuItemOpenBook());
79 add(createMenuItemExport());
80 if (status
.isWritable()) {
81 add(createMenuItemMoveTo());
82 add(createMenuItemSetCoverForSource());
83 add(createMenuItemSetCoverForAuthor());
85 add(createMenuItemDownloadToCache());
86 add(createMenuItemClearCache());
87 if (status
.isWritable()) {
88 add(createMenuItemRedownload());
90 add(createMenuItemRename());
91 add(createMenuItemSetAuthor());
93 add(createMenuItemDelete());
96 add(createMenuItemProperties());
99 private String
trans(StringIdGui id
) {
100 return Instance
.getInstance().getTransGui().getString(id
);
104 * Create the Fanfix Configuration menu item.
108 private JMenuItem
createMenuItemConfig() {
109 final String title
= trans(StringIdGui
.TITLE_CONFIG
);
110 JMenuItem item
= new JMenuItem(title
);
111 item
.setMnemonic(KeyEvent
.VK_F
);
113 item
.addActionListener(new ActionListener() {
115 public void actionPerformed(ActionEvent e
) {
116 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(Config
.class,
117 Instance
.getInstance().getConfig(),
118 trans(StringIdGui
.SUBTITLE_CONFIG
));
119 JFrame frame
= new JFrame(title
);
121 frame
.setSize(850, 600);
122 frame
.setVisible(true);
130 * Create the UI Configuration menu item.
134 private JMenuItem
createMenuItemUiConfig() {
135 final String title
= trans(StringIdGui
.TITLE_CONFIG_UI
);
136 JMenuItem item
= new JMenuItem(title
);
137 item
.setMnemonic(KeyEvent
.VK_U
);
139 item
.addActionListener(new ActionListener() {
141 public void actionPerformed(ActionEvent e
) {
142 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(
143 UiConfig
.class, Instance
.getInstance().getUiConfig(),
144 trans(StringIdGui
.SUBTITLE_CONFIG_UI
));
145 JFrame frame
= new JFrame(title
);
147 frame
.setSize(800, 600);
148 frame
.setVisible(true);
156 * Create the export menu item.
160 private JMenuItem
createMenuItemExport() {
162 // TODO: allow dir for multiple selection?
164 final JFileChooser fc
= new JFileChooser();
165 fc
.setAcceptAllFileFilterUsed(false);
167 // Add the "ALL" filters first, then the others
168 final Map
<FileFilter
, OutputType
> otherFilters
= new HashMap
<FileFilter
, OutputType
>();
169 for (OutputType type
: OutputType
.values()) {
170 String ext
= type
.getDefaultExtension(false);
171 String desc
= type
.getDesc(false);
173 if (ext
== null || ext
.isEmpty()) {
174 fc
.addChoosableFileFilter(createAllFilter(desc
));
176 otherFilters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
180 for (Entry
<FileFilter
, OutputType
> entry
: otherFilters
.entrySet()) {
181 fc
.addChoosableFileFilter(entry
.getKey());
185 JMenuItem export
= new JMenuItem(trans(StringIdGui
.MENU_FILE_EXPORT
),
187 export
.addActionListener(new ActionListener() {
189 public void actionPerformed(ActionEvent e
) {
190 final BookInfo book
= informer
.getUniqueSelected();
192 fc
.showDialog(BookPopup
.this.getParent(),
193 trans(StringIdGui
.TITLE_SAVE
));
194 if (fc
.getSelectedFile() != null) {
195 final OutputType type
= otherFilters
196 .get(fc
.getFileFilter());
197 final String path
= fc
.getSelectedFile()
199 + type
.getDefaultExtension(false);
200 final Progress pg
= new Progress();
202 new SwingWorker
<Void
, Void
>() {
204 protected Void
doInBackground() throws Exception
{
205 lib
.export(book
.getMeta().getLuid(), type
, path
,
211 protected void done() {
214 } catch (Exception e
) {
215 UiHelper
.error(BookPopup
.this.getParent(),
216 e
.getLocalizedMessage(),
230 * Create a {@link FileFilter} that accepts all files and return the given
238 private FileFilter
createAllFilter(final String desc
) {
239 return new FileFilter() {
241 public String
getDescription() {
246 public boolean accept(File f
) {
253 * Create the refresh (delete cache) menu item.
257 private JMenuItem
createMenuItemClearCache() {
258 JMenuItem refresh
= new JMenuItem(
259 trans(StringIdGui
.MENU_EDIT_CLEAR_CACHE
), KeyEvent
.VK_C
);
260 refresh
.addActionListener(new ActionListener() {
262 public void actionPerformed(ActionEvent e
) {
263 final List
<BookInfo
> selected
= informer
.getSelected();
264 if (!selected
.isEmpty()) {
265 new SwingWorker
<Void
, Void
>() {
267 protected Void
doInBackground() throws Exception
{
268 for (BookInfo book
: selected
) {
269 lib
.clearFromCache(book
.getMeta().getLuid());
270 BookCoverImager
.clearIcon(book
);
276 protected void done() {
279 for (BookInfo book
: selected
) {
280 informer
.setCached(book
, false);
282 } catch (Exception e
) {
283 UiHelper
.error(BookPopup
.this.getParent(),
284 e
.getLocalizedMessage(), "IOException",
297 * Create the "move to" menu item.
301 private JMenuItem
createMenuItemMoveTo() {
302 JMenu changeTo
= new JMenu(trans(StringIdGui
.MENU_FILE_MOVE_TO
));
303 changeTo
.setMnemonic(KeyEvent
.VK_M
);
305 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
307 groupedSources
= lib
.getSourcesGrouped();
308 } catch (IOException e
) {
309 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(),
313 JMenuItem item
= new JMenuItem(
314 trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_TYPE
));
315 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, null));
317 changeTo
.addSeparator();
319 for (final String type
: groupedSources
.keySet()) {
320 List
<String
> list
= groupedSources
.get(type
);
321 if (list
.size() == 1 && list
.get(0).isEmpty()) {
322 item
= new JMenuItem(type
);
323 item
.addActionListener(
324 createMoveAction(ChangeAction
.SOURCE
, type
));
327 JMenu dir
= new JMenu(type
);
328 for (String sub
: list
) {
329 // " " instead of "" for the visual height
330 String itemName
= sub
.isEmpty() ?
" " : sub
;
331 String actualType
= type
;
332 if (!sub
.isEmpty()) {
333 actualType
+= "/" + sub
;
336 item
= new JMenuItem(itemName
);
337 item
.addActionListener(
338 createMoveAction(ChangeAction
.SOURCE
, actualType
));
349 * Create the "set author" menu item.
353 private JMenuItem
createMenuItemSetAuthor() {
354 JMenu changeTo
= new JMenu(trans(StringIdGui
.MENU_FILE_SET_AUTHOR
));
355 changeTo
.setMnemonic(KeyEvent
.VK_A
);
358 JMenuItem newItem
= new JMenuItem(
359 trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_AUTHOR
));
360 changeTo
.add(newItem
);
361 changeTo
.addSeparator();
362 newItem
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, null));
365 Map
<String
, List
<String
>> groupedAuthors
;
368 groupedAuthors
= lib
.getAuthorsGrouped();
369 } catch (IOException e
) {
370 UiHelper
.error(BookPopup
.this.getParent(), e
.getLocalizedMessage(),
372 groupedAuthors
= new HashMap
<String
, List
<String
>>();
376 if (groupedAuthors
.size() > 1) {
377 for (String key
: groupedAuthors
.keySet()) {
378 JMenu group
= new JMenu(key
);
379 for (String value
: groupedAuthors
.get(key
)) {
380 JMenuItem item
= new JMenuItem(value
.isEmpty()
381 ?
trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
383 item
.addActionListener(
384 createMoveAction(ChangeAction
.AUTHOR
, value
));
389 } else if (groupedAuthors
.size() == 1) {
390 for (String value
: groupedAuthors
.values().iterator().next()) {
391 JMenuItem item
= new JMenuItem(value
.isEmpty()
392 ?
trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
394 item
.addActionListener(
395 createMoveAction(ChangeAction
.AUTHOR
, value
));
404 * Create the "rename" menu item.
408 private JMenuItem
createMenuItemRename() {
409 JMenuItem changeTo
= new JMenuItem(trans(StringIdGui
.MENU_FILE_RENAME
));
410 changeTo
.setMnemonic(KeyEvent
.VK_R
);
411 changeTo
.addActionListener(createMoveAction(ChangeAction
.TITLE
, null));
415 private ActionListener
createMoveAction(final ChangeAction what
,
417 return new ActionListener() {
419 public void actionPerformed(ActionEvent e
) {
420 final List
<BookInfo
> selected
= informer
.getSelected();
421 if (!selected
.isEmpty()) {
422 String changeTo
= type
;
426 if (selected
.size() == 1) {
427 MetaData meta
= selected
.get(0).getMeta();
428 if (what
== ChangeAction
.SOURCE
) {
429 init
= meta
.getSource();
430 } else if (what
== ChangeAction
.TITLE
) {
431 init
= meta
.getTitle();
432 } else if (what
== ChangeAction
.AUTHOR
) {
433 init
= meta
.getAuthor();
437 Object rep
= JOptionPane
.showInputDialog(
438 BookPopup
.this.getParent(),
439 trans(StringIdGui
.SUBTITLE_MOVE_TO
),
440 trans(StringIdGui
.TITLE_MOVE_TO
),
441 JOptionPane
.QUESTION_MESSAGE
, null, null, init
);
447 changeTo
= rep
.toString();
450 final String fChangeTo
= changeTo
;
451 new SwingWorker
<Void
, Void
>() {
453 protected Void
doInBackground() throws Exception
{
454 for (BookInfo book
: selected
) {
455 String luid
= book
.getMeta().getLuid();
456 if (what
== ChangeAction
.SOURCE
) {
457 lib
.changeSource(luid
, fChangeTo
, null);
458 } else if (what
== ChangeAction
.TITLE
) {
459 lib
.changeTitle(luid
, fChangeTo
, null);
460 } else if (what
== ChangeAction
.AUTHOR
) {
461 lib
.changeAuthor(luid
, fChangeTo
, null);
469 protected void done() {
471 // this can create new sources/authors, so a
472 // simple fireElementChanged is not
473 // enough, we need to clear the whole cache (for
474 // BrowserPanel for instance)
475 informer
.invalidateCache();
477 // TODO: also refresh the
478 // Sources/Authors(/Tags?) list
480 // Even if problems occurred, still invalidate
483 } catch (Exception e
) {
484 UiHelper
.error(BookPopup
.this.getParent(),
485 e
.getLocalizedMessage(), "IOException",
496 * Create the re-download (then delete original) menu item.
500 private JMenuItem
createMenuItemRedownload() {
501 JMenuItem refresh
= new JMenuItem(
502 trans(StringIdGui
.MENU_EDIT_REDOWNLOAD
), KeyEvent
.VK_R
);
503 refresh
.addActionListener(new ActionListener() {
505 public void actionPerformed(ActionEvent e
) {
506 // final GuiReaderBook selectedBook =
507 // mainPanel.getSelectedBook();
508 // if (selectedBook != null) {
509 // final MetaData meta = selectedBook.getInfo().getMeta();
510 // mainPanel.imprt(meta.getUrl(), new MetaDataRunnable() {
512 // public void run(MetaData newMeta) {
513 // if (!newMeta.getSource().equals(meta.getSource())) {
514 // reader.changeSource(newMeta.getLuid(), meta.getSource());
517 // }, trans(StringIdGui.PROGRESS_CHANGE_SOURCE));
526 * Create the download to cache menu item.
530 private JMenuItem
createMenuItemDownloadToCache() {
531 JMenuItem refresh
= new JMenuItem(
532 trans(StringIdGui
.MENU_EDIT_DOWNLOAD_TO_CACHE
), KeyEvent
.VK_T
);
533 refresh
.addActionListener(new ActionListener() {
535 public void actionPerformed(ActionEvent e
) {
536 final List
<BookInfo
> selected
= informer
.getSelected();
538 new SwingWorker
<Void
, Void
>() {
540 protected Void
doInBackground() throws Exception
{
542 final List
<String
> luids
= new LinkedList
<String
>();
543 for (BookInfo book
: selected
) {
544 switch (book
.getType()) {
546 luids
.add(book
.getMeta().getLuid());
549 for (MetaData meta
: lib
.getList().filter(
550 book
.getMainInfo(), null, null)) {
551 luids
.add(meta
.getLuid());
555 for (MetaData meta
: lib
.getList().filter(null,
556 book
.getMainInfo(), null)) {
557 luids
.add(meta
.getLuid());
561 for (MetaData meta
: lib
.getList().filter(null,
562 null, book
.getMainInfo())) {
563 luids
.add(meta
.getLuid());
569 // TODO: do something with pg?
570 final Progress pg
= new Progress();
571 pg
.setMax(luids
.size());
572 for (String luid
: luids
) {
573 Progress pgStep
= new Progress();
574 pg
.addProgress(pgStep
, 1);
576 lib
.getFile(luid
, pgStep
);
583 protected void done() {
586 for (BookInfo book
: selected
) {
587 informer
.setCached(book
, true);
589 } catch (Exception e
) {
590 UiHelper
.error(BookPopup
.this.getParent(),
591 e
.getLocalizedMessage(), "IOException", e
);
602 * Create the delete menu item.
606 private JMenuItem
createMenuItemDelete() {
607 JMenuItem delete
= new JMenuItem(trans(StringIdGui
.MENU_EDIT_DELETE
),
609 delete
.addActionListener(new ActionListener() {
611 public void actionPerformed(ActionEvent e
) {
612 // final GuiReaderBook selectedBook =
613 // mainPanel.getSelectedBook();
614 // if (selectedBook != null && selectedBook.getInfo().getMeta()
617 // final MetaData meta = selectedBook.getInfo().getMeta();
618 // int rep = JOptionPane.showConfirmDialog(GuiReaderFrame.this,
619 // trans(StringIdGui.SUBTITLE_DELETE, meta.getLuid(),
621 // trans(StringIdGui.TITLE_DELETE),
622 // JOptionPane.OK_CANCEL_OPTION);
624 // if (rep == JOptionPane.OK_OPTION) {
625 // mainPanel.outOfUi(null, true, new Runnable() {
627 // public void run() {
628 // reader.delete(meta.getLuid());
629 // mainPanel.unsetSelectedBook();
641 * Create the properties menu item.
645 private JMenuItem
createMenuItemProperties() {
646 JMenuItem delete
= new JMenuItem(
647 trans(StringIdGui
.MENU_FILE_PROPERTIES
), KeyEvent
.VK_P
);
648 delete
.addActionListener(new ActionListener() {
650 public void actionPerformed(ActionEvent e
) {
651 // final GuiReaderBook selectedBook =
652 // mainPanel.getSelectedBook();
653 // if (selectedBook != null) {
654 // mainPanel.outOfUi(null, false, new Runnable() {
656 // public void run() {
657 // new GuiReaderPropertiesFrame(lib,
658 // selectedBook.getInfo().getMeta())
659 // .setVisible(true);
670 * Create the open menu item for a book, a source/type or an author.
674 public JMenuItem
createMenuItemOpenBook() {
675 JMenuItem open
= new JMenuItem(trans(StringIdGui
.MENU_FILE_OPEN
),
677 open
.addActionListener(new ActionListener() {
679 public void actionPerformed(ActionEvent e
) {
680 final BookInfo book
= informer
.getUniqueSelected();
682 Actions
.openExternal(lib
, book
.getMeta(),
683 BookPopup
.this.getParent(), new Runnable() {
686 informer
.setCached(book
, true);
697 * Create the SetCover menu item for a book to change the linked source
702 private JMenuItem
createMenuItemSetCoverForSource() {
703 JMenuItem open
= new JMenuItem(
704 trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_SOURCE
),
706 open
.addActionListener(new ActionListener() {
708 public void actionPerformed(ActionEvent ae
) {
709 // final GuiReaderBook selectedBook =
710 // mainPanel.getSelectedBook();
711 // if (selectedBook != null) {
712 // BasicLibrary lib = lib;
713 // String luid = selectedBook.getInfo().getMeta().getLuid();
714 // String source = selectedBook.getInfo().getMeta().getSource();
717 // lib.setSourceCover(source, luid);
718 // } catch (IOException e) {
719 // error(e.getLocalizedMessage(), "IOException", e);
722 // GuiReaderBookInfo sourceInfo =
723 // GuiReaderBookInfo.fromSource(lib, source);
724 // GuiReaderCoverImager.clearIcon(sourceInfo);
733 * Create the SetCover menu item for a book to change the linked source
738 private JMenuItem
createMenuItemSetCoverForAuthor() {
739 JMenuItem open
= new JMenuItem(
740 trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_AUTHOR
),
742 open
.addActionListener(new ActionListener() {
744 public void actionPerformed(ActionEvent ae
) {
745 // final GuiReaderBook selectedBook =
746 // mainPanel.getSelectedBook();
747 // if (selectedBook != null) {
748 // String luid = selectedBook.getInfo().getMeta().getLuid();
749 // String author = selectedBook.getInfo().getMeta().getAuthor();
752 // lib.setAuthorCover(author, luid);
753 // } catch (IOException e) {
754 // error(e.getLocalizedMessage(), "IOException", e);
757 // GuiReaderBookInfo authorInfo =
758 // GuiReaderBookInfo.fromAuthor(lib, author);
759 // GuiReaderCoverImager.clearIcon(authorInfo);