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