4a9fa76320b612ea467add021a553fef96fb7bb8
[fanfix.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderFrame.java
1 package be.nikiroo.fanfix.reader.ui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Frame;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.awt.event.KeyEvent;
8 import java.awt.event.WindowEvent;
9 import java.io.File;
10 import java.io.IOException;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Map.Entry;
15
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;
26
27 import be.nikiroo.fanfix.Instance;
28 import be.nikiroo.fanfix.bundles.Config;
29 import be.nikiroo.fanfix.bundles.UiConfig;
30 import be.nikiroo.fanfix.data.MetaData;
31 import be.nikiroo.fanfix.data.Story;
32 import be.nikiroo.fanfix.library.LocalLibrary;
33 import be.nikiroo.fanfix.output.BasicOutput.OutputType;
34 import be.nikiroo.fanfix.reader.BasicReader;
35 import be.nikiroo.fanfix.reader.ui.GuiReaderMainPanel.FrameHelper;
36 import be.nikiroo.fanfix.reader.ui.GuiReaderMainPanel.StoryRunnable;
37 import be.nikiroo.utils.Progress;
38 import be.nikiroo.utils.Version;
39 import be.nikiroo.utils.ui.ConfigEditor;
40
41 /**
42 * A {@link Frame} that will show a {@link GuiReaderBook} item for each
43 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
44 * way to copy them to the {@link GuiReader} cache (
45 * {@link BasicReader#getLibrary()}), read them, delete them...
46 *
47 * @author niki
48 */
49 class GuiReaderFrame extends JFrame implements FrameHelper {
50 private static final long serialVersionUID = 1L;
51 private GuiReader reader;
52 private GuiReaderMainPanel mainPanel;
53
54 private enum MoveAction {
55 SOURCE, TITLE, AUTHOR
56 }
57
58 /**
59 * Create a new {@link GuiReaderFrame}.
60 *
61 * @param reader
62 * the associated {@link GuiReader} to forward some commands and
63 * access its {@link LocalLibrary}
64 * @param type
65 * the type of {@link Story} to load, or NULL for all types
66 */
67 public GuiReaderFrame(GuiReader reader, String type) {
68 super(String.format("Fanfix %s Library", Version.getCurrentVersion()));
69
70 this.reader = reader;
71
72 mainPanel = new GuiReaderMainPanel(this, type);
73
74 setSize(800, 600);
75 setLayout(new BorderLayout());
76 add(mainPanel);
77 }
78
79 @Override
80 public JPopupMenu createBookPopup() {
81 JPopupMenu popup = new JPopupMenu();
82 popup.add(createMenuItemOpenBook());
83 popup.addSeparator();
84 popup.add(createMenuItemExport());
85 popup.add(createMenuItemMoveTo(true));
86 popup.add(createMenuItemSetCover());
87 popup.add(createMenuItemClearCache());
88 popup.add(createMenuItemRedownload());
89 popup.addSeparator();
90 popup.add(createMenuItemRename(true));
91 popup.add(createMenuItemSetAuthor(true));
92 popup.addSeparator();
93 popup.add(createMenuItemDelete());
94 popup.addSeparator();
95 popup.add(createMenuItemProperties());
96 return popup;
97 }
98
99 @Override
100 public JPopupMenu createSourcePopup() {
101 JPopupMenu popup = new JPopupMenu();
102 popup.add(createMenuItemOpenBook());
103 return popup;
104 }
105
106 @Override
107 public void createMenu(boolean libOk) {
108 JMenuBar bar = new JMenuBar();
109
110 JMenu file = new JMenu("File");
111 file.setMnemonic(KeyEvent.VK_F);
112
113 JMenuItem imprt = new JMenuItem("Import URL...", KeyEvent.VK_U);
114 imprt.addActionListener(new ActionListener() {
115 @Override
116 public void actionPerformed(ActionEvent e) {
117 mainPanel.imprt(true);
118 }
119 });
120 JMenuItem imprtF = new JMenuItem("Import File...", KeyEvent.VK_F);
121 imprtF.addActionListener(new ActionListener() {
122 @Override
123 public void actionPerformed(ActionEvent e) {
124 mainPanel.imprt(false);
125 }
126 });
127 JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X);
128 exit.addActionListener(new ActionListener() {
129 @Override
130 public void actionPerformed(ActionEvent e) {
131 GuiReaderFrame.this.dispatchEvent(new WindowEvent(
132 GuiReaderFrame.this, WindowEvent.WINDOW_CLOSING));
133 }
134 });
135
136 file.add(createMenuItemOpenBook());
137 file.add(createMenuItemExport());
138 file.add(createMenuItemMoveTo(libOk));
139 file.addSeparator();
140 file.add(imprt);
141 file.add(imprtF);
142 file.addSeparator();
143 file.add(createMenuItemRename(libOk));
144 file.add(createMenuItemSetAuthor(libOk));
145 file.addSeparator();
146 file.add(exit);
147
148 bar.add(file);
149
150 JMenu edit = new JMenu("Edit");
151 edit.setMnemonic(KeyEvent.VK_E);
152
153 edit.add(createMenuItemClearCache());
154 edit.add(createMenuItemRedownload());
155 edit.addSeparator();
156 edit.add(createMenuItemDelete());
157
158 bar.add(edit);
159
160 JMenu view = new JMenu("View");
161 view.setMnemonic(KeyEvent.VK_V);
162 JMenuItem vauthors = new JMenuItem("Author");
163 vauthors.setMnemonic(KeyEvent.VK_A);
164 vauthors.addActionListener(new ActionListener() {
165 @Override
166 public void actionPerformed(ActionEvent e) {
167 mainPanel.setWords(false);
168 mainPanel.refreshBooks();
169 }
170 });
171 view.add(vauthors);
172 JMenuItem vwords = new JMenuItem("Word count");
173 vwords.setMnemonic(KeyEvent.VK_W);
174 vwords.addActionListener(new ActionListener() {
175 @Override
176 public void actionPerformed(ActionEvent e) {
177 mainPanel.setWords(true);
178 mainPanel.refreshBooks();
179 }
180 });
181 view.add(vwords);
182 bar.add(view);
183
184 Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
185 if (libOk) {
186 groupedSources = reader.getLibrary().getSourcesGrouped();
187 }
188 JMenu sources = new JMenu("Sources");
189 sources.setMnemonic(KeyEvent.VK_S);
190 populateMenuSA(sources, groupedSources, true);
191 bar.add(sources);
192
193 Map<String, List<String>> goupedAuthors = new HashMap<String, List<String>>();
194 if (libOk) {
195 goupedAuthors = reader.getLibrary().getAuthorsGrouped();
196 }
197 JMenu authors = new JMenu("Authors");
198 authors.setMnemonic(KeyEvent.VK_A);
199 populateMenuSA(authors, goupedAuthors, false);
200 bar.add(authors);
201
202 JMenu options = new JMenu("Options");
203 options.setMnemonic(KeyEvent.VK_O);
204 options.add(createMenuItemConfig());
205 options.add(createMenuItemUiConfig());
206 bar.add(options);
207
208 setJMenuBar(bar);
209 }
210
211 // "" = [unknown]
212 private void populateMenuSA(JMenu menu,
213 Map<String, List<String>> groupedValues, boolean type) {
214
215 // "All" and "Listing" special items
216 JMenuItem item = new JMenuItem("All");
217 item.addActionListener(getActionOpenList(type, false));
218 menu.add(item);
219 item = new JMenuItem("Listing");
220 item.addActionListener(getActionOpenList(type, true));
221 menu.add(item);
222 menu.addSeparator();
223
224 for (final String value : groupedValues.keySet()) {
225 List<String> list = groupedValues.get(value);
226 if (type && list.size() == 1 && list.get(0).isEmpty()) {
227 // leaf item source/type
228 item = new JMenuItem(value.isEmpty() ? "[unknown]" : value);
229 item.addActionListener(getActionOpen(value, type));
230 menu.add(item);
231 } else {
232 JMenu dir;
233 if (!type && groupedValues.size() == 1) {
234 // only one group of authors
235 dir = menu;
236 } else {
237 dir = new JMenu(value.isEmpty() ? "[unknown]" : value);
238 }
239
240 for (String sub : list) {
241 // " " instead of "" for the visual height
242 String itemName = sub.isEmpty() ? " " : sub;
243 String actualValue = value;
244
245 if (type) {
246 if (!sub.isEmpty()) {
247 actualValue += "/" + sub;
248 }
249 } else {
250 actualValue = sub;
251 }
252
253 item = new JMenuItem(itemName);
254 item.addActionListener(getActionOpen(actualValue, type));
255 dir.add(item);
256 }
257
258 if (menu != dir) {
259 menu.add(dir);
260 }
261 }
262 }
263 }
264
265 /**
266 * Return an {@link ActionListener} that will set the given source (type) as
267 * the selected/displayed one.
268 *
269 * @param type
270 * the type (source) to select, cannot be NULL
271 *
272 * @return the {@link ActionListener}
273 */
274 private ActionListener getActionOpen(final String source, final boolean type) {
275 return new ActionListener() {
276 @Override
277 public void actionPerformed(ActionEvent e) {
278 mainPanel.removeBookPanes();
279 mainPanel.addBookPane(source, type);
280 mainPanel.refreshBooks();
281 }
282 };
283 }
284
285 private ActionListener getActionOpenList(final boolean type,
286 final boolean listMode) {
287 return new ActionListener() {
288 @Override
289 public void actionPerformed(ActionEvent e) {
290 mainPanel.removeBookPanes();
291 mainPanel.addBookPane(type, listMode);
292 mainPanel.refreshBooks();
293 }
294 };
295 }
296
297 /**
298 * Create the Fanfix Configuration menu item.
299 *
300 * @return the item
301 */
302 private JMenuItem createMenuItemConfig() {
303 final String title = "Fanfix Configuration";
304 JMenuItem item = new JMenuItem(title);
305 item.setMnemonic(KeyEvent.VK_F);
306
307 item.addActionListener(new ActionListener() {
308 @Override
309 public void actionPerformed(ActionEvent e) {
310 ConfigEditor<Config> ed = new ConfigEditor<Config>(
311 Config.class, Instance.getConfig(),
312 "This is where you configure the options of the program.");
313 JFrame frame = new JFrame(title);
314 frame.add(ed);
315 frame.setSize(800, 600);
316 frame.setVisible(true);
317 }
318 });
319
320 return item;
321 }
322
323 /**
324 * Create the UI Configuration menu item.
325 *
326 * @return the item
327 */
328 private JMenuItem createMenuItemUiConfig() {
329 final String title = "UI Configuration";
330 JMenuItem item = new JMenuItem(title);
331 item.setMnemonic(KeyEvent.VK_U);
332
333 item.addActionListener(new ActionListener() {
334 @Override
335 public void actionPerformed(ActionEvent e) {
336 ConfigEditor<UiConfig> ed = new ConfigEditor<UiConfig>(
337 UiConfig.class, Instance.getUiConfig(),
338 "This is where you configure the graphical appearence of the program.");
339 JFrame frame = new JFrame(title);
340 frame.add(ed);
341 frame.setSize(800, 600);
342 frame.setVisible(true);
343 }
344 });
345
346 return item;
347 }
348
349 /**
350 * Create the export menu item.
351 *
352 * @return the item
353 */
354 private JMenuItem createMenuItemExport() {
355 final JFileChooser fc = new JFileChooser();
356 fc.setAcceptAllFileFilterUsed(false);
357
358 final Map<FileFilter, OutputType> filters = new HashMap<FileFilter, OutputType>();
359 for (OutputType type : OutputType.values()) {
360 String ext = type.getDefaultExtension(false);
361 String desc = type.getDesc(false);
362
363 if (ext == null || ext.isEmpty()) {
364 filters.put(createAllFilter(desc), type);
365 } else {
366 filters.put(new FileNameExtensionFilter(desc, ext), type);
367 }
368 }
369
370 // First the "ALL" filters, then, the extension filters
371 for (Entry<FileFilter, OutputType> entry : filters.entrySet()) {
372 if (!(entry.getKey() instanceof FileNameExtensionFilter)) {
373 fc.addChoosableFileFilter(entry.getKey());
374 }
375 }
376 for (Entry<FileFilter, OutputType> entry : filters.entrySet()) {
377 if (entry.getKey() instanceof FileNameExtensionFilter) {
378 fc.addChoosableFileFilter(entry.getKey());
379 }
380 }
381 //
382
383 JMenuItem export = new JMenuItem("Save as...", KeyEvent.VK_S);
384 export.addActionListener(new ActionListener() {
385 @Override
386 public void actionPerformed(ActionEvent e) {
387 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
388 if (selectedBook != null) {
389 fc.showDialog(GuiReaderFrame.this, "Save");
390 if (fc.getSelectedFile() != null) {
391 final OutputType type = filters.get(fc.getFileFilter());
392 final String path = fc.getSelectedFile()
393 .getAbsolutePath()
394 + type.getDefaultExtension(false);
395 final Progress pg = new Progress();
396 mainPanel.outOfUi(pg, new Runnable() {
397 @Override
398 public void run() {
399 try {
400 reader.getLibrary().export(
401 selectedBook.getMeta().getLuid(),
402 type, path, pg);
403 } catch (IOException e) {
404 Instance.getTraceHandler().error(e);
405 }
406 }
407 });
408 }
409 }
410 }
411 });
412
413 return export;
414 }
415
416 /**
417 * Create a {@link FileFilter} that accepts all files and return the given
418 * description.
419 *
420 * @param desc
421 * the description
422 *
423 * @return the filter
424 */
425 private FileFilter createAllFilter(final String desc) {
426 return new FileFilter() {
427 @Override
428 public String getDescription() {
429 return desc;
430 }
431
432 @Override
433 public boolean accept(File f) {
434 return true;
435 }
436 };
437 }
438
439 /**
440 * Create the refresh (delete cache) menu item.
441 *
442 * @return the item
443 */
444 private JMenuItem createMenuItemClearCache() {
445 JMenuItem refresh = new JMenuItem("Clear cache", KeyEvent.VK_C);
446 refresh.addActionListener(new ActionListener() {
447 @Override
448 public void actionPerformed(ActionEvent e) {
449 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
450 if (selectedBook != null) {
451 mainPanel.outOfUi(null, new Runnable() {
452 @Override
453 public void run() {
454 reader.clearLocalReaderCache(selectedBook.getMeta()
455 .getLuid());
456 selectedBook.setCached(false);
457 GuiReaderCoverImager.clearIcon(selectedBook
458 .getMeta());
459 SwingUtilities.invokeLater(new Runnable() {
460 @Override
461 public void run() {
462 selectedBook.repaint();
463 }
464 });
465 }
466 });
467 }
468 }
469 });
470
471 return refresh;
472 }
473
474 /**
475 * Create the "move to" menu item.
476 *
477 * @param libOk
478 * the library can be queried
479 *
480 * @return the item
481 */
482 private JMenuItem createMenuItemMoveTo(boolean libOk) {
483 JMenu changeTo = new JMenu("Move to");
484 changeTo.setMnemonic(KeyEvent.VK_M);
485
486 Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
487 if (libOk) {
488 groupedSources = reader.getLibrary().getSourcesGrouped();
489 }
490
491 JMenuItem item = new JMenuItem("New type...");
492 item.addActionListener(createMoveAction(MoveAction.SOURCE, null));
493 changeTo.add(item);
494 changeTo.addSeparator();
495
496 for (final String type : groupedSources.keySet()) {
497 List<String> list = groupedSources.get(type);
498 if (list.size() == 1 && list.get(0).isEmpty()) {
499 item = new JMenuItem(type);
500 item.addActionListener(createMoveAction(MoveAction.SOURCE, type));
501 changeTo.add(item);
502 } else {
503 JMenu dir = new JMenu(type);
504 for (String sub : list) {
505 // " " instead of "" for the visual height
506 String itemName = sub.isEmpty() ? " " : sub;
507 String actualType = type;
508 if (!sub.isEmpty()) {
509 actualType += "/" + sub;
510 }
511
512 item = new JMenuItem(itemName);
513 item.addActionListener(createMoveAction(MoveAction.SOURCE,
514 actualType));
515 dir.add(item);
516 }
517 changeTo.add(dir);
518 }
519 }
520
521 return changeTo;
522 }
523
524 /**
525 * Create the "set author" menu item.
526 *
527 * @param libOk
528 * the library can be queried
529 *
530 * @return the item
531 */
532 private JMenuItem createMenuItemSetAuthor(boolean libOk) {
533 JMenu changeTo = new JMenu("Set author");
534 changeTo.setMnemonic(KeyEvent.VK_A);
535
536 // New author
537 JMenuItem newItem = new JMenuItem("New author...");
538 changeTo.add(newItem);
539 changeTo.addSeparator();
540 newItem.addActionListener(createMoveAction(MoveAction.AUTHOR, null));
541
542 // Existing authors
543 if (libOk) {
544 Map<String, List<String>> groupedAuthors = reader.getLibrary()
545 .getAuthorsGrouped();
546
547 if (groupedAuthors.size() > 1) {
548 for (String key : groupedAuthors.keySet()) {
549 JMenu group = new JMenu(key);
550 for (String value : groupedAuthors.get(key)) {
551 JMenuItem item = new JMenuItem(value);
552 item.addActionListener(createMoveAction(
553 MoveAction.AUTHOR, value));
554 group.add(item);
555 }
556 changeTo.add(group);
557 }
558 } else if (groupedAuthors.size() == 1) {
559 for (String value : groupedAuthors.values().iterator().next()) {
560 JMenuItem item = new JMenuItem(value);
561 item.addActionListener(createMoveAction(MoveAction.AUTHOR,
562 value));
563 changeTo.add(item);
564 }
565 }
566 }
567
568 return changeTo;
569 }
570
571 /**
572 * Create the "rename" menu item.
573 *
574 * @param libOk
575 * the library can be queried
576 *
577 * @return the item
578 */
579 private JMenuItem createMenuItemRename(
580 @SuppressWarnings("unused") boolean libOk) {
581 JMenuItem changeTo = new JMenuItem("Rename...");
582 changeTo.setMnemonic(KeyEvent.VK_R);
583 changeTo.addActionListener(createMoveAction(MoveAction.TITLE, null));
584 return changeTo;
585 }
586
587 private ActionListener createMoveAction(final MoveAction what,
588 final String type) {
589 return new ActionListener() {
590 @Override
591 public void actionPerformed(ActionEvent e) {
592 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
593 if (selectedBook != null) {
594 String changeTo = type;
595 if (type == null) {
596 String init = "";
597 if (what == MoveAction.SOURCE) {
598 init = selectedBook.getMeta().getSource();
599 } else if (what == MoveAction.TITLE) {
600 init = selectedBook.getMeta().getTitle();
601 } else if (what == MoveAction.AUTHOR) {
602 init = selectedBook.getMeta().getAuthor();
603 }
604
605 Object rep = JOptionPane.showInputDialog(
606 GuiReaderFrame.this, "Move to:",
607 "Moving story", JOptionPane.QUESTION_MESSAGE,
608 null, null, init);
609
610 if (rep == null) {
611 return;
612 }
613
614 changeTo = rep.toString();
615 }
616
617 final String fChangeTo = changeTo;
618 mainPanel.outOfUi(null, new Runnable() {
619 @Override
620 public void run() {
621 if (what.equals("SOURCE")) {
622 reader.changeSource(selectedBook.getMeta()
623 .getLuid(), fChangeTo);
624 } else if (what.equals("TITLE")) {
625 reader.changeTitle(selectedBook.getMeta()
626 .getLuid(), fChangeTo);
627 } else if (what.equals("AUTHOR")) {
628 reader.changeAuthor(selectedBook.getMeta()
629 .getLuid(), fChangeTo);
630 }
631
632 mainPanel.unsetSelectedBook();
633
634 SwingUtilities.invokeLater(new Runnable() {
635 @Override
636 public void run() {
637 createMenu(true);
638 }
639 });
640 }
641 });
642 }
643 }
644 };
645 }
646
647 /**
648 * Create the redownload (then delete original) menu item.
649 *
650 * @return the item
651 */
652 private JMenuItem createMenuItemRedownload() {
653 JMenuItem refresh = new JMenuItem("Redownload", KeyEvent.VK_R);
654 refresh.addActionListener(new ActionListener() {
655 @Override
656 public void actionPerformed(ActionEvent e) {
657 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
658 if (selectedBook != null) {
659 final MetaData meta = selectedBook.getMeta();
660 mainPanel.imprt(meta.getUrl(), new StoryRunnable() {
661 @Override
662 public void run(Story story) {
663 reader.delete(meta.getLuid());
664 mainPanel.unsetSelectedBook();
665 MetaData newMeta = story.getMeta();
666 if (!newMeta.getSource().equals(meta.getSource())) {
667 reader.changeSource(newMeta.getLuid(),
668 meta.getSource());
669 }
670 }
671 }, "Removing old copy");
672 }
673 }
674 });
675
676 return refresh;
677 }
678
679 /**
680 * Create the delete menu item.
681 *
682 * @return the item
683 */
684 private JMenuItem createMenuItemDelete() {
685 JMenuItem delete = new JMenuItem("Delete", KeyEvent.VK_D);
686 delete.addActionListener(new ActionListener() {
687 @Override
688 public void actionPerformed(ActionEvent e) {
689 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
690 if (selectedBook != null) {
691 mainPanel.outOfUi(null, new Runnable() {
692 @Override
693 public void run() {
694 reader.delete(selectedBook.getMeta().getLuid());
695 mainPanel.unsetSelectedBook();
696 }
697 });
698 }
699 }
700 });
701
702 return delete;
703 }
704
705 /**
706 * Create the properties menu item.
707 *
708 * @return the item
709 */
710 private JMenuItem createMenuItemProperties() {
711 JMenuItem delete = new JMenuItem("Properties", KeyEvent.VK_P);
712 delete.addActionListener(new ActionListener() {
713 @Override
714 public void actionPerformed(ActionEvent e) {
715 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
716 if (selectedBook != null) {
717 mainPanel.outOfUi(null, new Runnable() {
718 @Override
719 public void run() {
720 new GuiReaderPropertiesFrame(reader, selectedBook
721 .getMeta()).setVisible(true);
722 }
723 });
724 }
725 }
726 });
727
728 return delete;
729 }
730
731 /**
732 * Create the open menu item for a book or a source/type (no LUID).
733 *
734 * @return the item
735 */
736 public JMenuItem createMenuItemOpenBook() {
737 JMenuItem open = new JMenuItem("Open", KeyEvent.VK_O);
738 open.addActionListener(new ActionListener() {
739 @Override
740 public void actionPerformed(ActionEvent e) {
741 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
742 if (selectedBook != null) {
743 if (selectedBook.getMeta().getLuid() == null) {
744 mainPanel.removeBookPanes();
745 mainPanel.addBookPane(selectedBook.getMeta()
746 .getSource(), true);
747 mainPanel.refreshBooks();
748 } else {
749 mainPanel.openBook(selectedBook);
750 }
751 }
752 }
753 });
754
755 return open;
756 }
757
758 /**
759 * Create the SetCover menu item for a book to change the linked source
760 * cover.
761 *
762 * @return the item
763 */
764 private JMenuItem createMenuItemSetCover() {
765 JMenuItem open = new JMenuItem("Set as cover for source", KeyEvent.VK_C);
766 open.addActionListener(new ActionListener() {
767 @Override
768 public void actionPerformed(ActionEvent e) {
769 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
770 if (selectedBook != null) {
771 reader.getLibrary().setSourceCover(
772 selectedBook.getMeta().getSource(),
773 selectedBook.getMeta().getLuid());
774 MetaData source = selectedBook.getMeta().clone();
775 source.setLuid(null);
776 GuiReaderCoverImager.clearIcon(source);
777 }
778 }
779 });
780
781 return open;
782 }
783
784 /**
785 * Display an error message and log the linked {@link Exception}.
786 *
787 * @param message
788 * the message
789 * @param title
790 * the title of the error message
791 * @param e
792 * the exception to log if any
793 */
794 public void error(final String message, final String title, Exception e) {
795 Instance.getTraceHandler().error(title + ": " + message);
796 if (e != null) {
797 Instance.getTraceHandler().error(e);
798 }
799
800 SwingUtilities.invokeLater(new Runnable() {
801 @Override
802 public void run() {
803 JOptionPane.showMessageDialog(GuiReaderFrame.this, message,
804 title, JOptionPane.ERROR_MESSAGE);
805 }
806 });
807 }
808
809 @Override
810 public GuiReader getReader() {
811 return reader;
812 }
813 }