Fix cover not deleted, add new UI option "Move to"
[fanfix.git] / src / be / nikiroo / fanfix / reader / LocalReaderFrame.java
CommitLineData
a6395bef
NR
1package be.nikiroo.fanfix.reader;
2
d3c84ac3 3import java.awt.BorderLayout;
b4dc6ab5 4import java.awt.Color;
4d205683 5import java.awt.Frame;
5130ce84
NR
6import java.awt.Toolkit;
7import java.awt.datatransfer.DataFlavor;
a6395bef
NR
8import java.awt.event.ActionEvent;
9import java.awt.event.ActionListener;
333f0e7b 10import java.awt.event.KeyEvent;
9843a5e5 11import java.awt.event.MouseEvent;
333f0e7b
NR
12import java.awt.event.WindowEvent;
13import java.io.File;
a6395bef 14import java.io.IOException;
4d205683 15import java.net.URL;
70c9b112 16import java.util.ArrayList;
4d205683 17import java.util.HashMap;
a6395bef 18import java.util.List;
4d205683
NR
19import java.util.Map;
20import java.util.Map.Entry;
a6395bef 21
4310bae9 22import javax.swing.BoxLayout;
10d558d2 23import javax.swing.JFileChooser;
a6395bef 24import javax.swing.JFrame;
333f0e7b
NR
25import javax.swing.JMenu;
26import javax.swing.JMenuBar;
27import javax.swing.JMenuItem;
28import javax.swing.JOptionPane;
29import javax.swing.JPanel;
9843a5e5 30import javax.swing.JPopupMenu;
d3c84ac3 31import javax.swing.JScrollPane;
3b2b638f 32import javax.swing.SwingUtilities;
4d205683
NR
33import javax.swing.filechooser.FileFilter;
34import javax.swing.filechooser.FileNameExtensionFilter;
a6395bef
NR
35
36import be.nikiroo.fanfix.Instance;
4d205683 37import be.nikiroo.fanfix.Library;
e8eeea0a 38import be.nikiroo.fanfix.bundles.Config;
b4dc6ab5 39import be.nikiroo.fanfix.bundles.UiConfig;
a6395bef 40import be.nikiroo.fanfix.data.MetaData;
4d205683
NR
41import be.nikiroo.fanfix.data.Story;
42import be.nikiroo.fanfix.output.BasicOutput.OutputType;
92fb0719 43import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListener;
3b2b638f 44import be.nikiroo.utils.Progress;
39c3c689 45import be.nikiroo.utils.Version;
e8eeea0a 46import be.nikiroo.utils.ui.ConfigEditor;
3b2b638f 47import be.nikiroo.utils.ui.ProgressBar;
a6395bef 48
4d205683
NR
49/**
50 * A {@link Frame} that will show a {@link LocalReaderBook} item for each
51 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
52 * way to copy them to the {@link LocalReader} cache ({@link LocalReader#lib}),
53 * read them, delete them...
54 *
55 * @author niki
56 */
a6395bef
NR
57class LocalReaderFrame extends JFrame {
58 private static final long serialVersionUID = 1L;
59 private LocalReader reader;
4310bae9
NR
60 private Map<LocalReaderGroup, String> booksByType;
61 private Map<LocalReaderGroup, String> booksByAuthor;
62 private JPanel pane;
b4dc6ab5 63 private Color color;
3b2b638f
NR
64 private ProgressBar pgBar;
65 private JMenuBar bar;
10d558d2 66 private LocalReaderBook selectedBook;
793f1071 67 private boolean words; // words or authors (secondary info on books)
a6395bef 68
4d205683
NR
69 /**
70 * Create a new {@link LocalReaderFrame}.
71 *
72 * @param reader
73 * the associated {@link LocalReader} to forward some commands
74 * and access its {@link Library}
75 * @param type
76 * the type of {@link Story} to load, or NULL for all types
77 */
333f0e7b 78 public LocalReaderFrame(LocalReader reader, String type) {
39c3c689 79 super(String.format("Fanfix %s Library", Version.getCurrentVersion()));
a6395bef
NR
80
81 this.reader = reader;
82
83 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
84 setSize(800, 600);
d3c84ac3 85 setLayout(new BorderLayout());
a6395bef 86
4310bae9
NR
87 pane = new JPanel();
88 pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
b4dc6ab5 89
3b2b638f 90 color = Instance.getUiConfig().getColor(UiConfig.BACKGROUND_COLOR);
b4dc6ab5
NR
91 if (color != null) {
92 setBackground(color);
4310bae9 93 pane.setBackground(color);
b4dc6ab5 94 }
d3c84ac3 95
4310bae9 96 JScrollPane scroll = new JScrollPane(pane);
b4dc6ab5
NR
97 scroll.getVerticalScrollBar().setUnitIncrement(16);
98 add(scroll, BorderLayout.CENTER);
a6395bef 99
3b2b638f
NR
100 pgBar = new ProgressBar();
101 add(pgBar, BorderLayout.SOUTH);
102
754a5bc2
NR
103 pgBar.addActionListener(new ActionListener() {
104 public void actionPerformed(ActionEvent e) {
105 invalidate();
106 pgBar.setProgress(null);
107 validate();
108 setEnabled(true);
109 }
110 });
111
112 pgBar.addUpdateListener(new ActionListener() {
113 public void actionPerformed(ActionEvent e) {
80791ae8
NR
114 invalidate();
115 validate();
754a5bc2
NR
116 repaint();
117 }
118 });
119
4310bae9
NR
120 booksByType = new HashMap<LocalReaderGroup, String>();
121 booksByAuthor = new HashMap<LocalReaderGroup, String>();
122
4f661b2b
NR
123 pane.setVisible(false);
124 final Progress pg = new Progress();
125 final String typeF = type;
126 outOfUi(pg, new Runnable() {
127 public void run() {
128 Instance.getLibrary().refresh(pg);
129 invalidate();
130 setJMenuBar(createMenu());
131 addBookPane(typeF, true);
132 refreshBooks();
133 validate();
134 pane.setVisible(true);
135 }
136 });
4310bae9 137
333f0e7b
NR
138 setVisible(true);
139 }
140
4d205683 141 /**
4310bae9
NR
142 * Add a new {@link LocalReaderGroup} on the frame to display the books of
143 * the selected type or author.
4d205683 144 *
4310bae9 145 * @param value
70c9b112
NR
146 * the author or the type, or NULL to get all the
147 * authors-or-types
4d205683 148 * @param type
4310bae9 149 * TRUE for type, FALSE for author
4d205683 150 */
4310bae9
NR
151 private void addBookPane(String value, boolean type) {
152 if (value == null) {
153 if (type) {
154 for (String tt : Instance.getLibrary().getTypes()) {
155 if (tt != null) {
156 addBookPane(tt, type);
157 }
158 }
159 } else {
160 for (String tt : Instance.getLibrary().getAuthors()) {
161 if (tt != null) {
162 addBookPane(tt, type);
163 }
164 }
b4dc6ab5
NR
165 }
166
4310bae9
NR
167 return;
168 }
4d205683 169
4310bae9
NR
170 LocalReaderGroup bookPane = new LocalReaderGroup(reader, value, color);
171 if (type) {
172 booksByType.put(bookPane, value);
173 } else {
174 booksByAuthor.put(bookPane, value);
175 }
333f0e7b 176
4310bae9
NR
177 this.invalidate();
178 pane.invalidate();
179 pane.add(bookPane);
180 pane.validate();
181 this.validate();
9843a5e5 182
4310bae9
NR
183 bookPane.setActionListener(new BookActionListener() {
184 public void select(LocalReaderBook book) {
185 selectedBook = book;
186 }
187
188 public void popupRequested(LocalReaderBook book, MouseEvent e) {
189 JPopupMenu popup = new JPopupMenu();
190 popup.add(createMenuItemOpenBook());
191 popup.addSeparator();
192 popup.add(createMenuItemExport());
70c9b112 193 popup.add(createMenuItemMove());
4310bae9
NR
194 popup.add(createMenuItemClearCache());
195 popup.add(createMenuItemRedownload());
196 popup.addSeparator();
197 popup.add(createMenuItemDelete());
198 popup.show(e.getComponent(), e.getX(), e.getY());
199 }
200
201 public void action(final LocalReaderBook book) {
202 openBook(book);
203 }
204 });
205 }
a6395bef 206
4310bae9
NR
207 private void removeBookPanes() {
208 booksByType.clear();
209 booksByAuthor.clear();
210 pane.invalidate();
211 this.invalidate();
212 pane.removeAll();
213 pane.validate();
214 this.validate();
215 }
216
217 /**
218 * Refresh the list of {@link LocalReaderBook}s from disk.
219 *
4310bae9
NR
220 */
221 private void refreshBooks() {
222 for (LocalReaderGroup group : booksByType.keySet()) {
223 List<MetaData> stories = Instance.getLibrary().getListByType(
224 booksByType.get(group));
793f1071 225 group.refreshBooks(stories, words);
4310bae9
NR
226 }
227
228 for (LocalReaderGroup group : booksByAuthor.keySet()) {
229 List<MetaData> stories = Instance.getLibrary().getListByAuthor(
230 booksByAuthor.get(group));
793f1071 231 group.refreshBooks(stories, words);
a6395bef
NR
232 }
233
4310bae9
NR
234 pane.repaint();
235 this.repaint();
333f0e7b
NR
236 }
237
4d205683
NR
238 /**
239 * Create the main menu bar.
240 *
241 * @return the bar
242 */
333f0e7b 243 private JMenuBar createMenu() {
3b2b638f 244 bar = new JMenuBar();
333f0e7b
NR
245
246 JMenu file = new JMenu("File");
10d558d2 247 file.setMnemonic(KeyEvent.VK_F);
333f0e7b 248
22848428 249 JMenuItem imprt = new JMenuItem("Import URL...", KeyEvent.VK_U);
333f0e7b
NR
250 imprt.addActionListener(new ActionListener() {
251 public void actionPerformed(ActionEvent e) {
10d558d2
NR
252 imprt(true);
253 }
254 });
22848428 255 JMenuItem imprtF = new JMenuItem("Import File...", KeyEvent.VK_F);
10d558d2
NR
256 imprtF.addActionListener(new ActionListener() {
257 public void actionPerformed(ActionEvent e) {
258 imprt(false);
259 }
260 });
261 JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X);
262 exit.addActionListener(new ActionListener() {
263 public void actionPerformed(ActionEvent e) {
264 LocalReaderFrame.this.dispatchEvent(new WindowEvent(
265 LocalReaderFrame.this, WindowEvent.WINDOW_CLOSING));
266 }
267 });
3b2b638f 268
9843a5e5
NR
269 file.add(createMenuItemOpenBook());
270 file.add(createMenuItemExport());
70c9b112 271 file.add(createMenuItemMove());
9843a5e5 272 file.addSeparator();
10d558d2
NR
273 file.add(imprt);
274 file.add(imprtF);
275 file.addSeparator();
276 file.add(exit);
3b2b638f 277
10d558d2
NR
278 bar.add(file);
279
280 JMenu edit = new JMenu("Edit");
281 edit.setMnemonic(KeyEvent.VK_E);
282
22848428
NR
283 edit.add(createMenuItemClearCache());
284 edit.add(createMenuItemRedownload());
9843a5e5
NR
285 edit.addSeparator();
286 edit.add(createMenuItemDelete());
287
288 bar.add(edit);
289
793f1071
NR
290 JMenu view = new JMenu("View");
291 view.setMnemonic(KeyEvent.VK_V);
292 JMenuItem vauthors = new JMenuItem("Author");
293 vauthors.setMnemonic(KeyEvent.VK_A);
294 vauthors.addActionListener(new ActionListener() {
295 public void actionPerformed(ActionEvent e) {
296 words = false;
297 refreshBooks();
298 }
299 });
300 view.add(vauthors);
301 JMenuItem vwords = new JMenuItem("Word count");
302 vwords.setMnemonic(KeyEvent.VK_W);
303 vwords.addActionListener(new ActionListener() {
304 public void actionPerformed(ActionEvent e) {
305 words = true;
306 refreshBooks();
307 }
308 });
309 view.add(vwords);
310 bar.add(view);
311
4310bae9
NR
312 JMenu sources = new JMenu("Sources");
313 sources.setMnemonic(KeyEvent.VK_S);
9843a5e5
NR
314
315 List<String> tt = Instance.getLibrary().getTypes();
316 tt.add(0, null);
317 for (final String type : tt) {
4310bae9 318 JMenuItem item = new JMenuItem(type == null ? "All" : type);
9843a5e5
NR
319 item.addActionListener(new ActionListener() {
320 public void actionPerformed(ActionEvent e) {
4310bae9
NR
321 removeBookPanes();
322 addBookPane(type, true);
323 refreshBooks();
9843a5e5
NR
324 }
325 });
4310bae9 326 sources.add(item);
9843a5e5
NR
327
328 if (type == null) {
4310bae9 329 sources.addSeparator();
9843a5e5
NR
330 }
331 }
10d558d2 332
4310bae9
NR
333 bar.add(sources);
334
335 JMenu authors = new JMenu("Authors");
336 authors.setMnemonic(KeyEvent.VK_A);
337
338 List<String> aa = Instance.getLibrary().getAuthors();
339 aa.add(0, null);
340 for (final String author : aa) {
341 JMenuItem item = new JMenuItem(author == null ? "All"
342 : author.isEmpty() ? "[unknown]" : author);
343 item.addActionListener(new ActionListener() {
344 public void actionPerformed(ActionEvent e) {
345 removeBookPanes();
346 addBookPane(author, false);
347 refreshBooks();
348 }
349 });
350 authors.add(item);
351
352 if (author == null || author.isEmpty()) {
353 authors.addSeparator();
354 }
355 }
356
357 bar.add(authors);
9843a5e5 358
e8eeea0a
NR
359 JMenu options = new JMenu("Options");
360 options.setMnemonic(KeyEvent.VK_O);
361 options.add(createMenuItemConfig());
362 options.add(createMenuItemUiConfig());
363 bar.add(options);
364
9843a5e5
NR
365 return bar;
366 }
367
e8eeea0a
NR
368 /**
369 * Create the Fanfix Configuration menu item.
370 *
371 * @return the item
372 */
373 private JMenuItem createMenuItemConfig() {
374 final String title = "Fanfix Configuration";
375 JMenuItem item = new JMenuItem(title);
376 item.setMnemonic(KeyEvent.VK_F);
377
378 item.addActionListener(new ActionListener() {
379 public void actionPerformed(ActionEvent e) {
380 ConfigEditor<Config> ed = new ConfigEditor<Config>(
381 Config.class, Instance.getConfig(),
382 "This is where you configure the options of the program.");
383 JFrame frame = new JFrame(title);
384 frame.add(ed);
385 frame.setSize(800, 600);
386 frame.setVisible(true);
387 }
388 });
389
390 return item;
391 }
392
393 /**
394 * Create the UI Configuration menu item.
395 *
396 * @return the item
397 */
398 private JMenuItem createMenuItemUiConfig() {
399 final String title = "UI Configuration";
400 JMenuItem item = new JMenuItem(title);
401 item.setMnemonic(KeyEvent.VK_U);
402
403 item.addActionListener(new ActionListener() {
404 public void actionPerformed(ActionEvent e) {
405 ConfigEditor<UiConfig> ed = new ConfigEditor<UiConfig>(
406 UiConfig.class, Instance.getUiConfig(),
407 "This is where you configure the graphical appearence of the program.");
408 JFrame frame = new JFrame(title);
409 frame.add(ed);
410 frame.setSize(800, 600);
411 frame.setVisible(true);
412 }
413 });
414
415 return item;
416 }
417
4d205683
NR
418 /**
419 * Create the export menu item.
420 *
421 * @return the item
422 */
9843a5e5 423 private JMenuItem createMenuItemExport() {
4d205683
NR
424 final JFileChooser fc = new JFileChooser();
425 fc.setAcceptAllFileFilterUsed(false);
426
427 final Map<FileFilter, OutputType> filters = new HashMap<FileFilter, OutputType>();
428 for (OutputType type : OutputType.values()) {
429 String ext = type.getDefaultExtension(false);
430 String desc = type.getDesc(false);
b2612f9d 431
4d205683
NR
432 if (ext == null || ext.isEmpty()) {
433 filters.put(createAllFilter(desc), type);
434 } else {
435 filters.put(new FileNameExtensionFilter(desc, ext), type);
436 }
437 }
438
439 // First the "ALL" filters, then, the extension filters
440 for (Entry<FileFilter, OutputType> entry : filters.entrySet()) {
441 if (!(entry.getKey() instanceof FileNameExtensionFilter)) {
442 fc.addChoosableFileFilter(entry.getKey());
443 }
444 }
445 for (Entry<FileFilter, OutputType> entry : filters.entrySet()) {
446 if (entry.getKey() instanceof FileNameExtensionFilter) {
447 fc.addChoosableFileFilter(entry.getKey());
448 }
449 }
450 //
9843a5e5 451
4d205683 452 JMenuItem export = new JMenuItem("Save as...", KeyEvent.VK_S);
10d558d2
NR
453 export.addActionListener(new ActionListener() {
454 public void actionPerformed(ActionEvent e) {
4d205683
NR
455 if (selectedBook != null) {
456 fc.showDialog(LocalReaderFrame.this, "Save");
b2612f9d
NR
457 if (fc.getSelectedFile() != null) {
458 final OutputType type = filters.get(fc.getFileFilter());
459 final String path = fc.getSelectedFile()
460 .getAbsolutePath()
461 + type.getDefaultExtension(false);
462 final Progress pg = new Progress();
463 outOfUi(pg, new Runnable() {
464 public void run() {
465 try {
466 Instance.getLibrary().export(
467 selectedBook.getMeta().getLuid(),
468 type, path, pg);
469 } catch (IOException e) {
470 Instance.syserr(e);
471 }
4d205683 472 }
b2612f9d
NR
473 });
474 }
4d205683 475 }
10d558d2
NR
476 }
477 });
478
9843a5e5
NR
479 return export;
480 }
481
edd46289
NR
482 /**
483 * Create a {@link FileFilter} that accepts all files and return the given
484 * description.
485 *
486 * @param desc
487 * the description
488 *
489 * @return the filter
490 */
4d205683
NR
491 private FileFilter createAllFilter(final String desc) {
492 return new FileFilter() {
493 @Override
494 public String getDescription() {
495 return desc;
496 }
497
498 @Override
499 public boolean accept(File f) {
500 return true;
501 }
502 };
503 }
504
505 /**
506 * Create the refresh (delete cache) menu item.
507 *
508 * @return the item
509 */
22848428 510 private JMenuItem createMenuItemClearCache() {
4d205683 511 JMenuItem refresh = new JMenuItem("Clear cache", KeyEvent.VK_C);
10d558d2
NR
512 refresh.addActionListener(new ActionListener() {
513 public void actionPerformed(ActionEvent e) {
514 if (selectedBook != null) {
515 outOfUi(null, new Runnable() {
516 public void run() {
754a5bc2
NR
517 reader.clearLocalReaderCache(selectedBook.getMeta()
518 .getLuid());
10d558d2 519 selectedBook.setCached(false);
3b2b638f
NR
520 SwingUtilities.invokeLater(new Runnable() {
521 public void run() {
10d558d2 522 selectedBook.repaint();
3b2b638f
NR
523 }
524 });
525 }
526 });
333f0e7b
NR
527 }
528 }
529 });
10d558d2 530
9843a5e5
NR
531 return refresh;
532 }
533
70c9b112
NR
534 /**
535 * Create the delete menu item.
536 *
537 * @return the item
538 */
539 private JMenuItem createMenuItemMove() {
540 JMenu moveTo = new JMenu("Move to...");
541 moveTo.setMnemonic(KeyEvent.VK_M);
542
543 List<String> types = new ArrayList<String>();
544 types.add(null);
545 types.addAll(Instance.getLibrary().getTypes());
546
547 for (String type : types) {
548 JMenuItem item = new JMenuItem(type == null ? "New type..." : type);
549
550 moveTo.add(item);
551 if (type == null) {
552 moveTo.addSeparator();
553 }
554
555 final String ftype = type;
556 item.addActionListener(new ActionListener() {
557 public void actionPerformed(ActionEvent e) {
558 if (selectedBook != null) {
559 String type = ftype;
560 if (type == null) {
561 Object rep = JOptionPane.showInputDialog(
562 LocalReaderFrame.this, "Move to:",
563 "Moving story",
564 JOptionPane.QUESTION_MESSAGE, null, null,
565 selectedBook.getMeta().getSource());
566 if (rep == null) {
567 return;
568 } else {
569 type = rep.toString();
570 }
571 }
572
573 final String ftype = type;
574 outOfUi(null, new Runnable() {
575 public void run() {
576 reader.changeType(selectedBook.getMeta()
577 .getLuid(), ftype);
578
579 selectedBook = null;
580
581 SwingUtilities.invokeLater(new Runnable() {
582 public void run() {
583 setJMenuBar(createMenu());
584 }
585 });
586 }
587 });
588 }
589 }
590 });
591 }
592
593 return moveTo;
594 }
595
22848428
NR
596 /**
597 * Create the redownload (then delete original) menu item.
598 *
599 * @return the item
600 */
601 private JMenuItem createMenuItemRedownload() {
602 JMenuItem refresh = new JMenuItem("Redownload", KeyEvent.VK_R);
603 refresh.addActionListener(new ActionListener() {
604 public void actionPerformed(ActionEvent e) {
605 if (selectedBook != null) {
754a5bc2
NR
606 final MetaData meta = selectedBook.getMeta();
607 imprt(meta.getUrl(), new Runnable() {
22848428 608 public void run() {
754a5bc2
NR
609 reader.delete(meta.getLuid());
610 LocalReaderFrame.this.selectedBook = null;
22848428 611 }
754a5bc2 612 }, "Removing old copy");
22848428
NR
613 }
614 }
615 });
616
617 return refresh;
618 }
619
4d205683
NR
620 /**
621 * Create the delete menu item.
622 *
623 * @return the item
624 */
9843a5e5 625 private JMenuItem createMenuItemDelete() {
10d558d2
NR
626 JMenuItem delete = new JMenuItem("Delete", KeyEvent.VK_D);
627 delete.addActionListener(new ActionListener() {
628 public void actionPerformed(ActionEvent e) {
629 if (selectedBook != null) {
630 outOfUi(null, new Runnable() {
631 public void run() {
22848428 632 reader.delete(selectedBook.getMeta().getLuid());
10d558d2 633 selectedBook = null;
10d558d2
NR
634 }
635 });
636 }
637 }
638 });
639
9843a5e5
NR
640 return delete;
641 }
10d558d2 642
4d205683
NR
643 /**
644 * Create the open menu item.
645 *
646 * @return the item
647 */
9843a5e5
NR
648 private JMenuItem createMenuItemOpenBook() {
649 JMenuItem open = new JMenuItem("Open", KeyEvent.VK_O);
650 open.addActionListener(new ActionListener() {
651 public void actionPerformed(ActionEvent e) {
652 if (selectedBook != null) {
653 openBook(selectedBook);
654 }
655 }
656 });
10d558d2 657
9843a5e5
NR
658 return open;
659 }
10d558d2 660
4d205683
NR
661 /**
662 * Open a {@link LocalReaderBook} item.
663 *
664 * @param book
665 * the {@link LocalReaderBook} to open
666 */
9843a5e5
NR
667 private void openBook(final LocalReaderBook book) {
668 final Progress pg = new Progress();
669 outOfUi(pg, new Runnable() {
670 public void run() {
671 try {
22848428 672 reader.open(book.getMeta().getLuid(), pg);
edd46289
NR
673 SwingUtilities.invokeLater(new Runnable() {
674 public void run() {
675 book.setCached(true);
9843a5e5 676 }
edd46289 677 });
9843a5e5 678 } catch (IOException e) {
edd46289 679 // TODO: error message?
9843a5e5 680 Instance.syserr(e);
333f0e7b 681 }
10d558d2 682 }
9843a5e5 683 });
a6395bef 684 }
3b2b638f 685
4d205683
NR
686 /**
687 * Process the given action out of the Swing UI thread and link the given
688 * {@link ProgressBar} to the action.
689 * <p>
690 * The code will make sure that the {@link ProgressBar} (if not NULL) is set
691 * to done when the action is done.
692 *
693 * @param pg
694 * the {@link ProgressBar} or NULL
695 * @param run
696 * the action to run
697 */
754a5bc2
NR
698 private void outOfUi(Progress progress, final Runnable run) {
699 final Progress pg = new Progress();
d5a7153c 700 final Progress reload = new Progress("Reload books");
754a5bc2
NR
701 if (progress == null) {
702 progress = new Progress();
703 }
3b2b638f 704
754a5bc2
NR
705 pg.addProgress(progress, 90);
706 pg.addProgress(reload, 10);
707
708 invalidate();
709 pgBar.setProgress(pg);
710 validate();
a4143cd7 711 setEnabled(false);
3b2b638f
NR
712
713 new Thread(new Runnable() {
714 public void run() {
715 run.run();
754a5bc2 716 refreshBooks();
5ce869b8 717 reload.done();
754a5bc2
NR
718 if (!pg.isDone()) {
719 // will trigger pgBar ActionListener:
5ce869b8 720 pg.done();
3b2b638f
NR
721 }
722 }
754a5bc2 723 }, "outOfUi thread").start();
3b2b638f
NR
724 }
725
4d205683
NR
726 /**
727 * Import a {@link Story} into the main {@link Library}.
22848428
NR
728 * <p>
729 * Should be called inside the UI thread.
4d205683
NR
730 *
731 * @param askUrl
732 * TRUE for an {@link URL}, false for a {@link File}
733 */
10d558d2
NR
734 private void imprt(boolean askUrl) {
735 JFileChooser fc = new JFileChooser();
736
5130ce84 737 Object url;
10d558d2 738 if (askUrl) {
5130ce84
NR
739 String clipboard = "";
740 try {
741 clipboard = ("" + Toolkit.getDefaultToolkit()
742 .getSystemClipboard().getData(DataFlavor.stringFlavor))
743 .trim();
744 } catch (Exception e) {
745 // No data will be handled
746 }
747
748 if (clipboard == null || !clipboard.startsWith("http")) {
749 clipboard = "";
750 }
751
10d558d2
NR
752 url = JOptionPane.showInputDialog(LocalReaderFrame.this,
753 "url of the story to import?", "Importing from URL",
5130ce84 754 JOptionPane.QUESTION_MESSAGE, null, null, clipboard);
10d558d2
NR
755 } else if (fc.showOpenDialog(this) != JFileChooser.CANCEL_OPTION) {
756 url = fc.getSelectedFile().getAbsolutePath();
757 } else {
758 url = null;
759 }
760
5130ce84 761 if (url != null && !url.toString().isEmpty()) {
754a5bc2 762 imprt(url.toString(), null, null);
22848428
NR
763 }
764 }
10d558d2 765
22848428
NR
766 /**
767 * Actually import the {@link Story} into the main {@link Library}.
768 * <p>
769 * Should be called inside the UI thread.
770 *
771 * @param url
772 * the {@link Story} to import by {@link URL}
773 * @param onSuccess
774 * Action to execute on success
775 */
754a5bc2
NR
776 private void imprt(final String url, final Runnable onSuccess,
777 String onSuccessPgName) {
778 final Progress pg = new Progress();
779 final Progress pgImprt = new Progress();
780 final Progress pgOnSuccess = new Progress(onSuccessPgName);
781 pg.addProgress(pgImprt, 95);
782 pg.addProgress(pgOnSuccess, 5);
783
22848428
NR
784 outOfUi(pg, new Runnable() {
785 public void run() {
786 Exception ex = null;
787 try {
754a5bc2
NR
788 Instance.getLibrary().imprt(BasicReader.getUrl(url),
789 pgImprt);
22848428
NR
790 } catch (IOException e) {
791 ex = e;
792 }
10d558d2 793
22848428
NR
794 final Exception e = ex;
795
796 final boolean ok = (e == null);
754a5bc2
NR
797
798 pgOnSuccess.setProgress(0);
799 if (!ok) {
800 Instance.syserr(e);
801 SwingUtilities.invokeLater(new Runnable() {
802 public void run() {
22848428
NR
803 JOptionPane.showMessageDialog(
804 LocalReaderFrame.this, "Cannot import: "
805 + url, e.getMessage(),
806 JOptionPane.ERROR_MESSAGE);
10d558d2 807 }
754a5bc2
NR
808 });
809 } else {
810 if (onSuccess != null) {
811 onSuccess.run();
22848428 812 }
754a5bc2 813 }
5ce869b8 814 pgOnSuccess.done();
22848428
NR
815 }
816 });
10d558d2
NR
817 }
818
4d205683
NR
819 /**
820 * Enables or disables this component, depending on the value of the
821 * parameter <code>b</code>. An enabled component can respond to user input
822 * and generate events. Components are enabled initially by default.
823 * <p>
824 * Disabling this component will also affect its children.
825 *
826 * @param b
827 * If <code>true</code>, this component is enabled; otherwise
828 * this component is disabled
829 */
830 @Override
831 public void setEnabled(boolean b) {
4f661b2b
NR
832 if (bar != null) {
833 bar.setEnabled(b);
834 }
835
4310bae9
NR
836 for (LocalReaderGroup group : booksByType.keySet()) {
837 group.setEnabled(b);
838 }
839 for (LocalReaderGroup group : booksByAuthor.keySet()) {
840 group.setEnabled(b);
841 }
4d205683 842 super.setEnabled(b);
3b2b638f
NR
843 repaint();
844 }
a6395bef 845}