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.ui; |
17 | |
18 | import java.awt.event.ActionEvent; |
19 | import java.awt.event.ActionListener; |
20 | import java.io.File; |
21 | import java.util.ArrayList; |
22 | import java.util.Collections; |
23 | import java.util.Iterator; |
24 | import java.util.List; |
25 | |
26 | import javax.swing.JProgressBar; |
27 | |
28 | import net.sf.jomic.comic.ComicException; |
29 | import net.sf.jomic.comic.ComicFileFilter; |
30 | import net.sf.jomic.comic.ComicToConvert; |
31 | import net.sf.jomic.comic.Conversion; |
32 | import net.sf.jomic.comic.ConversionReport; |
33 | import net.sf.jomic.comic.ConversionReportItem; |
34 | import net.sf.jomic.comic.ConvertComicTask; |
35 | import net.sf.jomic.common.JomicTools; |
36 | import net.sf.jomic.tools.FileTools; |
37 | import net.sf.jomic.tools.LocaleTools; |
38 | import net.sf.jomic.tools.NestedTask; |
39 | import net.sf.jomic.tools.ProgressChangeListener; |
40 | import net.sf.jomic.tools.StringTools; |
41 | import net.sf.jomic.tools.SwingWorker; |
42 | import net.sf.jomic.tools.Task; |
43 | |
44 | import org.apache.commons.logging.Log; |
45 | import org.apache.commons.logging.LogFactory; |
46 | |
47 | /** |
48 | * SwingWorker to convert comics. |
49 | * |
50 | * @author Thomas Aglassinger |
51 | */ |
52 | public class ConvertWorker extends SwingWorker implements ActionListener, ProgressChangeListener |
53 | { |
54 | private ComicFileFilter comicFileFilter; |
55 | private Conversion conversion; |
56 | private FileTools fileTools; |
57 | private File[] filesToConvert; |
58 | private JomicTools jomicTools; |
59 | private LocaleTools localeTools; |
60 | private Log logger; |
61 | private JProgressBar progressBar; |
62 | private StringTools stringTools; |
63 | private File targetDir; |
64 | |
65 | public ConvertWorker(File newTargetDir, File[] newFilesToConvert, Conversion newConversion) { |
66 | this(); |
67 | assert newTargetDir != null; |
68 | assert newFilesToConvert != null; |
69 | assert newFilesToConvert.length != 0; |
70 | assert newConversion != null; |
71 | |
72 | if (logger.isInfoEnabled()) { |
73 | logger.info("setup worker to convert to: " + stringTools.sourced(newTargetDir)); |
74 | } |
75 | targetDir = newTargetDir; |
76 | filesToConvert = newFilesToConvert; |
77 | conversion = newConversion; |
78 | } |
79 | |
80 | private ConvertWorker() { |
81 | super(); |
82 | logger = LogFactory.getLog(ConvertWorker.class); |
83 | fileTools = FileTools.instance(); |
84 | jomicTools = JomicTools.instance(); |
85 | stringTools = StringTools.instance(); |
86 | localeTools = LocaleTools.instance(); |
87 | comicFileFilter = new ComicFileFilter(); |
88 | } |
89 | |
90 | /** |
91 | * Create a list of comics to be converted to <code>targetDir</code>. according to the rules |
92 | * listed below. |
93 | * <ul> |
94 | * <li> For all files in <code>filesToConvert</code>, add a target comic with the same name |
95 | * directly in <code>targetDir</code>. For example, /comics/s00paman/issue03.cbz with a |
96 | * <code>targetDir</code> /stuff converts the comic to /stuff/issue03.cbz. |
97 | * <li> For all directories in <code>filesToConvert</code>, recursively scan the directory |
98 | * for comic files, and add target comics in <code>targetDir</code> relative to the path of |
99 | * the source comic regarding the initial source directory from which the scan started. For |
100 | * example a scan starting at /comics and detecting /comics/s00paman/issue03.cbz with a |
101 | * <code>targetDir</code> /stuff converts the comic to /stuff/s00paman/issue03.cbz. |
102 | * </ul> |
103 | * |
104 | */ |
105 | public List getComicsToConvert() { |
106 | List result = new ArrayList(); |
107 | |
108 | for (int i = 0; i < filesToConvert.length; i += 1) { |
109 | File fileToConvert = filesToConvert[i]; |
110 | |
111 | if (fileToConvert.isDirectory()) { |
112 | addComicsToConvert(result, fileToConvert, fileToConvert); |
113 | } else { |
114 | ComicToConvert comicToConvert = new ComicToConvert( |
115 | fileToConvert.getParentFile(), |
116 | targetDir, |
117 | fileToConvert.getName(), |
118 | conversion.getComicFormatSuffix()); |
119 | |
120 | result.add(comicToConvert); |
121 | } |
122 | } |
123 | // TODO: remove comics with same source and target |
124 | // TODO: warn about comics with different source and same target |
125 | Collections.sort(result); |
126 | return result; |
127 | } |
128 | |
129 | /** |
130 | * Get int-progress from long currentValue normalized in relation to maxValue. |
131 | */ |
132 | private int getNormalizedProgress(long currentValue, long maxValue) { |
133 | double value = (double) currentValue / maxValue; |
134 | |
135 | return (int) (value * (Integer.MAX_VALUE / 2)); |
136 | } |
137 | |
138 | /** |
139 | * Called when on of the buttons in report frame is pressed. |
140 | */ |
141 | public void actionPerformed(ActionEvent event) { |
142 | try { |
143 | assert event != null; |
144 | String command = event.getActionCommand(); |
145 | |
146 | assert command != null; |
147 | if (command.equals(ConversionReportFrame.COMMAND_CANCEL)) { |
148 | interrupt(); |
149 | } else { |
150 | assert false : "command=" + command; |
151 | } |
152 | } catch (Throwable error) { |
153 | jomicTools.showError(event, error); |
154 | } |
155 | } |
156 | |
157 | public Object construct() { |
158 | // Reduce thread priority so the user can still reasonbly view comics |
159 | // while the conversion takes place in the background. |
160 | Thread.currentThread().setPriority(Thread.MIN_PRIORITY); |
161 | |
162 | ConversionReportFrame reportFrame = new ConversionReportFrame(); |
163 | |
164 | progressBar = reportFrame.getProgressBar(); |
165 | reportFrame.addActionListener(this); |
166 | |
167 | try { |
168 | reportFrame.setVisible(true); |
169 | |
170 | List comicsToConvert = getComicsToConvert(); |
171 | Iterator comicRider; |
172 | |
173 | // Set up report and compute total size of all comics to convert. |
174 | ConversionReport report = new ConversionReport(); |
175 | Task[] convertTasks = new Task[comicsToConvert.size()]; |
176 | int convertTaskIndex = 0; |
177 | |
178 | comicRider = comicsToConvert.iterator(); |
179 | while (comicRider.hasNext()) { |
180 | ComicToConvert comicToConvert = (ComicToConvert) comicRider.next(); |
181 | Task convertTask; |
182 | ConversionReportItem reportItem; |
183 | |
184 | report.put(comicToConvert); |
185 | reportItem = report.get(comicToConvert); |
186 | convertTask = new ConvertComicTask( |
187 | comicToConvert.getSourceComicFile(), comicToConvert.getTargetComicFile(), |
188 | conversion, reportItem); |
189 | convertTasks[convertTaskIndex] = convertTask; |
190 | convertTaskIndex += 1; |
191 | } |
192 | |
193 | // Convert comics. |
194 | Task convertAllComicsTask = new NestedTask(convertTasks, true); |
195 | long totalSize = convertAllComicsTask.getMaxProgress(); |
196 | |
197 | progressBar.setMaximum(getNormalizedProgress(totalSize, totalSize)); |
198 | progressBar.setValue(0); |
199 | progressBar.setIndeterminate(false); |
200 | reportFrame.setReport(report); |
201 | convertAllComicsTask.addProgressChangeListener(this); |
202 | try { |
203 | convertAllComicsTask.start(); |
204 | } catch (Exception error) { |
205 | String errorMessage = localeTools.getMessage("errors.cannotConvertComics"); |
206 | |
207 | throw new ComicException(errorMessage, error); |
208 | } finally { |
209 | convertAllComicsTask.removeProgressChangeListener(this); |
210 | } |
211 | } finally { |
212 | reportFrame.removeActionListener(this); |
213 | reportFrame.done(); |
214 | progressBar = null; |
215 | } |
216 | |
217 | return reportFrame; |
218 | } |
219 | |
220 | /** |
221 | * Update progress bar in report frame. |
222 | */ |
223 | public void progressChanged(Task source) { |
224 | progressBar.setValue(getNormalizedProgress(source.getProgress(), source.getMaxProgress())); |
225 | } |
226 | |
227 | private void addComicsToConvert( |
228 | List comicsToBeConverted, File initialDir, File dirToScanForComics) { |
229 | assert comicsToBeConverted != null; |
230 | assert initialDir != null; |
231 | assert dirToScanForComics != null; |
232 | assert initialDir.equals(dirToScanForComics) |
233 | || (fileTools.getRelativePath(initialDir, dirToScanForComics) != null); |
234 | |
235 | if (logger.isDebugEnabled()) { |
236 | logger.debug("lookup comics in " + stringTools.sourced(dirToScanForComics)); |
237 | } |
238 | File[] filesToConsiderForAdding = dirToScanForComics.listFiles(); |
239 | |
240 | for (int i = 0; i < filesToConsiderForAdding.length; i += 1) { |
241 | File fileToConsiderForAdding = filesToConsiderForAdding[i]; |
242 | |
243 | if (fileToConsiderForAdding.isDirectory()) { |
244 | addComicsToConvert(comicsToBeConverted, initialDir, fileToConsiderForAdding); |
245 | } else if (comicFileFilter.accept( |
246 | fileToConsiderForAdding.getParentFile(), fileToConsiderForAdding.getName())) { |
247 | String targetSuffix; |
248 | String comicFormat = conversion.getComicFormat(); |
249 | |
250 | if (comicFormat.equals(Conversion.COMIC_FORMAT_CBZ)) { |
251 | targetSuffix = "cbz"; |
252 | } else if (comicFormat.equals(Conversion.COMIC_FORMAT_PDF)) { |
253 | targetSuffix = "pdf"; |
254 | } else { |
255 | assert false : "comicFormat=" + stringTools.sourced(comicFormat); |
256 | targetSuffix = null; |
257 | } |
258 | |
259 | ComicToConvert comicToConvert = new ComicToConvert( |
260 | initialDir, |
261 | targetDir, |
262 | fileTools.getRelativePath(initialDir, fileToConsiderForAdding), |
263 | targetSuffix); |
264 | |
265 | comicsToBeConverted.add(comicToConvert); |
266 | } else { |
267 | if (logger.isDebugEnabled()) { |
268 | logger.debug("ignore for conversion: " + stringTools.sourced(fileToConsiderForAdding)); |
269 | } |
270 | } |
271 | } |
272 | } |
273 | } |