1 package be
.nikiroo
.fanfix
.reader
.ui
;
3 import java
.awt
.BorderLayout
;
7 import java
.awt
.Toolkit
;
8 import java
.awt
.datatransfer
.DataFlavor
;
9 import java
.awt
.event
.ActionEvent
;
10 import java
.awt
.event
.ActionListener
;
11 import java
.awt
.event
.KeyEvent
;
12 import java
.awt
.event
.MouseEvent
;
13 import java
.awt
.event
.WindowEvent
;
15 import java
.io
.IOException
;
17 import java
.net
.UnknownHostException
;
18 import java
.util
.ArrayList
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
22 import java
.util
.Map
.Entry
;
24 import javax
.swing
.BorderFactory
;
25 import javax
.swing
.BoxLayout
;
26 import javax
.swing
.ImageIcon
;
27 import javax
.swing
.JFileChooser
;
28 import javax
.swing
.JFrame
;
29 import javax
.swing
.JLabel
;
30 import javax
.swing
.JMenu
;
31 import javax
.swing
.JMenuBar
;
32 import javax
.swing
.JMenuItem
;
33 import javax
.swing
.JOptionPane
;
34 import javax
.swing
.JPanel
;
35 import javax
.swing
.JPopupMenu
;
36 import javax
.swing
.JScrollPane
;
37 import javax
.swing
.JTextArea
;
38 import javax
.swing
.SwingConstants
;
39 import javax
.swing
.SwingUtilities
;
40 import javax
.swing
.filechooser
.FileFilter
;
41 import javax
.swing
.filechooser
.FileNameExtensionFilter
;
43 import be
.nikiroo
.fanfix
.Instance
;
44 import be
.nikiroo
.fanfix
.bundles
.Config
;
45 import be
.nikiroo
.fanfix
.bundles
.UiConfig
;
46 import be
.nikiroo
.fanfix
.data
.MetaData
;
47 import be
.nikiroo
.fanfix
.data
.Story
;
48 import be
.nikiroo
.fanfix
.library
.BasicLibrary
;
49 import be
.nikiroo
.fanfix
.library
.BasicLibrary
.Status
;
50 import be
.nikiroo
.fanfix
.library
.LocalLibrary
;
51 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
52 import be
.nikiroo
.fanfix
.reader
.BasicReader
;
53 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderBook
.BookActionListener
;
54 import be
.nikiroo
.utils
.Progress
;
55 import be
.nikiroo
.utils
.Version
;
56 import be
.nikiroo
.utils
.ui
.ConfigEditor
;
57 import be
.nikiroo
.utils
.ui
.ProgressBar
;
60 * A {@link Frame} that will show a {@link GuiReaderBook} item for each
61 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
62 * way to copy them to the {@link GuiReader} cache (
63 * {@link BasicReader#getLibrary()}), read them, delete them...
67 class GuiReaderFrame
extends JFrame
{
68 private static final long serialVersionUID
= 1L;
69 private GuiReader reader
;
70 private Map
<GuiReaderGroup
, String
> booksByType
;
71 private Map
<GuiReaderGroup
, String
> booksByAuthor
;
74 private ProgressBar pgBar
;
76 private GuiReaderBook selectedBook
;
77 private boolean words
; // words or authors (secondary info on books)
80 * A {@link Runnable} with a {@link Story} parameter.
84 private interface StoryRunnable
{
91 public void run(Story story
);
95 * Create a new {@link GuiReaderFrame}.
98 * the associated {@link GuiReader} to forward some commands and
99 * access its {@link LocalLibrary}
101 * the type of {@link Story} to load, or NULL for all types
103 public GuiReaderFrame(GuiReader reader
, String type
) {
104 super(String
.format("Fanfix %s Library", Version
.getCurrentVersion()));
106 this.reader
= reader
;
108 setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
110 setLayout(new BorderLayout());
113 pane
.setLayout(new BoxLayout(pane
, BoxLayout
.PAGE_AXIS
));
115 Integer icolor
= Instance
.getUiConfig().getColor(
116 UiConfig
.BACKGROUND_COLOR
);
117 if (icolor
!= null) {
118 color
= new Color(icolor
);
119 setBackground(color
);
120 pane
.setBackground(color
);
123 JScrollPane scroll
= new JScrollPane(pane
);
124 scroll
.getVerticalScrollBar().setUnitIncrement(16);
125 add(scroll
, BorderLayout
.CENTER
);
127 String message
= reader
.getLibrary().getLibraryName();
128 if (!message
.isEmpty()) {
129 JLabel name
= new JLabel(message
, SwingConstants
.CENTER
);
130 add(name
, BorderLayout
.NORTH
);
133 pgBar
= new ProgressBar();
134 add(pgBar
, BorderLayout
.SOUTH
);
136 pgBar
.addActionListener(new ActionListener() {
138 public void actionPerformed(ActionEvent e
) {
140 pgBar
.setProgress(null);
146 pgBar
.addUpdateListener(new ActionListener() {
148 public void actionPerformed(ActionEvent e
) {
155 booksByType
= new HashMap
<GuiReaderGroup
, String
>();
156 booksByAuthor
= new HashMap
<GuiReaderGroup
, String
>();
158 pane
.setVisible(false);
159 final Progress pg
= new Progress();
160 final String typeF
= type
;
161 outOfUi(pg
, new Runnable() {
164 BasicLibrary lib
= GuiReaderFrame
.this.reader
.getLibrary();
165 Status status
= lib
.getStatus();
167 if (status
== Status
.READY
) {
170 setJMenuBar(createMenu(true));
171 addBookPane(typeF
, true);
174 pane
.setVisible(true);
177 setJMenuBar(createMenu(false));
180 String err
= lib
.getLibraryName() + "\n";
183 err
+= "Library not valid";
187 err
+= "You are not allowed to access this library";
191 err
+= "Library currently unavailable";
195 err
+= "An error occured when contacting the library";
199 error(err
, "Library error", null);
207 private void addSourcePanes() {
209 GuiReaderGroup bookPane
= new GuiReaderGroup(reader
, "Sources", color
);
211 List
<MetaData
> sources
= new ArrayList
<MetaData
>();
212 for (String source
: reader
.getLibrary().getSources()) {
213 MetaData mSource
= new MetaData();
214 mSource
.setLuid(null);
215 mSource
.setTitle(source
);
216 mSource
.setSource(source
);
217 sources
.add(mSource
);
220 bookPane
.refreshBooks(sources
, false);
228 bookPane
.setActionListener(new BookActionListener() {
230 public void select(GuiReaderBook book
) {
235 public void popupRequested(GuiReaderBook book
, MouseEvent e
) {
236 JPopupMenu popup
= new JPopupMenu();
237 popup
.add(createMenuItemOpenBook());
238 popup
.show(e
.getComponent(), e
.getX(), e
.getY());
242 public void action(final GuiReaderBook book
) {
244 addBookPane(book
.getMeta().getSource(), true);
251 * Add a new {@link GuiReaderGroup} on the frame to display the books of the
252 * selected type or author.
255 * the author or the type, or NULL to get all the
258 * TRUE for type, FALSE for author
260 private void addBookPane(String value
, boolean type
) {
263 if (Instance
.getUiConfig().getBoolean(UiConfig
.SOURCE_PAGE
,
267 for (String tt
: reader
.getLibrary().getSources()) {
269 addBookPane(tt
, type
);
274 for (String tt
: reader
.getLibrary().getAuthors()) {
276 addBookPane(tt
, type
);
284 GuiReaderGroup bookPane
= new GuiReaderGroup(reader
, value
, color
);
286 booksByType
.put(bookPane
, value
);
288 booksByAuthor
.put(bookPane
, value
);
297 bookPane
.setActionListener(new BookActionListener() {
299 public void select(GuiReaderBook book
) {
304 public void popupRequested(GuiReaderBook book
, MouseEvent e
) {
305 JPopupMenu popup
= new JPopupMenu();
306 popup
.add(createMenuItemOpenBook());
307 popup
.addSeparator();
308 popup
.add(createMenuItemExport());
309 popup
.add(createMenuItemMove(true));
310 popup
.add(createMenuItemSetCover());
311 popup
.add(createMenuItemClearCache());
312 popup
.add(createMenuItemRedownload());
313 popup
.addSeparator();
314 popup
.add(createMenuItemDelete());
315 popup
.addSeparator();
316 popup
.add(createMenuItemProperties());
317 popup
.show(e
.getComponent(), e
.getX(), e
.getY());
321 public void action(final GuiReaderBook book
) {
327 private void removeBookPanes() {
329 booksByAuthor
.clear();
338 * Refresh the list of {@link GuiReaderBook}s from disk.
340 private void refreshBooks() {
341 for (GuiReaderGroup group
: booksByType
.keySet()) {
342 List
<MetaData
> stories
= reader
.getLibrary().getListBySource(
343 booksByType
.get(group
));
344 group
.refreshBooks(stories
, words
);
347 for (GuiReaderGroup group
: booksByAuthor
.keySet()) {
348 List
<MetaData
> stories
= reader
.getLibrary().getListByAuthor(
349 booksByAuthor
.get(group
));
350 group
.refreshBooks(stories
, words
);
358 * Create the main menu bar.
361 * the library can be queried
365 private JMenuBar
createMenu(boolean libOk
) {
366 bar
= new JMenuBar();
368 JMenu file
= new JMenu("File");
369 file
.setMnemonic(KeyEvent
.VK_F
);
371 JMenuItem imprt
= new JMenuItem("Import URL...", KeyEvent
.VK_U
);
372 imprt
.addActionListener(new ActionListener() {
374 public void actionPerformed(ActionEvent e
) {
378 JMenuItem imprtF
= new JMenuItem("Import File...", KeyEvent
.VK_F
);
379 imprtF
.addActionListener(new ActionListener() {
381 public void actionPerformed(ActionEvent e
) {
385 JMenuItem exit
= new JMenuItem("Exit", KeyEvent
.VK_X
);
386 exit
.addActionListener(new ActionListener() {
388 public void actionPerformed(ActionEvent e
) {
389 GuiReaderFrame
.this.dispatchEvent(new WindowEvent(
390 GuiReaderFrame
.this, WindowEvent
.WINDOW_CLOSING
));
394 file
.add(createMenuItemOpenBook());
395 file
.add(createMenuItemExport());
396 file
.add(createMenuItemMove(libOk
));
405 JMenu edit
= new JMenu("Edit");
406 edit
.setMnemonic(KeyEvent
.VK_E
);
408 edit
.add(createMenuItemClearCache());
409 edit
.add(createMenuItemRedownload());
411 edit
.add(createMenuItemDelete());
415 JMenu view
= new JMenu("View");
416 view
.setMnemonic(KeyEvent
.VK_V
);
417 JMenuItem vauthors
= new JMenuItem("Author");
418 vauthors
.setMnemonic(KeyEvent
.VK_A
);
419 vauthors
.addActionListener(new ActionListener() {
421 public void actionPerformed(ActionEvent e
) {
427 JMenuItem vwords
= new JMenuItem("Word count");
428 vwords
.setMnemonic(KeyEvent
.VK_W
);
429 vwords
.addActionListener(new ActionListener() {
431 public void actionPerformed(ActionEvent e
) {
439 JMenu sources
= new JMenu("Sources");
440 sources
.setMnemonic(KeyEvent
.VK_S
);
442 List
<String
> tt
= new ArrayList
<String
>();
444 tt
.addAll(reader
.getLibrary().getSources());
448 for (final String type
: tt
) {
449 JMenuItem item
= new JMenuItem(type
== null ?
"All" : type
);
450 item
.addActionListener(new ActionListener() {
452 public void actionPerformed(ActionEvent e
) {
454 addBookPane(type
, true);
461 sources
.addSeparator();
467 JMenu authors
= new JMenu("Authors");
468 authors
.setMnemonic(KeyEvent
.VK_A
);
470 List
<String
> aa
= new ArrayList
<String
>();
472 aa
.addAll(reader
.getLibrary().getAuthors());
475 for (final String author
: aa
) {
476 JMenuItem item
= new JMenuItem(author
== null ?
"All"
477 : author
.isEmpty() ?
"[unknown]" : author
);
478 item
.addActionListener(new ActionListener() {
480 public void actionPerformed(ActionEvent e
) {
482 addBookPane(author
, false);
488 if (author
== null || author
.isEmpty()) {
489 authors
.addSeparator();
495 JMenu options
= new JMenu("Options");
496 options
.setMnemonic(KeyEvent
.VK_O
);
497 options
.add(createMenuItemConfig());
498 options
.add(createMenuItemUiConfig());
505 * Create the Fanfix Configuration menu item.
509 private JMenuItem
createMenuItemConfig() {
510 final String title
= "Fanfix Configuration";
511 JMenuItem item
= new JMenuItem(title
);
512 item
.setMnemonic(KeyEvent
.VK_F
);
514 item
.addActionListener(new ActionListener() {
516 public void actionPerformed(ActionEvent e
) {
517 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(
518 Config
.class, Instance
.getConfig(),
519 "This is where you configure the options of the program.");
520 JFrame frame
= new JFrame(title
);
522 frame
.setSize(800, 600);
523 frame
.setVisible(true);
531 * Create the UI Configuration menu item.
535 private JMenuItem
createMenuItemUiConfig() {
536 final String title
= "UI Configuration";
537 JMenuItem item
= new JMenuItem(title
);
538 item
.setMnemonic(KeyEvent
.VK_U
);
540 item
.addActionListener(new ActionListener() {
542 public void actionPerformed(ActionEvent e
) {
543 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(
544 UiConfig
.class, Instance
.getUiConfig(),
545 "This is where you configure the graphical appearence of the program.");
546 JFrame frame
= new JFrame(title
);
548 frame
.setSize(800, 600);
549 frame
.setVisible(true);
557 * Create the export menu item.
561 private JMenuItem
createMenuItemExport() {
562 final JFileChooser fc
= new JFileChooser();
563 fc
.setAcceptAllFileFilterUsed(false);
565 final Map
<FileFilter
, OutputType
> filters
= new HashMap
<FileFilter
, OutputType
>();
566 for (OutputType type
: OutputType
.values()) {
567 String ext
= type
.getDefaultExtension(false);
568 String desc
= type
.getDesc(false);
570 if (ext
== null || ext
.isEmpty()) {
571 filters
.put(createAllFilter(desc
), type
);
573 filters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
577 // First the "ALL" filters, then, the extension filters
578 for (Entry
<FileFilter
, OutputType
> entry
: filters
.entrySet()) {
579 if (!(entry
.getKey() instanceof FileNameExtensionFilter
)) {
580 fc
.addChoosableFileFilter(entry
.getKey());
583 for (Entry
<FileFilter
, OutputType
> entry
: filters
.entrySet()) {
584 if (entry
.getKey() instanceof FileNameExtensionFilter
) {
585 fc
.addChoosableFileFilter(entry
.getKey());
590 JMenuItem export
= new JMenuItem("Save as...", KeyEvent
.VK_S
);
591 export
.addActionListener(new ActionListener() {
593 public void actionPerformed(ActionEvent e
) {
594 if (selectedBook
!= null) {
595 fc
.showDialog(GuiReaderFrame
.this, "Save");
596 if (fc
.getSelectedFile() != null) {
597 final OutputType type
= filters
.get(fc
.getFileFilter());
598 final String path
= fc
.getSelectedFile()
600 + type
.getDefaultExtension(false);
601 final Progress pg
= new Progress();
602 outOfUi(pg
, new Runnable() {
606 reader
.getLibrary().export(
607 selectedBook
.getMeta().getLuid(),
609 } catch (IOException e
) {
610 Instance
.getTraceHandler().error(e
);
623 * Create a {@link FileFilter} that accepts all files and return the given
631 private FileFilter
createAllFilter(final String desc
) {
632 return new FileFilter() {
634 public String
getDescription() {
639 public boolean accept(File f
) {
646 * Create the refresh (delete cache) menu item.
650 private JMenuItem
createMenuItemClearCache() {
651 JMenuItem refresh
= new JMenuItem("Clear cache", KeyEvent
.VK_C
);
652 refresh
.addActionListener(new ActionListener() {
654 public void actionPerformed(ActionEvent e
) {
655 if (selectedBook
!= null) {
656 outOfUi(null, new Runnable() {
659 reader
.clearLocalReaderCache(selectedBook
.getMeta()
661 selectedBook
.setCached(false);
662 GuiReaderCoverImager
.clearIcon(selectedBook
664 SwingUtilities
.invokeLater(new Runnable() {
667 selectedBook
.repaint();
680 * Create the delete menu item.
683 * the library can be queried
687 private JMenuItem
createMenuItemMove(boolean libOk
) {
688 JMenu moveTo
= new JMenu("Move to...");
689 moveTo
.setMnemonic(KeyEvent
.VK_M
);
691 List
<String
> types
= new ArrayList
<String
>();
694 types
.addAll(reader
.getLibrary().getSources());
697 for (String type
: types
) {
698 JMenuItem item
= new JMenuItem(type
== null ?
"New type..." : type
);
702 moveTo
.addSeparator();
705 final String ftype
= type
;
706 item
.addActionListener(new ActionListener() {
708 public void actionPerformed(ActionEvent e
) {
709 if (selectedBook
!= null) {
712 Object rep
= JOptionPane
.showInputDialog(
713 GuiReaderFrame
.this, "Move to:",
715 JOptionPane
.QUESTION_MESSAGE
, null, null,
716 selectedBook
.getMeta().getSource());
722 type
= rep
.toString();
725 final String ftype
= type
;
726 outOfUi(null, new Runnable() {
729 reader
.changeSource(selectedBook
.getMeta()
734 SwingUtilities
.invokeLater(new Runnable() {
737 setJMenuBar(createMenu(true));
751 * Create the redownload (then delete original) menu item.
755 private JMenuItem
createMenuItemRedownload() {
756 JMenuItem refresh
= new JMenuItem("Redownload", KeyEvent
.VK_R
);
757 refresh
.addActionListener(new ActionListener() {
759 public void actionPerformed(ActionEvent e
) {
760 if (selectedBook
!= null) {
761 final MetaData meta
= selectedBook
.getMeta();
762 imprt(meta
.getUrl(), new StoryRunnable() {
764 public void run(Story story
) {
765 reader
.delete(meta
.getLuid());
766 GuiReaderFrame
.this.selectedBook
= null;
767 MetaData newMeta
= story
.getMeta();
768 if (!newMeta
.getSource().equals(meta
.getSource())) {
769 reader
.changeSource(newMeta
.getLuid(),
773 }, "Removing old copy");
782 * Create the delete menu item.
786 private JMenuItem
createMenuItemDelete() {
787 JMenuItem delete
= new JMenuItem("Delete", KeyEvent
.VK_D
);
788 delete
.addActionListener(new ActionListener() {
790 public void actionPerformed(ActionEvent e
) {
791 if (selectedBook
!= null) {
792 outOfUi(null, new Runnable() {
795 reader
.delete(selectedBook
.getMeta().getLuid());
807 * Create the properties menu item.
811 private JMenuItem
createMenuItemProperties() {
812 JMenuItem delete
= new JMenuItem("Properties", KeyEvent
.VK_P
);
813 delete
.addActionListener(new ActionListener() {
815 public void actionPerformed(ActionEvent e
) {
816 if (selectedBook
!= null) {
817 outOfUi(null, new Runnable() {
820 final MetaData meta
= selectedBook
.getMeta();
822 private static final long serialVersionUID
= 1L;
823 @SuppressWarnings("unused")
824 private Object init
= init();
826 private Object
init() {
832 ImageIcon img
= GuiReaderCoverImager
834 reader
.getLibrary(), meta
);
837 setTitle(meta
.getLuid() + ": "
840 setSize(800, img
.getIconHeight() + 2 * top
);
841 setLayout(new BorderLayout());
844 JPanel mainPanel
= new JPanel(
846 JPanel mainPanelKeys
= new JPanel();
847 mainPanelKeys
.setLayout(new BoxLayout(
848 mainPanelKeys
, BoxLayout
.Y_AXIS
));
849 JPanel mainPanelValues
= new JPanel();
850 mainPanelValues
.setLayout(new BoxLayout(
851 mainPanelValues
, BoxLayout
.Y_AXIS
));
853 mainPanel
.add(mainPanelKeys
,
855 mainPanel
.add(mainPanelValues
,
856 BorderLayout
.CENTER
);
858 List
<Entry
<String
, String
>> infos
= BasicReader
861 Color trans
= new Color(0, 0, 0, 1);
862 for (Entry
<String
, String
> info
: infos
) {
863 JTextArea key
= new JTextArea(info
865 key
.setFont(new Font(key
.getFont()
866 .getFontName(), Font
.BOLD
, key
867 .getFont().getSize()));
868 key
.setEditable(false);
869 key
.setLineWrap(false);
870 key
.setBackground(trans
);
871 mainPanelKeys
.add(key
);
873 JTextArea value
= new JTextArea(info
875 value
.setEditable(false);
876 value
.setLineWrap(false);
877 value
.setBackground(trans
);
878 mainPanelValues
.add(value
);
882 JLabel imgLabel
= new JLabel(img
);
883 imgLabel
.setVerticalAlignment(JLabel
.TOP
);
886 mainPanelKeys
.setBorder(BorderFactory
887 .createEmptyBorder(top
, space
, 0, 0));
888 mainPanelValues
.setBorder(BorderFactory
889 .createEmptyBorder(top
, space
, 0, 0));
890 imgLabel
.setBorder(BorderFactory
891 .createEmptyBorder(0, space
, 0, 0));
894 add(imgLabel
, BorderLayout
.WEST
);
895 add(mainPanel
, BorderLayout
.CENTER
);
911 * Create the open menu item for a book or a source (no LUID).
915 private JMenuItem
createMenuItemOpenBook() {
916 JMenuItem open
= new JMenuItem("Open", KeyEvent
.VK_O
);
917 open
.addActionListener(new ActionListener() {
919 public void actionPerformed(ActionEvent e
) {
920 if (selectedBook
!= null) {
921 if (selectedBook
.getMeta().getLuid() == null) {
923 addBookPane(selectedBook
.getMeta().getSource(), true);
926 openBook(selectedBook
);
936 * Create the SetCover menu item for a book to change the linked source
941 private JMenuItem
createMenuItemSetCover() {
942 JMenuItem open
= new JMenuItem("Set as cover for source", KeyEvent
.VK_C
);
943 open
.addActionListener(new ActionListener() {
945 public void actionPerformed(ActionEvent e
) {
946 if (selectedBook
!= null) {
947 reader
.getLibrary().setSourceCover(
948 selectedBook
.getMeta().getSource(),
949 selectedBook
.getMeta().getLuid());
950 MetaData source
= selectedBook
.getMeta().clone();
951 source
.setLuid(null);
952 GuiReaderCoverImager
.clearIcon(source
);
961 * Open a {@link GuiReaderBook} item.
964 * the {@link GuiReaderBook} to open
966 private void openBook(final GuiReaderBook book
) {
967 final Progress pg
= new Progress();
968 outOfUi(pg
, new Runnable() {
972 reader
.read(book
.getMeta().getLuid(), false, pg
);
973 SwingUtilities
.invokeLater(new Runnable() {
976 book
.setCached(true);
979 } catch (IOException e
) {
980 // TODO: error message?
981 Instance
.getTraceHandler().error(e
);
988 * Process the given action out of the Swing UI thread and link the given
989 * {@link ProgressBar} to the action.
991 * The code will make sure that the {@link ProgressBar} (if not NULL) is set
992 * to done when the action is done.
995 * the {@link ProgressBar} or NULL
999 private void outOfUi(Progress progress
, final Runnable run
) {
1000 final Progress pg
= new Progress();
1001 final Progress reload
= new Progress("Reload books");
1002 if (progress
== null) {
1003 progress
= new Progress();
1006 pg
.addProgress(progress
, 90);
1007 pg
.addProgress(reload
, 10);
1010 pgBar
.setProgress(pg
);
1014 new Thread(new Runnable() {
1023 // will trigger pgBar ActionListener:
1028 }, "outOfUi thread").start();
1032 * Import a {@link Story} into the main {@link LocalLibrary}.
1034 * Should be called inside the UI thread.
1037 * TRUE for an {@link URL}, false for a {@link File}
1039 private void imprt(boolean askUrl
) {
1040 JFileChooser fc
= new JFileChooser();
1044 String clipboard
= "";
1046 clipboard
= ("" + Toolkit
.getDefaultToolkit()
1047 .getSystemClipboard().getData(DataFlavor
.stringFlavor
))
1049 } catch (Exception e
) {
1050 // No data will be handled
1053 if (clipboard
== null || !clipboard
.startsWith("http")) {
1057 url
= JOptionPane
.showInputDialog(GuiReaderFrame
.this,
1058 "url of the story to import?", "Importing from URL",
1059 JOptionPane
.QUESTION_MESSAGE
, null, null, clipboard
);
1060 } else if (fc
.showOpenDialog(this) != JFileChooser
.CANCEL_OPTION
) {
1061 url
= fc
.getSelectedFile().getAbsolutePath();
1066 if (url
!= null && !url
.toString().isEmpty()) {
1067 imprt(url
.toString(), null, null);
1072 * Actually import the {@link Story} into the main {@link LocalLibrary}.
1074 * Should be called inside the UI thread.
1077 * the {@link Story} to import by {@link URL}
1079 * Action to execute on success
1081 private void imprt(final String url
, final StoryRunnable onSuccess
,
1082 String onSuccessPgName
) {
1083 final Progress pg
= new Progress();
1084 final Progress pgImprt
= new Progress();
1085 final Progress pgOnSuccess
= new Progress(onSuccessPgName
);
1086 pg
.addProgress(pgImprt
, 95);
1087 pg
.addProgress(pgOnSuccess
, 5);
1089 outOfUi(pg
, new Runnable() {
1092 Exception ex
= null;
1095 story
= reader
.getLibrary().imprt(BasicReader
.getUrl(url
),
1097 } catch (IOException e
) {
1101 final Exception e
= ex
;
1103 final boolean ok
= (e
== null);
1105 pgOnSuccess
.setProgress(0);
1107 if (e
instanceof UnknownHostException
) {
1108 error("URL not supported: " + url
, "Cannot import URL",
1111 error("Failed to import " + url
+ ": \n"
1112 + e
.getMessage(), "Cannot import URL", e
);
1115 if (onSuccess
!= null) {
1116 onSuccess
.run(story
);
1125 * Enables or disables this component, depending on the value of the
1126 * parameter <code>b</code>. An enabled component can respond to user input
1127 * and generate events. Components are enabled initially by default.
1129 * Disabling this component will also affect its children.
1132 * If <code>true</code>, this component is enabled; otherwise
1133 * this component is disabled
1136 public void setEnabled(boolean b
) {
1141 for (GuiReaderGroup group
: booksByType
.keySet()) {
1142 group
.setEnabled(b
);
1144 for (GuiReaderGroup group
: booksByAuthor
.keySet()) {
1145 group
.setEnabled(b
);
1147 super.setEnabled(b
);
1152 * Display an error message and log the linked {@link Exception}.
1157 * the title of the error message
1159 * the exception to log if any
1161 private void error(final String message
, final String title
, Exception e
) {
1162 Instance
.getTraceHandler().error(title
+ ": " + message
);
1164 Instance
.getTraceHandler().error(e
);
1167 SwingUtilities
.invokeLater(new Runnable() {
1170 JOptionPane
.showMessageDialog(GuiReaderFrame
.this, message
,
1171 title
, JOptionPane
.ERROR_MESSAGE
);