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