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