1 | // Jomic - a viewer for comic book archives. |
2 | // Copyright (C) 2004-2011 Thomas Aglassinger |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | package net.sf.jomic; |
17 | |
18 | import java.io.File; |
19 | import java.io.IOException; |
20 | import java.io.PrintStream; |
21 | import java.util.Iterator; |
22 | |
23 | import javax.imageio.ImageIO; |
24 | import javax.swing.SwingUtilities; |
25 | |
26 | import net.sf.jomic.comic.ComicCache; |
27 | import net.sf.jomic.common.JomicJSAP; |
28 | import net.sf.jomic.common.JomicStartup; |
29 | import net.sf.jomic.common.PropertyConstants; |
30 | import net.sf.jomic.common.Settings; |
31 | import net.sf.jomic.common.SplashScreen; |
32 | import net.sf.jomic.common.StartupTools; |
33 | import net.sf.jomic.common.Version; |
34 | import net.sf.jomic.tools.FileTools; |
35 | import net.sf.jomic.tools.ItemMustBeDownloadedException; |
36 | import net.sf.jomic.tools.LocaleTools; |
37 | import net.sf.jomic.tools.StandardConstants; |
38 | import net.sf.jomic.tools.StringTools; |
39 | import net.sf.jomic.ui.AwtExceptionHandler; |
40 | import net.sf.jomic.ui.JomicApplication; |
41 | |
42 | import org.apache.commons.logging.Log; |
43 | import org.apache.commons.logging.LogFactory; |
44 | |
45 | import com.martiansoftware.jsap.JSAPResult; |
46 | |
47 | /** |
48 | * Platform independent Jomic application to view comic book archives. |
49 | * |
50 | * @author Thomas Aglassinger |
51 | */ |
52 | public class Jomic implements StandardConstants |
53 | { |
54 | private static Log logger = LogFactory.getLog(Jomic.class); |
55 | |
56 | /** |
57 | * The comic to open on startup; <code>null</code> means no comic. |
58 | */ |
59 | private File comicFile; |
60 | private boolean readyForOpen; |
61 | |
62 | public Jomic(File newComicFile) { |
63 | assert JomicStartup.instance().isStartup() : JomicStartup.class.getName() |
64 | + ".startup() must be called before creating a instance of " + Jomic.class.getName(); |
65 | comicFile = newComicFile; |
66 | logEnvironment(); |
67 | } |
68 | |
69 | protected void setComicFile(File newComicFile) { |
70 | comicFile = newComicFile; |
71 | } |
72 | |
73 | private void setupImaging(Settings settings) |
74 | throws IOException { |
75 | assert settings != null; |
76 | |
77 | // Set image cache |
78 | ComicCache comicCache = ComicCache.instance(); |
79 | |
80 | comicCache.setUp(settings.getCacheDir(), MEGA_BYTE * settings.getArchiveCacheSizeInMb()); |
81 | |
82 | // Set cache directory for ImageIO |
83 | try { |
84 | File imageIoCacheDir = settings.getImageIOCache(); |
85 | |
86 | ImageIO.setCacheDirectory(imageIoCacheDir); |
87 | } catch (IOException error) { |
88 | logger.warn("cannot set ImageIO cache directory; using internal default cache", error); |
89 | } |
90 | } |
91 | |
92 | /** |
93 | * Is the JomiFrame ready to open archives, in other words: is the initialization finished? |
94 | */ |
95 | protected boolean isReadyForOpen() { |
96 | return readyForOpen; |
97 | } |
98 | |
99 | /** |
100 | * Run the Jomic application using the command line arguments specified in <code>arguments</code> |
101 | * . |
102 | */ |
103 | public static void main(final String[] arguments) { |
104 | JomicStartup startup = JomicStartup.instance(); |
105 | StartupTools startupTools = StartupTools.instance(); |
106 | SplashScreen splashScreen = startupTools.openSplashScreen(); |
107 | |
108 | try { |
109 | startup.startup(); |
110 | |
111 | JomicJSAP jsap = new JomicJSAP(); |
112 | JSAPResult options = jsap.parse(arguments); |
113 | |
114 | if (!options.success()) { |
115 | // Throw exception for broken command line. |
116 | Iterator errorRider = options.getErrorMessageIterator(); |
117 | String errorMessage; |
118 | |
119 | if (errorRider.hasNext()) { |
120 | errorMessage = (String) errorRider.next(); |
121 | } else { |
122 | errorMessage = null; |
123 | } |
124 | throw new IllegalArgumentException(errorMessage); |
125 | } |
126 | |
127 | Settings settings = Settings.instance(); |
128 | |
129 | if (options.getBoolean(JomicJSAP.ARG_CLEAR_CACHE)) { |
130 | clearCache(); |
131 | } else if (options.getBoolean(JomicJSAP.ARG_HELP)) { |
132 | jsap.printHelp(System.err); |
133 | } else if (options.getBoolean(JomicJSAP.ARG_LICENSE)) { |
134 | jsap.printLicense(System.out); |
135 | } else if (options.getBoolean(JomicJSAP.ARG_VERSION)) { |
136 | jsap.printVersion(System.out); |
137 | } else { |
138 | String comicFileName = options.getString(JomicJSAP.ARG_FILE); |
139 | boolean continueWithLastFile = |
140 | options.getBoolean((JomicJSAP.ARG_CONTINUE)); |
141 | File comicFile = null; |
142 | |
143 | settings.read(settings.getSettingsFile()); |
144 | if (comicFileName == null) { |
145 | if (continueWithLastFile) { |
146 | comicFile = settings.getMostRecentFile(); |
147 | } |
148 | } else { |
149 | comicFile = new File(comicFileName); |
150 | } |
151 | |
152 | System.setProperty("sun.awt.exception.handler", AwtExceptionHandler.class.getName()); |
153 | if (logger.isInfoEnabled()) { |
154 | logger.info("comic archive passed from command line: \"" + comicFile + "\""); |
155 | } |
156 | SwingUtilities.invokeLater(new JomicRunner(new Jomic(comicFile))); |
157 | } |
158 | } catch (Throwable error) { |
159 | exitWithError(error); |
160 | } finally { |
161 | startupTools.disposeSplashScreen(splashScreen); |
162 | } |
163 | } |
164 | |
165 | /** |
166 | * Open comic viewer. |
167 | */ |
168 | public void show() |
169 | throws IOException { |
170 | Settings settings = Settings.instance(); |
171 | |
172 | setupImaging(settings); |
173 | |
174 | // TODO: fix that size is always reset |
175 | // fixSizeAndLocation(); |
176 | JomicApplication application = JomicApplication.instance(); |
177 | |
178 | readyForOpen = true; |
179 | if (comicFile != null) { |
180 | application.openInNewWindow(comicFile); |
181 | comicFile = null; |
182 | } else { |
183 | application.openInNewWindow(); |
184 | } |
185 | } |
186 | |
187 | /** |
188 | * Show dialog for <code>error</code> and exit. If <code>error</code> is a <code>ItemMustBeDownloadedException</code> |
189 | * , also offer to download the missing item. |
190 | * |
191 | * @see ItemMustBeDownloadedException |
192 | */ |
193 | protected static void exitWithError(Throwable error) { |
194 | logger.error("stopping application", error); |
195 | if (error instanceof ItemMustBeDownloadedException) { |
196 | ItemMustBeDownloadedException downloadError = (ItemMustBeDownloadedException) error; |
197 | |
198 | downloadError.show(); |
199 | try { |
200 | // HACK: prevent browser process from being killed prematurely when Jomic exits |
201 | Thread.sleep(PropertyConstants.BROWSER_STARTUP_DELAY); |
202 | } catch (InterruptedException interruption) { |
203 | logger.warn("sleep interrupted", interruption); |
204 | } |
205 | } else { |
206 | String message = LocaleTools.instance().getMessage("errors.cannotLaunchJomic"); |
207 | |
208 | JomicStartup.instance().showError(message, error); |
209 | } |
210 | JomicStartup.instance().exit(1); |
211 | } |
212 | |
213 | /** |
214 | * Remove cache directory and all files stored in any of the caches. |
215 | */ |
216 | static void clearCache() |
217 | throws IOException { |
218 | Settings settings = Settings.instance(); |
219 | |
220 | File settingsFile = settings.getSettingsFile(); |
221 | |
222 | settings.read(settingsFile); |
223 | |
224 | File cacheDir = settings.getCacheDir(); |
225 | FileTools fileTools = FileTools.instance(); |
226 | StringTools stringTools = StringTools.instance(); |
227 | PrintStream errStream = System.err; |
228 | |
229 | errStream.println("Clearing cache directory: " + stringTools.sourced(cacheDir)); |
230 | logger.info("Clearing cache directory: " + stringTools.sourced(cacheDir)); |
231 | fileTools.attemptToDeleteAll(cacheDir, logger); |
232 | } |
233 | |
234 | /** |
235 | * Log information about the environment Jomic runs on. This comes in handy when users submit |
236 | * logs with bug reports. |
237 | */ |
238 | private void logEnvironment() { |
239 | if (logger.isInfoEnabled()) { |
240 | logger.info( |
241 | "version: jomic = " |
242 | + Version.VERSION_TAG |
243 | + ", java = " |
244 | + System.getProperty("java.version") |
245 | + ", platform = " |
246 | + System.getProperty("os.name") |
247 | + "-" |
248 | + System.getProperty("os.version") |
249 | + "-" |
250 | + System.getProperty("os.arch")); |
251 | } |
252 | } |
253 | |
254 | /** |
255 | * Runnable to show Jomic while in event-dispatching thread. |
256 | * |
257 | * @author Thomas Aglassinger |
258 | */ |
259 | protected static final class JomicRunner implements Runnable |
260 | { |
261 | private Jomic jomic; |
262 | |
263 | protected JomicRunner(Jomic newJomic) { |
264 | jomic = newJomic; |
265 | } |
266 | |
267 | public void run() { |
268 | try { |
269 | JomicApplication.instance().setUpFramelessMenuBar(); |
270 | jomic.show(); |
271 | } catch (Throwable error) { |
272 | exitWithError(error); |
273 | } |
274 | } |
275 | } |
276 | } |