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