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