search: cleanup
[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
b31a0db0
NR
129 @Override
130 public void search(boolean sync) throws IOException {
131 reader.search(sync);
132 }
133
91b82a5c 134 @Override
f76de465
NR
135 public void search(SupportType searchOn, String keywords, int page,
136 int item, boolean sync) throws IOException {
137 reader.search(searchOn, keywords, page, item, sync);
91b82a5c 138 }
8b153400 139
91b82a5c 140 @Override
8b153400 141 public void searchTag(SupportType searchOn, int page, int item,
f76de465
NR
142 boolean sync, Integer... tags) throws IOException {
143 reader.searchTag(searchOn, page, item, sync, tags);
91b82a5c 144 }
bc2ea776 145
350bc060
NR
146 /**
147 * Open the given {@link Story} for reading. This may or may not start an
148 * external program to read said {@link Story}.
149 *
150 * @param story
151 * the {@link Story} to read
152 * @param sync
153 * execute the process synchronously (wait until it is terminated
154 * before returning)
155 *
156 * @throws IOException
157 * in case of I/O errors
158 */
159 public void read(Story story, boolean sync) throws IOException {
e0fb1417
NR
160 if (story == null) {
161 throw new IOException("No story to read");
162 }
163
164 // TODO: open in editor + external option
165 if (!story.getMeta().isImageDocument()) {
166 TWindow window = new TuiReaderStoryWindow(this, story, getChapter());
167 window.maximize();
168 } else {
169 try {
350bc060 170 openExternal(getLibrary(), story.getMeta().getLuid(), sync);
e0fb1417
NR
171 } catch (IOException e) {
172 messageBox("Error when trying to open the story",
173 e.getMessage(), TMessageBox.Type.OK);
174 }
175 }
176 }
177
5b00c122
NR
178 /**
179 * Set the default status bar when this window appear.
180 * <p>
181 * Some shortcuts are always visible, and will be put here.
182 * <p>
183 * Note that shortcuts placed this way on menu won't work unless the menu
184 * also implement them.
185 *
186 * @param window
187 * the new window or menu on screen
188 * @param description
189 * the description to show on the status ba
190 */
191 public TStatusBar setStatusBar(TWindow window, String description) {
192 TStatusBar statusBar = window.newStatusBar(description);
cf9c5ed1 193 statusBar.addShortcutKeypress(TKeypress.kbF10, CMD_EXIT, "Exit");
5b00c122
NR
194 return statusBar;
195
196 }
197
e0fb1417 198 private void showMain() {
5b00c122
NR
199 if (main != null && main.isVisible()) {
200 main.activate();
201 } else {
202 if (main != null) {
203 main.close();
204 }
205 main = new TuiReaderMainWindow(this);
11758a0f 206 main.maximize();
5b00c122
NR
207 }
208 }
209
bc2ea776
NR
210 private void init(Reader reader) {
211 this.reader = reader;
212
581d42c0
NR
213 // TODO: traces/errors?
214 Instance.setTraceHandler(null);
a8209dd0 215
e2d017a3
NR
216 // Add the menus TODO: i18n
217 TMenu fileMenu = addMenu("&File");
cd6b47c9 218 fileMenu.addItem(MENU_OPEN, "&Open...");
e2d017a3 219 fileMenu.addItem(MENU_EXPORT, "&Save as...");
cd6b47c9 220 fileMenu.addItem(MENU_DELETE, "&Delete...");
e2d017a3
NR
221 // TODO: Move to...
222 fileMenu.addSeparator();
223 fileMenu.addItem(MENU_IMPORT_URL, "Import &URL...");
224 fileMenu.addItem(MENU_IMPORT_FILE, "Import &file...");
225 fileMenu.addSeparator();
4f66bfa8
NR
226 fileMenu.addItem(MENU_LIBRARY, "Lib&rary");
227 fileMenu.addSeparator();
e2d017a3
NR
228 fileMenu.addItem(MENU_EXIT, "E&xit");
229
5b00c122 230 setStatusBar(fileMenu, "File-management "
e2d017a3 231 + "commands (Open, Save, Print, etc.)");
e2d017a3
NR
232
233 // TODO: Edit: re-download, delete
234
235 //
236
bc2ea776 237 addWindowMenu();
bc2ea776
NR
238
239 getBackend().setTitle("Fanfix");
240 }
e2d017a3 241
bce88a00
NR
242 @Override
243 protected boolean onCommand(TCommandEvent command) {
244 if (command.getCmd().equals(TuiReaderMainWindow.CMD_SEARCH)) {
245 messageBox("title", "caption");
246 return true;
247 }
248 return super.onCommand(command);
249 }
250
e2d017a3
NR
251 @Override
252 protected boolean onMenu(TMenuEvent menu) {
253 // TODO: i18n
254 switch (menu.getId()) {
255 case MENU_EXIT:
cf9c5ed1 256 close(this);
e0fb1417
NR
257 return true;
258 case MENU_OPEN:
259 String openfile = null;
260 try {
261 openfile = fileOpenBox(".");
262 reader.setMeta(BasicReader.getUrl(openfile), null);
350bc060 263 read(false);
e0fb1417
NR
264 } catch (IOException e) {
265 // TODO: i18n
266 error("Fail to open file"
267 + (openfile == null ? "" : ": " + openfile),
268 "Import error", e);
269 }
270
cd6b47c9
NR
271 return true;
272 case MENU_DELETE:
273 String luid = null;
274 String story = null;
275 MetaData meta = null;
276 if (main != null) {
277 meta = main.getSelectedMeta();
278 }
279 if (meta != null) {
280 luid = meta.getLuid();
281 story = luid + ": " + meta.getTitle();
282 }
283
284 // TODO: i18n
285 TMessageBox mbox = messageBox("Delete story", "Delete story \""
286 + story + "\"", Type.OKCANCEL);
287 if (mbox.getResult() == Result.OK) {
288 try {
289 reader.getLibrary().delete(luid);
290 if (main != null) {
291 main.refreshStories();
292 }
293 } catch (IOException e) {
294 // TODO: i18n
295 error("Fail to delete the story: \"" + story + "\"",
296 "Error", e);
297 }
298 }
299
e2d017a3
NR
300 return true;
301 case MENU_IMPORT_URL:
302 String clipboard = "";
303 try {
304 clipboard = ("" + Toolkit.getDefaultToolkit()
305 .getSystemClipboard().getData(DataFlavor.stringFlavor))
306 .trim();
307 } catch (Exception e) {
308 // No data will be handled
309 }
310
311 if (clipboard == null || !clipboard.startsWith("http")) {
312 clipboard = "";
313 }
314
315 String url = inputBox("Import story", "URL to import", clipboard)
316 .getText();
317
4f9478dc
NR
318 try {
319 if (!imprt(url)) {
e0fb1417 320 // TODO: i18n
4f9478dc
NR
321 error("URK not supported: " + url, "Import error");
322 }
323 } catch (IOException e) {
e0fb1417 324 // TODO: i18n
4f9478dc 325 error("Fail to import URL: " + url, "Import error", e);
e2d017a3
NR
326 }
327
328 return true;
329 case MENU_IMPORT_FILE:
4f9478dc 330 String filename = null;
e2d017a3 331 try {
4f9478dc 332 filename = fileOpenBox(".");
e2d017a3 333 if (!imprt(filename)) {
e0fb1417 334 // TODO: i18n
4f9478dc 335 error("File not supported: " + filename, "Import error");
e2d017a3
NR
336 }
337 } catch (IOException e) {
e0fb1417 338 // TODO: i18n
4f9478dc
NR
339 error("Fail to import file"
340 + (filename == null ? "" : ": " + filename),
341 "Import error", e);
e2d017a3 342 }
4f66bfa8
NR
343 return true;
344 case MENU_LIBRARY:
e0fb1417 345 showMain();
e2d017a3
NR
346 return true;
347 }
348
349 return super.onMenu(menu);
350 }
351
4f9478dc
NR
352 /**
353 * Import the given url.
354 * <p>
355 * Can fail if the host is not supported.
356 *
357 * @param url
358 *
359 * @return TRUE in case of success, FALSE if the host is not supported
360 *
361 * @throws IOException
362 * in case of I/O error
363 */
364 private boolean imprt(String url) throws IOException {
e2d017a3
NR
365 try {
366 reader.getLibrary().imprt(BasicReader.getUrl(url), null);
367 main.refreshStories();
368 return true;
4f9478dc 369 } catch (UnknownHostException e) {
e2d017a3
NR
370 return false;
371 }
372 }
16a81ef7
NR
373
374 @Override
350bc060
NR
375 public void openExternal(BasicLibrary lib, String luid, boolean sync)
376 throws IOException {
377 reader.openExternal(lib, luid, sync);
16a81ef7 378 }
cf9c5ed1 379
4f9478dc
NR
380 /**
381 * Display an error message and log it.
382 *
383 * @param message
384 * the message
385 * @param title
386 * the title of the error message
387 */
388 private void error(String message, String title) {
389 error(message, title, null);
390 }
391
392 /**
393 * Display an error message and log it, including the linked
394 * {@link Exception}.
395 *
396 * @param message
397 * the message
398 * @param title
399 * the title of the error message
400 * @param e
401 * the exception to log if any (can be NULL)
402 */
403 private void error(String message, String title, Exception e) {
404 Instance.getTraceHandler().error(title + ": " + message);
405 if (e != null) {
406 Instance.getTraceHandler().error(e);
407 }
408
409 if (e != null) {
410 messageBox(title, message //
411 + "\n" + e.getMessage());
412 } else {
413 messageBox(title, message);
414 }
415 }
416
cf9c5ed1
NR
417 /**
418 * Ask the user and, if confirmed, close the {@link TApplication} this
419 * {@link TWidget} is running on.
420 * <p>
421 * This should result in the program terminating.
422 *
423 * @param widget
424 * the {@link TWidget}
425 */
426 static public void close(TWidget widget) {
427 close(widget.getApplication());
428 }
429
430 /**
431 * Ask the user and, if confirmed, close the {@link TApplication}.
432 * <p>
433 * This should result in the program terminating.
434 *
435 * @param app
436 * the {@link TApplication}
437 */
438 static void close(TApplication app) {
439 // TODO: i18n
440 if (app.messageBox("Confirmation", "(TODO: i18n) Exit application?",
441 TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
442 app.exit();
443 }
444 }
c1873e56 445}