Add a new TUI system based upon Jexer (WIP)
[fanfix.git] / src / be / nikiroo / fanfix / reader / BasicReader.java
1 package be.nikiroo.fanfix.reader;
2
3 import java.awt.Desktop;
4 import java.io.File;
5 import java.io.IOException;
6 import java.net.MalformedURLException;
7 import java.net.URL;
8
9 import be.nikiroo.fanfix.Instance;
10 import be.nikiroo.fanfix.Library;
11 import be.nikiroo.fanfix.bundles.Config;
12 import be.nikiroo.fanfix.bundles.UiConfig;
13 import be.nikiroo.fanfix.data.MetaData;
14 import be.nikiroo.fanfix.data.Story;
15 import be.nikiroo.fanfix.supported.BasicSupport;
16 import be.nikiroo.utils.Progress;
17 import be.nikiroo.utils.ui.UIUtils;
18
19 /**
20 * The class that handles the different {@link Story} readers you can use.
21 * <p>
22 * All the readers should be accessed via {@link BasicReader#getReader()}.
23 *
24 * @author niki
25 */
26 public abstract class BasicReader {
27 public enum ReaderType {
28 /** Simple reader that outputs everything on the console */
29 CLI,
30 /** Reader that starts local programs to handle the stories */
31 GUI,
32 /** A text (UTF-8) reader with menu and text windows */
33 TUI,
34 }
35
36 private static ReaderType defaultType = ReaderType.GUI;
37 private Story story;
38 private ReaderType type;
39
40 /**
41 * Take the default reader type configuration from the config file.
42 */
43 static {
44 String typeString = Instance.getConfig().getString(Config.READER_TYPE);
45 if (typeString != null && !typeString.isEmpty()) {
46 try {
47 ReaderType type = ReaderType.valueOf(typeString.toUpperCase());
48 defaultType = type;
49 } catch (IllegalArgumentException e) {
50 // Do nothing
51 }
52 }
53 }
54
55 /**
56 * The type of this reader.
57 *
58 * @return the type
59 */
60 public ReaderType getType() {
61 return type;
62 }
63
64 /**
65 * The type of this reader.
66 *
67 * @param type
68 * the new type
69 */
70 protected BasicReader setType(ReaderType type) {
71 this.type = type;
72 return this;
73 }
74
75 /**
76 * Return the current {@link Story}.
77 *
78 * @return the {@link Story}
79 */
80 public Story getStory() {
81 return story;
82 }
83
84 /**
85 * Create a new {@link BasicReader} for a {@link Story} in the
86 * {@link Library} .
87 *
88 * @param luid
89 * the {@link Story} ID
90 * @param pg
91 * the optional progress reporter
92 *
93 * @throws IOException
94 * in case of I/O error
95 */
96 public void setStory(String luid, Progress pg) throws IOException {
97 story = Instance.getLibrary().getStory(luid, pg);
98 if (story == null) {
99 throw new IOException("Cannot retrieve story from library: " + luid);
100 }
101 }
102
103 /**
104 * Create a new {@link BasicReader} for an external {@link Story}.
105 *
106 * @param source
107 * the {@link Story} {@link URL}
108 * @param pg
109 * the optional progress reporter
110 *
111 * @throws IOException
112 * in case of I/O error
113 */
114 public void setStory(URL source, Progress pg) throws IOException {
115 BasicSupport support = BasicSupport.getSupport(source);
116 if (support == null) {
117 throw new IOException("URL not supported: " + source.toString());
118 }
119
120 story = support.process(source, pg);
121 if (story == null) {
122 throw new IOException(
123 "Cannot retrieve story from external source: "
124 + source.toString());
125
126 }
127 }
128
129 /**
130 * Start the {@link Story} Reading.
131 *
132 * @throws IOException
133 * in case of I/O error or if the {@link Story} was not
134 * previously set
135 */
136 public abstract void read() throws IOException;
137
138 /**
139 * Read the selected chapter (starting at 1).
140 *
141 * @param chapter
142 * the chapter
143 *
144 * @throws IOException
145 * in case of I/O error or if the {@link Story} was not
146 * previously set
147 */
148 public abstract void read(int chapter) throws IOException;
149
150 /**
151 * Start the reader in browse mode for the given type (or pass NULL for all
152 * types).
153 *
154 * @param type
155 * the type of {@link Story} to take into account, or NULL for
156 * all
157 */
158 public abstract void start(String type);
159
160 /**
161 * Return a new {@link BasicReader} ready for use if one is configured.
162 * <p>
163 * Can return NULL if none are configured.
164 *
165 * @return a {@link BasicReader}, or NULL if none configured
166 */
167 public static BasicReader getReader() {
168 try {
169 if (defaultType != null) {
170 switch (defaultType) {
171 case GUI:
172 UIUtils.setLookAndFeel();
173 return new LocalReader().setType(ReaderType.GUI);
174 case CLI:
175 return new CliReader().setType(ReaderType.CLI);
176 case TUI:
177 return new TuiReader().setType(ReaderType.TUI);
178 }
179 }
180 } catch (IOException e) {
181 Instance.syserr(new Exception("Cannot create a reader of type: "
182 + defaultType, e));
183 }
184
185 return null;
186 }
187
188 /**
189 * The default {@link ReaderType} used when calling
190 * {@link BasicReader#getReader()}.
191 *
192 * @return the default type
193 */
194 public static ReaderType getDefaultReaderType() {
195 return defaultType;
196 }
197
198 /**
199 * The default {@link ReaderType} used when calling
200 * {@link BasicReader#getReader()}.
201 *
202 * @param defaultType
203 * the new default type
204 */
205 public static void setDefaultReaderType(ReaderType defaultType) {
206 BasicReader.defaultType = defaultType;
207 }
208
209 /**
210 * Return an {@link URL} from this {@link String}, be it a file path or an
211 * actual {@link URL}.
212 *
213 * @param sourceString
214 * the source
215 *
216 * @return the corresponding {@link URL}
217 *
218 * @throws MalformedURLException
219 * if this is neither a file nor a conventional {@link URL}
220 */
221 public static URL getUrl(String sourceString) throws MalformedURLException {
222 if (sourceString == null || sourceString.isEmpty()) {
223 throw new MalformedURLException("Empty url");
224 }
225
226 URL source = null;
227 try {
228 source = new URL(sourceString);
229 } catch (MalformedURLException e) {
230 File sourceFile = new File(sourceString);
231 source = sourceFile.toURI().toURL();
232 }
233
234 return source;
235 }
236
237 // open with external player the related file
238 public static void open(String luid) throws IOException {
239 MetaData meta = Instance.getLibrary().getInfo(luid);
240 File target = Instance.getLibrary().getFile(luid);
241
242 open(meta, target);
243 }
244
245 // open with external player the related file
246 protected static void open(MetaData meta, File target) throws IOException {
247 String program = null;
248 if (meta.isImageDocument()) {
249 program = Instance.getUiConfig().getString(
250 UiConfig.IMAGES_DOCUMENT_READER);
251 } else {
252 program = Instance.getUiConfig().getString(
253 UiConfig.NON_IMAGES_DOCUMENT_READER);
254 }
255
256 if (program != null && program.trim().isEmpty()) {
257 program = null;
258 }
259
260 if (program == null) {
261 try {
262 Desktop.getDesktop().browse(target.toURI());
263 } catch (UnsupportedOperationException e) {
264 Runtime.getRuntime().exec(
265 new String[] { "xdg-open", target.getAbsolutePath() });
266
267 }
268 } else {
269 Runtime.getRuntime().exec(
270 new String[] { program, target.getAbsolutePath() });
271 }
272 }
273 }