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
.fanfix
.searchable
.BasicSearchable
;
40 import be
.nikiroo
.fanfix
.supported
.SupportType
;
41 import be
.nikiroo
.utils
.Progress
;
42 import be
.nikiroo
.utils
.Version
;
43 import be
.nikiroo
.utils
.ui
.ConfigEditor
;
46 * A {@link Frame} that will show a {@link GuiReaderBook} item for each
47 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
48 * way to copy them to the {@link GuiReader} cache (
49 * {@link BasicReader#getLibrary()}), read them, delete them...
53 class GuiReaderFrame
extends JFrame
implements FrameHelper
{
54 private static final long serialVersionUID
= 1L;
55 private GuiReader reader
;
56 private GuiReaderMainPanel mainPanel
;
59 * The different modification actions you can use on {@link Story} items.
63 private enum ChangeAction
{
64 /** Change the source/type, that is, move it to another source. */
66 /** Change its name. */
68 /** Change its author. */
73 * Create a new {@link GuiReaderFrame}.
76 * the associated {@link GuiReader} to forward some commands and
77 * access its {@link LocalLibrary}
79 * the type of {@link Story} to load, or NULL for all types
81 public GuiReaderFrame(GuiReader reader
, String type
) {
82 super(getAppTitle(reader
.getLibrary().getLibraryName()));
86 mainPanel
= new GuiReaderMainPanel(this, type
);
89 setLayout(new BorderLayout());
90 add(mainPanel
, BorderLayout
.CENTER
);
94 public JPopupMenu
createBookPopup() {
95 JPopupMenu popup
= new JPopupMenu();
96 popup
.add(createMenuItemOpenBook());
98 popup
.add(createMenuItemExport());
99 popup
.add(createMenuItemMoveTo(true));
100 popup
.add(createMenuItemSetCoverForSource());
101 popup
.add(createMenuItemSetCoverForAuthor());
102 popup
.add(createMenuItemClearCache());
103 popup
.add(createMenuItemRedownload());
104 popup
.addSeparator();
105 popup
.add(createMenuItemRename(true));
106 popup
.add(createMenuItemSetAuthor(true));
107 popup
.addSeparator();
108 popup
.add(createMenuItemDelete());
109 popup
.addSeparator();
110 popup
.add(createMenuItemProperties());
115 public JPopupMenu
createSourceAuthorPopup() {
116 JPopupMenu popup
= new JPopupMenu();
117 popup
.add(createMenuItemOpenBook());
122 public void createMenu(boolean libOk
) {
125 JMenuBar bar
= new JMenuBar();
127 JMenu file
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_FILE
));
128 file
.setMnemonic(KeyEvent
.VK_F
);
130 JMenuItem imprt
= new JMenuItem(
131 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_URL
),
133 imprt
.addActionListener(new ActionListener() {
135 public void actionPerformed(ActionEvent e
) {
136 mainPanel
.imprt(true);
139 JMenuItem imprtF
= new JMenuItem(
140 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_FILE
),
142 imprtF
.addActionListener(new ActionListener() {
144 public void actionPerformed(ActionEvent e
) {
145 mainPanel
.imprt(false);
148 JMenuItem exit
= new JMenuItem(
149 GuiReader
.trans(StringIdGui
.MENU_FILE_EXIT
), KeyEvent
.VK_X
);
150 exit
.addActionListener(new ActionListener() {
152 public void actionPerformed(ActionEvent e
) {
153 GuiReaderFrame
.this.dispatchEvent(new WindowEvent(
154 GuiReaderFrame
.this, WindowEvent
.WINDOW_CLOSING
));
158 file
.add(createMenuItemOpenBook());
159 file
.add(createMenuItemExport());
160 file
.add(createMenuItemMoveTo(libOk
));
165 file
.add(createMenuItemRename(libOk
));
166 file
.add(createMenuItemSetAuthor(libOk
));
168 file
.add(createMenuItemProperties());
174 JMenu edit
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_EDIT
));
175 edit
.setMnemonic(KeyEvent
.VK_E
);
177 edit
.add(createMenuItemSetCoverForSource());
178 edit
.add(createMenuItemSetCoverForAuthor());
179 edit
.add(createMenuItemClearCache());
180 edit
.add(createMenuItemRedownload());
182 edit
.add(createMenuItemDelete());
186 JMenu search
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_SEARCH
));
187 search
.setMnemonic(KeyEvent
.VK_H
);
188 for (final SupportType type
: SupportType
.values()) {
189 BasicSearchable searchable
= BasicSearchable
.getSearchable(type
);
190 if (searchable
!= null) {
191 JMenuItem searchItem
= new JMenuItem(type
.getSourceName());
192 searchItem
.addActionListener(new ActionListener() {
194 public void actionPerformed(ActionEvent e
) {
195 reader
.search(type
, null, 1, 0, false);
198 search
.add(searchItem
);
204 JMenu view
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_VIEW
));
205 view
.setMnemonic(KeyEvent
.VK_V
);
206 JMenuItem vauthors
= new JMenuItem(
207 GuiReader
.trans(StringIdGui
.MENU_VIEW_AUTHOR
));
208 vauthors
.setMnemonic(KeyEvent
.VK_A
);
209 vauthors
.addActionListener(new ActionListener() {
211 public void actionPerformed(ActionEvent e
) {
212 mainPanel
.setWords(false);
213 mainPanel
.refreshBooks();
217 JMenuItem vwords
= new JMenuItem(
218 GuiReader
.trans(StringIdGui
.MENU_VIEW_WCOUNT
));
219 vwords
.setMnemonic(KeyEvent
.VK_W
);
220 vwords
.addActionListener(new ActionListener() {
222 public void actionPerformed(ActionEvent e
) {
223 mainPanel
.setWords(true);
224 mainPanel
.refreshBooks();
230 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
232 groupedSources
= reader
.getLibrary().getSourcesGrouped();
234 JMenu sources
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_SOURCES
));
235 sources
.setMnemonic(KeyEvent
.VK_S
);
236 populateMenuSA(sources
, groupedSources
, true);
239 Map
<String
, List
<String
>> goupedAuthors
= new HashMap
<String
, List
<String
>>();
241 goupedAuthors
= reader
.getLibrary().getAuthorsGrouped();
243 JMenu authors
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_AUTHORS
));
244 authors
.setMnemonic(KeyEvent
.VK_A
);
245 populateMenuSA(authors
, goupedAuthors
, false);
248 JMenu options
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_OPTIONS
));
249 options
.setMnemonic(KeyEvent
.VK_O
);
250 options
.add(createMenuItemConfig());
251 options
.add(createMenuItemUiConfig());
258 private void populateMenuSA(JMenu menu
,
259 Map
<String
, List
<String
>> groupedValues
, boolean type
) {
261 // "All" and "Listing" special items first
262 JMenuItem item
= new JMenuItem(
263 GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_GROUPED
));
264 item
.addActionListener(getActionOpenList(type
, false));
266 item
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_LISTING
));
267 item
.addActionListener(getActionOpenList(type
, true));
272 for (final String value
: groupedValues
.keySet()) {
273 List
<String
> list
= groupedValues
.get(value
);
274 if (type
&& list
.size() == 1 && list
.get(0).isEmpty()) {
275 // leaf item source/type
276 item
= new JMenuItem(
277 value
.isEmpty() ? GuiReader
278 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
280 item
.addActionListener(getActionOpen(value
, type
));
284 if (!type
&& groupedValues
.size() == 1) {
285 // only one group of authors
289 value
.isEmpty() ? GuiReader
290 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
294 for (String sub
: list
) {
295 // " " instead of "" for the visual height
296 String itemName
= sub
;
297 if (itemName
.isEmpty()) {
298 itemName
= type ?
" " : GuiReader
299 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
);
302 String actualValue
= value
;
304 if (!sub
.isEmpty()) {
305 actualValue
+= "/" + sub
;
311 item
= new JMenuItem(itemName
);
312 item
.addActionListener(getActionOpen(actualValue
, type
));
324 * Return an {@link ActionListener} that will set the given source (type) as
325 * the selected/displayed one.
328 * the type (source) to select, cannot be NULL
330 * @return the {@link ActionListener}
332 private ActionListener
getActionOpen(final String source
, final boolean type
) {
333 return new ActionListener() {
335 public void actionPerformed(ActionEvent e
) {
336 mainPanel
.removeBookPanes();
337 mainPanel
.addBookPane(source
, type
);
338 mainPanel
.refreshBooks();
343 private ActionListener
getActionOpenList(final boolean type
,
344 final boolean listMode
) {
345 return new ActionListener() {
347 public void actionPerformed(ActionEvent e
) {
348 mainPanel
.removeBookPanes();
349 mainPanel
.addBookPane(type
, listMode
);
350 mainPanel
.refreshBooks();
356 * Create the Fanfix Configuration menu item.
360 private JMenuItem
createMenuItemConfig() {
361 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG
);
362 JMenuItem item
= new JMenuItem(title
);
363 item
.setMnemonic(KeyEvent
.VK_F
);
365 item
.addActionListener(new ActionListener() {
367 public void actionPerformed(ActionEvent e
) {
368 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(
369 Config
.class, Instance
.getConfig(), GuiReader
370 .trans(StringIdGui
.SUBTITLE_CONFIG
));
371 JFrame frame
= new JFrame(title
);
373 frame
.setSize(800, 600);
374 frame
.setVisible(true);
382 * Create the UI Configuration menu item.
386 private JMenuItem
createMenuItemUiConfig() {
387 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG_UI
);
388 JMenuItem item
= new JMenuItem(title
);
389 item
.setMnemonic(KeyEvent
.VK_U
);
391 item
.addActionListener(new ActionListener() {
393 public void actionPerformed(ActionEvent e
) {
394 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(
395 UiConfig
.class, Instance
.getUiConfig(), GuiReader
396 .trans(StringIdGui
.SUBTITLE_CONFIG_UI
));
397 JFrame frame
= new JFrame(title
);
399 frame
.setSize(800, 600);
400 frame
.setVisible(true);
408 * Create the export menu item.
412 private JMenuItem
createMenuItemExport() {
413 final JFileChooser fc
= new JFileChooser();
414 fc
.setAcceptAllFileFilterUsed(false);
416 // Add the "ALL" filters first, then the others
417 final Map
<FileFilter
, OutputType
> otherFilters
= new HashMap
<FileFilter
, OutputType
>();
418 for (OutputType type
: OutputType
.values()) {
419 String ext
= type
.getDefaultExtension(false);
420 String desc
= type
.getDesc(false);
422 if (ext
== null || ext
.isEmpty()) {
423 fc
.addChoosableFileFilter(createAllFilter(desc
));
425 otherFilters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
429 for (Entry
<FileFilter
, OutputType
> entry
: otherFilters
.entrySet()) {
430 fc
.addChoosableFileFilter(entry
.getKey());
434 JMenuItem export
= new JMenuItem(
435 GuiReader
.trans(StringIdGui
.MENU_FILE_EXPORT
), KeyEvent
.VK_S
);
436 export
.addActionListener(new ActionListener() {
438 public void actionPerformed(ActionEvent e
) {
439 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
440 if (selectedBook
!= null) {
441 fc
.showDialog(GuiReaderFrame
.this,
442 GuiReader
.trans(StringIdGui
.TITLE_SAVE
));
443 if (fc
.getSelectedFile() != null) {
444 final OutputType type
= otherFilters
.get(fc
446 final String path
= fc
.getSelectedFile()
448 + type
.getDefaultExtension(false);
449 final Progress pg
= new Progress();
450 mainPanel
.outOfUi(pg
, false, new Runnable() {
454 reader
.getLibrary().export(
455 selectedBook
.getInfo().getMeta()
456 .getLuid(), type
, path
, pg
);
457 } catch (IOException e
) {
458 Instance
.getTraceHandler().error(e
);
471 * Create a {@link FileFilter} that accepts all files and return the given
479 private FileFilter
createAllFilter(final String desc
) {
480 return new FileFilter() {
482 public String
getDescription() {
487 public boolean accept(File f
) {
494 * Create the refresh (delete cache) menu item.
498 private JMenuItem
createMenuItemClearCache() {
499 JMenuItem refresh
= new JMenuItem(
500 GuiReader
.trans(StringIdGui
.MENU_EDIT_CLEAR_CACHE
),
502 refresh
.addActionListener(new ActionListener() {
504 public void actionPerformed(ActionEvent e
) {
505 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
506 if (selectedBook
!= null) {
507 mainPanel
.outOfUi(null, false, new Runnable() {
510 reader
.clearLocalReaderCache(selectedBook
.getInfo()
511 .getMeta().getLuid());
512 selectedBook
.setCached(false);
513 GuiReaderCoverImager
.clearIcon(selectedBook
515 SwingUtilities
.invokeLater(new Runnable() {
518 selectedBook
.repaint();
531 * Create the "move to" menu item.
534 * the library can be queried
538 private JMenuItem
createMenuItemMoveTo(boolean libOk
) {
539 JMenu changeTo
= new JMenu(
540 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO
));
541 changeTo
.setMnemonic(KeyEvent
.VK_M
);
543 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
545 groupedSources
= reader
.getLibrary().getSourcesGrouped();
548 JMenuItem item
= new JMenuItem(
549 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_TYPE
));
550 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, null));
552 changeTo
.addSeparator();
554 for (final String type
: groupedSources
.keySet()) {
555 List
<String
> list
= groupedSources
.get(type
);
556 if (list
.size() == 1 && list
.get(0).isEmpty()) {
557 item
= new JMenuItem(type
);
558 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
,
562 JMenu dir
= new JMenu(type
);
563 for (String sub
: list
) {
564 // " " instead of "" for the visual height
565 String itemName
= sub
.isEmpty() ?
" " : sub
;
566 String actualType
= type
;
567 if (!sub
.isEmpty()) {
568 actualType
+= "/" + sub
;
571 item
= new JMenuItem(itemName
);
572 item
.addActionListener(createMoveAction(
573 ChangeAction
.SOURCE
, actualType
));
584 * Create the "set author" menu item.
587 * the library can be queried
591 private JMenuItem
createMenuItemSetAuthor(boolean libOk
) {
592 JMenu changeTo
= new JMenu(
593 GuiReader
.trans(StringIdGui
.MENU_FILE_SET_AUTHOR
));
594 changeTo
.setMnemonic(KeyEvent
.VK_A
);
597 JMenuItem newItem
= new JMenuItem(
598 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_AUTHOR
));
599 changeTo
.add(newItem
);
600 changeTo
.addSeparator();
601 newItem
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, null));
605 Map
<String
, List
<String
>> groupedAuthors
= reader
.getLibrary()
606 .getAuthorsGrouped();
608 if (groupedAuthors
.size() > 1) {
609 for (String key
: groupedAuthors
.keySet()) {
610 JMenu group
= new JMenu(key
);
611 for (String value
: groupedAuthors
.get(key
)) {
612 JMenuItem item
= new JMenuItem(
613 value
.isEmpty() ? GuiReader
614 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
616 item
.addActionListener(createMoveAction(
617 ChangeAction
.AUTHOR
, value
));
622 } else if (groupedAuthors
.size() == 1) {
623 for (String value
: groupedAuthors
.values().iterator().next()) {
624 JMenuItem item
= new JMenuItem(
625 value
.isEmpty() ? GuiReader
626 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
628 item
.addActionListener(createMoveAction(
629 ChangeAction
.AUTHOR
, value
));
639 * Create the "rename" menu item.
642 * the library can be queried
646 private JMenuItem
createMenuItemRename(
647 @SuppressWarnings("unused") boolean libOk
) {
648 JMenuItem changeTo
= new JMenuItem(
649 GuiReader
.trans(StringIdGui
.MENU_FILE_RENAME
));
650 changeTo
.setMnemonic(KeyEvent
.VK_R
);
651 changeTo
.addActionListener(createMoveAction(ChangeAction
.TITLE
, null));
655 private ActionListener
createMoveAction(final ChangeAction what
,
657 return new ActionListener() {
659 public void actionPerformed(ActionEvent e
) {
660 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
661 if (selectedBook
!= null) {
662 boolean refreshRequired
= false;
664 if (what
== ChangeAction
.SOURCE
) {
665 refreshRequired
= mainPanel
.getCurrentType();
666 } else if (what
== ChangeAction
.TITLE
) {
667 refreshRequired
= false;
668 } else if (what
== ChangeAction
.AUTHOR
) {
669 refreshRequired
= !mainPanel
.getCurrentType();
672 String changeTo
= type
;
674 MetaData meta
= selectedBook
.getInfo().getMeta();
676 if (what
== ChangeAction
.SOURCE
) {
677 init
= meta
.getSource();
678 } else if (what
== ChangeAction
.TITLE
) {
679 init
= meta
.getTitle();
680 } else if (what
== ChangeAction
.AUTHOR
) {
681 init
= meta
.getAuthor();
684 Object rep
= JOptionPane
.showInputDialog(
686 GuiReader
.trans(StringIdGui
.SUBTITLE_MOVE_TO
),
687 GuiReader
.trans(StringIdGui
.TITLE_MOVE_TO
),
688 JOptionPane
.QUESTION_MESSAGE
, null, null, init
);
694 changeTo
= rep
.toString();
697 final String fChangeTo
= changeTo
;
698 mainPanel
.outOfUi(null, refreshRequired
, new Runnable() {
701 String luid
= selectedBook
.getInfo().getMeta()
703 if (what
== ChangeAction
.SOURCE
) {
704 reader
.changeSource(luid
, fChangeTo
);
705 } else if (what
== ChangeAction
.TITLE
) {
706 reader
.changeTitle(luid
, fChangeTo
);
707 } else if (what
== ChangeAction
.AUTHOR
) {
708 reader
.changeAuthor(luid
, fChangeTo
);
711 mainPanel
.getSelectedBook().repaint();
712 mainPanel
.unsetSelectedBook();
714 SwingUtilities
.invokeLater(new Runnable() {
728 * Create the re-download (then delete original) menu item.
732 private JMenuItem
createMenuItemRedownload() {
733 JMenuItem refresh
= new JMenuItem(
734 GuiReader
.trans(StringIdGui
.MENU_EDIT_REDOWNLOAD
),
736 refresh
.addActionListener(new ActionListener() {
738 public void actionPerformed(ActionEvent e
) {
739 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
740 if (selectedBook
!= null) {
741 final MetaData meta
= selectedBook
.getInfo().getMeta();
742 mainPanel
.imprt(meta
.getUrl(), new StoryRunnable() {
744 public void run(Story story
) {
745 MetaData newMeta
= story
.getMeta();
746 if (!newMeta
.getSource().equals(meta
.getSource())) {
747 reader
.changeSource(newMeta
.getLuid(),
751 }, GuiReader
.trans(StringIdGui
.PROGRESS_CHANGE_SOURCE
));
760 * Create the delete menu item.
764 private JMenuItem
createMenuItemDelete() {
765 JMenuItem delete
= new JMenuItem(
766 GuiReader
.trans(StringIdGui
.MENU_EDIT_DELETE
), KeyEvent
.VK_D
);
767 delete
.addActionListener(new ActionListener() {
769 public void actionPerformed(ActionEvent e
) {
770 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
771 if (selectedBook
!= null
772 && selectedBook
.getInfo().getMeta() != null) {
774 final MetaData meta
= selectedBook
.getInfo().getMeta();
775 int rep
= JOptionPane
.showConfirmDialog(
777 GuiReader
.trans(StringIdGui
.SUBTITLE_DELETE
,
778 meta
.getLuid(), meta
.getTitle()),
779 GuiReader
.trans(StringIdGui
.TITLE_DELETE
),
780 JOptionPane
.OK_CANCEL_OPTION
);
782 if (rep
== JOptionPane
.OK_OPTION
) {
783 mainPanel
.outOfUi(null, true, new Runnable() {
786 reader
.delete(meta
.getLuid());
787 mainPanel
.unsetSelectedBook();
799 * Create the properties menu item.
803 private JMenuItem
createMenuItemProperties() {
804 JMenuItem delete
= new JMenuItem(
805 GuiReader
.trans(StringIdGui
.MENU_FILE_PROPERTIES
),
807 delete
.addActionListener(new ActionListener() {
809 public void actionPerformed(ActionEvent e
) {
810 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
811 if (selectedBook
!= null) {
812 mainPanel
.outOfUi(null, false, new Runnable() {
815 new GuiReaderPropertiesFrame(reader
.getLibrary(),
816 selectedBook
.getInfo().getMeta())
828 * Create the open menu item for a book, a source/type or an author.
832 public JMenuItem
createMenuItemOpenBook() {
833 JMenuItem open
= new JMenuItem(
834 GuiReader
.trans(StringIdGui
.MENU_FILE_OPEN
), KeyEvent
.VK_O
);
835 open
.addActionListener(new ActionListener() {
837 public void actionPerformed(ActionEvent e
) {
838 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
839 if (selectedBook
!= null) {
840 if (selectedBook
.getInfo().getMeta() == null) {
841 mainPanel
.removeBookPanes();
842 mainPanel
.addBookPane(selectedBook
.getInfo()
843 .getMainInfo(), mainPanel
.getCurrentType());
844 mainPanel
.refreshBooks();
846 mainPanel
.openBook(selectedBook
);
856 * Create the SetCover menu item for a book to change the linked source
861 private JMenuItem
createMenuItemSetCoverForSource() {
862 JMenuItem open
= new JMenuItem(
863 GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_SOURCE
),
865 open
.addActionListener(new ActionListener() {
867 public void actionPerformed(ActionEvent e
) {
868 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
869 if (selectedBook
!= null) {
870 BasicLibrary lib
= reader
.getLibrary();
871 String luid
= selectedBook
.getInfo().getMeta().getLuid();
872 String source
= selectedBook
.getInfo().getMeta()
875 lib
.setSourceCover(source
, luid
);
877 GuiReaderBookInfo sourceInfo
= GuiReaderBookInfo
878 .fromSource(lib
, source
);
879 GuiReaderCoverImager
.clearIcon(sourceInfo
);
888 * Create the SetCover menu item for a book to change the linked source
893 private JMenuItem
createMenuItemSetCoverForAuthor() {
894 JMenuItem open
= new JMenuItem(
895 GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_AUTHOR
),
897 open
.addActionListener(new ActionListener() {
899 public void actionPerformed(ActionEvent e
) {
900 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
901 if (selectedBook
!= null) {
902 BasicLibrary lib
= reader
.getLibrary();
903 String luid
= selectedBook
.getInfo().getMeta().getLuid();
904 String author
= selectedBook
.getInfo().getMeta()
907 lib
.setAuthorCover(author
, luid
);
909 GuiReaderBookInfo authorInfo
= GuiReaderBookInfo
910 .fromAuthor(lib
, author
);
911 GuiReaderCoverImager
.clearIcon(authorInfo
);
920 * Display an error message and log the linked {@link Exception}.
925 * the title of the error message
927 * the exception to log if any
929 public void error(final String message
, final String title
, Exception e
) {
930 Instance
.getTraceHandler().error(title
+ ": " + message
);
932 Instance
.getTraceHandler().error(e
);
935 SwingUtilities
.invokeLater(new Runnable() {
938 JOptionPane
.showMessageDialog(GuiReaderFrame
.this, message
,
939 title
, JOptionPane
.ERROR_MESSAGE
);
945 public GuiReader
getReader() {
950 * Return the title of the application.
953 * the name of the associated {@link BasicLibrary}, which can be
958 static private String
getAppTitle(String libraryName
) {
959 if (!libraryName
.isEmpty()) {
960 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY_WITH_NAME
, Version
961 .getCurrentVersion().toString(), libraryName
);
964 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY
, Version
965 .getCurrentVersion().toString());