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