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