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