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