change search syntax
[nikiroo-utils.git] / src / be / nikiroo / fanfix / reader / tui / TuiReaderApplication.java
CommitLineData
16a81ef7 1package be.nikiroo.fanfix.reader.tui;
c1873e56 2
e2d017a3
NR
3import java.awt.Toolkit;
4import java.awt.datatransfer.DataFlavor;
c1873e56 5import java.io.IOException;
6322ab64 6import java.net.URL;
4f9478dc 7import java.net.UnknownHostException;
c1873e56
NR
8
9import jexer.TApplication;
e2d017a3
NR
10import jexer.TCommand;
11import jexer.TKeypress;
c1873e56 12import jexer.TMessageBox;
cd6b47c9
NR
13import jexer.TMessageBox.Result;
14import jexer.TMessageBox.Type;
e2d017a3 15import jexer.TStatusBar;
cf9c5ed1 16import jexer.TWidget;
6322ab64 17import jexer.TWindow;
bce88a00 18import jexer.event.TCommandEvent;
e2d017a3
NR
19import jexer.event.TMenuEvent;
20import jexer.menu.TMenu;
a8209dd0 21import be.nikiroo.fanfix.Instance;
c1873e56 22import be.nikiroo.fanfix.data.MetaData;
6322ab64
NR
23import be.nikiroo.fanfix.data.Story;
24import be.nikiroo.fanfix.library.BasicLibrary;
16a81ef7
NR
25import be.nikiroo.fanfix.reader.BasicReader;
26import be.nikiroo.fanfix.reader.Reader;
b2274262 27import be.nikiroo.fanfix.reader.tui.TuiReaderMainWindow.Mode;
91b82a5c 28import be.nikiroo.fanfix.supported.SupportType;
6322ab64 29import be.nikiroo.utils.Progress;
c1873e56 30
6322ab64 31/**
bc2ea776
NR
32 * Manages the TUI general mode and links and manages the {@link TWindow}s.
33 * <p>
34 * It will also enclose a {@link Reader} and simply handle the reading part
35 * differently (it will create the required sub-windows and display them).
6322ab64
NR
36 *
37 * @author niki
38 */
39class TuiReaderApplication extends TApplication implements Reader {
e2d017a3
NR
40 public static final int MENU_OPEN = 1025;
41 public static final int MENU_IMPORT_URL = 1026;
42 public static final int MENU_IMPORT_FILE = 1027;
43 public static final int MENU_EXPORT = 1028;
cd6b47c9
NR
44 public static final int MENU_DELETE = 1029;
45 public static final int MENU_LIBRARY = 1030;
46 public static final int MENU_EXIT = 1031;
e2d017a3 47
cf9c5ed1
NR
48 public static final TCommand CMD_EXIT = new TCommand(MENU_EXIT) {
49 };
50
6322ab64 51 private Reader reader;
e2d017a3 52 private TuiReaderMainWindow main;
4f66bfa8 53
bc2ea776
NR
54 // start reading if meta present
55 public TuiReaderApplication(Reader reader, BackendType backend)
56 throws Exception {
57 super(backend);
58 init(reader);
c1873e56 59
350bc060
NR
60 if (getMeta() != null) {
61 read(false);
e0fb1417 62 }
6322ab64
NR
63 }
64
e2d017a3 65 public TuiReaderApplication(Reader reader, String source,
bc2ea776 66 TApplication.BackendType backend) throws Exception {
c1873e56 67 super(backend);
bc2ea776 68 init(reader);
c1873e56 69
e0fb1417 70 showMain();
b2274262 71 main.setMode(Mode.SOURCE, source);
4f66bfa8
NR
72 }
73
211f7ddb 74 @Override
350bc060
NR
75 public void read(boolean sync) throws IOException {
76 read(getStory(null), sync);
c1873e56 77 }
6322ab64 78
211f7ddb 79 @Override
bc2ea776
NR
80 public MetaData getMeta() {
81 return reader.getMeta();
82 }
83
211f7ddb 84 @Override
bc2ea776
NR
85 public Story getStory(Progress pg) {
86 return reader.getStory(pg);
6322ab64
NR
87 }
88
211f7ddb 89 @Override
6322ab64
NR
90 public BasicLibrary getLibrary() {
91 return reader.getLibrary();
92 }
93
211f7ddb 94 @Override
bc2ea776 95 public void setLibrary(BasicLibrary lib) {
6322ab64
NR
96 reader.setLibrary(lib);
97 }
98
211f7ddb 99 @Override
bc2ea776
NR
100 public void setMeta(MetaData meta) throws IOException {
101 reader.setMeta(meta);
102 }
103
211f7ddb 104 @Override
bc2ea776
NR
105 public void setMeta(String luid) throws IOException {
106 reader.setMeta(luid);
6322ab64
NR
107 }
108
211f7ddb 109 @Override
bc2ea776
NR
110 public void setMeta(URL source, Progress pg) throws IOException {
111 reader.setMeta(source, pg);
6322ab64
NR
112 }
113
211f7ddb 114 @Override
6322ab64
NR
115 public void browse(String source) {
116 reader.browse(source);
117 }
bc2ea776 118
211f7ddb 119 @Override
bc2ea776
NR
120 public int getChapter() {
121 return reader.getChapter();
122 }
123
211f7ddb 124 @Override
bc2ea776
NR
125 public void setChapter(int chapter) {
126 reader.setChapter(chapter);
127 }
8b153400 128
91b82a5c 129 @Override
8b153400
NR
130 public void search(SupportType searchOn, String keywords, int page, int item) throws IOException {
131 reader.search(searchOn, keywords, page, item);
91b82a5c 132 }
8b153400 133
91b82a5c 134 @Override
8b153400
NR
135 public void searchTag(SupportType searchOn, int page, int item,
136 Integer... tags) throws IOException {
91b82a5c
NR
137 reader.searchTag(searchOn, page, item, tags);
138 }
bc2ea776 139
350bc060
NR
140 /**
141 * Open the given {@link Story} for reading. This may or may not start an
142 * external program to read said {@link Story}.
143 *
144 * @param story
145 * the {@link Story} to read
146 * @param sync
147 * execute the process synchronously (wait until it is terminated
148 * before returning)
149 *
150 * @throws IOException
151 * in case of I/O errors
152 */
153 public void read(Story story, boolean sync) throws IOException {
e0fb1417
NR
154 if (story == null) {
155 throw new IOException("No story to read");
156 }
157
158 // TODO: open in editor + external option
159 if (!story.getMeta().isImageDocument()) {
160 TWindow window = new TuiReaderStoryWindow(this, story, getChapter());
161 window.maximize();
162 } else {
163 try {
350bc060 164 openExternal(getLibrary(), story.getMeta().getLuid(), sync);
e0fb1417
NR
165 } catch (IOException e) {
166 messageBox("Error when trying to open the story",
167 e.getMessage(), TMessageBox.Type.OK);
168 }
169 }
170 }
171
5b00c122
NR
172 /**
173 * Set the default status bar when this window appear.
174 * <p>
175 * Some shortcuts are always visible, and will be put here.
176 * <p>
177 * Note that shortcuts placed this way on menu won't work unless the menu
178 * also implement them.
179 *
180 * @param window
181 * the new window or menu on screen
182 * @param description
183 * the description to show on the status ba
184 */
185 public TStatusBar setStatusBar(TWindow window, String description) {
186 TStatusBar statusBar = window.newStatusBar(description);
cf9c5ed1 187 statusBar.addShortcutKeypress(TKeypress.kbF10, CMD_EXIT, "Exit");
5b00c122
NR
188 return statusBar;
189
190 }
191
e0fb1417 192 private void showMain() {
5b00c122
NR
193 if (main != null && main.isVisible()) {
194 main.activate();
195 } else {
196 if (main != null) {
197 main.close();
198 }
199 main = new TuiReaderMainWindow(this);
11758a0f 200 main.maximize();
5b00c122
NR
201 }
202 }
203
bc2ea776
NR
204 private void init(Reader reader) {
205 this.reader = reader;
206
581d42c0
NR
207 // TODO: traces/errors?
208 Instance.setTraceHandler(null);
a8209dd0 209
e2d017a3
NR
210 // Add the menus TODO: i18n
211 TMenu fileMenu = addMenu("&File");
cd6b47c9 212 fileMenu.addItem(MENU_OPEN, "&Open...");
e2d017a3 213 fileMenu.addItem(MENU_EXPORT, "&Save as...");
cd6b47c9 214 fileMenu.addItem(MENU_DELETE, "&Delete...");
e2d017a3
NR
215 // TODO: Move to...
216 fileMenu.addSeparator();
217 fileMenu.addItem(MENU_IMPORT_URL, "Import &URL...");
218 fileMenu.addItem(MENU_IMPORT_FILE, "Import &file...");
219 fileMenu.addSeparator();
4f66bfa8
NR
220 fileMenu.addItem(MENU_LIBRARY, "Lib&rary");
221 fileMenu.addSeparator();
e2d017a3
NR
222 fileMenu.addItem(MENU_EXIT, "E&xit");
223
5b00c122 224 setStatusBar(fileMenu, "File-management "
e2d017a3 225 + "commands (Open, Save, Print, etc.)");
e2d017a3
NR
226
227 // TODO: Edit: re-download, delete
228
229 //
230
bc2ea776 231 addWindowMenu();
bc2ea776
NR
232
233 getBackend().setTitle("Fanfix");
234 }
e2d017a3 235
bce88a00
NR
236 @Override
237 protected boolean onCommand(TCommandEvent command) {
238 if (command.getCmd().equals(TuiReaderMainWindow.CMD_SEARCH)) {
239 messageBox("title", "caption");
240 return true;
241 }
242 return super.onCommand(command);
243 }
244
e2d017a3
NR
245 @Override
246 protected boolean onMenu(TMenuEvent menu) {
247 // TODO: i18n
248 switch (menu.getId()) {
249 case MENU_EXIT:
cf9c5ed1 250 close(this);
e0fb1417
NR
251 return true;
252 case MENU_OPEN:
253 String openfile = null;
254 try {
255 openfile = fileOpenBox(".");
256 reader.setMeta(BasicReader.getUrl(openfile), null);
350bc060 257 read(false);
e0fb1417
NR
258 } catch (IOException e) {
259 // TODO: i18n
260 error("Fail to open file"
261 + (openfile == null ? "" : ": " + openfile),
262 "Import error", e);
263 }
264
cd6b47c9
NR
265 return true;
266 case MENU_DELETE:
267 String luid = null;
268 String story = null;
269 MetaData meta = null;
270 if (main != null) {
271 meta = main.getSelectedMeta();
272 }
273 if (meta != null) {
274 luid = meta.getLuid();
275 story = luid + ": " + meta.getTitle();
276 }
277
278 // TODO: i18n
279 TMessageBox mbox = messageBox("Delete story", "Delete story \""
280 + story + "\"", Type.OKCANCEL);
281 if (mbox.getResult() == Result.OK) {
282 try {
283 reader.getLibrary().delete(luid);
284 if (main != null) {
285 main.refreshStories();
286 }
287 } catch (IOException e) {
288 // TODO: i18n
289 error("Fail to delete the story: \"" + story + "\"",
290 "Error", e);
291 }
292 }
293
e2d017a3
NR
294 return true;
295 case MENU_IMPORT_URL:
296 String clipboard = "";
297 try {
298 clipboard = ("" + Toolkit.getDefaultToolkit()
299 .getSystemClipboard().getData(DataFlavor.stringFlavor))
300 .trim();
301 } catch (Exception e) {
302 // No data will be handled
303 }
304
305 if (clipboard == null || !clipboard.startsWith("http")) {
306 clipboard = "";
307 }
308
309 String url = inputBox("Import story", "URL to import", clipboard)
310 .getText();
311
4f9478dc
NR
312 try {
313 if (!imprt(url)) {
e0fb1417 314 // TODO: i18n
4f9478dc
NR
315 error("URK not supported: " + url, "Import error");
316 }
317 } catch (IOException e) {
e0fb1417 318 // TODO: i18n
4f9478dc 319 error("Fail to import URL: " + url, "Import error", e);
e2d017a3
NR
320 }
321
322 return true;
323 case MENU_IMPORT_FILE:
4f9478dc 324 String filename = null;
e2d017a3 325 try {
4f9478dc 326 filename = fileOpenBox(".");
e2d017a3 327 if (!imprt(filename)) {
e0fb1417 328 // TODO: i18n
4f9478dc 329 error("File not supported: " + filename, "Import error");
e2d017a3
NR
330 }
331 } catch (IOException e) {
e0fb1417 332 // TODO: i18n
4f9478dc
NR
333 error("Fail to import file"
334 + (filename == null ? "" : ": " + filename),
335 "Import error", e);
e2d017a3 336 }
4f66bfa8
NR
337 return true;
338 case MENU_LIBRARY:
e0fb1417 339 showMain();
e2d017a3
NR
340 return true;
341 }
342
343 return super.onMenu(menu);
344 }
345
4f9478dc
NR
346 /**
347 * Import the given url.
348 * <p>
349 * Can fail if the host is not supported.
350 *
351 * @param url
352 *
353 * @return TRUE in case of success, FALSE if the host is not supported
354 *
355 * @throws IOException
356 * in case of I/O error
357 */
358 private boolean imprt(String url) throws IOException {
e2d017a3
NR
359 try {
360 reader.getLibrary().imprt(BasicReader.getUrl(url), null);
361 main.refreshStories();
362 return true;
4f9478dc 363 } catch (UnknownHostException e) {
e2d017a3
NR
364 return false;
365 }
366 }
16a81ef7
NR
367
368 @Override
350bc060
NR
369 public void openExternal(BasicLibrary lib, String luid, boolean sync)
370 throws IOException {
371 reader.openExternal(lib, luid, sync);
16a81ef7 372 }
cf9c5ed1 373
4f9478dc
NR
374 /**
375 * Display an error message and log it.
376 *
377 * @param message
378 * the message
379 * @param title
380 * the title of the error message
381 */
382 private void error(String message, String title) {
383 error(message, title, null);
384 }
385
386 /**
387 * Display an error message and log it, including the linked
388 * {@link Exception}.
389 *
390 * @param message
391 * the message
392 * @param title
393 * the title of the error message
394 * @param e
395 * the exception to log if any (can be NULL)
396 */
397 private void error(String message, String title, Exception e) {
398 Instance.getTraceHandler().error(title + ": " + message);
399 if (e != null) {
400 Instance.getTraceHandler().error(e);
401 }
402
403 if (e != null) {
404 messageBox(title, message //
405 + "\n" + e.getMessage());
406 } else {
407 messageBox(title, message);
408 }
409 }
410
cf9c5ed1
NR
411 /**
412 * Ask the user and, if confirmed, close the {@link TApplication} this
413 * {@link TWidget} is running on.
414 * <p>
415 * This should result in the program terminating.
416 *
417 * @param widget
418 * the {@link TWidget}
419 */
420 static public void close(TWidget widget) {
421 close(widget.getApplication());
422 }
423
424 /**
425 * Ask the user and, if confirmed, close the {@link TApplication}.
426 * <p>
427 * This should result in the program terminating.
428 *
429 * @param app
430 * the {@link TApplication}
431 */
432 static void close(TApplication app) {
433 // TODO: i18n
434 if (app.messageBox("Confirmation", "(TODO: i18n) Exit application?",
435 TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
436 app.exit();
437 }
438 }
c1873e56 439}