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
.LocalLibrary
;
35 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
36 import be
.nikiroo
.fanfix
.reader
.BasicReader
;
37 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderMainPanel
.FrameHelper
;
38 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderMainPanel
.StoryRunnable
;
39 import be
.nikiroo
.utils
.Progress
;
40 import be
.nikiroo
.utils
.Version
;
41 import be
.nikiroo
.utils
.ui
.ConfigEditor
;
44 * A {@link Frame} that will show a {@link GuiReaderBook} item for each
45 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
46 * way to copy them to the {@link GuiReader} cache (
47 * {@link BasicReader#getLibrary()}), read them, delete them...
51 class GuiReaderFrame
extends JFrame
implements FrameHelper
{
52 private static final long serialVersionUID
= 1L;
53 private GuiReader reader
;
54 private GuiReaderMainPanel mainPanel
;
57 * The different modification actions you can use on {@link Story} items.
61 private enum ChangeAction
{
62 /** Change the source/type, that is, move it to another source. */
64 /** Change its name. */
66 /** Change its author. */
71 * Create a new {@link GuiReaderFrame}.
74 * the associated {@link GuiReader} to forward some commands and
75 * access its {@link LocalLibrary}
77 * the type of {@link Story} to load, or NULL for all types
79 public GuiReaderFrame(GuiReader reader
, String type
) {
80 super(getAppTitle(reader
.getLibrary().getLibraryName()));
84 mainPanel
= new GuiReaderMainPanel(this, type
);
87 setLayout(new BorderLayout());
88 add(mainPanel
, BorderLayout
.CENTER
);
92 public JPopupMenu
createBookPopup() {
93 JPopupMenu popup
= new JPopupMenu();
94 popup
.add(createMenuItemOpenBook());
96 popup
.add(createMenuItemExport());
97 popup
.add(createMenuItemMoveTo(true));
98 popup
.add(createMenuItemSetCoverForSource());
99 popup
.add(createMenuItemSetCoverForAuthor());
100 popup
.add(createMenuItemClearCache());
101 popup
.add(createMenuItemRedownload());
102 popup
.addSeparator();
103 popup
.add(createMenuItemRename(true));
104 popup
.add(createMenuItemSetAuthor(true));
105 popup
.addSeparator();
106 popup
.add(createMenuItemDelete());
107 popup
.addSeparator();
108 popup
.add(createMenuItemProperties());
113 public JPopupMenu
createSourceAuthorPopup() {
114 JPopupMenu popup
= new JPopupMenu();
115 popup
.add(createMenuItemOpenBook());
120 public void createMenu(boolean libOk
) {
123 JMenuBar bar
= new JMenuBar();
125 JMenu file
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_FILE
));
126 file
.setMnemonic(KeyEvent
.VK_F
);
128 JMenuItem imprt
= new JMenuItem(
129 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_URL
),
131 imprt
.addActionListener(new ActionListener() {
133 public void actionPerformed(ActionEvent e
) {
134 mainPanel
.imprt(true);
137 JMenuItem imprtF
= new JMenuItem(
138 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_FILE
),
140 imprtF
.addActionListener(new ActionListener() {
142 public void actionPerformed(ActionEvent e
) {
143 mainPanel
.imprt(false);
146 JMenuItem exit
= new JMenuItem(
147 GuiReader
.trans(StringIdGui
.MENU_FILE_EXIT
), KeyEvent
.VK_X
);
148 exit
.addActionListener(new ActionListener() {
150 public void actionPerformed(ActionEvent e
) {
151 GuiReaderFrame
.this.dispatchEvent(new WindowEvent(
152 GuiReaderFrame
.this, WindowEvent
.WINDOW_CLOSING
));
156 file
.add(createMenuItemOpenBook());
157 file
.add(createMenuItemExport());
158 file
.add(createMenuItemMoveTo(libOk
));
163 file
.add(createMenuItemRename(libOk
));
164 file
.add(createMenuItemSetAuthor(libOk
));
166 file
.add(createMenuItemProperties());
172 JMenu edit
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_EDIT
));
173 edit
.setMnemonic(KeyEvent
.VK_E
);
175 edit
.add(createMenuItemSetCoverForSource());
176 edit
.add(createMenuItemSetCoverForAuthor());
177 edit
.add(createMenuItemClearCache());
178 edit
.add(createMenuItemRedownload());
180 edit
.add(createMenuItemDelete());
184 JMenu view
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_VIEW
));
185 view
.setMnemonic(KeyEvent
.VK_V
);
186 JMenuItem vauthors
= new JMenuItem(
187 GuiReader
.trans(StringIdGui
.MENU_VIEW_AUTHOR
));
188 vauthors
.setMnemonic(KeyEvent
.VK_A
);
189 vauthors
.addActionListener(new ActionListener() {
191 public void actionPerformed(ActionEvent e
) {
192 mainPanel
.setWords(false);
193 mainPanel
.refreshBooks();
197 JMenuItem vwords
= new JMenuItem(
198 GuiReader
.trans(StringIdGui
.MENU_VIEW_WCOUNT
));
199 vwords
.setMnemonic(KeyEvent
.VK_W
);
200 vwords
.addActionListener(new ActionListener() {
202 public void actionPerformed(ActionEvent e
) {
203 mainPanel
.setWords(true);
204 mainPanel
.refreshBooks();
210 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
212 groupedSources
= reader
.getLibrary().getSourcesGrouped();
214 JMenu sources
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_SOURCES
));
215 sources
.setMnemonic(KeyEvent
.VK_S
);
216 populateMenuSA(sources
, groupedSources
, true);
219 Map
<String
, List
<String
>> goupedAuthors
= new HashMap
<String
, List
<String
>>();
221 goupedAuthors
= reader
.getLibrary().getAuthorsGrouped();
223 JMenu authors
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_AUTHORS
));
224 authors
.setMnemonic(KeyEvent
.VK_A
);
225 populateMenuSA(authors
, goupedAuthors
, false);
228 JMenu options
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_OPTIONS
));
229 options
.setMnemonic(KeyEvent
.VK_O
);
230 options
.add(createMenuItemConfig());
231 options
.add(createMenuItemUiConfig());
238 private void populateMenuSA(JMenu menu
,
239 Map
<String
, List
<String
>> groupedValues
, boolean type
) {
241 // "All" and "Listing" special items first
242 JMenuItem item
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_GROUPED
));
243 item
.addActionListener(getActionOpenList(type
, false));
245 item
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_LISTING
));
246 item
.addActionListener(getActionOpenList(type
, true));
251 for (final String value
: groupedValues
.keySet()) {
252 List
<String
> list
= groupedValues
.get(value
);
253 if (type
&& list
.size() == 1 && list
.get(0).isEmpty()) {
254 // leaf item source/type
255 item
= new JMenuItem(value
.isEmpty() ? GuiReader
.trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
256 item
.addActionListener(getActionOpen(value
, type
));
260 if (!type
&& groupedValues
.size() == 1) {
261 // only one group of authors
264 dir
= new JMenu(value
.isEmpty() ? GuiReader
.trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
267 for (String sub
: list
) {
268 // " " instead of "" for the visual height
269 String itemName
= sub
;
270 if (itemName
.isEmpty()) {
271 itemName
= type ?
" " : GuiReader
.trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
);
274 String actualValue
= value
;
276 if (!sub
.isEmpty()) {
277 actualValue
+= "/" + sub
;
283 item
= new JMenuItem(itemName
);
284 item
.addActionListener(getActionOpen(actualValue
, type
));
296 * Return an {@link ActionListener} that will set the given source (type) as
297 * the selected/displayed one.
300 * the type (source) to select, cannot be NULL
302 * @return the {@link ActionListener}
304 private ActionListener
getActionOpen(final String source
, final boolean type
) {
305 return new ActionListener() {
307 public void actionPerformed(ActionEvent e
) {
308 mainPanel
.removeBookPanes();
309 mainPanel
.addBookPane(source
, type
);
310 mainPanel
.refreshBooks();
315 private ActionListener
getActionOpenList(final boolean type
,
316 final boolean listMode
) {
317 return new ActionListener() {
319 public void actionPerformed(ActionEvent e
) {
320 mainPanel
.removeBookPanes();
321 mainPanel
.addBookPane(type
, listMode
);
322 mainPanel
.refreshBooks();
328 * Create the Fanfix Configuration menu item.
332 private JMenuItem
createMenuItemConfig() {
333 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG
);
334 JMenuItem item
= new JMenuItem(title
);
335 item
.setMnemonic(KeyEvent
.VK_F
);
337 item
.addActionListener(new ActionListener() {
339 public void actionPerformed(ActionEvent e
) {
340 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(
341 Config
.class, Instance
.getConfig(),
342 GuiReader
.trans(StringIdGui
.SUBTITLE_CONFIG
));
343 JFrame frame
= new JFrame(title
);
345 frame
.setSize(800, 600);
346 frame
.setVisible(true);
354 * Create the UI Configuration menu item.
358 private JMenuItem
createMenuItemUiConfig() {
359 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG_UI
);
360 JMenuItem item
= new JMenuItem(title
);
361 item
.setMnemonic(KeyEvent
.VK_U
);
363 item
.addActionListener(new ActionListener() {
365 public void actionPerformed(ActionEvent e
) {
366 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(
367 UiConfig
.class, Instance
.getUiConfig(),
368 GuiReader
.trans(StringIdGui
.SUBTITLE_CONFIG_UI
));
369 JFrame frame
= new JFrame(title
);
371 frame
.setSize(800, 600);
372 frame
.setVisible(true);
380 * Create the export menu item.
384 private JMenuItem
createMenuItemExport() {
385 final JFileChooser fc
= new JFileChooser();
386 fc
.setAcceptAllFileFilterUsed(false);
388 final Map
<FileFilter
, OutputType
> filters
= new HashMap
<FileFilter
, OutputType
>();
389 for (OutputType type
: OutputType
.values()) {
390 String ext
= type
.getDefaultExtension(false);
391 String desc
= type
.getDesc(false);
393 if (ext
== null || ext
.isEmpty()) {
394 filters
.put(createAllFilter(desc
), type
);
396 filters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
400 // First the "ALL" filters, then, the extension filters
401 for (Entry
<FileFilter
, OutputType
> entry
: filters
.entrySet()) {
402 if (!(entry
.getKey() instanceof FileNameExtensionFilter
)) {
403 fc
.addChoosableFileFilter(entry
.getKey());
406 for (Entry
<FileFilter
, OutputType
> entry
: filters
.entrySet()) {
407 if (entry
.getKey() instanceof FileNameExtensionFilter
) {
408 fc
.addChoosableFileFilter(entry
.getKey());
413 JMenuItem export
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_FILE_EXPORT
), KeyEvent
.VK_S
);
414 export
.addActionListener(new ActionListener() {
416 public void actionPerformed(ActionEvent e
) {
417 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
418 if (selectedBook
!= null) {
419 fc
.showDialog(GuiReaderFrame
.this, GuiReader
.trans(StringIdGui
.TITLE_SAVE
));
420 if (fc
.getSelectedFile() != null) {
421 final OutputType type
= filters
.get(fc
.getFileFilter());
422 final String path
= fc
.getSelectedFile()
424 + type
.getDefaultExtension(false);
425 final Progress pg
= new Progress();
426 mainPanel
.outOfUi(pg
, false, new Runnable() {
430 reader
.getLibrary().export(
431 selectedBook
.getInfo().getMeta()
432 .getLuid(), type
, path
, pg
);
433 } catch (IOException e
) {
434 Instance
.getTraceHandler().error(e
);
447 * Create a {@link FileFilter} that accepts all files and return the given
455 private FileFilter
createAllFilter(final String desc
) {
456 return new FileFilter() {
458 public String
getDescription() {
463 public boolean accept(File f
) {
470 * Create the refresh (delete cache) menu item.
474 private JMenuItem
createMenuItemClearCache() {
475 JMenuItem refresh
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_EDIT_CLEAR_CACHE
), KeyEvent
.VK_C
);
476 refresh
.addActionListener(new ActionListener() {
478 public void actionPerformed(ActionEvent e
) {
479 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
480 if (selectedBook
!= null) {
481 mainPanel
.outOfUi(null, false, new Runnable() {
484 reader
.clearLocalReaderCache(selectedBook
.getInfo()
485 .getMeta().getLuid());
486 selectedBook
.setCached(false);
487 GuiReaderCoverImager
.clearIcon(selectedBook
489 SwingUtilities
.invokeLater(new Runnable() {
492 selectedBook
.repaint();
505 * Create the "move to" menu item.
508 * the library can be queried
512 private JMenuItem
createMenuItemMoveTo(boolean libOk
) {
513 JMenu changeTo
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO
));
514 changeTo
.setMnemonic(KeyEvent
.VK_M
);
516 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
518 groupedSources
= reader
.getLibrary().getSourcesGrouped();
521 JMenuItem item
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_TYPE
));
522 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, null));
524 changeTo
.addSeparator();
526 for (final String type
: groupedSources
.keySet()) {
527 List
<String
> list
= groupedSources
.get(type
);
528 if (list
.size() == 1 && list
.get(0).isEmpty()) {
529 item
= new JMenuItem(type
);
530 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
,
534 JMenu dir
= new JMenu(type
);
535 for (String sub
: list
) {
536 // " " instead of "" for the visual height
537 String itemName
= sub
.isEmpty() ?
" " : sub
;
538 String actualType
= type
;
539 if (!sub
.isEmpty()) {
540 actualType
+= "/" + sub
;
543 item
= new JMenuItem(itemName
);
544 item
.addActionListener(createMoveAction(
545 ChangeAction
.SOURCE
, actualType
));
556 * Create the "set author" menu item.
559 * the library can be queried
563 private JMenuItem
createMenuItemSetAuthor(boolean libOk
) {
564 JMenu changeTo
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_FILE_SET_AUTHOR
));
565 changeTo
.setMnemonic(KeyEvent
.VK_A
);
568 JMenuItem newItem
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_AUTHOR
));
569 changeTo
.add(newItem
);
570 changeTo
.addSeparator();
571 newItem
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, null));
575 Map
<String
, List
<String
>> groupedAuthors
= reader
.getLibrary()
576 .getAuthorsGrouped();
578 if (groupedAuthors
.size() > 1) {
579 for (String key
: groupedAuthors
.keySet()) {
580 JMenu group
= new JMenu(key
);
581 for (String value
: groupedAuthors
.get(key
)) {
582 JMenuItem item
= new JMenuItem(
583 value
.isEmpty() ? GuiReader
.trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
584 item
.addActionListener(createMoveAction(
585 ChangeAction
.AUTHOR
, value
));
590 } else if (groupedAuthors
.size() == 1) {
591 for (String value
: groupedAuthors
.values().iterator().next()) {
592 JMenuItem item
= new JMenuItem(
593 value
.isEmpty() ? GuiReader
.trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
) : value
);
594 item
.addActionListener(createMoveAction(
595 ChangeAction
.AUTHOR
, value
));
605 * Create the "rename" menu item.
608 * the library can be queried
612 private JMenuItem
createMenuItemRename(
613 @SuppressWarnings("unused") boolean libOk
) {
614 JMenuItem changeTo
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_FILE_RENAME
));
615 changeTo
.setMnemonic(KeyEvent
.VK_R
);
616 changeTo
.addActionListener(createMoveAction(ChangeAction
.TITLE
, null));
620 private ActionListener
createMoveAction(final ChangeAction what
,
622 return new ActionListener() {
624 public void actionPerformed(ActionEvent e
) {
625 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
626 if (selectedBook
!= null) {
627 boolean refreshRequired
= false;
629 if (what
== ChangeAction
.SOURCE
) {
630 refreshRequired
= mainPanel
.getCurrentType();
631 } else if (what
== ChangeAction
.TITLE
) {
632 refreshRequired
= false;
633 } else if (what
== ChangeAction
.AUTHOR
) {
634 refreshRequired
= !mainPanel
.getCurrentType();
637 String changeTo
= type
;
639 MetaData meta
= selectedBook
.getInfo().getMeta();
641 if (what
== ChangeAction
.SOURCE
) {
642 init
= meta
.getSource();
643 } else if (what
== ChangeAction
.TITLE
) {
644 init
= meta
.getTitle();
645 } else if (what
== ChangeAction
.AUTHOR
) {
646 init
= meta
.getAuthor();
649 Object rep
= JOptionPane
.showInputDialog(
650 GuiReaderFrame
.this, GuiReader
.trans(StringIdGui
.SUBTITLE_MOVE_TO
),
651 GuiReader
.trans(StringIdGui
.TITLE_MOVE_TO
), JOptionPane
.QUESTION_MESSAGE
,
658 changeTo
= rep
.toString();
661 final String fChangeTo
= changeTo
;
662 mainPanel
.outOfUi(null, refreshRequired
, new Runnable() {
665 String luid
= selectedBook
.getInfo().getMeta()
667 if (what
== ChangeAction
.SOURCE
) {
668 reader
.changeSource(luid
, fChangeTo
);
669 } else if (what
== ChangeAction
.TITLE
) {
670 reader
.changeTitle(luid
, fChangeTo
);
671 } else if (what
== ChangeAction
.AUTHOR
) {
672 reader
.changeAuthor(luid
, fChangeTo
);
675 mainPanel
.getSelectedBook().repaint();
676 mainPanel
.unsetSelectedBook();
678 SwingUtilities
.invokeLater(new Runnable() {
692 * Create the re-download (then delete original) menu item.
696 private JMenuItem
createMenuItemRedownload() {
697 JMenuItem refresh
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_EDIT_REDOWNLOAD
), KeyEvent
.VK_R
);
698 refresh
.addActionListener(new ActionListener() {
700 public void actionPerformed(ActionEvent e
) {
701 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
702 if (selectedBook
!= null) {
703 final MetaData meta
= selectedBook
.getInfo().getMeta();
704 mainPanel
.imprt(meta
.getUrl(), new StoryRunnable() {
706 public void run(Story story
) {
707 reader
.delete(meta
.getLuid());
708 mainPanel
.unsetSelectedBook();
709 MetaData newMeta
= story
.getMeta();
710 if (!newMeta
.getSource().equals(meta
.getSource())) {
711 reader
.changeSource(newMeta
.getLuid(),
715 }, GuiReader
.trans(StringIdGui
.PROGRESS_REDOWNLOAD_REMOVE_OLD_COPY
));
724 * Create the delete menu item.
728 private JMenuItem
createMenuItemDelete() {
729 JMenuItem delete
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_EDIT_DELETE
), KeyEvent
.VK_D
);
730 delete
.addActionListener(new ActionListener() {
732 public void actionPerformed(ActionEvent e
) {
733 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
734 if (selectedBook
!= null
735 && selectedBook
.getInfo().getMeta() != null) {
737 final MetaData meta
= selectedBook
.getInfo().getMeta();
738 int rep
= JOptionPane
.showConfirmDialog(
740 GuiReader
.trans(StringIdGui
.SUBTITLE_DELETE
, meta
.getLuid(),
741 meta
.getTitle()), GuiReader
.trans(StringIdGui
.TITLE_DELETE
),
742 JOptionPane
.OK_CANCEL_OPTION
);
744 if (rep
== JOptionPane
.OK_OPTION
) {
745 mainPanel
.outOfUi(null, true, new Runnable() {
748 reader
.delete(meta
.getLuid());
749 mainPanel
.unsetSelectedBook();
761 * Create the properties menu item.
765 private JMenuItem
createMenuItemProperties() {
766 JMenuItem delete
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_FILE_PROPERTIES
), KeyEvent
.VK_P
);
767 delete
.addActionListener(new ActionListener() {
769 public void actionPerformed(ActionEvent e
) {
770 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
771 if (selectedBook
!= null) {
772 mainPanel
.outOfUi(null, false, new Runnable() {
775 new GuiReaderPropertiesFrame(reader
.getLibrary(),
776 selectedBook
.getInfo().getMeta())
788 * Create the open menu item for a book, a source/type or an author.
792 public JMenuItem
createMenuItemOpenBook() {
793 JMenuItem open
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_FILE_OPEN
), KeyEvent
.VK_O
);
794 open
.addActionListener(new ActionListener() {
796 public void actionPerformed(ActionEvent e
) {
797 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
798 if (selectedBook
!= null) {
799 if (selectedBook
.getInfo().getMeta() == null) {
800 mainPanel
.removeBookPanes();
801 mainPanel
.addBookPane(selectedBook
.getInfo()
802 .getMainInfo(), mainPanel
.getCurrentType());
803 mainPanel
.refreshBooks();
805 mainPanel
.openBook(selectedBook
);
815 * Create the SetCover menu item for a book to change the linked source
820 private JMenuItem
createMenuItemSetCoverForSource() {
821 JMenuItem open
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_SOURCE
), KeyEvent
.VK_C
);
822 open
.addActionListener(new ActionListener() {
824 public void actionPerformed(ActionEvent e
) {
825 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
826 if (selectedBook
!= null) {
827 BasicLibrary lib
= reader
.getLibrary();
828 String luid
= selectedBook
.getInfo().getMeta().getLuid();
829 String source
= selectedBook
.getInfo().getMeta()
832 lib
.setSourceCover(source
, luid
);
834 GuiReaderBookInfo sourceInfo
= GuiReaderBookInfo
835 .fromSource(lib
, source
);
836 GuiReaderCoverImager
.clearIcon(sourceInfo
);
845 * Create the SetCover menu item for a book to change the linked source
850 private JMenuItem
createMenuItemSetCoverForAuthor() {
851 JMenuItem open
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_AUTHOR
), KeyEvent
.VK_A
);
852 open
.addActionListener(new ActionListener() {
854 public void actionPerformed(ActionEvent e
) {
855 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
856 if (selectedBook
!= null) {
857 BasicLibrary lib
= reader
.getLibrary();
858 String luid
= selectedBook
.getInfo().getMeta().getLuid();
859 String author
= selectedBook
.getInfo().getMeta()
862 lib
.setAuthorCover(author
, luid
);
864 GuiReaderBookInfo authorInfo
= GuiReaderBookInfo
865 .fromAuthor(lib
, author
);
866 GuiReaderCoverImager
.clearIcon(authorInfo
);
875 * Display an error message and log the linked {@link Exception}.
880 * the title of the error message
882 * the exception to log if any
884 public void error(final String message
, final String title
, Exception e
) {
885 Instance
.getTraceHandler().error(title
+ ": " + message
);
887 Instance
.getTraceHandler().error(e
);
890 SwingUtilities
.invokeLater(new Runnable() {
893 JOptionPane
.showMessageDialog(GuiReaderFrame
.this, message
,
894 title
, JOptionPane
.ERROR_MESSAGE
);
900 public GuiReader
getReader() {
905 * Return the title of the application.
908 * the name of the associated {@link BasicLibrary}, which can be
913 static private String
getAppTitle(String libraryName
) {
914 if (!libraryName
.isEmpty()) {
915 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY_WITH_NAME
, Version
916 .getCurrentVersion().toString(), libraryName
);
919 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY
, Version
.getCurrentVersion()