d4cfb8175959984f1760af7bab52fed57cd5396b
[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.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;
42
43 /**
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...
48 *
49 * @author niki
50 */
51 class GuiReaderFrame extends JFrame implements FrameHelper {
52 private static final long serialVersionUID = 1L;
53 private GuiReader reader;
54 private GuiReaderMainPanel mainPanel;
55
56 /**
57 * The different modification actions you can use on {@link Story} items.
58 *
59 * @author niki
60 */
61 private enum ChangeAction {
62 /** Change the source/type, that is, move it to another source. */
63 SOURCE,
64 /** Change its name. */
65 TITLE,
66 /** Change its author. */
67 AUTHOR
68 }
69
70 /**
71 * Create a new {@link GuiReaderFrame}.
72 *
73 * @param reader
74 * the associated {@link GuiReader} to forward some commands and
75 * access its {@link LocalLibrary}
76 * @param type
77 * the type of {@link Story} to load, or NULL for all types
78 */
79 public GuiReaderFrame(GuiReader reader, String type) {
80 super(getAppTitle(reader.getLibrary().getLibraryName()));
81
82 this.reader = reader;
83
84 mainPanel = new GuiReaderMainPanel(this, type);
85
86 setSize(800, 600);
87 setLayout(new BorderLayout());
88 add(mainPanel, BorderLayout.CENTER);
89 }
90
91 @Override
92 public JPopupMenu createBookPopup() {
93 JPopupMenu popup = new JPopupMenu();
94 popup.add(createMenuItemOpenBook());
95 popup.addSeparator();
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());
109 return popup;
110 }
111
112 @Override
113 public JPopupMenu createSourceAuthorPopup() {
114 JPopupMenu popup = new JPopupMenu();
115 popup.add(createMenuItemOpenBook());
116 return popup;
117 }
118
119 @Override
120 public void createMenu(boolean libOk) {
121 invalidate();
122
123 JMenuBar bar = new JMenuBar();
124
125 JMenu file = new JMenu(GuiReader.trans(StringIdGui.MENU_FILE));
126 file.setMnemonic(KeyEvent.VK_F);
127
128 JMenuItem imprt = new JMenuItem(
129 GuiReader.trans(StringIdGui.MENU_FILE_IMPORT_URL),
130 KeyEvent.VK_U);
131 imprt.addActionListener(new ActionListener() {
132 @Override
133 public void actionPerformed(ActionEvent e) {
134 mainPanel.imprt(true);
135 }
136 });
137 JMenuItem imprtF = new JMenuItem(
138 GuiReader.trans(StringIdGui.MENU_FILE_IMPORT_FILE),
139 KeyEvent.VK_F);
140 imprtF.addActionListener(new ActionListener() {
141 @Override
142 public void actionPerformed(ActionEvent e) {
143 mainPanel.imprt(false);
144 }
145 });
146 JMenuItem exit = new JMenuItem(
147 GuiReader.trans(StringIdGui.MENU_FILE_EXIT), KeyEvent.VK_X);
148 exit.addActionListener(new ActionListener() {
149 @Override
150 public void actionPerformed(ActionEvent e) {
151 GuiReaderFrame.this.dispatchEvent(new WindowEvent(
152 GuiReaderFrame.this, WindowEvent.WINDOW_CLOSING));
153 }
154 });
155
156 file.add(createMenuItemOpenBook());
157 file.add(createMenuItemExport());
158 file.add(createMenuItemMoveTo(libOk));
159 file.addSeparator();
160 file.add(imprt);
161 file.add(imprtF);
162 file.addSeparator();
163 file.add(createMenuItemRename(libOk));
164 file.add(createMenuItemSetAuthor(libOk));
165 file.addSeparator();
166 file.add(createMenuItemProperties());
167 file.addSeparator();
168 file.add(exit);
169
170 bar.add(file);
171
172 JMenu edit = new JMenu(GuiReader.trans(StringIdGui.MENU_EDIT));
173 edit.setMnemonic(KeyEvent.VK_E);
174
175 edit.add(createMenuItemSetCoverForSource());
176 edit.add(createMenuItemSetCoverForAuthor());
177 edit.add(createMenuItemClearCache());
178 edit.add(createMenuItemRedownload());
179 edit.addSeparator();
180 edit.add(createMenuItemDelete());
181
182 bar.add(edit);
183
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() {
190 @Override
191 public void actionPerformed(ActionEvent e) {
192 mainPanel.setWords(false);
193 mainPanel.refreshBooks();
194 }
195 });
196 view.add(vauthors);
197 JMenuItem vwords = new JMenuItem(
198 GuiReader.trans(StringIdGui.MENU_VIEW_WCOUNT));
199 vwords.setMnemonic(KeyEvent.VK_W);
200 vwords.addActionListener(new ActionListener() {
201 @Override
202 public void actionPerformed(ActionEvent e) {
203 mainPanel.setWords(true);
204 mainPanel.refreshBooks();
205 }
206 });
207 view.add(vwords);
208 bar.add(view);
209
210 Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
211 if (libOk) {
212 groupedSources = reader.getLibrary().getSourcesGrouped();
213 }
214 JMenu sources = new JMenu(GuiReader.trans(StringIdGui.MENU_SOURCES));
215 sources.setMnemonic(KeyEvent.VK_S);
216 populateMenuSA(sources, groupedSources, true);
217 bar.add(sources);
218
219 Map<String, List<String>> goupedAuthors = new HashMap<String, List<String>>();
220 if (libOk) {
221 goupedAuthors = reader.getLibrary().getAuthorsGrouped();
222 }
223 JMenu authors = new JMenu(GuiReader.trans(StringIdGui.MENU_AUTHORS));
224 authors.setMnemonic(KeyEvent.VK_A);
225 populateMenuSA(authors, goupedAuthors, false);
226 bar.add(authors);
227
228 JMenu options = new JMenu(GuiReader.trans(StringIdGui.MENU_OPTIONS));
229 options.setMnemonic(KeyEvent.VK_O);
230 options.add(createMenuItemConfig());
231 options.add(createMenuItemUiConfig());
232 bar.add(options);
233
234 setJMenuBar(bar);
235 }
236
237 // "" = [unknown]
238 private void populateMenuSA(JMenu menu,
239 Map<String, List<String>> groupedValues, boolean type) {
240
241 // "All" and "Listing" special items first
242 JMenuItem item = new JMenuItem(
243 GuiReader.trans(StringIdGui.MENU_XXX_ALL_GROUPED));
244 item.addActionListener(getActionOpenList(type, false));
245 menu.add(item);
246 item = new JMenuItem(GuiReader.trans(StringIdGui.MENU_XXX_ALL_LISTING));
247 item.addActionListener(getActionOpenList(type, true));
248 menu.add(item);
249
250 menu.addSeparator();
251
252 for (final String value : groupedValues.keySet()) {
253 List<String> list = groupedValues.get(value);
254 if (type && list.size() == 1 && list.get(0).isEmpty()) {
255 // leaf item source/type
256 item = new JMenuItem(
257 value.isEmpty() ? GuiReader
258 .trans(StringIdGui.MENU_AUTHORS_UNKNOWN)
259 : value);
260 item.addActionListener(getActionOpen(value, type));
261 menu.add(item);
262 } else {
263 JMenu dir;
264 if (!type && groupedValues.size() == 1) {
265 // only one group of authors
266 dir = menu;
267 } else {
268 dir = new JMenu(
269 value.isEmpty() ? GuiReader
270 .trans(StringIdGui.MENU_AUTHORS_UNKNOWN)
271 : value);
272 }
273
274 for (String sub : list) {
275 // " " instead of "" for the visual height
276 String itemName = sub;
277 if (itemName.isEmpty()) {
278 itemName = type ? " " : GuiReader
279 .trans(StringIdGui.MENU_AUTHORS_UNKNOWN);
280 }
281
282 String actualValue = value;
283 if (type) {
284 if (!sub.isEmpty()) {
285 actualValue += "/" + sub;
286 }
287 } else {
288 actualValue = sub;
289 }
290
291 item = new JMenuItem(itemName);
292 item.addActionListener(getActionOpen(actualValue, type));
293 dir.add(item);
294 }
295
296 if (menu != dir) {
297 menu.add(dir);
298 }
299 }
300 }
301 }
302
303 /**
304 * Return an {@link ActionListener} that will set the given source (type) as
305 * the selected/displayed one.
306 *
307 * @param type
308 * the type (source) to select, cannot be NULL
309 *
310 * @return the {@link ActionListener}
311 */
312 private ActionListener getActionOpen(final String source, final boolean type) {
313 return new ActionListener() {
314 @Override
315 public void actionPerformed(ActionEvent e) {
316 mainPanel.removeBookPanes();
317 mainPanel.addBookPane(source, type);
318 mainPanel.refreshBooks();
319 }
320 };
321 }
322
323 private ActionListener getActionOpenList(final boolean type,
324 final boolean listMode) {
325 return new ActionListener() {
326 @Override
327 public void actionPerformed(ActionEvent e) {
328 mainPanel.removeBookPanes();
329 mainPanel.addBookPane(type, listMode);
330 mainPanel.refreshBooks();
331 }
332 };
333 }
334
335 /**
336 * Create the Fanfix Configuration menu item.
337 *
338 * @return the item
339 */
340 private JMenuItem createMenuItemConfig() {
341 final String title = GuiReader.trans(StringIdGui.TITLE_CONFIG);
342 JMenuItem item = new JMenuItem(title);
343 item.setMnemonic(KeyEvent.VK_F);
344
345 item.addActionListener(new ActionListener() {
346 @Override
347 public void actionPerformed(ActionEvent e) {
348 ConfigEditor<Config> ed = new ConfigEditor<Config>(
349 Config.class, Instance.getConfig(), GuiReader
350 .trans(StringIdGui.SUBTITLE_CONFIG));
351 JFrame frame = new JFrame(title);
352 frame.add(ed);
353 frame.setSize(800, 600);
354 frame.setVisible(true);
355 }
356 });
357
358 return item;
359 }
360
361 /**
362 * Create the UI Configuration menu item.
363 *
364 * @return the item
365 */
366 private JMenuItem createMenuItemUiConfig() {
367 final String title = GuiReader.trans(StringIdGui.TITLE_CONFIG_UI);
368 JMenuItem item = new JMenuItem(title);
369 item.setMnemonic(KeyEvent.VK_U);
370
371 item.addActionListener(new ActionListener() {
372 @Override
373 public void actionPerformed(ActionEvent e) {
374 ConfigEditor<UiConfig> ed = new ConfigEditor<UiConfig>(
375 UiConfig.class, Instance.getUiConfig(), GuiReader
376 .trans(StringIdGui.SUBTITLE_CONFIG_UI));
377 JFrame frame = new JFrame(title);
378 frame.add(ed);
379 frame.setSize(800, 600);
380 frame.setVisible(true);
381 }
382 });
383
384 return item;
385 }
386
387 /**
388 * Create the export menu item.
389 *
390 * @return the item
391 */
392 private JMenuItem createMenuItemExport() {
393 final JFileChooser fc = new JFileChooser();
394 fc.setAcceptAllFileFilterUsed(false);
395
396 // Add the "ALL" filters first, then the others
397 final Map<FileFilter, OutputType> otherFilters = new HashMap<FileFilter, OutputType>();
398 for (OutputType type : OutputType.values()) {
399 String ext = type.getDefaultExtension(false);
400 String desc = type.getDesc(false);
401
402 if (ext == null || ext.isEmpty()) {
403 fc.addChoosableFileFilter(createAllFilter(desc));
404 } else {
405 otherFilters.put(new FileNameExtensionFilter(desc, ext), type);
406 }
407 }
408
409 for (Entry<FileFilter, OutputType> entry : otherFilters.entrySet()) {
410 fc.addChoosableFileFilter(entry.getKey());
411 }
412 //
413
414 JMenuItem export = new JMenuItem(
415 GuiReader.trans(StringIdGui.MENU_FILE_EXPORT), KeyEvent.VK_S);
416 export.addActionListener(new ActionListener() {
417 @Override
418 public void actionPerformed(ActionEvent e) {
419 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
420 if (selectedBook != null) {
421 fc.showDialog(GuiReaderFrame.this,
422 GuiReader.trans(StringIdGui.TITLE_SAVE));
423 if (fc.getSelectedFile() != null) {
424 final OutputType type = otherFilters.get(fc.getFileFilter());
425 final String path = fc.getSelectedFile()
426 .getAbsolutePath()
427 + type.getDefaultExtension(false);
428 final Progress pg = new Progress();
429 mainPanel.outOfUi(pg, false, new Runnable() {
430 @Override
431 public void run() {
432 try {
433 reader.getLibrary().export(
434 selectedBook.getInfo().getMeta()
435 .getLuid(), type, path, pg);
436 } catch (IOException e) {
437 Instance.getTraceHandler().error(e);
438 }
439 }
440 });
441 }
442 }
443 }
444 });
445
446 return export;
447 }
448
449 /**
450 * Create a {@link FileFilter} that accepts all files and return the given
451 * description.
452 *
453 * @param desc
454 * the description
455 *
456 * @return the filter
457 */
458 private FileFilter createAllFilter(final String desc) {
459 return new FileFilter() {
460 @Override
461 public String getDescription() {
462 return desc;
463 }
464
465 @Override
466 public boolean accept(File f) {
467 return true;
468 }
469 };
470 }
471
472 /**
473 * Create the refresh (delete cache) menu item.
474 *
475 * @return the item
476 */
477 private JMenuItem createMenuItemClearCache() {
478 JMenuItem refresh = new JMenuItem(
479 GuiReader.trans(StringIdGui.MENU_EDIT_CLEAR_CACHE),
480 KeyEvent.VK_C);
481 refresh.addActionListener(new ActionListener() {
482 @Override
483 public void actionPerformed(ActionEvent e) {
484 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
485 if (selectedBook != null) {
486 mainPanel.outOfUi(null, false, new Runnable() {
487 @Override
488 public void run() {
489 reader.clearLocalReaderCache(selectedBook.getInfo()
490 .getMeta().getLuid());
491 selectedBook.setCached(false);
492 GuiReaderCoverImager.clearIcon(selectedBook
493 .getInfo());
494 SwingUtilities.invokeLater(new Runnable() {
495 @Override
496 public void run() {
497 selectedBook.repaint();
498 }
499 });
500 }
501 });
502 }
503 }
504 });
505
506 return refresh;
507 }
508
509 /**
510 * Create the "move to" menu item.
511 *
512 * @param libOk
513 * the library can be queried
514 *
515 * @return the item
516 */
517 private JMenuItem createMenuItemMoveTo(boolean libOk) {
518 JMenu changeTo = new JMenu(
519 GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO));
520 changeTo.setMnemonic(KeyEvent.VK_M);
521
522 Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
523 if (libOk) {
524 groupedSources = reader.getLibrary().getSourcesGrouped();
525 }
526
527 JMenuItem item = new JMenuItem(
528 GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_TYPE));
529 item.addActionListener(createMoveAction(ChangeAction.SOURCE, null));
530 changeTo.add(item);
531 changeTo.addSeparator();
532
533 for (final String type : groupedSources.keySet()) {
534 List<String> list = groupedSources.get(type);
535 if (list.size() == 1 && list.get(0).isEmpty()) {
536 item = new JMenuItem(type);
537 item.addActionListener(createMoveAction(ChangeAction.SOURCE,
538 type));
539 changeTo.add(item);
540 } else {
541 JMenu dir = new JMenu(type);
542 for (String sub : list) {
543 // " " instead of "" for the visual height
544 String itemName = sub.isEmpty() ? " " : sub;
545 String actualType = type;
546 if (!sub.isEmpty()) {
547 actualType += "/" + sub;
548 }
549
550 item = new JMenuItem(itemName);
551 item.addActionListener(createMoveAction(
552 ChangeAction.SOURCE, actualType));
553 dir.add(item);
554 }
555 changeTo.add(dir);
556 }
557 }
558
559 return changeTo;
560 }
561
562 /**
563 * Create the "set author" menu item.
564 *
565 * @param libOk
566 * the library can be queried
567 *
568 * @return the item
569 */
570 private JMenuItem createMenuItemSetAuthor(boolean libOk) {
571 JMenu changeTo = new JMenu(
572 GuiReader.trans(StringIdGui.MENU_FILE_SET_AUTHOR));
573 changeTo.setMnemonic(KeyEvent.VK_A);
574
575 // New author
576 JMenuItem newItem = new JMenuItem(
577 GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_AUTHOR));
578 changeTo.add(newItem);
579 changeTo.addSeparator();
580 newItem.addActionListener(createMoveAction(ChangeAction.AUTHOR, null));
581
582 // Existing authors
583 if (libOk) {
584 Map<String, List<String>> groupedAuthors = reader.getLibrary()
585 .getAuthorsGrouped();
586
587 if (groupedAuthors.size() > 1) {
588 for (String key : groupedAuthors.keySet()) {
589 JMenu group = new JMenu(key);
590 for (String value : groupedAuthors.get(key)) {
591 JMenuItem item = new JMenuItem(
592 value.isEmpty() ? GuiReader
593 .trans(StringIdGui.MENU_AUTHORS_UNKNOWN)
594 : value);
595 item.addActionListener(createMoveAction(
596 ChangeAction.AUTHOR, value));
597 group.add(item);
598 }
599 changeTo.add(group);
600 }
601 } else if (groupedAuthors.size() == 1) {
602 for (String value : groupedAuthors.values().iterator().next()) {
603 JMenuItem item = new JMenuItem(
604 value.isEmpty() ? GuiReader
605 .trans(StringIdGui.MENU_AUTHORS_UNKNOWN)
606 : value);
607 item.addActionListener(createMoveAction(
608 ChangeAction.AUTHOR, value));
609 changeTo.add(item);
610 }
611 }
612 }
613
614 return changeTo;
615 }
616
617 /**
618 * Create the "rename" menu item.
619 *
620 * @param libOk
621 * the library can be queried
622 *
623 * @return the item
624 */
625 private JMenuItem createMenuItemRename(
626 @SuppressWarnings("unused") boolean libOk) {
627 JMenuItem changeTo = new JMenuItem(
628 GuiReader.trans(StringIdGui.MENU_FILE_RENAME));
629 changeTo.setMnemonic(KeyEvent.VK_R);
630 changeTo.addActionListener(createMoveAction(ChangeAction.TITLE, null));
631 return changeTo;
632 }
633
634 private ActionListener createMoveAction(final ChangeAction what,
635 final String type) {
636 return new ActionListener() {
637 @Override
638 public void actionPerformed(ActionEvent e) {
639 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
640 if (selectedBook != null) {
641 boolean refreshRequired = false;
642
643 if (what == ChangeAction.SOURCE) {
644 refreshRequired = mainPanel.getCurrentType();
645 } else if (what == ChangeAction.TITLE) {
646 refreshRequired = false;
647 } else if (what == ChangeAction.AUTHOR) {
648 refreshRequired = !mainPanel.getCurrentType();
649 }
650
651 String changeTo = type;
652 if (type == null) {
653 MetaData meta = selectedBook.getInfo().getMeta();
654 String init = "";
655 if (what == ChangeAction.SOURCE) {
656 init = meta.getSource();
657 } else if (what == ChangeAction.TITLE) {
658 init = meta.getTitle();
659 } else if (what == ChangeAction.AUTHOR) {
660 init = meta.getAuthor();
661 }
662
663 Object rep = JOptionPane.showInputDialog(
664 GuiReaderFrame.this,
665 GuiReader.trans(StringIdGui.SUBTITLE_MOVE_TO),
666 GuiReader.trans(StringIdGui.TITLE_MOVE_TO),
667 JOptionPane.QUESTION_MESSAGE, null, null, init);
668
669 if (rep == null) {
670 return;
671 }
672
673 changeTo = rep.toString();
674 }
675
676 final String fChangeTo = changeTo;
677 mainPanel.outOfUi(null, refreshRequired, new Runnable() {
678 @Override
679 public void run() {
680 String luid = selectedBook.getInfo().getMeta()
681 .getLuid();
682 if (what == ChangeAction.SOURCE) {
683 reader.changeSource(luid, fChangeTo);
684 } else if (what == ChangeAction.TITLE) {
685 reader.changeTitle(luid, fChangeTo);
686 } else if (what == ChangeAction.AUTHOR) {
687 reader.changeAuthor(luid, fChangeTo);
688 }
689
690 mainPanel.getSelectedBook().repaint();
691 mainPanel.unsetSelectedBook();
692
693 SwingUtilities.invokeLater(new Runnable() {
694 @Override
695 public void run() {
696 createMenu(true);
697 }
698 });
699 }
700 });
701 }
702 }
703 };
704 }
705
706 /**
707 * Create the re-download (then delete original) menu item.
708 *
709 * @return the item
710 */
711 private JMenuItem createMenuItemRedownload() {
712 JMenuItem refresh = new JMenuItem(
713 GuiReader.trans(StringIdGui.MENU_EDIT_REDOWNLOAD),
714 KeyEvent.VK_R);
715 refresh.addActionListener(new ActionListener() {
716 @Override
717 public void actionPerformed(ActionEvent e) {
718 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
719 if (selectedBook != null) {
720 final MetaData meta = selectedBook.getInfo().getMeta();
721 mainPanel.imprt(
722 meta.getUrl(),
723 new StoryRunnable() {
724 @Override
725 public void run(Story story) {
726 reader.delete(meta.getLuid());
727 mainPanel.unsetSelectedBook();
728 MetaData newMeta = story.getMeta();
729 if (!newMeta.getSource().equals(
730 meta.getSource())) {
731 reader.changeSource(newMeta.getLuid(),
732 meta.getSource());
733 }
734 }
735 },
736 GuiReader
737 .trans(StringIdGui.PROGRESS_REDOWNLOAD_REMOVE_OLD_COPY));
738 }
739 }
740 });
741
742 return refresh;
743 }
744
745 /**
746 * Create the delete menu item.
747 *
748 * @return the item
749 */
750 private JMenuItem createMenuItemDelete() {
751 JMenuItem delete = new JMenuItem(
752 GuiReader.trans(StringIdGui.MENU_EDIT_DELETE), KeyEvent.VK_D);
753 delete.addActionListener(new ActionListener() {
754 @Override
755 public void actionPerformed(ActionEvent e) {
756 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
757 if (selectedBook != null
758 && selectedBook.getInfo().getMeta() != null) {
759
760 final MetaData meta = selectedBook.getInfo().getMeta();
761 int rep = JOptionPane.showConfirmDialog(
762 GuiReaderFrame.this,
763 GuiReader.trans(StringIdGui.SUBTITLE_DELETE,
764 meta.getLuid(), meta.getTitle()),
765 GuiReader.trans(StringIdGui.TITLE_DELETE),
766 JOptionPane.OK_CANCEL_OPTION);
767
768 if (rep == JOptionPane.OK_OPTION) {
769 mainPanel.outOfUi(null, true, new Runnable() {
770 @Override
771 public void run() {
772 reader.delete(meta.getLuid());
773 mainPanel.unsetSelectedBook();
774 }
775 });
776 }
777 }
778 }
779 });
780
781 return delete;
782 }
783
784 /**
785 * Create the properties menu item.
786 *
787 * @return the item
788 */
789 private JMenuItem createMenuItemProperties() {
790 JMenuItem delete = new JMenuItem(
791 GuiReader.trans(StringIdGui.MENU_FILE_PROPERTIES),
792 KeyEvent.VK_P);
793 delete.addActionListener(new ActionListener() {
794 @Override
795 public void actionPerformed(ActionEvent e) {
796 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
797 if (selectedBook != null) {
798 mainPanel.outOfUi(null, false, new Runnable() {
799 @Override
800 public void run() {
801 new GuiReaderPropertiesFrame(reader.getLibrary(),
802 selectedBook.getInfo().getMeta())
803 .setVisible(true);
804 }
805 });
806 }
807 }
808 });
809
810 return delete;
811 }
812
813 /**
814 * Create the open menu item for a book, a source/type or an author.
815 *
816 * @return the item
817 */
818 public JMenuItem createMenuItemOpenBook() {
819 JMenuItem open = new JMenuItem(
820 GuiReader.trans(StringIdGui.MENU_FILE_OPEN), KeyEvent.VK_O);
821 open.addActionListener(new ActionListener() {
822 @Override
823 public void actionPerformed(ActionEvent e) {
824 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
825 if (selectedBook != null) {
826 if (selectedBook.getInfo().getMeta() == null) {
827 mainPanel.removeBookPanes();
828 mainPanel.addBookPane(selectedBook.getInfo()
829 .getMainInfo(), mainPanel.getCurrentType());
830 mainPanel.refreshBooks();
831 } else {
832 mainPanel.openBook(selectedBook);
833 }
834 }
835 }
836 });
837
838 return open;
839 }
840
841 /**
842 * Create the SetCover menu item for a book to change the linked source
843 * cover.
844 *
845 * @return the item
846 */
847 private JMenuItem createMenuItemSetCoverForSource() {
848 JMenuItem open = new JMenuItem(
849 GuiReader.trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_SOURCE),
850 KeyEvent.VK_C);
851 open.addActionListener(new ActionListener() {
852 @Override
853 public void actionPerformed(ActionEvent e) {
854 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
855 if (selectedBook != null) {
856 BasicLibrary lib = reader.getLibrary();
857 String luid = selectedBook.getInfo().getMeta().getLuid();
858 String source = selectedBook.getInfo().getMeta()
859 .getSource();
860
861 lib.setSourceCover(source, luid);
862
863 GuiReaderBookInfo sourceInfo = GuiReaderBookInfo
864 .fromSource(lib, source);
865 GuiReaderCoverImager.clearIcon(sourceInfo);
866 }
867 }
868 });
869
870 return open;
871 }
872
873 /**
874 * Create the SetCover menu item for a book to change the linked source
875 * cover.
876 *
877 * @return the item
878 */
879 private JMenuItem createMenuItemSetCoverForAuthor() {
880 JMenuItem open = new JMenuItem(
881 GuiReader.trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_AUTHOR),
882 KeyEvent.VK_A);
883 open.addActionListener(new ActionListener() {
884 @Override
885 public void actionPerformed(ActionEvent e) {
886 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
887 if (selectedBook != null) {
888 BasicLibrary lib = reader.getLibrary();
889 String luid = selectedBook.getInfo().getMeta().getLuid();
890 String author = selectedBook.getInfo().getMeta()
891 .getAuthor();
892
893 lib.setAuthorCover(author, luid);
894
895 GuiReaderBookInfo authorInfo = GuiReaderBookInfo
896 .fromAuthor(lib, author);
897 GuiReaderCoverImager.clearIcon(authorInfo);
898 }
899 }
900 });
901
902 return open;
903 }
904
905 /**
906 * Display an error message and log the linked {@link Exception}.
907 *
908 * @param message
909 * the message
910 * @param title
911 * the title of the error message
912 * @param e
913 * the exception to log if any
914 */
915 public void error(final String message, final String title, Exception e) {
916 Instance.getTraceHandler().error(title + ": " + message);
917 if (e != null) {
918 Instance.getTraceHandler().error(e);
919 }
920
921 SwingUtilities.invokeLater(new Runnable() {
922 @Override
923 public void run() {
924 JOptionPane.showMessageDialog(GuiReaderFrame.this, message,
925 title, JOptionPane.ERROR_MESSAGE);
926 }
927 });
928 }
929
930 @Override
931 public GuiReader getReader() {
932 return reader;
933 }
934
935 /**
936 * Return the title of the application.
937 *
938 * @param libraryName
939 * the name of the associated {@link BasicLibrary}, which can be
940 * EMPTY
941 *
942 * @return the title
943 */
944 static private String getAppTitle(String libraryName) {
945 if (!libraryName.isEmpty()) {
946 return GuiReader.trans(StringIdGui.TITLE_LIBRARY_WITH_NAME, Version
947 .getCurrentVersion().toString(), libraryName);
948 }
949
950 return GuiReader.trans(StringIdGui.TITLE_LIBRARY, Version
951 .getCurrentVersion().toString());
952 }
953 }