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