full selection
[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()));
17fafa56 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) {
c428e675 424 final OutputType type = otherFilters.get(fc.getFileFilter());
b2612f9d
NR
425 final String path = fc.getSelectedFile()
426 .getAbsolutePath()
427 + type.getDefaultExtension(false);
428 final Progress pg = new Progress();
2d56db73 429 mainPanel.outOfUi(pg, false, new Runnable() {
211f7ddb 430 @Override
b2612f9d
NR
431 public void run() {
432 try {
e42573a0 433 reader.getLibrary().export(
79a99506
NR
434 selectedBook.getInfo().getMeta()
435 .getLuid(), type, path, pg);
b2612f9d 436 } catch (IOException e) {
62c63b07 437 Instance.getTraceHandler().error(e);
b2612f9d 438 }
4d205683 439 }
b2612f9d
NR
440 });
441 }
4d205683 442 }
10d558d2
NR
443 }
444 });
445
9843a5e5
NR
446 return export;
447 }
448
edd46289
NR
449 /**
450 * Create a {@link FileFilter} that accepts all files and return the given
451 * description.
452 *
453 * @param desc
454 * the description
455 *
456 * @return the filter
457 */
4d205683
NR
458 private FileFilter createAllFilter(final String desc) {
459 return new FileFilter() {
460 @Override
461 public String getDescription() {
462 return desc;
463 }
464
465 @Override
466 public boolean accept(File f) {
467 return true;
468 }
469 };
470 }
471
472 /**
473 * Create the refresh (delete cache) menu item.
474 *
475 * @return the item
476 */
22848428 477 private JMenuItem createMenuItemClearCache() {
c428e675
NR
478 JMenuItem refresh = new JMenuItem(
479 GuiReader.trans(StringIdGui.MENU_EDIT_CLEAR_CACHE),
480 KeyEvent.VK_C);
10d558d2 481 refresh.addActionListener(new ActionListener() {
211f7ddb 482 @Override
10d558d2 483 public void actionPerformed(ActionEvent e) {
31e28683 484 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
10d558d2 485 if (selectedBook != null) {
2d56db73 486 mainPanel.outOfUi(null, false, new Runnable() {
211f7ddb 487 @Override
10d558d2 488 public void run() {
79a99506
NR
489 reader.clearLocalReaderCache(selectedBook.getInfo()
490 .getMeta().getLuid());
10d558d2 491 selectedBook.setCached(false);
df6e2d88 492 GuiReaderCoverImager.clearIcon(selectedBook
79a99506 493 .getInfo());
3b2b638f 494 SwingUtilities.invokeLater(new Runnable() {
211f7ddb 495 @Override
3b2b638f 496 public void run() {
10d558d2 497 selectedBook.repaint();
3b2b638f
NR
498 }
499 });
500 }
501 });
333f0e7b
NR
502 }
503 }
504 });
10d558d2 505
9843a5e5
NR
506 return refresh;
507 }
508
70c9b112 509 /**
c8d48938 510 * Create the "move to" menu item.
70c9b112 511 *
e6249b0f
NR
512 * @param libOk
513 * the library can be queried
514 *
70c9b112
NR
515 * @return the item
516 */
c8d48938 517 private JMenuItem createMenuItemMoveTo(boolean libOk) {
c428e675
NR
518 JMenu changeTo = new JMenu(
519 GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO));
c8d48938 520 changeTo.setMnemonic(KeyEvent.VK_M);
70c9b112 521
c1b93db3 522 Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
e6249b0f 523 if (libOk) {
c1b93db3 524 groupedSources = reader.getLibrary().getSourcesGrouped();
e6249b0f 525 }
70c9b112 526
c428e675
NR
527 JMenuItem item = new JMenuItem(
528 GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_TYPE));
79a99506 529 item.addActionListener(createMoveAction(ChangeAction.SOURCE, null));
c1b93db3
NR
530 changeTo.add(item);
531 changeTo.addSeparator();
70c9b112 532
c1b93db3
NR
533 for (final String type : groupedSources.keySet()) {
534 List<String> list = groupedSources.get(type);
535 if (list.size() == 1 && list.get(0).isEmpty()) {
536 item = new JMenuItem(type);
79a99506
NR
537 item.addActionListener(createMoveAction(ChangeAction.SOURCE,
538 type));
c1b93db3
NR
539 changeTo.add(item);
540 } else {
541 JMenu dir = new JMenu(type);
542 for (String sub : list) {
543 // " " instead of "" for the visual height
544 String itemName = sub.isEmpty() ? " " : sub;
545 String actualType = type;
546 if (!sub.isEmpty()) {
547 actualType += "/" + sub;
548 }
70c9b112 549
c1b93db3 550 item = new JMenuItem(itemName);
79a99506
NR
551 item.addActionListener(createMoveAction(
552 ChangeAction.SOURCE, actualType));
c1b93db3
NR
553 dir.add(item);
554 }
555 changeTo.add(dir);
c8d48938
NR
556 }
557 }
211f7ddb 558
c8d48938
NR
559 return changeTo;
560 }
70c9b112 561
c8d48938
NR
562 /**
563 * Create the "set author" menu item.
564 *
565 * @param libOk
566 * the library can be queried
567 *
568 * @return the item
569 */
570 private JMenuItem createMenuItemSetAuthor(boolean libOk) {
c428e675
NR
571 JMenu changeTo = new JMenu(
572 GuiReader.trans(StringIdGui.MENU_FILE_SET_AUTHOR));
c8d48938 573 changeTo.setMnemonic(KeyEvent.VK_A);
70c9b112 574
c8d48938 575 // New author
c428e675
NR
576 JMenuItem newItem = new JMenuItem(
577 GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_AUTHOR));
c8d48938
NR
578 changeTo.add(newItem);
579 changeTo.addSeparator();
79a99506 580 newItem.addActionListener(createMoveAction(ChangeAction.AUTHOR, null));
70c9b112 581
c8d48938
NR
582 // Existing authors
583 if (libOk) {
97b36d32
NR
584 Map<String, List<String>> groupedAuthors = reader.getLibrary()
585 .getAuthorsGrouped();
c8d48938 586
97b36d32
NR
587 if (groupedAuthors.size() > 1) {
588 for (String key : groupedAuthors.keySet()) {
589 JMenu group = new JMenu(key);
590 for (String value : groupedAuthors.get(key)) {
116904b8 591 JMenuItem item = new JMenuItem(
c428e675
NR
592 value.isEmpty() ? GuiReader
593 .trans(StringIdGui.MENU_AUTHORS_UNKNOWN)
594 : value);
14bb95fa 595 item.addActionListener(createMoveAction(
79a99506 596 ChangeAction.AUTHOR, value));
c8d48938 597 group.add(item);
70c9b112 598 }
c8d48938 599 changeTo.add(group);
70c9b112 600 }
97b36d32
NR
601 } else if (groupedAuthors.size() == 1) {
602 for (String value : groupedAuthors.values().iterator().next()) {
116904b8 603 JMenuItem item = new JMenuItem(
c428e675
NR
604 value.isEmpty() ? GuiReader
605 .trans(StringIdGui.MENU_AUTHORS_UNKNOWN)
606 : value);
79a99506
NR
607 item.addActionListener(createMoveAction(
608 ChangeAction.AUTHOR, value));
c8d48938
NR
609 changeTo.add(item);
610 }
611 }
70c9b112
NR
612 }
613
c8d48938
NR
614 return changeTo;
615 }
616
617 /**
618 * Create the "rename" menu item.
619 *
620 * @param libOk
621 * the library can be queried
622 *
623 * @return the item
624 */
625 private JMenuItem createMenuItemRename(
626 @SuppressWarnings("unused") boolean libOk) {
c428e675
NR
627 JMenuItem changeTo = new JMenuItem(
628 GuiReader.trans(StringIdGui.MENU_FILE_RENAME));
c8d48938 629 changeTo.setMnemonic(KeyEvent.VK_R);
79a99506 630 changeTo.addActionListener(createMoveAction(ChangeAction.TITLE, null));
c8d48938
NR
631 return changeTo;
632 }
633
79a99506 634 private ActionListener createMoveAction(final ChangeAction what,
14bb95fa 635 final String type) {
c8d48938
NR
636 return new ActionListener() {
637 @Override
638 public void actionPerformed(ActionEvent e) {
31e28683 639 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
c8d48938 640 if (selectedBook != null) {
c349fd48
NR
641 boolean refreshRequired = false;
642
643 if (what == ChangeAction.SOURCE) {
644 refreshRequired = mainPanel.getCurrentType();
645 } else if (what == ChangeAction.TITLE) {
646 refreshRequired = false;
647 } else if (what == ChangeAction.AUTHOR) {
648 refreshRequired = !mainPanel.getCurrentType();
649 }
650
c8d48938
NR
651 String changeTo = type;
652 if (type == null) {
79a99506 653 MetaData meta = selectedBook.getInfo().getMeta();
c8d48938 654 String init = "";
79a99506
NR
655 if (what == ChangeAction.SOURCE) {
656 init = meta.getSource();
657 } else if (what == ChangeAction.TITLE) {
658 init = meta.getTitle();
659 } else if (what == ChangeAction.AUTHOR) {
660 init = meta.getAuthor();
c8d48938
NR
661 }
662
663 Object rep = JOptionPane.showInputDialog(
c428e675
NR
664 GuiReaderFrame.this,
665 GuiReader.trans(StringIdGui.SUBTITLE_MOVE_TO),
666 GuiReader.trans(StringIdGui.TITLE_MOVE_TO),
667 JOptionPane.QUESTION_MESSAGE, null, null, init);
c8d48938
NR
668
669 if (rep == null) {
670 return;
671 }
672
673 changeTo = rep.toString();
674 }
675
676 final String fChangeTo = changeTo;
c349fd48 677 mainPanel.outOfUi(null, refreshRequired, new Runnable() {
c8d48938
NR
678 @Override
679 public void run() {
79a99506
NR
680 String luid = selectedBook.getInfo().getMeta()
681 .getLuid();
682 if (what == ChangeAction.SOURCE) {
683 reader.changeSource(luid, fChangeTo);
684 } else if (what == ChangeAction.TITLE) {
685 reader.changeTitle(luid, fChangeTo);
686 } else if (what == ChangeAction.AUTHOR) {
687 reader.changeAuthor(luid, fChangeTo);
c8d48938
NR
688 }
689
c349fd48 690 mainPanel.getSelectedBook().repaint();
31e28683 691 mainPanel.unsetSelectedBook();
c8d48938
NR
692
693 SwingUtilities.invokeLater(new Runnable() {
694 @Override
695 public void run() {
14bb95fa 696 createMenu(true);
c8d48938
NR
697 }
698 });
699 }
700 });
701 }
702 }
703 };
70c9b112
NR
704 }
705
22848428 706 /**
79a99506 707 * Create the re-download (then delete original) menu item.
22848428
NR
708 *
709 * @return the item
710 */
711 private JMenuItem createMenuItemRedownload() {
c428e675
NR
712 JMenuItem refresh = new JMenuItem(
713 GuiReader.trans(StringIdGui.MENU_EDIT_REDOWNLOAD),
714 KeyEvent.VK_R);
22848428 715 refresh.addActionListener(new ActionListener() {
211f7ddb 716 @Override
22848428 717 public void actionPerformed(ActionEvent e) {
31e28683 718 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
22848428 719 if (selectedBook != null) {
79a99506 720 final MetaData meta = selectedBook.getInfo().getMeta();
c428e675
NR
721 mainPanel.imprt(
722 meta.getUrl(),
723 new StoryRunnable() {
724 @Override
725 public void run(Story story) {
c428e675
NR
726 MetaData newMeta = story.getMeta();
727 if (!newMeta.getSource().equals(
728 meta.getSource())) {
729 reader.changeSource(newMeta.getLuid(),
730 meta.getSource());
731 }
732 }
733 },
734 GuiReader
233c0b06 735 .trans(StringIdGui.PROGRESS_CHANGE_SOURCE));
22848428
NR
736 }
737 }
738 });
739
740 return refresh;
741 }
742
4d205683
NR
743 /**
744 * Create the delete menu item.
745 *
746 * @return the item
747 */
9843a5e5 748 private JMenuItem createMenuItemDelete() {
c428e675
NR
749 JMenuItem delete = new JMenuItem(
750 GuiReader.trans(StringIdGui.MENU_EDIT_DELETE), KeyEvent.VK_D);
10d558d2 751 delete.addActionListener(new ActionListener() {
211f7ddb 752 @Override
10d558d2 753 public void actionPerformed(ActionEvent e) {
31e28683 754 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
c349fd48
NR
755 if (selectedBook != null
756 && selectedBook.getInfo().getMeta() != null) {
757
758 final MetaData meta = selectedBook.getInfo().getMeta();
759 int rep = JOptionPane.showConfirmDialog(
760 GuiReaderFrame.this,
c428e675
NR
761 GuiReader.trans(StringIdGui.SUBTITLE_DELETE,
762 meta.getLuid(), meta.getTitle()),
763 GuiReader.trans(StringIdGui.TITLE_DELETE),
c349fd48
NR
764 JOptionPane.OK_CANCEL_OPTION);
765
766 if (rep == JOptionPane.OK_OPTION) {
767 mainPanel.outOfUi(null, true, new Runnable() {
768 @Override
769 public void run() {
770 reader.delete(meta.getLuid());
771 mainPanel.unsetSelectedBook();
772 }
773 });
774 }
10d558d2
NR
775 }
776 }
777 });
778
9843a5e5
NR
779 return delete;
780 }
10d558d2 781
df6e2d88
NR
782 /**
783 * Create the properties menu item.
784 *
785 * @return the item
786 */
787 private JMenuItem createMenuItemProperties() {
c428e675
NR
788 JMenuItem delete = new JMenuItem(
789 GuiReader.trans(StringIdGui.MENU_FILE_PROPERTIES),
790 KeyEvent.VK_P);
df6e2d88
NR
791 delete.addActionListener(new ActionListener() {
792 @Override
793 public void actionPerformed(ActionEvent e) {
31e28683 794 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
df6e2d88 795 if (selectedBook != null) {
2d56db73 796 mainPanel.outOfUi(null, false, new Runnable() {
df6e2d88
NR
797 @Override
798 public void run() {
5beb7cdc
NR
799 new GuiReaderPropertiesFrame(reader.getLibrary(),
800 selectedBook.getInfo().getMeta())
801 .setVisible(true);
df6e2d88
NR
802 }
803 });
804 }
805 }
806 });
807
808 return delete;
809 }
810
4d205683 811 /**
32224dda 812 * Create the open menu item for a book, a source/type or an author.
4d205683
NR
813 *
814 * @return the item
815 */
14bb95fa 816 public JMenuItem createMenuItemOpenBook() {
c428e675
NR
817 JMenuItem open = new JMenuItem(
818 GuiReader.trans(StringIdGui.MENU_FILE_OPEN), KeyEvent.VK_O);
9843a5e5 819 open.addActionListener(new ActionListener() {
211f7ddb 820 @Override
9843a5e5 821 public void actionPerformed(ActionEvent e) {
31e28683 822 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
9843a5e5 823 if (selectedBook != null) {
8590da19 824 if (selectedBook.getInfo().getMeta() == null) {
31e28683 825 mainPanel.removeBookPanes();
5beb7cdc
NR
826 mainPanel.addBookPane(selectedBook.getInfo()
827 .getMainInfo(), mainPanel.getCurrentType());
31e28683 828 mainPanel.refreshBooks();
14b57448 829 } else {
31e28683 830 mainPanel.openBook(selectedBook);
14b57448
NR
831 }
832 }
833 }
834 });
835
836 return open;
837 }
838
839 /**
840 * Create the SetCover menu item for a book to change the linked source
841 * cover.
842 *
843 * @return the item
844 */
79a99506 845 private JMenuItem createMenuItemSetCoverForSource() {
c428e675
NR
846 JMenuItem open = new JMenuItem(
847 GuiReader.trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_SOURCE),
848 KeyEvent.VK_C);
14b57448
NR
849 open.addActionListener(new ActionListener() {
850 @Override
851 public void actionPerformed(ActionEvent e) {
31e28683 852 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
14b57448 853 if (selectedBook != null) {
79a99506
NR
854 BasicLibrary lib = reader.getLibrary();
855 String luid = selectedBook.getInfo().getMeta().getLuid();
856 String source = selectedBook.getInfo().getMeta()
857 .getSource();
858
859 lib.setSourceCover(source, luid);
860
861 GuiReaderBookInfo sourceInfo = GuiReaderBookInfo
862 .fromSource(lib, source);
863 GuiReaderCoverImager.clearIcon(sourceInfo);
864 }
865 }
866 });
867
868 return open;
869 }
870
871 /**
872 * Create the SetCover menu item for a book to change the linked source
873 * cover.
874 *
875 * @return the item
876 */
877 private JMenuItem createMenuItemSetCoverForAuthor() {
c428e675
NR
878 JMenuItem open = new JMenuItem(
879 GuiReader.trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_AUTHOR),
880 KeyEvent.VK_A);
79a99506
NR
881 open.addActionListener(new ActionListener() {
882 @Override
883 public void actionPerformed(ActionEvent e) {
884 final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
885 if (selectedBook != null) {
886 BasicLibrary lib = reader.getLibrary();
887 String luid = selectedBook.getInfo().getMeta().getLuid();
888 String author = selectedBook.getInfo().getMeta()
889 .getAuthor();
890
891 lib.setAuthorCover(author, luid);
892
893 GuiReaderBookInfo authorInfo = GuiReaderBookInfo
894 .fromAuthor(lib, author);
895 GuiReaderCoverImager.clearIcon(authorInfo);
9843a5e5
NR
896 }
897 }
898 });
10d558d2 899
9843a5e5
NR
900 return open;
901 }
10d558d2 902
e6249b0f
NR
903 /**
904 * Display an error message and log the linked {@link Exception}.
905 *
906 * @param message
907 * the message
908 * @param title
909 * the title of the error message
910 * @param e
911 * the exception to log if any
912 */
14bb95fa 913 public void error(final String message, final String title, Exception e) {
e6249b0f
NR
914 Instance.getTraceHandler().error(title + ": " + message);
915 if (e != null) {
916 Instance.getTraceHandler().error(e);
917 }
918
919 SwingUtilities.invokeLater(new Runnable() {
920 @Override
921 public void run() {
922 JOptionPane.showMessageDialog(GuiReaderFrame.this, message,
923 title, JOptionPane.ERROR_MESSAGE);
924 }
925 });
926 }
14bb95fa
NR
927
928 @Override
929 public GuiReader getReader() {
930 return reader;
931 }
5bc9573b
NR
932
933 /**
934 * Return the title of the application.
935 *
936 * @param libraryName
937 * the name of the associated {@link BasicLibrary}, which can be
938 * EMPTY
939 *
940 * @return the title
941 */
942 static private String getAppTitle(String libraryName) {
943 if (!libraryName.isEmpty()) {
944 return GuiReader.trans(StringIdGui.TITLE_LIBRARY_WITH_NAME, Version
945 .getCurrentVersion().toString(), libraryName);
946 }
947
c428e675
NR
948 return GuiReader.trans(StringIdGui.TITLE_LIBRARY, Version
949 .getCurrentVersion().toString());
5bc9573b 950 }
a6395bef 951}