tui: improve mode selection
[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;
b2274262 25import be.nikiroo.fanfix.reader.tui.TuiReaderMainWindow.Mode;
6322ab64 26import be.nikiroo.utils.Progress;
c1873e56 27
6322ab64 28/**
bc2ea776
NR
29 * Manages the TUI general mode and links and manages the {@link TWindow}s.
30 * <p>
31 * It will also enclose a {@link Reader} and simply handle the reading part
32 * differently (it will create the required sub-windows and display them).
6322ab64
NR
33 *
34 * @author niki
35 */
36class TuiReaderApplication extends TApplication implements Reader {
e2d017a3
NR
37 public static final int MENU_OPEN = 1025;
38 public static final int MENU_IMPORT_URL = 1026;
39 public static final int MENU_IMPORT_FILE = 1027;
40 public static final int MENU_EXPORT = 1028;
4f66bfa8
NR
41 public static final int MENU_LIBRARY = 1029;
42 public static final int MENU_EXIT = 1030;
e2d017a3 43
cf9c5ed1
NR
44 public static final TCommand CMD_EXIT = new TCommand(MENU_EXIT) {
45 };
46
6322ab64 47 private Reader reader;
e2d017a3 48 private TuiReaderMainWindow main;
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 67 showMain();
b2274262 68 main.setMode(Mode.SOURCE, 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
bc2ea776
NR
177 private void init(Reader reader) {
178 this.reader = reader;
179
581d42c0
NR
180 // TODO: traces/errors?
181 Instance.setTraceHandler(null);
a8209dd0 182
e2d017a3
NR
183 // Add the menus TODO: i18n
184 TMenu fileMenu = addMenu("&File");
185 fileMenu.addItem(MENU_OPEN, "&Open");
186 fileMenu.addItem(MENU_EXPORT, "&Save as...");
187 // TODO: Move to...
188 fileMenu.addSeparator();
189 fileMenu.addItem(MENU_IMPORT_URL, "Import &URL...");
190 fileMenu.addItem(MENU_IMPORT_FILE, "Import &file...");
191 fileMenu.addSeparator();
4f66bfa8
NR
192 fileMenu.addItem(MENU_LIBRARY, "Lib&rary");
193 fileMenu.addSeparator();
e2d017a3
NR
194 fileMenu.addItem(MENU_EXIT, "E&xit");
195
5b00c122 196 setStatusBar(fileMenu, "File-management "
e2d017a3 197 + "commands (Open, Save, Print, etc.)");
e2d017a3
NR
198
199 // TODO: Edit: re-download, delete
200
201 //
202
bc2ea776 203 addWindowMenu();
bc2ea776
NR
204
205 getBackend().setTitle("Fanfix");
206 }
e2d017a3 207
bce88a00
NR
208 @Override
209 protected boolean onCommand(TCommandEvent command) {
210 if (command.getCmd().equals(TuiReaderMainWindow.CMD_SEARCH)) {
211 messageBox("title", "caption");
212 return true;
213 }
214 return super.onCommand(command);
215 }
216
e2d017a3
NR
217 @Override
218 protected boolean onMenu(TMenuEvent menu) {
219 // TODO: i18n
220 switch (menu.getId()) {
221 case MENU_EXIT:
cf9c5ed1 222 close(this);
e0fb1417
NR
223 return true;
224 case MENU_OPEN:
225 String openfile = null;
226 try {
227 openfile = fileOpenBox(".");
228 reader.setMeta(BasicReader.getUrl(openfile), null);
229 read();
230 } catch (IOException e) {
231 // TODO: i18n
232 error("Fail to open file"
233 + (openfile == null ? "" : ": " + openfile),
234 "Import error", e);
235 }
236
e2d017a3
NR
237 return true;
238 case MENU_IMPORT_URL:
239 String clipboard = "";
240 try {
241 clipboard = ("" + Toolkit.getDefaultToolkit()
242 .getSystemClipboard().getData(DataFlavor.stringFlavor))
243 .trim();
244 } catch (Exception e) {
245 // No data will be handled
246 }
247
248 if (clipboard == null || !clipboard.startsWith("http")) {
249 clipboard = "";
250 }
251
252 String url = inputBox("Import story", "URL to import", clipboard)
253 .getText();
254
4f9478dc
NR
255 try {
256 if (!imprt(url)) {
e0fb1417 257 // TODO: i18n
4f9478dc
NR
258 error("URK not supported: " + url, "Import error");
259 }
260 } catch (IOException e) {
e0fb1417 261 // TODO: i18n
4f9478dc 262 error("Fail to import URL: " + url, "Import error", e);
e2d017a3
NR
263 }
264
265 return true;
266 case MENU_IMPORT_FILE:
4f9478dc 267 String filename = null;
e2d017a3 268 try {
4f9478dc 269 filename = fileOpenBox(".");
e2d017a3 270 if (!imprt(filename)) {
e0fb1417 271 // TODO: i18n
4f9478dc 272 error("File not supported: " + filename, "Import error");
e2d017a3
NR
273 }
274 } catch (IOException e) {
e0fb1417 275 // TODO: i18n
4f9478dc
NR
276 error("Fail to import file"
277 + (filename == null ? "" : ": " + filename),
278 "Import error", e);
e2d017a3 279 }
4f66bfa8
NR
280 return true;
281 case MENU_LIBRARY:
e0fb1417 282 showMain();
e2d017a3
NR
283 return true;
284 }
285
286 return super.onMenu(menu);
287 }
288
4f9478dc
NR
289 /**
290 * Import the given url.
291 * <p>
292 * Can fail if the host is not supported.
293 *
294 * @param url
295 *
296 * @return TRUE in case of success, FALSE if the host is not supported
297 *
298 * @throws IOException
299 * in case of I/O error
300 */
301 private boolean imprt(String url) throws IOException {
e2d017a3
NR
302 try {
303 reader.getLibrary().imprt(BasicReader.getUrl(url), null);
304 main.refreshStories();
305 return true;
4f9478dc 306 } catch (UnknownHostException e) {
e2d017a3
NR
307 return false;
308 }
309 }
16a81ef7
NR
310
311 @Override
312 public void openExternal(BasicLibrary lib, String luid) throws IOException {
313 reader.openExternal(lib, luid);
314 }
cf9c5ed1 315
4f9478dc
NR
316 /**
317 * Display an error message and log it.
318 *
319 * @param message
320 * the message
321 * @param title
322 * the title of the error message
323 */
324 private void error(String message, String title) {
325 error(message, title, null);
326 }
327
328 /**
329 * Display an error message and log it, including the linked
330 * {@link Exception}.
331 *
332 * @param message
333 * the message
334 * @param title
335 * the title of the error message
336 * @param e
337 * the exception to log if any (can be NULL)
338 */
339 private void error(String message, String title, Exception e) {
340 Instance.getTraceHandler().error(title + ": " + message);
341 if (e != null) {
342 Instance.getTraceHandler().error(e);
343 }
344
345 if (e != null) {
346 messageBox(title, message //
347 + "\n" + e.getMessage());
348 } else {
349 messageBox(title, message);
350 }
351 }
352
cf9c5ed1
NR
353 /**
354 * Ask the user and, if confirmed, close the {@link TApplication} this
355 * {@link TWidget} is running on.
356 * <p>
357 * This should result in the program terminating.
358 *
359 * @param widget
360 * the {@link TWidget}
361 */
362 static public void close(TWidget widget) {
363 close(widget.getApplication());
364 }
365
366 /**
367 * Ask the user and, if confirmed, close the {@link TApplication}.
368 * <p>
369 * This should result in the program terminating.
370 *
371 * @param app
372 * the {@link TApplication}
373 */
374 static void close(TApplication app) {
375 // TODO: i18n
376 if (app.messageBox("Confirmation", "(TODO: i18n) Exit application?",
377 TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
378 app.exit();
379 }
380 }
c1873e56 381}