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
.StoryRunnable
;
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(createMenuItemClearCache());
107 if (status
.isWritable()) {
108 popup
.add(createMenuItemRedownload());
109 popup
.addSeparator();
110 popup
.add(createMenuItemRename());
111 popup
.add(createMenuItemSetAuthor());
112 popup
.addSeparator();
113 popup
.add(createMenuItemDelete());
115 popup
.addSeparator();
116 popup
.add(createMenuItemProperties());
121 public JPopupMenu
createSourceAuthorPopup() {
122 JPopupMenu popup
= new JPopupMenu();
123 popup
.add(createMenuItemOpenBook());
128 public void createMenu(Status status
) {
131 JMenuBar bar
= new JMenuBar();
133 JMenu file
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_FILE
));
134 file
.setMnemonic(KeyEvent
.VK_F
);
136 JMenuItem imprt
= new JMenuItem(
137 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_URL
),
139 imprt
.addActionListener(new ActionListener() {
141 public void actionPerformed(ActionEvent e
) {
142 mainPanel
.imprt(true);
145 JMenuItem imprtF
= new JMenuItem(
146 GuiReader
.trans(StringIdGui
.MENU_FILE_IMPORT_FILE
),
148 imprtF
.addActionListener(new ActionListener() {
150 public void actionPerformed(ActionEvent e
) {
151 mainPanel
.imprt(false);
154 JMenuItem exit
= new JMenuItem(
155 GuiReader
.trans(StringIdGui
.MENU_FILE_EXIT
), KeyEvent
.VK_X
);
156 exit
.addActionListener(new ActionListener() {
158 public void actionPerformed(ActionEvent e
) {
159 GuiReaderFrame
.this.dispatchEvent(new WindowEvent(
160 GuiReaderFrame
.this, WindowEvent
.WINDOW_CLOSING
));
164 file
.add(createMenuItemOpenBook());
165 file
.add(createMenuItemExport());
166 if (status
.isWritable()) {
167 file
.add(createMenuItemMoveTo());
172 file
.add(createMenuItemRename());
173 file
.add(createMenuItemSetAuthor());
176 file
.add(createMenuItemProperties());
182 JMenu edit
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_EDIT
));
183 edit
.setMnemonic(KeyEvent
.VK_E
);
185 edit
.add(createMenuItemSetCoverForSource());
186 edit
.add(createMenuItemSetCoverForAuthor());
187 edit
.add(createMenuItemClearCache());
188 edit
.add(createMenuItemRedownload());
190 edit
.add(createMenuItemDelete());
194 JMenu search
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_SEARCH
));
195 search
.setMnemonic(KeyEvent
.VK_H
);
196 for (final SupportType type
: SupportType
.values()) {
197 BasicSearchable searchable
= BasicSearchable
.getSearchable(type
);
198 if (searchable
!= null) {
199 JMenuItem searchItem
= new JMenuItem(type
.getSourceName());
200 searchItem
.addActionListener(new ActionListener() {
202 public void actionPerformed(ActionEvent e
) {
203 reader
.search(type
, null, 1, 0, false);
206 search
.add(searchItem
);
212 JMenu view
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_VIEW
));
213 view
.setMnemonic(KeyEvent
.VK_V
);
214 JMenuItem vauthors
= new JMenuItem(
215 GuiReader
.trans(StringIdGui
.MENU_VIEW_AUTHOR
));
216 vauthors
.setMnemonic(KeyEvent
.VK_A
);
217 vauthors
.addActionListener(new ActionListener() {
219 public void actionPerformed(ActionEvent e
) {
220 mainPanel
.setWords(false);
221 mainPanel
.refreshBooks();
225 JMenuItem vwords
= new JMenuItem(
226 GuiReader
.trans(StringIdGui
.MENU_VIEW_WCOUNT
));
227 vwords
.setMnemonic(KeyEvent
.VK_W
);
228 vwords
.addActionListener(new ActionListener() {
230 public void actionPerformed(ActionEvent e
) {
231 mainPanel
.setWords(true);
232 mainPanel
.refreshBooks();
238 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
239 if (status
.isReady()) {
241 groupedSources
= reader
.getLibrary().getSourcesGrouped();
242 } catch (IOException e
) {
243 error(e
.getLocalizedMessage(), "IOException", e
);
246 JMenu sources
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_SOURCES
));
247 sources
.setMnemonic(KeyEvent
.VK_S
);
248 populateMenuSA(sources
, groupedSources
, true);
251 Map
<String
, List
<String
>> goupedAuthors
= new HashMap
<String
, List
<String
>>();
252 if (status
.isReady()) {
254 goupedAuthors
= reader
.getLibrary().getAuthorsGrouped();
255 } catch (IOException e
) {
256 error(e
.getLocalizedMessage(), "IOException", e
);
259 JMenu authors
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_AUTHORS
));
260 authors
.setMnemonic(KeyEvent
.VK_A
);
261 populateMenuSA(authors
, goupedAuthors
, false);
264 JMenu options
= new JMenu(GuiReader
.trans(StringIdGui
.MENU_OPTIONS
));
265 options
.setMnemonic(KeyEvent
.VK_O
);
266 options
.add(createMenuItemConfig());
267 options
.add(createMenuItemUiConfig());
274 private void populateMenuSA(JMenu menu
,
275 Map
<String
, List
<String
>> groupedValues
, boolean type
) {
277 // "All" and "Listing" special items first
278 JMenuItem item
= new JMenuItem(
279 GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_GROUPED
));
280 item
.addActionListener(getActionOpenList(type
, false));
282 item
= new JMenuItem(GuiReader
.trans(StringIdGui
.MENU_XXX_ALL_LISTING
));
283 item
.addActionListener(getActionOpenList(type
, true));
288 for (final String value
: groupedValues
.keySet()) {
289 List
<String
> list
= groupedValues
.get(value
);
290 if (type
&& list
.size() == 1 && list
.get(0).isEmpty()) {
291 // leaf item source/type
292 item
= new JMenuItem(
293 value
.isEmpty() ? GuiReader
294 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
296 item
.addActionListener(getActionOpen(value
, type
));
300 if (!type
&& groupedValues
.size() == 1) {
301 // only one group of authors
305 value
.isEmpty() ? GuiReader
306 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
310 for (String sub
: list
) {
311 // " " instead of "" for the visual height
312 String itemName
= sub
;
313 if (itemName
.isEmpty()) {
314 itemName
= type ?
" " : GuiReader
315 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
);
318 String actualValue
= value
;
320 if (!sub
.isEmpty()) {
321 actualValue
+= "/" + sub
;
327 item
= new JMenuItem(itemName
);
328 item
.addActionListener(getActionOpen(actualValue
, type
));
340 * Return an {@link ActionListener} that will set the given source (type) as
341 * the selected/displayed one.
344 * the type (source) to select, cannot be NULL
346 * @return the {@link ActionListener}
348 private ActionListener
getActionOpen(final String source
, final boolean type
) {
349 return new ActionListener() {
351 public void actionPerformed(ActionEvent e
) {
352 mainPanel
.removeBookPanes();
353 mainPanel
.addBookPane(source
, type
);
354 mainPanel
.refreshBooks();
359 private ActionListener
getActionOpenList(final boolean type
,
360 final boolean listMode
) {
361 return new ActionListener() {
363 public void actionPerformed(ActionEvent ae
) {
364 mainPanel
.removeBookPanes();
366 mainPanel
.addBookPane(type
, listMode
);
367 } catch (IOException e
) {
368 error(e
.getLocalizedMessage(), "IOException", e
);
370 mainPanel
.refreshBooks();
376 * Create the Fanfix Configuration menu item.
380 private JMenuItem
createMenuItemConfig() {
381 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG
);
382 JMenuItem item
= new JMenuItem(title
);
383 item
.setMnemonic(KeyEvent
.VK_F
);
385 item
.addActionListener(new ActionListener() {
387 public void actionPerformed(ActionEvent e
) {
388 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(
389 Config
.class, Instance
.getConfig(), GuiReader
390 .trans(StringIdGui
.SUBTITLE_CONFIG
));
391 JFrame frame
= new JFrame(title
);
393 frame
.setSize(800, 600);
394 frame
.setVisible(true);
402 * Create the UI Configuration menu item.
406 private JMenuItem
createMenuItemUiConfig() {
407 final String title
= GuiReader
.trans(StringIdGui
.TITLE_CONFIG_UI
);
408 JMenuItem item
= new JMenuItem(title
);
409 item
.setMnemonic(KeyEvent
.VK_U
);
411 item
.addActionListener(new ActionListener() {
413 public void actionPerformed(ActionEvent e
) {
414 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(
415 UiConfig
.class, Instance
.getUiConfig(), GuiReader
416 .trans(StringIdGui
.SUBTITLE_CONFIG_UI
));
417 JFrame frame
= new JFrame(title
);
419 frame
.setSize(800, 600);
420 frame
.setVisible(true);
428 * Create the export menu item.
432 private JMenuItem
createMenuItemExport() {
433 final JFileChooser fc
= new JFileChooser();
434 fc
.setAcceptAllFileFilterUsed(false);
436 // Add the "ALL" filters first, then the others
437 final Map
<FileFilter
, OutputType
> otherFilters
= new HashMap
<FileFilter
, OutputType
>();
438 for (OutputType type
: OutputType
.values()) {
439 String ext
= type
.getDefaultExtension(false);
440 String desc
= type
.getDesc(false);
442 if (ext
== null || ext
.isEmpty()) {
443 fc
.addChoosableFileFilter(createAllFilter(desc
));
445 otherFilters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
449 for (Entry
<FileFilter
, OutputType
> entry
: otherFilters
.entrySet()) {
450 fc
.addChoosableFileFilter(entry
.getKey());
454 JMenuItem export
= new JMenuItem(
455 GuiReader
.trans(StringIdGui
.MENU_FILE_EXPORT
), KeyEvent
.VK_S
);
456 export
.addActionListener(new ActionListener() {
458 public void actionPerformed(ActionEvent e
) {
459 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
460 if (selectedBook
!= null) {
461 fc
.showDialog(GuiReaderFrame
.this,
462 GuiReader
.trans(StringIdGui
.TITLE_SAVE
));
463 if (fc
.getSelectedFile() != null) {
464 final OutputType type
= otherFilters
.get(fc
466 final String path
= fc
.getSelectedFile()
468 + type
.getDefaultExtension(false);
469 final Progress pg
= new Progress();
470 mainPanel
.outOfUi(pg
, false, new Runnable() {
474 reader
.getLibrary().export(
475 selectedBook
.getInfo().getMeta()
476 .getLuid(), type
, path
, pg
);
477 } catch (IOException e
) {
478 Instance
.getTraceHandler().error(e
);
491 * Create a {@link FileFilter} that accepts all files and return the given
499 private FileFilter
createAllFilter(final String desc
) {
500 return new FileFilter() {
502 public String
getDescription() {
507 public boolean accept(File f
) {
514 * Create the refresh (delete cache) menu item.
518 private JMenuItem
createMenuItemClearCache() {
519 JMenuItem refresh
= new JMenuItem(
520 GuiReader
.trans(StringIdGui
.MENU_EDIT_CLEAR_CACHE
),
522 refresh
.addActionListener(new ActionListener() {
524 public void actionPerformed(ActionEvent e
) {
525 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
526 if (selectedBook
!= null) {
527 mainPanel
.outOfUi(null, false, new Runnable() {
530 reader
.clearLocalReaderCache(selectedBook
.getInfo()
531 .getMeta().getLuid());
532 selectedBook
.setCached(false);
533 GuiReaderCoverImager
.clearIcon(selectedBook
535 SwingUtilities
.invokeLater(new Runnable() {
538 selectedBook
.repaint();
551 * Create the "move to" menu item.
555 private JMenuItem
createMenuItemMoveTo() {
556 JMenu changeTo
= new JMenu(
557 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO
));
558 changeTo
.setMnemonic(KeyEvent
.VK_M
);
560 Map
<String
, List
<String
>> groupedSources
= new HashMap
<String
, List
<String
>>();
562 groupedSources
= reader
.getLibrary().getSourcesGrouped();
563 } catch (IOException e
) {
564 error(e
.getLocalizedMessage(), "IOException", e
);
567 JMenuItem item
= new JMenuItem(
568 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_TYPE
));
569 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
, null));
571 changeTo
.addSeparator();
573 for (final String type
: groupedSources
.keySet()) {
574 List
<String
> list
= groupedSources
.get(type
);
575 if (list
.size() == 1 && list
.get(0).isEmpty()) {
576 item
= new JMenuItem(type
);
577 item
.addActionListener(createMoveAction(ChangeAction
.SOURCE
,
581 JMenu dir
= new JMenu(type
);
582 for (String sub
: list
) {
583 // " " instead of "" for the visual height
584 String itemName
= sub
.isEmpty() ?
" " : sub
;
585 String actualType
= type
;
586 if (!sub
.isEmpty()) {
587 actualType
+= "/" + sub
;
590 item
= new JMenuItem(itemName
);
591 item
.addActionListener(createMoveAction(
592 ChangeAction
.SOURCE
, actualType
));
603 * Create the "set author" menu item.
607 private JMenuItem
createMenuItemSetAuthor() {
608 JMenu changeTo
= new JMenu(
609 GuiReader
.trans(StringIdGui
.MENU_FILE_SET_AUTHOR
));
610 changeTo
.setMnemonic(KeyEvent
.VK_A
);
613 JMenuItem newItem
= new JMenuItem(
614 GuiReader
.trans(StringIdGui
.MENU_FILE_MOVE_TO_NEW_AUTHOR
));
615 changeTo
.add(newItem
);
616 changeTo
.addSeparator();
617 newItem
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
, null));
620 Map
<String
, List
<String
>> groupedAuthors
;
623 groupedAuthors
= reader
.getLibrary().getAuthorsGrouped();
624 } catch (IOException e
) {
625 error(e
.getLocalizedMessage(), "IOException", e
);
626 groupedAuthors
= new HashMap
<String
, List
<String
>>();
630 if (groupedAuthors
.size() > 1) {
631 for (String key
: groupedAuthors
.keySet()) {
632 JMenu group
= new JMenu(key
);
633 for (String value
: groupedAuthors
.get(key
)) {
634 JMenuItem item
= new JMenuItem(
635 value
.isEmpty() ? GuiReader
636 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
638 item
.addActionListener(createMoveAction(
639 ChangeAction
.AUTHOR
, value
));
644 } else if (groupedAuthors
.size() == 1) {
645 for (String value
: groupedAuthors
.values().iterator().next()) {
646 JMenuItem item
= new JMenuItem(
647 value
.isEmpty() ? GuiReader
648 .trans(StringIdGui
.MENU_AUTHORS_UNKNOWN
)
650 item
.addActionListener(createMoveAction(ChangeAction
.AUTHOR
,
660 * Create the "rename" menu item.
664 private JMenuItem
createMenuItemRename() {
665 JMenuItem changeTo
= new JMenuItem(
666 GuiReader
.trans(StringIdGui
.MENU_FILE_RENAME
));
667 changeTo
.setMnemonic(KeyEvent
.VK_R
);
668 changeTo
.addActionListener(createMoveAction(ChangeAction
.TITLE
, null));
672 private ActionListener
createMoveAction(final ChangeAction what
,
674 return new ActionListener() {
676 public void actionPerformed(ActionEvent e
) {
677 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
678 if (selectedBook
!= null) {
679 boolean refreshRequired
= false;
681 if (what
== ChangeAction
.SOURCE
) {
682 refreshRequired
= mainPanel
.getCurrentType();
683 } else if (what
== ChangeAction
.TITLE
) {
684 refreshRequired
= false;
685 } else if (what
== ChangeAction
.AUTHOR
) {
686 refreshRequired
= !mainPanel
.getCurrentType();
689 String changeTo
= type
;
691 MetaData meta
= selectedBook
.getInfo().getMeta();
693 if (what
== ChangeAction
.SOURCE
) {
694 init
= meta
.getSource();
695 } else if (what
== ChangeAction
.TITLE
) {
696 init
= meta
.getTitle();
697 } else if (what
== ChangeAction
.AUTHOR
) {
698 init
= meta
.getAuthor();
701 Object rep
= JOptionPane
.showInputDialog(
703 GuiReader
.trans(StringIdGui
.SUBTITLE_MOVE_TO
),
704 GuiReader
.trans(StringIdGui
.TITLE_MOVE_TO
),
705 JOptionPane
.QUESTION_MESSAGE
, null, null, init
);
711 changeTo
= rep
.toString();
714 final String fChangeTo
= changeTo
;
715 mainPanel
.outOfUi(null, refreshRequired
, new Runnable() {
718 String luid
= selectedBook
.getInfo().getMeta()
720 if (what
== ChangeAction
.SOURCE
) {
721 reader
.changeSource(luid
, fChangeTo
);
722 } else if (what
== ChangeAction
.TITLE
) {
723 reader
.changeTitle(luid
, fChangeTo
);
724 } else if (what
== ChangeAction
.AUTHOR
) {
725 reader
.changeAuthor(luid
, fChangeTo
);
728 mainPanel
.getSelectedBook().repaint();
729 mainPanel
.unsetSelectedBook();
731 SwingUtilities
.invokeLater(new Runnable() {
734 createMenu(reader
.getLibrary().getStatus());
745 * Create the re-download (then delete original) menu item.
749 private JMenuItem
createMenuItemRedownload() {
750 JMenuItem refresh
= new JMenuItem(
751 GuiReader
.trans(StringIdGui
.MENU_EDIT_REDOWNLOAD
),
753 refresh
.addActionListener(new ActionListener() {
755 public void actionPerformed(ActionEvent e
) {
756 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
757 if (selectedBook
!= null) {
758 final MetaData meta
= selectedBook
.getInfo().getMeta();
759 mainPanel
.imprt(meta
.getUrl(), new StoryRunnable() {
761 public void run(Story story
) {
762 MetaData newMeta
= story
.getMeta();
763 if (!newMeta
.getSource().equals(meta
.getSource())) {
764 reader
.changeSource(newMeta
.getLuid(),
768 }, GuiReader
.trans(StringIdGui
.PROGRESS_CHANGE_SOURCE
));
777 * Create the delete menu item.
781 private JMenuItem
createMenuItemDelete() {
782 JMenuItem delete
= new JMenuItem(
783 GuiReader
.trans(StringIdGui
.MENU_EDIT_DELETE
), KeyEvent
.VK_D
);
784 delete
.addActionListener(new ActionListener() {
786 public void actionPerformed(ActionEvent e
) {
787 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
788 if (selectedBook
!= null
789 && selectedBook
.getInfo().getMeta() != null) {
791 final MetaData meta
= selectedBook
.getInfo().getMeta();
792 int rep
= JOptionPane
.showConfirmDialog(
794 GuiReader
.trans(StringIdGui
.SUBTITLE_DELETE
,
795 meta
.getLuid(), meta
.getTitle()),
796 GuiReader
.trans(StringIdGui
.TITLE_DELETE
),
797 JOptionPane
.OK_CANCEL_OPTION
);
799 if (rep
== JOptionPane
.OK_OPTION
) {
800 mainPanel
.outOfUi(null, true, new Runnable() {
803 reader
.delete(meta
.getLuid());
804 mainPanel
.unsetSelectedBook();
816 * Create the properties menu item.
820 private JMenuItem
createMenuItemProperties() {
821 JMenuItem delete
= new JMenuItem(
822 GuiReader
.trans(StringIdGui
.MENU_FILE_PROPERTIES
),
824 delete
.addActionListener(new ActionListener() {
826 public void actionPerformed(ActionEvent e
) {
827 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
828 if (selectedBook
!= null) {
829 mainPanel
.outOfUi(null, false, new Runnable() {
832 new GuiReaderPropertiesFrame(reader
.getLibrary(),
833 selectedBook
.getInfo().getMeta())
845 * Create the open menu item for a book, a source/type or an author.
849 public JMenuItem
createMenuItemOpenBook() {
850 JMenuItem open
= new JMenuItem(
851 GuiReader
.trans(StringIdGui
.MENU_FILE_OPEN
), KeyEvent
.VK_O
);
852 open
.addActionListener(new ActionListener() {
854 public void actionPerformed(ActionEvent e
) {
855 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
856 if (selectedBook
!= null) {
857 if (selectedBook
.getInfo().getMeta() == null) {
858 mainPanel
.removeBookPanes();
859 mainPanel
.addBookPane(selectedBook
.getInfo()
860 .getMainInfo(), mainPanel
.getCurrentType());
861 mainPanel
.refreshBooks();
863 mainPanel
.openBook(selectedBook
);
873 * Create the SetCover menu item for a book to change the linked source
878 private JMenuItem
createMenuItemSetCoverForSource() {
879 JMenuItem open
= new JMenuItem(
880 GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_SOURCE
),
882 open
.addActionListener(new ActionListener() {
884 public void actionPerformed(ActionEvent ae
) {
885 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
886 if (selectedBook
!= null) {
887 BasicLibrary lib
= reader
.getLibrary();
888 String luid
= selectedBook
.getInfo().getMeta().getLuid();
889 String source
= selectedBook
.getInfo().getMeta()
893 lib
.setSourceCover(source
, luid
);
894 } catch (IOException e
) {
895 error(e
.getLocalizedMessage(), "IOException", e
);
898 GuiReaderBookInfo sourceInfo
= GuiReaderBookInfo
899 .fromSource(lib
, source
);
900 GuiReaderCoverImager
.clearIcon(sourceInfo
);
909 * Create the SetCover menu item for a book to change the linked source
914 private JMenuItem
createMenuItemSetCoverForAuthor() {
915 JMenuItem open
= new JMenuItem(
916 GuiReader
.trans(StringIdGui
.MENU_EDIT_SET_COVER_FOR_AUTHOR
),
918 open
.addActionListener(new ActionListener() {
920 public void actionPerformed(ActionEvent ae
) {
921 final GuiReaderBook selectedBook
= mainPanel
.getSelectedBook();
922 if (selectedBook
!= null) {
923 BasicLibrary lib
= reader
.getLibrary();
924 String luid
= selectedBook
.getInfo().getMeta().getLuid();
925 String author
= selectedBook
.getInfo().getMeta()
929 lib
.setAuthorCover(author
, luid
);
930 } catch (IOException e
) {
931 error(e
.getLocalizedMessage(), "IOException", e
);
934 GuiReaderBookInfo authorInfo
= GuiReaderBookInfo
935 .fromAuthor(lib
, author
);
936 GuiReaderCoverImager
.clearIcon(authorInfo
);
945 * Display an error message and log the linked {@link Exception}.
950 * the title of the error message
952 * the exception to log if any
954 public void error(final String message
, final String title
, Exception e
) {
955 Instance
.getTraceHandler().error(title
+ ": " + message
);
957 Instance
.getTraceHandler().error(e
);
960 SwingUtilities
.invokeLater(new Runnable() {
963 JOptionPane
.showMessageDialog(GuiReaderFrame
.this, message
,
964 title
, JOptionPane
.ERROR_MESSAGE
);
970 public GuiReader
getReader() {
975 * Return the title of the application.
978 * the name of the associated {@link BasicLibrary}, which can be
983 static private String
getAppTitle(String libraryName
) {
984 if (!libraryName
.isEmpty()) {
985 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY_WITH_NAME
, Version
986 .getCurrentVersion().toString(), libraryName
);
989 return GuiReader
.trans(StringIdGui
.TITLE_LIBRARY
, Version
990 .getCurrentVersion().toString());