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