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