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