0710549feb3112f1233261ed79f47e8cedd01681
[fanfix.git] / src / be / nikiroo / fanfix_swing / gui / book / BookPopup.java
1 package be.nikiroo.fanfix_swing.gui.book;
2
3 import java.awt.event.ActionEvent;
4 import java.awt.event.ActionListener;
5 import java.awt.event.KeyEvent;
6 import java.io.File;
7 import java.io.IOException;
8 import java.util.HashMap;
9 import java.util.LinkedList;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Map.Entry;
13
14 import javax.swing.JFileChooser;
15 import javax.swing.JFrame;
16 import javax.swing.JMenu;
17 import javax.swing.JMenuItem;
18 import javax.swing.JOptionPane;
19 import javax.swing.JPopupMenu;
20 import javax.swing.SwingWorker;
21 import javax.swing.filechooser.FileFilter;
22 import javax.swing.filechooser.FileNameExtensionFilter;
23
24 import be.nikiroo.fanfix.Instance;
25 import be.nikiroo.fanfix.bundles.Config;
26 import be.nikiroo.fanfix.bundles.StringIdGui;
27 import be.nikiroo.fanfix.bundles.UiConfig;
28 import be.nikiroo.fanfix.data.MetaData;
29 import be.nikiroo.fanfix.data.Story;
30 import be.nikiroo.fanfix.library.BasicLibrary;
31 import be.nikiroo.fanfix.library.BasicLibrary.Status;
32 import be.nikiroo.fanfix.output.BasicOutput.OutputType;
33 import be.nikiroo.fanfix_swing.Actions;
34 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
35 import be.nikiroo.utils.Progress;
36 import be.nikiroo.utils.ui.ConfigEditor;
37
38 public class BookPopup extends JPopupMenu {
39 public abstract interface Informer {
40
41 // not null
42 public List<BookInfo> getSelected();
43
44 public void setCached(BookInfo book, boolean cached);
45
46 public BookInfo getUniqueSelected();
47
48 public void fireElementChanged(BookInfo book);
49
50 }
51
52 /**
53 * The different modification actions you can use on {@link Story} items.
54 *
55 * @author niki
56 */
57 private enum ChangeAction {
58 /** Change the source/type, that is, move it to another source. */
59 SOURCE,
60 /** Change its name. */
61 TITLE,
62 /** Change its author. */
63 AUTHOR
64 }
65
66 // be careful with that
67 private BasicLibrary lib;
68
69 private Informer informer;
70
71 private Object object;
72
73 private Object object2;
74
75 private Object object3;
76
77 public BookPopup(BasicLibrary lib, Informer informer) {
78 this.lib = lib;
79 this.informer = informer;
80
81 Status status = lib.getStatus();
82 add(createMenuItemOpenBook());
83 addSeparator();
84 add(createMenuItemExport());
85 if (status.isWritable()) {
86 add(createMenuItemMoveTo());
87 add(createMenuItemSetCoverForSource());
88 add(createMenuItemSetCoverForAuthor());
89 }
90 add(createMenuItemDownloadToCache());
91 add(createMenuItemClearCache());
92 if (status.isWritable()) {
93 add(createMenuItemRedownload());
94 addSeparator();
95 add(createMenuItemRename());
96 add(createMenuItemSetAuthor());
97 addSeparator();
98 add(createMenuItemDelete());
99 }
100 addSeparator();
101 add(createMenuItemProperties());
102 }
103
104 private String trans(StringIdGui id) {
105 return Instance.getInstance().getTransGui().getString(id);
106 }
107
108 /**
109 * Create the Fanfix Configuration menu item.
110 *
111 * @return the item
112 */
113 private JMenuItem createMenuItemConfig() {
114 final String title = trans(StringIdGui.TITLE_CONFIG);
115 JMenuItem item = new JMenuItem(title);
116 item.setMnemonic(KeyEvent.VK_F);
117
118 item.addActionListener(new ActionListener() {
119 @Override
120 public void actionPerformed(ActionEvent e) {
121 ConfigEditor<Config> ed = new ConfigEditor<Config>(Config.class, Instance.getInstance().getConfig(),
122 trans(StringIdGui.SUBTITLE_CONFIG));
123 JFrame frame = new JFrame(title);
124 frame.add(ed);
125 frame.setSize(850, 600);
126 frame.setVisible(true);
127 }
128 });
129
130 return item;
131 }
132
133 /**
134 * Create the UI Configuration menu item.
135 *
136 * @return the item
137 */
138 private JMenuItem createMenuItemUiConfig() {
139 final String title = trans(StringIdGui.TITLE_CONFIG_UI);
140 JMenuItem item = new JMenuItem(title);
141 item.setMnemonic(KeyEvent.VK_U);
142
143 item.addActionListener(new ActionListener() {
144 @Override
145 public void actionPerformed(ActionEvent e) {
146 ConfigEditor<UiConfig> ed = new ConfigEditor<UiConfig>(UiConfig.class,
147 Instance.getInstance().getUiConfig(), trans(StringIdGui.SUBTITLE_CONFIG_UI));
148 JFrame frame = new JFrame(title);
149 frame.add(ed);
150 frame.setSize(800, 600);
151 frame.setVisible(true);
152 }
153 });
154
155 return item;
156 }
157
158 /**
159 * Create the export menu item.
160 *
161 * @return the item
162 */
163 private JMenuItem createMenuItemExport() {
164
165 // TODO: allow dir for multiple selection?
166
167 final JFileChooser fc = new JFileChooser();
168 fc.setAcceptAllFileFilterUsed(false);
169
170 // Add the "ALL" filters first, then the others
171 final Map<FileFilter, OutputType> otherFilters = new HashMap<FileFilter, OutputType>();
172 for (OutputType type : OutputType.values()) {
173 String ext = type.getDefaultExtension(false);
174 String desc = type.getDesc(false);
175
176 if (ext == null || ext.isEmpty()) {
177 fc.addChoosableFileFilter(createAllFilter(desc));
178 } else {
179 otherFilters.put(new FileNameExtensionFilter(desc, ext), type);
180 }
181 }
182
183 for (Entry<FileFilter, OutputType> entry : otherFilters.entrySet()) {
184 fc.addChoosableFileFilter(entry.getKey());
185 }
186 //
187
188 JMenuItem export = new JMenuItem(trans(StringIdGui.MENU_FILE_EXPORT), KeyEvent.VK_S);
189 export.addActionListener(new ActionListener() {
190 @Override
191 public void actionPerformed(ActionEvent e) {
192 final BookInfo book = informer.getUniqueSelected();
193 if (book != null) {
194 fc.showDialog(BookPopup.this.getParent(), trans(StringIdGui.TITLE_SAVE));
195 if (fc.getSelectedFile() != null) {
196 final OutputType type = otherFilters.get(fc.getFileFilter());
197 final String path = fc.getSelectedFile().getAbsolutePath() + type.getDefaultExtension(false);
198 final Progress pg = new Progress();
199
200 new SwingWorker<Void, Void>() {
201 @Override
202 protected Void doInBackground() throws Exception {
203 lib.export(book.getMeta().getLuid(), type, path, pg);
204 return null;
205 }
206
207 @Override
208 protected void done() {
209 try {
210 get();
211 } catch (Exception e) {
212 UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), "IOException",
213 e);
214 }
215 }
216 }.execute();
217 }
218 }
219 }
220 });
221
222 return export;
223 }
224
225 /**
226 * Create a {@link FileFilter} that accepts all files and return the given
227 * description.
228 *
229 * @param desc the description
230 *
231 * @return the filter
232 */
233 private FileFilter createAllFilter(final String desc) {
234 return new FileFilter() {
235 @Override
236 public String getDescription() {
237 return desc;
238 }
239
240 @Override
241 public boolean accept(File f) {
242 return true;
243 }
244 };
245 }
246
247 /**
248 * Create the refresh (delete cache) menu item.
249 *
250 * @return the item
251 */
252 private JMenuItem createMenuItemClearCache() {
253 JMenuItem refresh = new JMenuItem(trans(StringIdGui.MENU_EDIT_CLEAR_CACHE), KeyEvent.VK_C);
254 refresh.addActionListener(new ActionListener() {
255 @Override
256 public void actionPerformed(ActionEvent e) {
257 final List<BookInfo> selected = informer.getSelected();
258 if (!selected.isEmpty()) {
259 new SwingWorker<Void, Void>() {
260 @Override
261 protected Void doInBackground() throws Exception {
262 for (BookInfo book : selected) {
263 lib.clearFromCache(book.getMeta().getLuid());
264 BookCoverImager.clearIcon(book);
265 }
266 return null;
267 }
268
269 @Override
270 protected void done() {
271 try {
272 get();
273 for (BookInfo book : selected) {
274 informer.setCached(book, false);
275 }
276 } catch (Exception e) {
277 UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), "IOException", e);
278 }
279 }
280 }.execute();
281 }
282 }
283 });
284
285 return refresh;
286 }
287
288 /**
289 * Create the "move to" menu item.
290 *
291 * @return the item
292 */
293 private JMenuItem createMenuItemMoveTo() {
294 JMenu changeTo = new JMenu(trans(StringIdGui.MENU_FILE_MOVE_TO));
295 changeTo.setMnemonic(KeyEvent.VK_M);
296
297 Map<String, List<String>> groupedSources = new HashMap<String, List<String>>();
298 try {
299 groupedSources = lib.getSourcesGrouped();
300 } catch (IOException e) {
301 UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), "IOException", e);
302 }
303
304 JMenuItem item = new JMenuItem(trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_TYPE));
305 item.addActionListener(createMoveAction(ChangeAction.SOURCE, null));
306 changeTo.add(item);
307 changeTo.addSeparator();
308
309 for (final String type : groupedSources.keySet()) {
310 List<String> list = groupedSources.get(type);
311 if (list.size() == 1 && list.get(0).isEmpty()) {
312 item = new JMenuItem(type);
313 item.addActionListener(createMoveAction(ChangeAction.SOURCE, type));
314 changeTo.add(item);
315 } else {
316 JMenu dir = new JMenu(type);
317 for (String sub : list) {
318 // " " instead of "" for the visual height
319 String itemName = sub.isEmpty() ? " " : sub;
320 String actualType = type;
321 if (!sub.isEmpty()) {
322 actualType += "/" + sub;
323 }
324
325 item = new JMenuItem(itemName);
326 item.addActionListener(createMoveAction(ChangeAction.SOURCE, actualType));
327 dir.add(item);
328 }
329 changeTo.add(dir);
330 }
331 }
332
333 return changeTo;
334 }
335
336 /**
337 * Create the "set author" menu item.
338 *
339 * @return the item
340 */
341 private JMenuItem createMenuItemSetAuthor() {
342 JMenu changeTo = new JMenu(trans(StringIdGui.MENU_FILE_SET_AUTHOR));
343 changeTo.setMnemonic(KeyEvent.VK_A);
344
345 // New author
346 JMenuItem newItem = new JMenuItem(trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_AUTHOR));
347 changeTo.add(newItem);
348 changeTo.addSeparator();
349 newItem.addActionListener(createMoveAction(ChangeAction.AUTHOR, null));
350
351 // Existing authors
352 Map<String, List<String>> groupedAuthors;
353
354 try {
355 groupedAuthors = lib.getAuthorsGrouped();
356 } catch (IOException e) {
357 UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), "IOException", e);
358 groupedAuthors = new HashMap<String, List<String>>();
359
360 }
361
362 if (groupedAuthors.size() > 1) {
363 for (String key : groupedAuthors.keySet()) {
364 JMenu group = new JMenu(key);
365 for (String value : groupedAuthors.get(key)) {
366 JMenuItem item = new JMenuItem(value.isEmpty() ? trans(StringIdGui.MENU_AUTHORS_UNKNOWN) : value);
367 item.addActionListener(createMoveAction(ChangeAction.AUTHOR, value));
368 group.add(item);
369 }
370 changeTo.add(group);
371 }
372 } else if (groupedAuthors.size() == 1) {
373 for (String value : groupedAuthors.values().iterator().next()) {
374 JMenuItem item = new JMenuItem(value.isEmpty() ? trans(StringIdGui.MENU_AUTHORS_UNKNOWN) : value);
375 item.addActionListener(createMoveAction(ChangeAction.AUTHOR, value));
376 changeTo.add(item);
377 }
378 }
379
380 return changeTo;
381 }
382
383 /**
384 * Create the "rename" menu item.
385 *
386 * @return the item
387 */
388 private JMenuItem createMenuItemRename() {
389 JMenuItem changeTo = new JMenuItem(trans(StringIdGui.MENU_FILE_RENAME));
390 changeTo.setMnemonic(KeyEvent.VK_R);
391 changeTo.addActionListener(createMoveAction(ChangeAction.TITLE, null));
392 return changeTo;
393 }
394
395 private ActionListener createMoveAction(final ChangeAction what, final String type) {
396 return new ActionListener() {
397 @Override
398 public void actionPerformed(ActionEvent e) {
399 final List<BookInfo> selected = informer.getSelected();
400 if (!selected.isEmpty()) {
401 String changeTo = type;
402 if (type == null) {
403 String init = "";
404
405 if (selected.size() == 1) {
406 MetaData meta = selected.get(0).getMeta();
407 if (what == ChangeAction.SOURCE) {
408 init = meta.getSource();
409 } else if (what == ChangeAction.TITLE) {
410 init = meta.getTitle();
411 } else if (what == ChangeAction.AUTHOR) {
412 init = meta.getAuthor();
413 }
414 }
415
416 Object rep = JOptionPane.showInputDialog(BookPopup.this.getParent(),
417 trans(StringIdGui.SUBTITLE_MOVE_TO), trans(StringIdGui.TITLE_MOVE_TO),
418 JOptionPane.QUESTION_MESSAGE, null, null, init);
419
420 if (rep == null) {
421 return;
422 }
423
424 changeTo = rep.toString();
425 }
426
427 final String fChangeTo = changeTo;
428 new SwingWorker<Void, Void>() {
429 @Override
430 protected Void doInBackground() throws Exception {
431 for (BookInfo book : selected) {
432 String luid = book.getMeta().getLuid();
433 if (what == ChangeAction.SOURCE) {
434 lib.changeSource(luid, fChangeTo, null);
435 } else if (what == ChangeAction.TITLE) {
436 lib.changeTitle(luid, fChangeTo, null);
437 } else if (what == ChangeAction.AUTHOR) {
438 lib.changeAuthor(luid, fChangeTo, null);
439 }
440 }
441 // TODO: ^-- this can create new sources/authors, update maybe required?
442
443 return null;
444 }
445
446 @Override
447 protected void done() {
448 try {
449 // Reload anyway
450 for (BookInfo book : selected) {
451 informer.fireElementChanged(book);
452 }
453
454 get();
455 } catch (Exception e) {
456 UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), "IOException", e);
457 }
458 }
459 }.execute();
460 }
461 }
462 };
463 }
464
465 /**
466 * Create the re-download (then delete original) menu item.
467 *
468 * @return the item
469 */
470 private JMenuItem createMenuItemRedownload() {
471 JMenuItem refresh = new JMenuItem(trans(StringIdGui.MENU_EDIT_REDOWNLOAD), KeyEvent.VK_R);
472 refresh.addActionListener(new ActionListener() {
473 @Override
474 public void actionPerformed(ActionEvent e) {
475 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
476 // if (selectedBook != null) {
477 // final MetaData meta = selectedBook.getInfo().getMeta();
478 // mainPanel.imprt(meta.getUrl(), new MetaDataRunnable() {
479 // @Override
480 // public void run(MetaData newMeta) {
481 // if (!newMeta.getSource().equals(meta.getSource())) {
482 // reader.changeSource(newMeta.getLuid(), meta.getSource());
483 // }
484 // }
485 // }, trans(StringIdGui.PROGRESS_CHANGE_SOURCE));
486 // }
487 }
488 });
489
490 return refresh;
491 }
492
493 /**
494 * Create the download to cache menu item.
495 *
496 * @return the item
497 */
498 private JMenuItem createMenuItemDownloadToCache() {
499 JMenuItem refresh = new JMenuItem(trans(StringIdGui.MENU_EDIT_DOWNLOAD_TO_CACHE), KeyEvent.VK_T);
500 refresh.addActionListener(new ActionListener() {
501 @Override
502 public void actionPerformed(ActionEvent e) {
503 final List<BookInfo> selected = informer.getSelected();
504
505 new SwingWorker<Void, Void>() {
506 @Override
507 protected Void doInBackground() throws Exception {
508
509 final List<String> luids = new LinkedList<String>();
510 for (BookInfo book : selected) {
511 switch (book.getType()) {
512 case STORY:
513 luids.add(book.getMeta().getLuid());
514 break;
515 case SOURCE:
516 for (MetaData meta : lib.getListBySource(book.getMainInfo())) {
517 luids.add(meta.getLuid());
518 }
519 break;
520 case AUTHOR:
521 for (MetaData meta : lib.getListByAuthor(book.getMainInfo())) {
522 luids.add(meta.getLuid());
523 }
524 break;
525 case TAG:
526 for (MetaData meta : lib.getList(null).filter(null, null, book.getMainInfo())) {
527 luids.add(meta.getLuid());
528 }
529 break;
530 }
531 }
532
533 // TODO: do something with pg?
534 final Progress pg = new Progress();
535 pg.setMax(luids.size());
536 for (String luid : luids) {
537 Progress pgStep = new Progress();
538 pg.addProgress(pgStep, 1);
539
540 lib.getFile(luid, pgStep);
541 }
542
543 return null;
544 }
545
546 @Override
547 protected void done() {
548 try {
549 get();
550 for (BookInfo book : selected) {
551 informer.setCached(book, true);
552 }
553 } catch (Exception e) {
554 UiHelper.error(BookPopup.this.getParent(), e.getLocalizedMessage(), "IOException", e);
555 }
556 }
557 }.execute();
558 }
559 });
560
561 return refresh;
562 }
563
564 /**
565 * Create the delete menu item.
566 *
567 * @return the item
568 */
569 private JMenuItem createMenuItemDelete() {
570 JMenuItem delete = new JMenuItem(trans(StringIdGui.MENU_EDIT_DELETE), KeyEvent.VK_D);
571 delete.addActionListener(new ActionListener() {
572 @Override
573 public void actionPerformed(ActionEvent e) {
574 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
575 // if (selectedBook != null && selectedBook.getInfo().getMeta() != null) {
576 //
577 // final MetaData meta = selectedBook.getInfo().getMeta();
578 // int rep = JOptionPane.showConfirmDialog(GuiReaderFrame.this,
579 // trans(StringIdGui.SUBTITLE_DELETE, meta.getLuid(), meta.getTitle()),
580 // trans(StringIdGui.TITLE_DELETE), JOptionPane.OK_CANCEL_OPTION);
581 //
582 // if (rep == JOptionPane.OK_OPTION) {
583 // mainPanel.outOfUi(null, true, new Runnable() {
584 // @Override
585 // public void run() {
586 // reader.delete(meta.getLuid());
587 // mainPanel.unsetSelectedBook();
588 // }
589 // });
590 // }
591 // }
592 }
593 });
594
595 return delete;
596 }
597
598 /**
599 * Create the properties menu item.
600 *
601 * @return the item
602 */
603 private JMenuItem createMenuItemProperties() {
604 JMenuItem delete = new JMenuItem(trans(StringIdGui.MENU_FILE_PROPERTIES), KeyEvent.VK_P);
605 delete.addActionListener(new ActionListener() {
606 @Override
607 public void actionPerformed(ActionEvent e) {
608 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
609 // if (selectedBook != null) {
610 // mainPanel.outOfUi(null, false, new Runnable() {
611 // @Override
612 // public void run() {
613 // new GuiReaderPropertiesFrame(lib, selectedBook.getInfo().getMeta())
614 // .setVisible(true);
615 // }
616 // });
617 // }
618 }
619 });
620
621 return delete;
622 }
623
624 /**
625 * Create the open menu item for a book, a source/type or an author.
626 *
627 * @return the item
628 */
629 public JMenuItem createMenuItemOpenBook() {
630 JMenuItem open = new JMenuItem(trans(StringIdGui.MENU_FILE_OPEN), KeyEvent.VK_O);
631 open.addActionListener(new ActionListener() {
632 @Override
633 public void actionPerformed(ActionEvent e) {
634 final BookInfo book = informer.getUniqueSelected();
635 if (book != null) {
636 Actions.openExternal(lib, book.getMeta(), BookPopup.this.getParent(), new Runnable() {
637 @Override
638 public void run() {
639 informer.setCached(book, true);
640 }
641 });
642 }
643 }
644 });
645
646 return open;
647 }
648
649 /**
650 * Create the SetCover menu item for a book to change the linked source cover.
651 *
652 * @return the item
653 */
654 private JMenuItem createMenuItemSetCoverForSource() {
655 JMenuItem open = new JMenuItem(trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_SOURCE), KeyEvent.VK_C);
656 open.addActionListener(new ActionListener() {
657 @Override
658 public void actionPerformed(ActionEvent ae) {
659 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
660 // if (selectedBook != null) {
661 // BasicLibrary lib = lib;
662 // String luid = selectedBook.getInfo().getMeta().getLuid();
663 // String source = selectedBook.getInfo().getMeta().getSource();
664 //
665 // try {
666 // lib.setSourceCover(source, luid);
667 // } catch (IOException e) {
668 // error(e.getLocalizedMessage(), "IOException", e);
669 // }
670 //
671 // GuiReaderBookInfo sourceInfo = GuiReaderBookInfo.fromSource(lib, source);
672 // GuiReaderCoverImager.clearIcon(sourceInfo);
673 // }
674 }
675 });
676
677 return open;
678 }
679
680 /**
681 * Create the SetCover menu item for a book to change the linked source cover.
682 *
683 * @return the item
684 */
685 private JMenuItem createMenuItemSetCoverForAuthor() {
686 JMenuItem open = new JMenuItem(trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_AUTHOR), KeyEvent.VK_A);
687 open.addActionListener(new ActionListener() {
688 @Override
689 public void actionPerformed(ActionEvent ae) {
690 // final GuiReaderBook selectedBook = mainPanel.getSelectedBook();
691 // if (selectedBook != null) {
692 // String luid = selectedBook.getInfo().getMeta().getLuid();
693 // String author = selectedBook.getInfo().getMeta().getAuthor();
694 //
695 // try {
696 // lib.setAuthorCover(author, luid);
697 // } catch (IOException e) {
698 // error(e.getLocalizedMessage(), "IOException", e);
699 // }
700 //
701 // GuiReaderBookInfo authorInfo = GuiReaderBookInfo.fromAuthor(lib, author);
702 // GuiReaderCoverImager.clearIcon(authorInfo);
703 // }
704 }
705 });
706
707 return open;
708 }
709 }