1 package be
.nikiroo
.fanfix
.reader
.ui
;
3 import java
.awt
.BorderLayout
;
5 import java
.awt
.event
.ActionEvent
;
6 import java
.awt
.event
.ActionListener
;
7 import java
.awt
.event
.KeyEvent
;
8 import java
.awt
.event
.WindowEvent
;
10 import java
.io
.IOException
;
11 import java
.util
.HashMap
;
12 import java
.util
.List
;
14 import java
.util
.Map
.Entry
;
16 import javax
.swing
.JFileChooser
;
17 import javax
.swing
.JFrame
;
18 import javax
.swing
.JMenu
;
19 import javax
.swing
.JMenuBar
;
20 import javax
.swing
.JMenuItem
;
21 import javax
.swing
.JOptionPane
;
22 import javax
.swing
.JPopupMenu
;
23 import javax
.swing
.SwingUtilities
;
24 import javax
.swing
.filechooser
.FileFilter
;
25 import javax
.swing
.filechooser
.FileNameExtensionFilter
;
27 import be
.nikiroo
.fanfix
.Instance
;
28 import be
.nikiroo
.fanfix
.bundles
.Config
;
29 import be
.nikiroo
.fanfix
.bundles
.StringIdGui
;
30 import be
.nikiroo
.fanfix
.bundles
.UiConfig
;
31 import be
.nikiroo
.fanfix
.data
.MetaData
;
32 import be
.nikiroo
.fanfix
.data
.Story
;
33 import be
.nikiroo
.fanfix
.library
.BasicLibrary
;
34 import be
.nikiroo
.fanfix
.library
.BasicLibrary
.Status
;
35 import be
.nikiroo
.fanfix
.library
.LocalLibrary
;
36 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
37 import be
.nikiroo
.fanfix
.reader
.BasicReader
;
38 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderMainPanel
.FrameHelper
;
39 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderMainPanel
.MetaDataRunnable
;
40 import be
.nikiroo
.fanfix
.searchable
.BasicSearchable
;
41 import be
.nikiroo
.fanfix
.supported
.SupportType
;
42 import be
.nikiroo
.utils
.Progress
;
43 import be
.nikiroo
.utils
.Version
;
44 import be
.nikiroo
.utils
.ui
.ConfigEditor
;
47 * A {@link Frame} that will show a {@link GuiReaderBook} item for each
48 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
49 * way to copy them to the {@link GuiReader} cache (
50 * {@link BasicReader#getLibrary()}), read them, delete them...
54 class GuiReaderFrame
extends JFrame
implements FrameHelper
{
55 private static final long serialVersionUID
= 1L;
56 private GuiReader reader
;
57 private GuiReaderMainPanel mainPanel
;
60 * The different modification actions you can use on {@link Story} items.
64 private enum ChangeAction
{
65 /** Change the source/type, that is, move it to another source. */
67 /** Change its name. */
69 /** Change its author. */
74 * Create a new {@link GuiReaderFrame}.
77 * the associated {@link GuiReader} to forward some commands and
78 * access its {@link LocalLibrary}
80 * the type of {@link Story} to load, or NULL for all types
82 public GuiReaderFrame(GuiReader reader
, String type
) {
83 super(getAppTitle(reader
.getLibrary().getLibraryName()));
87 mainPanel
= new GuiReaderMainPanel(this, type
);
90 setLayout(new BorderLayout());
91 add(mainPanel
, BorderLayout
.CENTER
);
95 public JPopupMenu
createBookPopup() {
96 Status status
= reader
.getLibrary().getStatus();
97 JPopupMenu popup
= new JPopupMenu();
98 popup
.add(createMenuItemOpenBook());
100 popup
.add(createMenuItemExport());
101 if (status
.isWritable()) {
102 popup
.add(createMenuItemMoveTo());
103 popup
.add(createMenuItemSetCoverForSource());
104 popup
.add(createMenuItemSetCoverForAuthor());
106 popup
.add(createMenuItemDownloadToCache());
107 popup
.add(createMenuItemClearCache());
108 if (status
.isWritable()) {
109 popup
.add(createMenuItemRedownload());
110 popup
.addSeparator();
111 popup
.add(createMenuItemRename());
112 popup
.add(createMenuItemSetAuthor());
113 popup
.addSeparator();
114 popup
.add(createMenuItemDelete());
116 popup
.addSeparator();
117 popup
.add(createMenuItemProperties());
122 public JPopupMenu
createSourceAuthorPopup() {
123 JPopupMenu popup
= new JPopupMenu();
124 popup
.add(createMenuItemOpenBook());
129 public void createMenu(Status status
) {
132 JMenuBar bar
= new JMenuBar();
134 JMenu file
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_FILE
));
135 file
.setMnemonic(KeyEvent
.VK_F
);
137 JMenuItem imprt
= new JMenuItem(
138 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_URL
),
140 imprt
.addActionListener(new ActionListener() {
142 public void actionPerformed(ActionEvent e
) {
143 mainPanel
.imprt(true);
146 JMenuItem imprtF
= new JMenuItem(
147 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_FILE
),
149 imprtF
.addActionListener(new ActionListener() {
151 public void actionPerformed(ActionEvent e
) {
152 mainPanel
.imprt(false);
155 JMenuItem exit
= new JMenuItem(
156 GuiReader
.trans(StringIdGui
.MENU_FILE_EXIT
), KeyEvent
.VK_X
);
157 exit
.addActionListener(new ActionListener() {
159 public void actionPerformed(ActionEvent e
) {
160 GuiReaderFrame
.this.dispatchEvent(new WindowEvent(
161 GuiReaderFrame
.this, WindowEvent
.WINDOW_CLOSING
));
165 file
.add(createMenuItemOpenBook());
166 file
.add(createMenuItemExport());
167 if (status
.isWritable()) {
168 file
.add(createMenuItemMoveTo());
173 file
.add(createMenuItemRename());
174 file
.add(createMenuItemSetAuthor());
177 file
.add(createMenuItemProperties());
183 JMenu edit
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_EDIT
));
184 edit
.setMnemonic(KeyEvent
.VK_E
);
186 edit
.add(createMenuItemSetCoverForSource());
187 edit
.add(createMenuItemSetCoverForAuthor());
188 edit
.add(createMenuItemDownloadToCache());
189 edit
.add(createMenuItemClearCache());
190 edit
.add(createMenuItemRedownload());
192 edit
.add(createMenuItemDelete());
196 JMenu search
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_SEARCH
));
197 search
.setMnemonic(KeyEvent
.VK_H
);
198 for (final SupportType type
: SupportType
.values()) {
199 BasicSearchable searchable
= BasicSearchable
.getSearchable(type
);
200 if (searchable
!= null) {
201 JMenuItem searchItem
= new JMenuItem(type
.getSourceName());
202 searchItem
.addActionListener(new ActionListener() {
204 public void actionPerformed(ActionEvent e
) {
205 reader
.search(type
, null, 1, 0, false);
208 search
.add(searchItem
);
214 JMenu view
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_VIEW
));
215 view
.setMnemonic(KeyEvent
.VK_V
);
216 JMenuItem vauthors
= new JMenuItem(
217 GuiReader
.trans(StringIdGui
.MENU_VIEW_AUTHOR
));
218 vauthors
.setMnemonic(KeyEvent
.VK_A
);
219 vauthors
.addActionListener(new ActionListener() {
221 public void actionPerformed(ActionEvent e
) {
222 mainPanel
.setWords(false);
223 mainPanel
.refreshBooks();
227 JMenuItem vwords
= new JMenuItem(
228 GuiReader
.trans(StringIdGui
.MENU_VIEW_WCOUNT
));
229 vwords
.setMnemonic(KeyEvent
.VK_W
);
230 vwords
.addActionListener(new ActionListener() {
232 public void actionPerformed(ActionEvent e
) {
233 mainPanel
.setWords(true);
234 mainPanel
.refreshBooks();
240 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
241 if (status
.isReady()) {
243 groupedSources
= reader
.getLibrary().getSourcesGrouped();
244 } catch (IOException e
) {
245 error(e
.getLocalizedMessage(), "IOException", e
);
248 JMenu sources
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_SOURCES
));
249 sources
.setMnemonic(KeyEvent
.VK_S
);
250 populateMenuSA(sources
, groupedSources
, true);
253 Map
<String
, List
<String
>> goupedAuthors
= new HashMap
<String
, List
<String
>>();
254 if (status
.isReady()) {
256 goupedAuthors
= reader
.getLibrary().getAuthorsGrouped();
257 } catch (IOException e
) {
258 error(e
.getLocalizedMessage(), "IOException", e
);
261 JMenu authors
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_AUTHORS
));
262 authors
.setMnemonic(KeyEvent
.VK_A
);
263 populateMenuSA(authors
, goupedAuthors
, false);
266 JMenu options
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_OPTIONS
));
267 options
.setMnemonic(KeyEvent
.VK_O
);
268 options
.add(createMenuItemConfig());
269 options
.add(createMenuItemUiConfig());
276 private void populateMenuSA(JMenu menu
,
277 Map
<String
, List
<String
>> groupedValues
, boolean type
) {
279 // "All" and "Listing" special items first
280 JMenuItem item
= new JMenuItem(
281 GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_GROUPED
));
282 item
.addActionListener(getActionOpenList(type
, false));
284 item
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_LISTING
));
285 item
.addActionListener(getActionOpenList(type
, true));
290 for (final String value
: groupedValues
.keySet()) {
291 List
<String
> list
= groupedValues
.get(value
);
292 if (type
&& list
.size() == 1 && list
.get(0).isEmpty()) {
293 // leaf item source/type
294 item
= new JMenuItem(
295 value
.isEmpty() ? GuiReader
296 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
298 item
.addActionListener(getActionOpen(value
, type
));
302 if (!type
&& groupedValues
.size() == 1) {
303 // only one group of authors
307 value
.isEmpty() ? GuiReader
308 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
312 for (String sub
: list
) {
313 // " " instead of "" for the visual height
314 String itemName
= sub
;
315 if (itemName
.isEmpty()) {
316 itemName
= type ?
" " : GuiReader
317 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
);
320 String actualValue
= value
;
322 if (!sub
.isEmpty()) {
323 actualValue
+= "/" + sub
;
329 item
= new JMenuItem(itemName
);
330 item
.addActionListener(getActionOpen(actualValue
, type
));
342 * Return an {@link ActionListener} that will set the given source (type) as
343 * the selected/displayed one.
346 * the type (source) to select, cannot be NULL
348 * @return the {@link ActionListener}
350 private ActionListener
getActionOpen(final String source
, final boolean type
) {
351 return new ActionListener() {
353 public void actionPerformed(ActionEvent e
) {
354 mainPanel
.removeBookPanes();
355 mainPanel
.addBookPane(source
, type
);
356 mainPanel
.refreshBooks();
361 private ActionListener
getActionOpenList(final boolean type
,
362 final boolean listMode
) {
363 return new ActionListener() {
365 public void actionPerformed(ActionEvent ae
) {
366 mainPanel
.removeBookPanes();
368 mainPanel
.addBookPane(type
, listMode
);
369 } catch (IOException e
) {
370 error(e
.getLocalizedMessage(), "IOException", e
);
372 mainPanel
.refreshBooks();
378 * Create the Fanfix Configuration menu item.
382 private JMenuItem
createMenuItemConfig() {
383 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG
);
384 JMenuItem item
= new JMenuItem(title
);
385 item
.setMnemonic(KeyEvent
.VK_F
);
387 item
.addActionListener(new ActionListener() {
389 public void actionPerformed(ActionEvent e
) {
390 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(
391 Config
.class, Instance
.getInstance().getConfig(), GuiReader
.trans(StringIdGui
.SUBTITLE_CONFIG
));
392 JFrame frame
= new JFrame(title
);
394 frame
.setSize(850, 600);
395 frame
.setVisible(true);
403 * Create the UI Configuration menu item.
407 private JMenuItem
createMenuItemUiConfig() {
408 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG_UI
);
409 JMenuItem item
= new JMenuItem(title
);
410 item
.setMnemonic(KeyEvent
.VK_U
);
412 item
.addActionListener(new ActionListener() {
414 public void actionPerformed(ActionEvent e
) {
415 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(
416 UiConfig
.class, Instance
.getInstance().getUiConfig(),
417 GuiReader
.trans(StringIdGui
.SUBTITLE_CONFIG_UI
));
418 JFrame frame
= new JFrame(title
);
420 frame
.setSize(800, 600);
421 frame
.setVisible(true);
429 * Create the export menu item.
433 private JMenuItem
createMenuItemExport() {
434 final JFileChooser fc
= new JFileChooser();
435 fc
.setAcceptAllFileFilterUsed(false);
437 // Add the "ALL" filters first, then the others
438 final Map
<FileFilter
, OutputType
> otherFilters
= new HashMap
<FileFilter
, OutputType
>();
439 for (OutputType type
: OutputType
.values()) {
440 String ext
= type
.getDefaultExtension(false);
441 String desc
= type
.getDesc(false);
443 if (ext
== null || ext
.isEmpty()) {
444 fc
.addChoosableFileFilter(createAllFilter(desc
));
446 otherFilters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
450 for (Entry
<FileFilter
, OutputType
> entry
: otherFilters
.entrySet()) {
451 fc
.addChoosableFileFilter(entry
.getKey());
455 JMenuItem export
= new JMenuItem(
456 GuiReader
.trans(StringIdGui
.MENU_FILE_EXPORT
), KeyEvent
.VK_S
);
457 export
.addActionListener(new ActionListener() {
459 public void actionPerformed(ActionEvent e
) {
460 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
461 if (selectedBook
!= null) {
462 fc
.showDialog(GuiReaderFrame
.this,
463 GuiReader
.trans(StringIdGui
.TITLE_SAVE
));
464 if (fc
.getSelectedFile() != null) {
465 final OutputType type
= otherFilters
.get(fc
467 final String path
= fc
.getSelectedFile()
469 + type
.getDefaultExtension(false);
470 final Progress pg
= new Progress();
471 mainPanel
.outOfUi(pg
, false, new Runnable() {
475 reader
.getLibrary().export(
476 selectedBook
.getInfo().getMeta()
477 .getLuid(), type
, path
, pg
);
478 } catch (IOException e
) {
479 Instance
.getInstance().getTraceHandler().error(e
);
492 * Create a {@link FileFilter} that accepts all files and return the given
500 private FileFilter
createAllFilter(final String desc
) {
501 return new FileFilter() {
503 public String
getDescription() {
508 public boolean accept(File f
) {
515 * Create the refresh (delete cache) menu item.
519 private JMenuItem
createMenuItemClearCache() {
520 JMenuItem refresh
= new JMenuItem(
521 GuiReader
.trans(StringIdGui
.MENU_EDIT_CLEAR_CACHE
),
523 refresh
.addActionListener(new ActionListener() {
525 public void actionPerformed(ActionEvent e
) {
526 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
527 if (selectedBook
!= null) {
528 mainPanel
.outOfUi(null, false, new Runnable() {
531 reader
.clearLocalReaderCache(selectedBook
.getInfo()
532 .getMeta().getLuid());
533 selectedBook
.setCached(false);
534 GuiReaderCoverImager
.clearIcon(selectedBook
536 SwingUtilities
.invokeLater(new Runnable() {
539 selectedBook
.repaint();
552 * Create the "move to" menu item.
556 private JMenuItem
createMenuItemMoveTo() {
557 JMenu changeTo
= new JMenu(
558 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO
));
559 changeTo
.setMnemonic(KeyEvent
.VK_M
);
561 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
563 groupedSources
= reader
.getLibrary().getSourcesGrouped();
564 } catch (IOException e
) {
565 error(e
.getLocalizedMessage(), "IOException", e
);
568 JMenuItem item
= new JMenuItem(
569 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_TYPE
));
570 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, null));
572 changeTo
.addSeparator();
574 for (final String type
: groupedSources
.keySet()) {
575 List
<String
> list
= groupedSources
.get(type
);
576 if (list
.size() == 1 && list
.get(0).isEmpty()) {
577 item
= new JMenuItem(type
);
578 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
,
582 JMenu dir
= new JMenu(type
);
583 for (String sub
: list
) {
584 // " " instead of "" for the visual height
585 String itemName
= sub
.isEmpty() ?
" " : sub
;
586 String actualType
= type
;
587 if (!sub
.isEmpty()) {
588 actualType
+= "/" + sub
;
591 item
= new JMenuItem(itemName
);
592 item
.addActionListener(createMoveAction(
593 ChangeAction
.SOURCE
, actualType
));
604 * Create the "set author" menu item.
608 private JMenuItem
createMenuItemSetAuthor() {
609 JMenu changeTo
= new JMenu(
610 GuiReader
.trans(StringIdGui
.MENU_FILE_SET_AUTHOR
));
611 changeTo
.setMnemonic(KeyEvent
.VK_A
);
614 JMenuItem newItem
= new JMenuItem(
615 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_AUTHOR
));
616 changeTo
.add(newItem
);
617 changeTo
.addSeparator();
618 newItem
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, null));
621 Map
<String
, List
<String
>> groupedAuthors
;
624 groupedAuthors
= reader
.getLibrary().getAuthorsGrouped();
625 } catch (IOException e
) {
626 error(e
.getLocalizedMessage(), "IOException", e
);
627 groupedAuthors
= new HashMap
<String
, List
<String
>>();
631 if (groupedAuthors
.size() > 1) {
632 for (String key
: groupedAuthors
.keySet()) {
633 JMenu group
= new JMenu(key
);
634 for (String value
: groupedAuthors
.get(key
)) {
635 JMenuItem item
= new JMenuItem(
636 value
.isEmpty() ? GuiReader
637 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
639 item
.addActionListener(createMoveAction(
640 ChangeAction
.AUTHOR
, value
));
645 } else if (groupedAuthors
.size() == 1) {
646 for (String value
: groupedAuthors
.values().iterator().next()) {
647 JMenuItem item
= new JMenuItem(
648 value
.isEmpty() ? GuiReader
649 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
651 item
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
,
661 * Create the "rename" menu item.
665 private JMenuItem
createMenuItemRename() {
666 JMenuItem changeTo
= new JMenuItem(
667 GuiReader
.trans(StringIdGui
.MENU_FILE_RENAME
));
668 changeTo
.setMnemonic(KeyEvent
.VK_R
);
669 changeTo
.addActionListener(createMoveAction(ChangeAction
.TITLE
, null));
673 private ActionListener
createMoveAction(final ChangeAction what
,
675 return new ActionListener() {
677 public void actionPerformed(ActionEvent e
) {
678 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
679 if (selectedBook
!= null) {
680 boolean refreshRequired
= false;
682 if (what
== ChangeAction
.SOURCE
) {
683 refreshRequired
= mainPanel
.getCurrentType();
684 } else if (what
== ChangeAction
.TITLE
) {
685 refreshRequired
= false;
686 } else if (what
== ChangeAction
.AUTHOR
) {
687 refreshRequired
= !mainPanel
.getCurrentType();
690 String changeTo
= type
;
692 MetaData meta
= selectedBook
.getInfo().getMeta();
694 if (what
== ChangeAction
.SOURCE
) {
695 init
= meta
.getSource();
696 } else if (what
== ChangeAction
.TITLE
) {
697 init
= meta
.getTitle();
698 } else if (what
== ChangeAction
.AUTHOR
) {
699 init
= meta
.getAuthor();
702 Object rep
= JOptionPane
.showInputDialog(
704 GuiReader
.trans(StringIdGui
.SUBTITLE_MOVE_TO
),
705 GuiReader
.trans(StringIdGui
.TITLE_MOVE_TO
),
706 JOptionPane
.QUESTION_MESSAGE
, null, null, init
);
712 changeTo
= rep
.toString();
715 final String fChangeTo
= changeTo
;
716 mainPanel
.outOfUi(null, refreshRequired
, new Runnable() {
719 String luid
= selectedBook
.getInfo().getMeta()
721 if (what
== ChangeAction
.SOURCE
) {
722 reader
.changeSource(luid
, fChangeTo
);
723 } else if (what
== ChangeAction
.TITLE
) {
724 reader
.changeTitle(luid
, fChangeTo
);
725 } else if (what
== ChangeAction
.AUTHOR
) {
726 reader
.changeAuthor(luid
, fChangeTo
);
729 mainPanel
.getSelectedBook().repaint();
730 mainPanel
.unsetSelectedBook();
732 SwingUtilities
.invokeLater(new Runnable() {
735 createMenu(reader
.getLibrary().getStatus());
746 * Create the re-download (then delete original) menu item.
750 private JMenuItem
createMenuItemRedownload() {
751 JMenuItem refresh
= new JMenuItem(
752 GuiReader
.trans(StringIdGui
.MENU_EDIT_REDOWNLOAD
),
754 refresh
.addActionListener(new ActionListener() {
756 public void actionPerformed(ActionEvent e
) {
757 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
758 if (selectedBook
!= null) {
759 final MetaData meta
= selectedBook
.getInfo().getMeta();
760 mainPanel
.imprt(meta
.getUrl(), new MetaDataRunnable() {
762 public void run(MetaData newMeta
) {
763 if (!newMeta
.getSource().equals(meta
.getSource())) {
764 reader
.changeSource(newMeta
.getLuid(),
768 }, GuiReader
.trans(StringIdGui
.PROGRESS_CHANGE_SOURCE
));
777 * Create the download to cache menu item.
781 private JMenuItem
createMenuItemDownloadToCache() {
782 JMenuItem refresh
= new JMenuItem(
783 GuiReader
.trans(StringIdGui
.MENU_EDIT_DOWNLOAD_TO_CACHE
),
785 refresh
.addActionListener(new ActionListener() {
787 public void actionPerformed(ActionEvent e
) {
788 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
789 if (selectedBook
!= null) {
790 mainPanel
.prefetchBook(selectedBook
);
800 * Create the delete menu item.
804 private JMenuItem
createMenuItemDelete() {
805 JMenuItem delete
= new JMenuItem(
806 GuiReader
.trans(StringIdGui
.MENU_EDIT_DELETE
), KeyEvent
.VK_D
);
807 delete
.addActionListener(new ActionListener() {
809 public void actionPerformed(ActionEvent e
) {
810 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
811 if (selectedBook
!= null
812 && selectedBook
.getInfo().getMeta() != null) {
814 final MetaData meta
= selectedBook
.getInfo().getMeta();
815 int rep
= JOptionPane
.showConfirmDialog(
817 GuiReader
.trans(StringIdGui
.SUBTITLE_DELETE
,
818 meta
.getLuid(), meta
.getTitle()),
819 GuiReader
.trans(StringIdGui
.TITLE_DELETE
),
820 JOptionPane
.OK_CANCEL_OPTION
);
822 if (rep
== JOptionPane
.OK_OPTION
) {
823 mainPanel
.outOfUi(null, true, new Runnable() {
826 reader
.delete(meta
.getLuid());
827 mainPanel
.unsetSelectedBook();
839 * Create the properties menu item.
843 private JMenuItem
createMenuItemProperties() {
844 JMenuItem delete
= new JMenuItem(
845 GuiReader
.trans(StringIdGui
.MENU_FILE_PROPERTIES
),
847 delete
.addActionListener(new ActionListener() {
849 public void actionPerformed(ActionEvent e
) {
850 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
851 if (selectedBook
!= null) {
852 mainPanel
.outOfUi(null, false, new Runnable() {
855 new GuiReaderPropertiesFrame(reader
.getLibrary(),
856 selectedBook
.getInfo().getMeta())
868 * Create the open menu item for a book, a source/type or an author.
872 public JMenuItem
createMenuItemOpenBook() {
873 JMenuItem open
= new JMenuItem(
874 GuiReader
.trans(StringIdGui
.MENU_FILE_OPEN
), KeyEvent
.VK_O
);
875 open
.addActionListener(new ActionListener() {
877 public void actionPerformed(ActionEvent e
) {
878 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
879 if (selectedBook
!= null) {
880 if (selectedBook
.getInfo().getMeta() == null) {
881 mainPanel
.removeBookPanes();
882 mainPanel
.addBookPane(selectedBook
.getInfo()
883 .getMainInfo(), mainPanel
.getCurrentType());
884 mainPanel
.refreshBooks();
886 mainPanel
.openBook(selectedBook
);
896 * Create the SetCover menu item for a book to change the linked source
901 private JMenuItem
createMenuItemSetCoverForSource() {
902 JMenuItem open
= new JMenuItem(
903 GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_SOURCE
),
905 open
.addActionListener(new ActionListener() {
907 public void actionPerformed(ActionEvent ae
) {
908 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
909 if (selectedBook
!= null) {
910 BasicLibrary lib
= reader
.getLibrary();
911 String luid
= selectedBook
.getInfo().getMeta().getLuid();
912 String source
= selectedBook
.getInfo().getMeta()
916 lib
.setSourceCover(source
, luid
);
917 } catch (IOException e
) {
918 error(e
.getLocalizedMessage(), "IOException", e
);
921 GuiReaderBookInfo sourceInfo
= GuiReaderBookInfo
922 .fromSource(lib
, source
);
923 GuiReaderCoverImager
.clearIcon(sourceInfo
);
932 * Create the SetCover menu item for a book to change the linked source
937 private JMenuItem
createMenuItemSetCoverForAuthor() {
938 JMenuItem open
= new JMenuItem(
939 GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_AUTHOR
),
941 open
.addActionListener(new ActionListener() {
943 public void actionPerformed(ActionEvent ae
) {
944 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
945 if (selectedBook
!= null) {
946 BasicLibrary lib
= reader
.getLibrary();
947 String luid
= selectedBook
.getInfo().getMeta().getLuid();
948 String author
= selectedBook
.getInfo().getMeta()
952 lib
.setAuthorCover(author
, luid
);
953 } catch (IOException e
) {
954 error(e
.getLocalizedMessage(), "IOException", e
);
957 GuiReaderBookInfo authorInfo
= GuiReaderBookInfo
958 .fromAuthor(lib
, author
);
959 GuiReaderCoverImager
.clearIcon(authorInfo
);
968 * Display an error message and log the linked {@link Exception}.
973 * the title of the error message
975 * the exception to log if any
977 public void error(final String message
, final String title
, Exception e
) {
978 Instance
.getInstance().getTraceHandler().error(title
+ ": " + message
);
980 Instance
.getInstance().getTraceHandler().error(e
);
983 SwingUtilities
.invokeLater(new Runnable() {
986 JOptionPane
.showMessageDialog(GuiReaderFrame
.this, message
,
987 title
, JOptionPane
.ERROR_MESSAGE
);
993 public GuiReader
getReader() {
998 * Return the title of the application.
1000 * @param libraryName
1001 * the name of the associated {@link BasicLibrary}, which can be
1006 static private String
getAppTitle(String libraryName
) {
1007 if (!libraryName
.isEmpty()) {
1008 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY_WITH_NAME
, Version
1009 .getCurrentVersion().toString(), libraryName
);
1012 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY
, Version
1013 .getCurrentVersion().toString());