Version 1.2.1: fixes for GUI, new buttons
[fanfix.git] / src / be / nikiroo / fanfix / reader / LocalReaderFrame.java
1 package be.nikiroo.fanfix.reader;
2
3 import java.awt.BorderLayout;
4 import java.awt.Color;
5 import java.awt.Desktop;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.awt.event.KeyEvent;
9 import java.awt.event.MouseEvent;
10 import java.awt.event.MouseListener;
11 import java.awt.event.WindowEvent;
12 import java.io.File;
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import javax.swing.JFileChooser;
18 import javax.swing.JFrame;
19 import javax.swing.JMenu;
20 import javax.swing.JMenuBar;
21 import javax.swing.JMenuItem;
22 import javax.swing.JOptionPane;
23 import javax.swing.JPanel;
24 import javax.swing.JPopupMenu;
25 import javax.swing.JScrollPane;
26 import javax.swing.SwingUtilities;
27
28 import be.nikiroo.fanfix.Instance;
29 import be.nikiroo.fanfix.bundles.UiConfig;
30 import be.nikiroo.fanfix.data.MetaData;
31 import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListener;
32 import be.nikiroo.utils.Progress;
33 import be.nikiroo.utils.ui.ProgressBar;
34 import be.nikiroo.utils.ui.WrapLayout;
35
36 class LocalReaderFrame extends JFrame {
37 private static final long serialVersionUID = 1L;
38 private LocalReader reader;
39 private List<MetaData> stories;
40 private List<LocalReaderBook> books;
41 private JPanel bookPane;
42 private String type;
43 private Color color;
44 private ProgressBar pgBar;
45 private JMenuBar bar;
46 private LocalReaderBook selectedBook;
47
48 public LocalReaderFrame(LocalReader reader, String type) {
49 super("Fanfix Library");
50
51 this.reader = reader;
52
53 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
54 setSize(800, 600);
55 setLayout(new BorderLayout());
56
57 books = new ArrayList<LocalReaderBook>();
58 bookPane = new JPanel(new WrapLayout(WrapLayout.LEADING, 5, 5));
59
60 color = Instance.getUiConfig().getColor(UiConfig.BACKGROUND_COLOR);
61
62 if (color != null) {
63 setBackground(color);
64 bookPane.setBackground(color);
65 }
66
67 JScrollPane scroll = new JScrollPane(bookPane);
68 scroll.getVerticalScrollBar().setUnitIncrement(16);
69 add(scroll, BorderLayout.CENTER);
70
71 pgBar = new ProgressBar();
72 add(pgBar, BorderLayout.SOUTH);
73
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) {
86 LocalReaderBook book = new LocalReaderBook(meta,
87 reader.isCached(meta.getLuid()));
88 if (color != null) {
89 book.setBackground(color);
90 }
91
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
122 books.add(book);
123 book.addActionListener(new BookActionListener() {
124 public void select(LocalReaderBook book) {
125 selectedBook = book;
126 for (LocalReaderBook abook : books) {
127 abook.setSelected(abook == book);
128 }
129 }
130
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
142 public void action(final LocalReaderBook book) {
143 openBook(book);
144 }
145 });
146
147 bookPane.add(book);
148 }
149
150 bookPane.validate();
151 bookPane.repaint();
152 }
153
154 private JMenuBar createMenu() {
155 bar = new JMenuBar();
156
157 JMenu file = new JMenu("File");
158 file.setMnemonic(KeyEvent.VK_F);
159
160 JMenuItem imprt = new JMenuItem("Import URL", KeyEvent.VK_U);
161 imprt.addActionListener(new ActionListener() {
162 public void actionPerformed(ActionEvent e) {
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 });
179
180 file.add(createMenuItemOpenBook());
181 file.add(createMenuItemExport());
182 file.addSeparator();
183 file.add(imprt);
184 file.add(imprtF);
185 file.addSeparator();
186 file.add(exit);
187
188 bar.add(file);
189
190 JMenu edit = new JMenu("Edit");
191 edit.setMnemonic(KeyEvent.VK_E);
192
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 }
218
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);
229 export.addActionListener(new ActionListener() {
230 public void actionPerformed(ActionEvent e) {
231 JOptionPane.showMessageDialog(LocalReaderFrame.this, notYet);
232 }
233 });
234
235 return export;
236 }
237
238 private JMenuItem createMenuItemRefresh() {
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);
247 SwingUtilities.invokeLater(new Runnable() {
248 public void run() {
249 selectedBook.repaint();
250 }
251 });
252 }
253 });
254 }
255 }
256 });
257
258 return refresh;
259 }
260
261 private JMenuItem createMenuItemDelete() {
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
281 return delete;
282 }
283
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 });
293
294 return open;
295 }
296
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 }
325
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);
333 }
334 }
335 });
336 }
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();
356 if (pg == null) {
357 SwingUtilities.invokeLater(new Runnable() {
358 public void run() {
359 setAllEnabled(true);
360 }
361 });
362 } else if (!pg.isDone()) {
363 pg.setProgress(pg.getMax());
364 }
365 }
366 }).start();
367 }
368
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
418 public void setAllEnabled(boolean enabled) {
419 for (LocalReaderBook book : books) {
420 book.setEnabled(enabled);
421 book.validate();
422 book.repaint();
423 }
424
425 bar.setEnabled(enabled);
426 bookPane.setEnabled(enabled);
427 bookPane.validate();
428 bookPane.repaint();
429
430 setEnabled(enabled);
431 validate();
432 repaint();
433 }
434 }