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