Commit | Line | Data |
---|---|---|
a6395bef NR |
1 | package be.nikiroo.fanfix.reader; |
2 | ||
d3c84ac3 | 3 | import java.awt.BorderLayout; |
b4dc6ab5 | 4 | import java.awt.Color; |
a6395bef | 5 | import java.awt.Desktop; |
a6395bef NR |
6 | import java.awt.event.ActionEvent; |
7 | import java.awt.event.ActionListener; | |
333f0e7b NR |
8 | import java.awt.event.KeyEvent; |
9 | import java.awt.event.WindowEvent; | |
10 | import java.io.File; | |
a6395bef | 11 | import java.io.IOException; |
333f0e7b | 12 | import java.util.ArrayList; |
a6395bef NR |
13 | import java.util.List; |
14 | ||
10d558d2 | 15 | import javax.swing.JFileChooser; |
a6395bef | 16 | import javax.swing.JFrame; |
333f0e7b NR |
17 | import javax.swing.JMenu; |
18 | import javax.swing.JMenuBar; | |
19 | import javax.swing.JMenuItem; | |
20 | import javax.swing.JOptionPane; | |
21 | import javax.swing.JPanel; | |
d3c84ac3 | 22 | import javax.swing.JScrollPane; |
3b2b638f | 23 | import javax.swing.SwingUtilities; |
a6395bef NR |
24 | |
25 | import be.nikiroo.fanfix.Instance; | |
b4dc6ab5 | 26 | import be.nikiroo.fanfix.bundles.UiConfig; |
a6395bef | 27 | import be.nikiroo.fanfix.data.MetaData; |
92fb0719 | 28 | import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListener; |
3b2b638f NR |
29 | import be.nikiroo.utils.Progress; |
30 | import be.nikiroo.utils.ui.ProgressBar; | |
33ab6d12 | 31 | import be.nikiroo.utils.ui.WrapLayout; |
a6395bef NR |
32 | |
33 | class LocalReaderFrame extends JFrame { | |
34 | private static final long serialVersionUID = 1L; | |
35 | private LocalReader reader; | |
333f0e7b NR |
36 | private List<MetaData> stories; |
37 | private List<LocalReaderBook> books; | |
38 | private JPanel bookPane; | |
39 | private String type; | |
b4dc6ab5 | 40 | private Color color; |
3b2b638f NR |
41 | private ProgressBar pgBar; |
42 | private JMenuBar bar; | |
10d558d2 | 43 | private LocalReaderBook selectedBook; |
a6395bef | 44 | |
333f0e7b | 45 | public LocalReaderFrame(LocalReader reader, String type) { |
b4dc6ab5 | 46 | super("Fanfix Library"); |
a6395bef NR |
47 | |
48 | this.reader = reader; | |
49 | ||
50 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
51 | setSize(800, 600); | |
d3c84ac3 | 52 | setLayout(new BorderLayout()); |
a6395bef | 53 | |
333f0e7b | 54 | books = new ArrayList<LocalReaderBook>(); |
b4dc6ab5 NR |
55 | bookPane = new JPanel(new WrapLayout(WrapLayout.LEADING, 5, 5)); |
56 | ||
3b2b638f | 57 | color = Instance.getUiConfig().getColor(UiConfig.BACKGROUND_COLOR); |
b4dc6ab5 NR |
58 | |
59 | if (color != null) { | |
60 | setBackground(color); | |
61 | bookPane.setBackground(color); | |
62 | } | |
d3c84ac3 | 63 | |
b4dc6ab5 NR |
64 | JScrollPane scroll = new JScrollPane(bookPane); |
65 | scroll.getVerticalScrollBar().setUnitIncrement(16); | |
66 | add(scroll, BorderLayout.CENTER); | |
a6395bef | 67 | |
3b2b638f NR |
68 | pgBar = new ProgressBar(); |
69 | add(pgBar, BorderLayout.SOUTH); | |
70 | ||
333f0e7b NR |
71 | refreshBooks(type); |
72 | setJMenuBar(createMenu()); | |
73 | ||
74 | setVisible(true); | |
75 | } | |
76 | ||
77 | private void refreshBooks(String type) { | |
78 | this.type = type; | |
79 | stories = Instance.getLibrary().getList(type); | |
80 | books.clear(); | |
81 | bookPane.removeAll(); | |
82 | for (MetaData meta : stories) { | |
10d558d2 NR |
83 | LocalReaderBook book = new LocalReaderBook(meta, |
84 | reader.isCached(meta.getLuid())); | |
b4dc6ab5 NR |
85 | if (color != null) { |
86 | book.setBackground(color); | |
87 | } | |
88 | ||
333f0e7b NR |
89 | books.add(book); |
90 | final String luid = meta.getLuid(); | |
92fb0719 | 91 | book.addActionListener(new BookActionListener() { |
333f0e7b | 92 | public void select(LocalReaderBook book) { |
10d558d2 | 93 | selectedBook = book; |
333f0e7b NR |
94 | for (LocalReaderBook abook : books) { |
95 | abook.setSelected(abook == book); | |
96 | } | |
97 | } | |
98 | ||
10d558d2 | 99 | public void action(final LocalReaderBook book) { |
3b2b638f NR |
100 | final Progress pg = new Progress(); |
101 | outOfUi(pg, new Runnable() { | |
102 | public void run() { | |
103 | try { | |
104 | File target = LocalReaderFrame.this.reader | |
105 | .getTarget(luid, pg); | |
10d558d2 | 106 | book.setCached(true); |
63985d92 NR |
107 | // TODO: allow custom programs, with |
108 | // Desktop/xdg-open fallback | |
109 | try { | |
110 | Desktop.getDesktop().browse(target.toURI()); | |
111 | } catch (UnsupportedOperationException e) { | |
112 | String browsers[] = new String[] { | |
113 | "xdg-open", "epiphany", | |
114 | "konqueror", "firefox", "chrome", | |
115 | "google-chrome", "mozilla" }; | |
116 | ||
117 | Runtime runtime = Runtime.getRuntime(); | |
118 | for (String browser : browsers) { | |
119 | try { | |
120 | runtime.exec(new String[] { | |
121 | browser, | |
122 | target.getAbsolutePath() }); | |
123 | runtime = null; | |
124 | break; | |
125 | } catch (IOException ioe) { | |
126 | // continue, try next browser | |
127 | } | |
128 | } | |
129 | ||
130 | if (runtime != null) { | |
131 | throw new IOException( | |
132 | "Cannot find a working GUI browser..."); | |
133 | } | |
134 | } | |
3b2b638f NR |
135 | } catch (IOException e) { |
136 | Instance.syserr(e); | |
137 | } | |
138 | } | |
139 | }); | |
a6395bef NR |
140 | } |
141 | }); | |
142 | ||
333f0e7b | 143 | bookPane.add(book); |
a6395bef NR |
144 | } |
145 | ||
333f0e7b NR |
146 | bookPane.validate(); |
147 | bookPane.repaint(); | |
148 | } | |
149 | ||
150 | private JMenuBar createMenu() { | |
3b2b638f | 151 | bar = new JMenuBar(); |
333f0e7b NR |
152 | |
153 | JMenu file = new JMenu("File"); | |
10d558d2 | 154 | file.setMnemonic(KeyEvent.VK_F); |
333f0e7b | 155 | |
10d558d2 | 156 | JMenuItem imprt = new JMenuItem("Import URL", KeyEvent.VK_U); |
333f0e7b NR |
157 | imprt.addActionListener(new ActionListener() { |
158 | public void actionPerformed(ActionEvent e) { | |
10d558d2 NR |
159 | imprt(true); |
160 | } | |
161 | }); | |
162 | JMenuItem imprtF = new JMenuItem("Import File", KeyEvent.VK_F); | |
163 | imprtF.addActionListener(new ActionListener() { | |
164 | public void actionPerformed(ActionEvent e) { | |
165 | imprt(false); | |
166 | } | |
167 | }); | |
168 | JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X); | |
169 | exit.addActionListener(new ActionListener() { | |
170 | public void actionPerformed(ActionEvent e) { | |
171 | LocalReaderFrame.this.dispatchEvent(new WindowEvent( | |
172 | LocalReaderFrame.this, WindowEvent.WINDOW_CLOSING)); | |
173 | } | |
174 | }); | |
3b2b638f | 175 | |
10d558d2 NR |
176 | file.add(imprt); |
177 | file.add(imprtF); | |
178 | file.addSeparator(); | |
179 | file.add(exit); | |
3b2b638f | 180 | |
10d558d2 NR |
181 | bar.add(file); |
182 | ||
183 | JMenu edit = new JMenu("Edit"); | |
184 | edit.setMnemonic(KeyEvent.VK_E); | |
185 | ||
186 | final String notYet = "[TODO] Show not ready yet, but you can do it on command line, see: fanfix --help"; | |
187 | ||
188 | JMenuItem export = new JMenuItem("Export", KeyEvent.VK_E); | |
189 | export.addActionListener(new ActionListener() { | |
190 | public void actionPerformed(ActionEvent e) { | |
191 | JOptionPane.showMessageDialog(LocalReaderFrame.this, notYet); | |
192 | } | |
193 | }); | |
194 | ||
195 | JMenuItem refresh = new JMenuItem("Refresh", KeyEvent.VK_R); | |
196 | refresh.addActionListener(new ActionListener() { | |
197 | public void actionPerformed(ActionEvent e) { | |
198 | if (selectedBook != null) { | |
199 | outOfUi(null, new Runnable() { | |
200 | public void run() { | |
201 | reader.refresh(selectedBook.getLuid()); | |
202 | selectedBook.setCached(false); | |
3b2b638f NR |
203 | SwingUtilities.invokeLater(new Runnable() { |
204 | public void run() { | |
10d558d2 | 205 | selectedBook.repaint(); |
3b2b638f NR |
206 | } |
207 | }); | |
208 | } | |
209 | }); | |
333f0e7b NR |
210 | } |
211 | } | |
212 | }); | |
10d558d2 NR |
213 | |
214 | JMenuItem delete = new JMenuItem("Delete", KeyEvent.VK_D); | |
215 | delete.addActionListener(new ActionListener() { | |
216 | public void actionPerformed(ActionEvent e) { | |
217 | if (selectedBook != null) { | |
218 | outOfUi(null, new Runnable() { | |
219 | public void run() { | |
220 | reader.delete(selectedBook.getLuid()); | |
221 | selectedBook = null; | |
222 | SwingUtilities.invokeLater(new Runnable() { | |
223 | public void run() { | |
224 | refreshBooks(type); | |
225 | } | |
226 | }); | |
227 | } | |
228 | }); | |
229 | } | |
230 | } | |
231 | }); | |
232 | ||
233 | edit.add(export); | |
234 | edit.add(refresh); | |
235 | edit.add(delete); | |
236 | ||
237 | bar.add(edit); | |
238 | ||
239 | JMenu view = new JMenu("View"); | |
240 | view.setMnemonic(KeyEvent.VK_V); | |
241 | ||
333f0e7b NR |
242 | List<String> tt = Instance.getLibrary().getTypes(); |
243 | tt.add(0, null); | |
244 | for (final String type : tt) { | |
10d558d2 NR |
245 | |
246 | JMenuItem item = new JMenuItem(type == null ? "All books" : type); | |
333f0e7b NR |
247 | item.addActionListener(new ActionListener() { |
248 | public void actionPerformed(ActionEvent e) { | |
249 | refreshBooks(type); | |
250 | } | |
251 | }); | |
10d558d2 | 252 | view.add(item); |
333f0e7b | 253 | |
10d558d2 NR |
254 | if (type == null) { |
255 | view.addSeparator(); | |
256 | } | |
257 | } | |
333f0e7b | 258 | |
10d558d2 | 259 | bar.add(view); |
333f0e7b NR |
260 | |
261 | return bar; | |
a6395bef | 262 | } |
3b2b638f NR |
263 | |
264 | private void outOfUi(final Progress pg, final Runnable run) { | |
265 | pgBar.setProgress(pg); | |
266 | ||
267 | SwingUtilities.invokeLater(new Runnable() { | |
268 | public void run() { | |
269 | setAllEnabled(false); | |
270 | pgBar.addActioListener(new ActionListener() { | |
271 | public void actionPerformed(ActionEvent e) { | |
272 | pgBar.setProgress(null); | |
273 | setAllEnabled(true); | |
274 | } | |
275 | }); | |
276 | } | |
277 | }); | |
278 | ||
279 | new Thread(new Runnable() { | |
280 | public void run() { | |
281 | run.run(); | |
10d558d2 NR |
282 | if (pg == null) { |
283 | SwingUtilities.invokeLater(new Runnable() { | |
284 | public void run() { | |
285 | setAllEnabled(true); | |
286 | } | |
287 | }); | |
288 | } else if (!pg.isDone()) { | |
3b2b638f NR |
289 | pg.setProgress(pg.getMax()); |
290 | } | |
291 | } | |
292 | }).start(); | |
293 | } | |
294 | ||
10d558d2 NR |
295 | private void imprt(boolean askUrl) { |
296 | JFileChooser fc = new JFileChooser(); | |
297 | ||
298 | final String url; | |
299 | if (askUrl) { | |
300 | url = JOptionPane.showInputDialog(LocalReaderFrame.this, | |
301 | "url of the story to import?", "Importing from URL", | |
302 | JOptionPane.QUESTION_MESSAGE); | |
303 | } else if (fc.showOpenDialog(this) != JFileChooser.CANCEL_OPTION) { | |
304 | url = fc.getSelectedFile().getAbsolutePath(); | |
305 | } else { | |
306 | url = null; | |
307 | } | |
308 | ||
309 | if (url != null && !url.isEmpty()) { | |
310 | final Progress pg = new Progress("Importing " + url); | |
311 | outOfUi(pg, new Runnable() { | |
312 | public void run() { | |
313 | Exception ex = null; | |
314 | try { | |
315 | Instance.getLibrary() | |
316 | .imprt(BasicReader.getUrl(url), pg); | |
317 | } catch (IOException e) { | |
318 | ex = e; | |
319 | } | |
320 | ||
321 | final Exception e = ex; | |
322 | ||
323 | final boolean ok = (e == null); | |
324 | SwingUtilities.invokeLater(new Runnable() { | |
325 | public void run() { | |
326 | if (!ok) { | |
327 | JOptionPane.showMessageDialog( | |
328 | LocalReaderFrame.this, | |
329 | "Cannot import: " + url, | |
330 | e.getMessage(), | |
331 | JOptionPane.ERROR_MESSAGE); | |
332 | ||
333 | setAllEnabled(true); | |
334 | } else { | |
335 | refreshBooks(type); | |
336 | } | |
337 | } | |
338 | }); | |
339 | } | |
340 | }); | |
341 | } | |
342 | } | |
343 | ||
3b2b638f NR |
344 | public void setAllEnabled(boolean enabled) { |
345 | for (LocalReaderBook book : books) { | |
346 | book.setEnabled(enabled); | |
347 | book.validate(); | |
348 | book.repaint(); | |
349 | } | |
10d558d2 | 350 | |
3b2b638f NR |
351 | bar.setEnabled(enabled); |
352 | bookPane.setEnabled(enabled); | |
353 | bookPane.validate(); | |
354 | bookPane.repaint(); | |
10d558d2 | 355 | |
3b2b638f NR |
356 | setEnabled(enabled); |
357 | validate(); | |
358 | repaint(); | |
359 | } | |
a6395bef | 360 | } |