Commit | Line | Data |
---|---|---|
16a81ef7 | 1 | package be.nikiroo.fanfix.reader.ui; |
a6395bef | 2 | |
edd46289 | 3 | import java.awt.Desktop; |
a6395bef NR |
4 | import java.awt.EventQueue; |
5 | import java.io.File; | |
6 | import java.io.IOException; | |
b42117f1 NR |
7 | import java.net.URISyntaxException; |
8 | ||
9 | import javax.swing.JEditorPane; | |
10 | import javax.swing.JLabel; | |
11 | import javax.swing.JOptionPane; | |
12 | import javax.swing.event.HyperlinkEvent; | |
13 | import javax.swing.event.HyperlinkListener; | |
a6395bef NR |
14 | |
15 | import be.nikiroo.fanfix.Instance; | |
b42117f1 | 16 | import be.nikiroo.fanfix.VersionCheck; |
bc2ea776 | 17 | import be.nikiroo.fanfix.data.MetaData; |
a6395bef | 18 | import be.nikiroo.fanfix.data.Story; |
ff05b828 NR |
19 | import be.nikiroo.fanfix.library.BasicLibrary; |
20 | import be.nikiroo.fanfix.library.CacheLibrary; | |
16a81ef7 | 21 | import be.nikiroo.fanfix.reader.BasicReader; |
c3b229a1 | 22 | import be.nikiroo.fanfix.reader.Reader; |
3b2b638f | 23 | import be.nikiroo.utils.Progress; |
b42117f1 | 24 | import be.nikiroo.utils.Version; |
b0e88ebd | 25 | import be.nikiroo.utils.ui.UIUtils; |
a6395bef | 26 | |
c3b229a1 NR |
27 | /** |
28 | * This class implements a graphical {@link Reader} using the Swing library from | |
29 | * Java. | |
30 | * <p> | |
31 | * It can thus be themed to look native-like, or metal-like, or use any other | |
32 | * theme you may want to try. | |
33 | * <p> | |
34 | * We actually try to enable native look-alike mode on start. | |
35 | * | |
36 | * @author niki | |
37 | */ | |
5dd985cf | 38 | class GuiReader extends BasicReader { |
b0e88ebd NR |
39 | static private boolean nativeLookLoaded; |
40 | ||
ff05b828 NR |
41 | private CacheLibrary cacheLib; |
42 | ||
43 | private File cacheDir; | |
a6395bef | 44 | |
c3b229a1 NR |
45 | /** |
46 | * Create a new graphical {@link Reader}. | |
47 | * | |
48 | * @throws IOException | |
49 | * in case of I/O errors | |
50 | */ | |
5dd985cf | 51 | public GuiReader() throws IOException { |
c3b229a1 | 52 | // TODO: allow different themes? |
b0e88ebd NR |
53 | if (!nativeLookLoaded) { |
54 | UIUtils.setLookAndFeel(); | |
55 | nativeLookLoaded = true; | |
56 | } | |
57 | ||
ff05b828 NR |
58 | cacheDir = Instance.getReaderDir(); |
59 | cacheDir.mkdirs(); | |
60 | if (!cacheDir.exists()) { | |
a6395bef | 61 | throw new IOException( |
ff05b828 NR |
62 | "Cannote create cache directory for local reader: " |
63 | + cacheDir); | |
64 | } | |
65 | } | |
66 | ||
67 | @Override | |
68 | public synchronized BasicLibrary getLibrary() { | |
69 | if (cacheLib == null) { | |
70 | BasicLibrary lib = super.getLibrary(); | |
71 | if (lib instanceof CacheLibrary) { | |
72 | cacheLib = (CacheLibrary) lib; | |
73 | } else { | |
74 | cacheLib = new CacheLibrary(cacheDir, lib); | |
75 | } | |
a6395bef NR |
76 | } |
77 | ||
ff05b828 | 78 | return cacheLib; |
a6395bef NR |
79 | } |
80 | ||
211f7ddb | 81 | @Override |
a6395bef | 82 | public void read() throws IOException { |
bc2ea776 NR |
83 | MetaData meta = getMeta(); |
84 | ||
85 | if (meta == null) { | |
edd46289 NR |
86 | throw new IOException("No story to read"); |
87 | } | |
88 | ||
bc2ea776 | 89 | read(meta.getLuid(), null); |
a6395bef NR |
90 | } |
91 | ||
9843a5e5 NR |
92 | /** |
93 | * Check if the {@link Story} denoted by this Library UID is present in the | |
5dd985cf | 94 | * {@link GuiReader} cache. |
9843a5e5 NR |
95 | * |
96 | * @param luid | |
97 | * the Library UID | |
98 | * | |
99 | * @return TRUE if it is | |
100 | */ | |
10d558d2 | 101 | public boolean isCached(String luid) { |
ff05b828 | 102 | return cacheLib.isCached(luid); |
10d558d2 NR |
103 | } |
104 | ||
211f7ddb | 105 | @Override |
b0e88ebd | 106 | public void browse(String type) { |
b42117f1 NR |
107 | // TODO: improve presentation of update message |
108 | final VersionCheck updates = VersionCheck.check(); | |
109 | StringBuilder builder = new StringBuilder(); | |
110 | ||
111 | final JEditorPane updateMessage = new JEditorPane("text/html", ""); | |
112 | if (updates.isNewVersionAvailable()) { | |
113 | builder.append("A new version of the program is available at <span style='color: blue;'>https://github.com/nikiroo/fanfix/releases</span>"); | |
114 | builder.append("<br>"); | |
115 | builder.append("<br>"); | |
116 | for (Version v : updates.getNewer()) { | |
117 | builder.append("\t<b>Version " + v + "</b>"); | |
118 | builder.append("<br>"); | |
119 | builder.append("<ul>"); | |
120 | for (String item : updates.getChanges().get(v)) { | |
121 | builder.append("<li>" + item + "</li>"); | |
122 | } | |
123 | builder.append("</ul>"); | |
124 | } | |
125 | ||
126 | // html content | |
127 | updateMessage.setText("<html><body>" // | |
128 | + builder// | |
129 | + "</body></html>"); | |
130 | ||
131 | // handle link events | |
132 | updateMessage.addHyperlinkListener(new HyperlinkListener() { | |
211f7ddb | 133 | @Override |
b42117f1 NR |
134 | public void hyperlinkUpdate(HyperlinkEvent e) { |
135 | if (e.getEventType().equals( | |
136 | HyperlinkEvent.EventType.ACTIVATED)) | |
137 | try { | |
138 | Desktop.getDesktop().browse(e.getURL().toURI()); | |
139 | } catch (IOException ee) { | |
62c63b07 | 140 | Instance.getTraceHandler().error(ee); |
b42117f1 | 141 | } catch (URISyntaxException ee) { |
62c63b07 | 142 | Instance.getTraceHandler().error(ee); |
b42117f1 NR |
143 | } |
144 | } | |
145 | }); | |
146 | updateMessage.setEditable(false); | |
147 | updateMessage.setBackground(new JLabel().getBackground()); | |
148 | } | |
149 | ||
333f0e7b | 150 | final String typeFinal = type; |
a6395bef | 151 | EventQueue.invokeLater(new Runnable() { |
211f7ddb | 152 | @Override |
a6395bef | 153 | public void run() { |
b42117f1 NR |
154 | if (updates.isNewVersionAvailable()) { |
155 | int rep = JOptionPane.showConfirmDialog(null, | |
156 | updateMessage, "Updates available", | |
157 | JOptionPane.OK_CANCEL_OPTION); | |
158 | if (rep == JOptionPane.OK_OPTION) { | |
159 | updates.ok(); | |
160 | } else { | |
161 | updates.ignore(); | |
162 | } | |
163 | } | |
164 | ||
e42573a0 | 165 | new GuiReaderFrame(GuiReader.this, typeFinal).setVisible(true); |
a6395bef NR |
166 | } |
167 | }); | |
168 | } | |
10d558d2 | 169 | |
16a81ef7 NR |
170 | @Override |
171 | public void start(File target, String program) throws IOException { | |
172 | if (program == null) { | |
173 | try { | |
174 | Desktop.getDesktop().browse(target.toURI()); | |
175 | } catch (UnsupportedOperationException e) { | |
176 | super.start(target, program); | |
177 | } | |
178 | } else { | |
179 | super.start(target, program); | |
180 | } | |
181 | } | |
182 | ||
c3b229a1 NR |
183 | /** |
184 | * Delete the {@link Story} from the cache if it is present, but <b>NOT</b> | |
185 | * from the main library. | |
186 | * <p> | |
187 | * The next time we try to retrieve the {@link Story}, it may be required to | |
188 | * cache it again. | |
189 | * | |
190 | * @param luid | |
191 | * the luid of the {@link Story} | |
192 | */ | |
754a5bc2 | 193 | void clearLocalReaderCache(String luid) { |
68e2c6d2 | 194 | try { |
ff05b828 | 195 | cacheLib.clearFromCache(luid); |
68e2c6d2 | 196 | } catch (IOException e) { |
62c63b07 | 197 | Instance.getTraceHandler().error(e); |
68e2c6d2 | 198 | } |
10d558d2 NR |
199 | } |
200 | ||
c3b229a1 NR |
201 | /** |
202 | * Forward the delete operation to the main library. | |
203 | * <p> | |
204 | * The {@link Story} will be deleted from the main library as well as the | |
205 | * cache if present. | |
206 | * | |
207 | * @param luid | |
208 | * the {@link Story} to delete | |
209 | */ | |
10d558d2 | 210 | void delete(String luid) { |
68e2c6d2 | 211 | try { |
ff05b828 | 212 | cacheLib.delete(luid); |
68e2c6d2 | 213 | } catch (IOException e) { |
62c63b07 | 214 | Instance.getTraceHandler().error(e); |
68e2c6d2 | 215 | } |
10d558d2 | 216 | } |
edd46289 | 217 | |
c3b229a1 NR |
218 | /** |
219 | * "Open" the given {@link Story}. It usually involves starting an external | |
220 | * program adapted to the given file type. | |
221 | * | |
222 | * @param luid | |
223 | * the luid of the {@link Story} to open | |
224 | * @param pg | |
225 | * the optional progress (we may need to prepare the | |
226 | * {@link Story} for reading | |
227 | * | |
228 | * @throws IOException | |
229 | * in case of I/O errors | |
230 | */ | |
bc2ea776 | 231 | void read(String luid, Progress pg) throws IOException { |
ff05b828 | 232 | File file = cacheLib.getFile(luid, pg); |
edd46289 | 233 | |
bc2ea776 | 234 | // TODO: show a special page for the chapter? |
c3b229a1 NR |
235 | // We could also implement an internal viewer, both for image and |
236 | // non-image documents | |
6322ab64 | 237 | openExternal(getLibrary().getInfo(luid), file); |
edd46289 | 238 | } |
70c9b112 | 239 | |
c3b229a1 NR |
240 | /** |
241 | * Change the source of the given {@link Story} (the source is the main | |
242 | * information used to group the stories together). | |
243 | * <p> | |
244 | * In other words, <b>move</b> the {@link Story} into other source. | |
245 | * <p> | |
246 | * The source can be a new one, it needs not exist before hand. | |
247 | * | |
248 | * @param luid | |
249 | * the luid of the {@link Story} to move | |
250 | * @param newSource | |
251 | * the new source | |
252 | */ | |
253 | void changeSource(String luid, String newSource) { | |
68e2c6d2 | 254 | try { |
ff05b828 | 255 | cacheLib.changeSource(luid, newSource, null); |
68e2c6d2 | 256 | } catch (IOException e) { |
62c63b07 | 257 | Instance.getTraceHandler().error(e); |
68e2c6d2 | 258 | } |
70c9b112 | 259 | } |
a6395bef | 260 | } |