Commit | Line | Data |
---|---|---|
16a81ef7 | 1 | package be.nikiroo.fanfix.reader.ui; |
a6395bef | 2 | |
d3c84ac3 | 3 | import java.awt.BorderLayout; |
4d205683 | 4 | import java.awt.Frame; |
a6395bef NR |
5 | import java.awt.event.ActionEvent; |
6 | import java.awt.event.ActionListener; | |
333f0e7b NR |
7 | import java.awt.event.KeyEvent; |
8 | import java.awt.event.WindowEvent; | |
9 | import java.io.File; | |
a6395bef | 10 | import java.io.IOException; |
4d205683 | 11 | import java.util.HashMap; |
a6395bef | 12 | import java.util.List; |
4d205683 NR |
13 | import java.util.Map; |
14 | import java.util.Map.Entry; | |
a6395bef | 15 | |
10d558d2 | 16 | import javax.swing.JFileChooser; |
a6395bef | 17 | import javax.swing.JFrame; |
333f0e7b NR |
18 | import javax.swing.JMenu; |
19 | import javax.swing.JMenuBar; | |
20 | import javax.swing.JMenuItem; | |
21 | import javax.swing.JOptionPane; | |
9843a5e5 | 22 | import javax.swing.JPopupMenu; |
3b2b638f | 23 | import javax.swing.SwingUtilities; |
4d205683 NR |
24 | import javax.swing.filechooser.FileFilter; |
25 | import javax.swing.filechooser.FileNameExtensionFilter; | |
a6395bef NR |
26 | |
27 | import be.nikiroo.fanfix.Instance; | |
e8eeea0a | 28 | import be.nikiroo.fanfix.bundles.Config; |
5bc9573b | 29 | import be.nikiroo.fanfix.bundles.StringIdGui; |
b4dc6ab5 | 30 | import be.nikiroo.fanfix.bundles.UiConfig; |
a6395bef | 31 | import be.nikiroo.fanfix.data.MetaData; |
4d205683 | 32 | import be.nikiroo.fanfix.data.Story; |
79a99506 | 33 | import be.nikiroo.fanfix.library.BasicLibrary; |
0bb51c9c | 34 | import be.nikiroo.fanfix.library.BasicLibrary.Status; |
e42573a0 | 35 | import be.nikiroo.fanfix.library.LocalLibrary; |
4d205683 | 36 | import be.nikiroo.fanfix.output.BasicOutput.OutputType; |
16a81ef7 | 37 | import be.nikiroo.fanfix.reader.BasicReader; |
14bb95fa NR |
38 | import be.nikiroo.fanfix.reader.ui.GuiReaderMainPanel.FrameHelper; |
39 | import be.nikiroo.fanfix.reader.ui.GuiReaderMainPanel.StoryRunnable; | |
99e1c5ec NR |
40 | import be.nikiroo.fanfix.searchable.BasicSearchable; |
41 | import be.nikiroo.fanfix.supported.SupportType; | |
3b2b638f | 42 | import be.nikiroo.utils.Progress; |
39c3c689 | 43 | import be.nikiroo.utils.Version; |
e8eeea0a | 44 | import be.nikiroo.utils.ui.ConfigEditor; |
a6395bef | 45 | |
4d205683 | 46 | /** |
5dd985cf | 47 | * A {@link Frame} that will show a {@link GuiReaderBook} item for each |
4d205683 | 48 | * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a |
5dd985cf NR |
49 | * way to copy them to the {@link GuiReader} cache ( |
50 | * {@link BasicReader#getLibrary()}), read them, delete them... | |
4d205683 NR |
51 | * |
52 | * @author niki | |
53 | */ | |
14bb95fa | 54 | class GuiReaderFrame extends JFrame implements FrameHelper { |
a6395bef | 55 | private static final long serialVersionUID = 1L; |
5dd985cf | 56 | private GuiReader reader; |
31e28683 | 57 | private GuiReaderMainPanel mainPanel; |
a6395bef | 58 | |
79a99506 NR |
59 | /** |
60 | * The different modification actions you can use on {@link Story} items. | |
61 | * | |
62 | * @author niki | |
63 | */ | |
64 | private enum ChangeAction { | |
65 | /** Change the source/type, that is, move it to another source. */ | |
66 | SOURCE, | |
67 | /** Change its name. */ | |
68 | TITLE, | |
69 | /** Change its author. */ | |
70 | AUTHOR | |
c8faa52a NR |
71 | } |
72 | ||
4d205683 | 73 | /** |
5dd985cf | 74 | * Create a new {@link GuiReaderFrame}. |
4d205683 NR |
75 | * |
76 | * @param reader | |
5dd985cf NR |
77 | * the associated {@link GuiReader} to forward some commands and |
78 | * access its {@link LocalLibrary} | |
4d205683 NR |
79 | * @param type |
80 | * the type of {@link Story} to load, or NULL for all types | |
81 | */ | |
5dd985cf | 82 | public GuiReaderFrame(GuiReader reader, String type) { |
5bc9573b | 83 | super(getAppTitle(reader.getLibrary().getLibraryName())); |
0b39fb9f | 84 | |
a6395bef NR |
85 | this.reader = reader; |
86 | ||
31e28683 | 87 | mainPanel = new GuiReaderMainPanel(this, type); |
4310bae9 | 88 | |
14bb95fa NR |
89 | setSize(800, 600); |
90 | setLayout(new BorderLayout()); | |
5beb7cdc | 91 | add(mainPanel, BorderLayout.CENTER); |
333f0e7b NR |
92 | } |
93 | ||
14bb95fa NR |
94 | @Override |
95 | public JPopupMenu createBookPopup() { | |
0bb51c9c | 96 | Status status = reader.getLibrary().getStatus(); |
14bb95fa NR |
97 | JPopupMenu popup = new JPopupMenu(); |
98 | popup.add(createMenuItemOpenBook()); | |
99 | popup.addSeparator(); | |
100 | popup.add(createMenuItemExport()); | |
0bb51c9c NR |
101 | if (status.isWritable()) { |
102 | popup.add(createMenuItemMoveTo()); | |
103 | popup.add(createMenuItemSetCoverForSource()); | |
104 | popup.add(createMenuItemSetCoverForAuthor()); | |
105 | } | |
14bb95fa | 106 | popup.add(createMenuItemClearCache()); |
0bb51c9c NR |
107 | if (status.isWritable()) { |
108 | popup.add(createMenuItemRedownload()); | |
109 | popup.addSeparator(); | |
110 | popup.add(createMenuItemRename()); | |
111 | popup.add(createMenuItemSetAuthor()); | |
112 | popup.addSeparator(); | |
113 | popup.add(createMenuItemDelete()); | |
114 | } | |
14bb95fa NR |
115 | popup.addSeparator(); |
116 | popup.add(createMenuItemProperties()); | |
117 | return popup; | |
4310bae9 NR |
118 | } |
119 | ||
14bb95fa | 120 | @Override |
8590da19 | 121 | public JPopupMenu createSourceAuthorPopup() { |
14bb95fa NR |
122 | JPopupMenu popup = new JPopupMenu(); |
123 | popup.add(createMenuItemOpenBook()); | |
124 | return popup; | |
333f0e7b NR |
125 | } |
126 | ||
14bb95fa | 127 | @Override |
0bb51c9c | 128 | public void createMenu(Status status) { |
32224dda N |
129 | invalidate(); |
130 | ||
14bb95fa | 131 | JMenuBar bar = new JMenuBar(); |
333f0e7b | 132 | |
5bc9573b | 133 | JMenu file = new JMenu(GuiReader.trans(StringIdGui.MENU_FILE)); |
10d558d2 | 134 | file.setMnemonic(KeyEvent.VK_F); |
333f0e7b | 135 | |
5bc9573b NR |
136 | JMenuItem imprt = new JMenuItem( |
137 | GuiReader.trans(StringIdGui.MENU_FILE_IMPORT_URL), | |
138 | KeyEvent.VK_U); | |
333f0e7b | 139 | imprt.addActionListener(new ActionListener() { |
211f7ddb | 140 | @Override |
333f0e7b | 141 | public void actionPerformed(ActionEvent e) { |
31e28683 | 142 | mainPanel.imprt(true); |
10d558d2 NR |
143 | } |
144 | }); | |
5bc9573b NR |
145 | JMenuItem imprtF = new JMenuItem( |
146 | GuiReader.trans(StringIdGui.MENU_FILE_IMPORT_FILE), | |
147 | KeyEvent.VK_F); | |
10d558d2 | 148 | imprtF.addActionListener(new ActionListener() { |
211f7ddb | 149 | @Override |
10d558d2 | 150 | public void actionPerformed(ActionEvent e) { |
31e28683 | 151 | mainPanel.imprt(false); |
10d558d2 NR |
152 | } |
153 | }); | |
5bc9573b NR |
154 | JMenuItem exit = new JMenuItem( |
155 | GuiReader.trans(StringIdGui.MENU_FILE_EXIT), KeyEvent.VK_X); | |
10d558d2 | 156 | exit.addActionListener(new ActionListener() { |
211f7ddb | 157 | @Override |
10d558d2 | 158 | public void actionPerformed(ActionEvent e) { |
5dd985cf NR |
159 | GuiReaderFrame.this.dispatchEvent(new WindowEvent( |
160 | GuiReaderFrame.this, WindowEvent.WINDOW_CLOSING)); | |
10d558d2 NR |
161 | } |
162 | }); | |
3b2b638f | 163 | |
9843a5e5 NR |
164 | file.add(createMenuItemOpenBook()); |
165 | file.add(createMenuItemExport()); | |
0bb51c9c NR |
166 | if (status.isWritable()) { |
167 | file.add(createMenuItemMoveTo()); | |
168 | file.addSeparator(); | |
169 | file.add(imprt); | |
170 | file.add(imprtF); | |
171 | file.addSeparator(); | |
172 | file.add(createMenuItemRename()); | |
173 | file.add(createMenuItemSetAuthor()); | |
174 | } | |
c8d48938 | 175 | file.addSeparator(); |
5bc9573b NR |
176 | file.add(createMenuItemProperties()); |
177 | file.addSeparator(); | |
10d558d2 | 178 | file.add(exit); |
3b2b638f | 179 | |
10d558d2 NR |
180 | bar.add(file); |
181 | ||
5bc9573b | 182 | JMenu edit = new JMenu(GuiReader.trans(StringIdGui.MENU_EDIT)); |
10d558d2 NR |
183 | edit.setMnemonic(KeyEvent.VK_E); |
184 | ||
5bc9573b NR |
185 | edit.add(createMenuItemSetCoverForSource()); |
186 | edit.add(createMenuItemSetCoverForAuthor()); | |
22848428 NR |
187 | edit.add(createMenuItemClearCache()); |
188 | edit.add(createMenuItemRedownload()); | |
9843a5e5 NR |
189 | edit.addSeparator(); |
190 | edit.add(createMenuItemDelete()); | |
191 | ||
192 | bar.add(edit); | |
193 | ||
99e1c5ec NR |
194 | JMenu search = new JMenu(GuiReader.trans(StringIdGui.MENU_SEARCH)); |
195 | search.setMnemonic(KeyEvent.VK_H); | |
0b39fb9f | 196 | for (final SupportType type : SupportType.values()) { |
99e1c5ec NR |
197 | BasicSearchable searchable = BasicSearchable.getSearchable(type); |
198 | if (searchable != null) { | |
199 | JMenuItem searchItem = new JMenuItem(type.getSourceName()); | |
200 | searchItem.addActionListener(new ActionListener() { | |
201 | @Override | |
202 | public void actionPerformed(ActionEvent e) { | |
cde97f46 | 203 | reader.search(type, null, 1, 0, false); |
99e1c5ec NR |
204 | } |
205 | }); | |
206 | search.add(searchItem); | |
207 | } | |
208 | } | |
0b39fb9f | 209 | |
c5097604 | 210 | bar.add(search); |
0b39fb9f | 211 | |
5bc9573b | 212 | JMenu view = new JMenu(GuiReader.trans(StringIdGui.MENU_VIEW)); |
793f1071 | 213 | view.setMnemonic(KeyEvent.VK_V); |
5bc9573b NR |
214 | JMenuItem vauthors = new JMenuItem( |
215 | GuiReader.trans(StringIdGui.MENU_VIEW_AUTHOR)); | |
793f1071 NR |
216 | vauthors.setMnemonic(KeyEvent.VK_A); |
217 | vauthors.addActionListener(new ActionListener() { | |
211f7ddb | 218 | @Override |
793f1071 | 219 | public void actionPerformed(ActionEvent e) { |
31e28683 NR |
220 | mainPanel.setWords(false); |
221 | mainPanel.refreshBooks(); | |
793f1071 NR |
222 | } |
223 | }); | |
224 | view.add(vauthors); | |
5bc9573b NR |
225 | JMenuItem vwords = new JMenuItem( |
226 | GuiReader.trans(StringIdGui.MENU_VIEW_WCOUNT)); | |
793f1071 NR |
227 | vwords.setMnemonic(KeyEvent.VK_W); |
228 | vwords.addActionListener(new ActionListener() { | |
211f7ddb | 229 | @Override |
793f1071 | 230 | public void actionPerformed(ActionEvent e) { |
31e28683 NR |
231 | mainPanel.setWords(true); |
232 | mainPanel.refreshBooks(); | |
793f1071 NR |
233 | } |
234 | }); | |
235 | view.add(vwords); | |
236 | bar.add(view); | |
237 | ||
97b36d32 | 238 | Map<String, List<String>> groupedSources = new HashMap<String, List<String>>(); |
0bb51c9c NR |
239 | if (status.isReady()) { |
240 | try { | |
241 | groupedSources = reader.getLibrary().getSourcesGrouped(); | |
242 | } catch (IOException e) { | |
243 | error(e.getLocalizedMessage(), "IOException", e); | |
244 | } | |
97b36d32 | 245 | } |
5bc9573b | 246 | JMenu sources = new JMenu(GuiReader.trans(StringIdGui.MENU_SOURCES)); |
4310bae9 | 247 | sources.setMnemonic(KeyEvent.VK_S); |
97b36d32 NR |
248 | populateMenuSA(sources, groupedSources, true); |
249 | bar.add(sources); | |
9843a5e5 | 250 | |
97b36d32 | 251 | Map<String, List<String>> goupedAuthors = new HashMap<String, List<String>>(); |
0bb51c9c NR |
252 | if (status.isReady()) { |
253 | try { | |
254 | goupedAuthors = reader.getLibrary().getAuthorsGrouped(); | |
255 | } catch (IOException e) { | |
256 | error(e.getLocalizedMessage(), "IOException", e); | |
257 | } | |
e6249b0f | 258 | } |
5bc9573b | 259 | JMenu authors = new JMenu(GuiReader.trans(StringIdGui.MENU_AUTHORS)); |
97b36d32 NR |
260 | authors.setMnemonic(KeyEvent.VK_A); |
261 | populateMenuSA(authors, goupedAuthors, false); | |
262 | bar.add(authors); | |
e6249b0f | 263 | |
5bc9573b | 264 | JMenu options = new JMenu(GuiReader.trans(StringIdGui.MENU_OPTIONS)); |
97b36d32 NR |
265 | options.setMnemonic(KeyEvent.VK_O); |
266 | options.add(createMenuItemConfig()); | |
267 | options.add(createMenuItemUiConfig()); | |
268 | bar.add(options); | |
c1b93db3 | 269 | |
14bb95fa | 270 | setJMenuBar(bar); |
97b36d32 NR |
271 | } |
272 | ||
273 | // "" = [unknown] | |
274 | private void populateMenuSA(JMenu menu, | |
275 | Map<String, List<String>> groupedValues, boolean type) { | |
276 | ||
5bc9573b | 277 | // "All" and "Listing" special items first |
c428e675 NR |
278 | JMenuItem item = new JMenuItem( |
279 | GuiReader.trans(StringIdGui.MENU_XXX_ALL_GROUPED)); | |
97b36d32 NR |
280 | item.addActionListener(getActionOpenList(type, false)); |
281 | menu.add(item); | |
5bc9573b | 282 | item = new JMenuItem(GuiReader.trans(StringIdGui.MENU_XXX_ALL_LISTING)); |
97b36d32 NR |
283 | item.addActionListener(getActionOpenList(type, true)); |
284 | menu.add(item); | |
c428e675 | 285 | |
97b36d32 NR |
286 | menu.addSeparator(); |
287 | ||
288 | for (final String value : groupedValues.keySet()) { | |
289 | List<String> list = groupedValues.get(value); | |
290 | if (type && list.size() == 1 && list.get(0).isEmpty()) { | |
291 | // leaf item source/type | |
c428e675 NR |
292 | item = new JMenuItem( |
293 | value.isEmpty() ? GuiReader | |
294 | .trans(StringIdGui.MENU_AUTHORS_UNKNOWN) | |
295 | : value); | |
97b36d32 NR |
296 | item.addActionListener(getActionOpen(value, type)); |
297 | menu.add(item); | |
c1b93db3 | 298 | } else { |
97b36d32 NR |
299 | JMenu dir; |
300 | if (!type && groupedValues.size() == 1) { | |
301 | // only one group of authors | |
302 | dir = menu; | |
303 | } else { | |
c428e675 NR |
304 | dir = new JMenu( |
305 | value.isEmpty() ? GuiReader | |
306 | .trans(StringIdGui.MENU_AUTHORS_UNKNOWN) | |
307 | : value); | |
97b36d32 NR |
308 | } |
309 | ||
c1b93db3 NR |
310 | for (String sub : list) { |
311 | // " " instead of "" for the visual height | |
116904b8 NR |
312 | String itemName = sub; |
313 | if (itemName.isEmpty()) { | |
c428e675 NR |
314 | itemName = type ? " " : GuiReader |
315 | .trans(StringIdGui.MENU_AUTHORS_UNKNOWN); | |
116904b8 | 316 | } |
97b36d32 | 317 | |
116904b8 | 318 | String actualValue = value; |
97b36d32 NR |
319 | if (type) { |
320 | if (!sub.isEmpty()) { | |
321 | actualValue += "/" + sub; | |
322 | } | |
323 | } else { | |
324 | actualValue = sub; | |
c1b93db3 | 325 | } |
9843a5e5 | 326 | |
c1b93db3 | 327 | item = new JMenuItem(itemName); |
97b36d32 | 328 | item.addActionListener(getActionOpen(actualValue, type)); |
c1b93db3 NR |
329 | dir.add(item); |
330 | } | |
5f42f329 | 331 | |
97b36d32 NR |
332 | if (menu != dir) { |
333 | menu.add(dir); | |
334 | } | |
5f42f329 | 335 | } |
4310bae9 | 336 | } |
9843a5e5 NR |
337 | } |
338 | ||
c1b93db3 NR |
339 | /** |
340 | * Return an {@link ActionListener} that will set the given source (type) as | |
341 | * the selected/displayed one. | |
342 | * | |
343 | * @param type | |
97b36d32 | 344 | * the type (source) to select, cannot be NULL |
c1b93db3 NR |
345 | * |
346 | * @return the {@link ActionListener} | |
347 | */ | |
97b36d32 | 348 | private ActionListener getActionOpen(final String source, final boolean type) { |
c1b93db3 NR |
349 | return new ActionListener() { |
350 | @Override | |
351 | public void actionPerformed(ActionEvent e) { | |
31e28683 NR |
352 | mainPanel.removeBookPanes(); |
353 | mainPanel.addBookPane(source, type); | |
354 | mainPanel.refreshBooks(); | |
c1b93db3 NR |
355 | } |
356 | }; | |
357 | } | |
358 | ||
97b36d32 NR |
359 | private ActionListener getActionOpenList(final boolean type, |
360 | final boolean listMode) { | |
361 | return new ActionListener() { | |
362 | @Override | |
0bb51c9c | 363 | public void actionPerformed(ActionEvent ae) { |
31e28683 | 364 | mainPanel.removeBookPanes(); |
0bb51c9c NR |
365 | try { |
366 | mainPanel.addBookPane(type, listMode); | |
367 | } catch (IOException e) { | |
368 | error(e.getLocalizedMessage(), "IOException", e); | |
369 | } | |
31e28683 | 370 | mainPanel.refreshBooks(); |
5f42f329 | 371 | } |
97b36d32 | 372 | }; |
5f42f329 NR |
373 | } |
374 | ||
e8eeea0a NR |
375 | /** |
376 | * Create the Fanfix Configuration menu item. | |
377 | * | |
378 | * @return the item | |
379 | */ | |
380 | private JMenuItem createMenuItemConfig() { | |
5bc9573b | 381 | final String title = GuiReader.trans(StringIdGui.TITLE_CONFIG); |
e8eeea0a NR |
382 | JMenuItem item = new JMenuItem(title); |
383 | item.setMnemonic(KeyEvent.VK_F); | |
384 | ||
385 | item.addActionListener(new ActionListener() { | |
211f7ddb | 386 | @Override |
e8eeea0a NR |
387 | public void actionPerformed(ActionEvent e) { |
388 | ConfigEditor<Config> ed = new ConfigEditor<Config>( | |
c428e675 NR |
389 | Config.class, Instance.getConfig(), GuiReader |
390 | .trans(StringIdGui.SUBTITLE_CONFIG)); | |
e8eeea0a NR |
391 | JFrame frame = new JFrame(title); |
392 | frame.add(ed); | |
393 | frame.setSize(800, 600); | |
394 | frame.setVisible(true); | |
395 | } | |
396 | }); | |
397 | ||
398 | return item; | |
399 | } | |
400 | ||
401 | /** | |
402 | * Create the UI Configuration menu item. | |
403 | * | |
404 | * @return the item | |
405 | */ | |
406 | private JMenuItem createMenuItemUiConfig() { | |
5bc9573b | 407 | final String title = GuiReader.trans(StringIdGui.TITLE_CONFIG_UI); |
e8eeea0a NR |
408 | JMenuItem item = new JMenuItem(title); |
409 | item.setMnemonic(KeyEvent.VK_U); | |
410 | ||
411 | item.addActionListener(new ActionListener() { | |
211f7ddb | 412 | @Override |
e8eeea0a NR |
413 | public void actionPerformed(ActionEvent e) { |
414 | ConfigEditor<UiConfig> ed = new ConfigEditor<UiConfig>( | |
c428e675 NR |
415 | UiConfig.class, Instance.getUiConfig(), GuiReader |
416 | .trans(StringIdGui.SUBTITLE_CONFIG_UI)); | |
e8eeea0a NR |
417 | JFrame frame = new JFrame(title); |
418 | frame.add(ed); | |
419 | frame.setSize(800, 600); | |
420 | frame.setVisible(true); | |
421 | } | |
422 | }); | |
423 | ||
424 | return item; | |
425 | } | |
426 | ||
4d205683 NR |
427 | /** |
428 | * Create the export menu item. | |
429 | * | |
430 | * @return the item | |
431 | */ | |
9843a5e5 | 432 | private JMenuItem createMenuItemExport() { |
4d205683 NR |
433 | final JFileChooser fc = new JFileChooser(); |
434 | fc.setAcceptAllFileFilterUsed(false); | |
435 | ||
c428e675 NR |
436 | // Add the "ALL" filters first, then the others |
437 | final Map<FileFilter, OutputType> otherFilters = new HashMap<FileFilter, OutputType>(); | |
4d205683 NR |
438 | for (OutputType type : OutputType.values()) { |
439 | String ext = type.getDefaultExtension(false); | |
440 | String desc = type.getDesc(false); | |
b2612f9d | 441 | |
4d205683 | 442 | if (ext == null || ext.isEmpty()) { |
c428e675 | 443 | fc.addChoosableFileFilter(createAllFilter(desc)); |
4d205683 | 444 | } else { |
c428e675 | 445 | otherFilters.put(new FileNameExtensionFilter(desc, ext), type); |
4d205683 NR |
446 | } |
447 | } | |
448 | ||
c428e675 NR |
449 | for (Entry<FileFilter, OutputType> entry : otherFilters.entrySet()) { |
450 | fc.addChoosableFileFilter(entry.getKey()); | |
4d205683 NR |
451 | } |
452 | // | |
9843a5e5 | 453 | |
c428e675 NR |
454 | JMenuItem export = new JMenuItem( |
455 | GuiReader.trans(StringIdGui.MENU_FILE_EXPORT), KeyEvent.VK_S); | |
10d558d2 | 456 | export.addActionListener(new ActionListener() { |
211f7ddb | 457 | @Override |
10d558d2 | 458 | public void actionPerformed(ActionEvent e) { |
31e28683 | 459 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
4d205683 | 460 | if (selectedBook != null) { |
c428e675 NR |
461 | fc.showDialog(GuiReaderFrame.this, |
462 | GuiReader.trans(StringIdGui.TITLE_SAVE)); | |
b2612f9d | 463 | if (fc.getSelectedFile() != null) { |
0b39fb9f NR |
464 | final OutputType type = otherFilters.get(fc |
465 | .getFileFilter()); | |
b2612f9d NR |
466 | final String path = fc.getSelectedFile() |
467 | .getAbsolutePath() | |
468 | + type.getDefaultExtension(false); | |
469 | final Progress pg = new Progress(); | |
2d56db73 | 470 | mainPanel.outOfUi(pg, false, new Runnable() { |
211f7ddb | 471 | @Override |
b2612f9d NR |
472 | public void run() { |
473 | try { | |
e42573a0 | 474 | reader.getLibrary().export( |
79a99506 NR |
475 | selectedBook.getInfo().getMeta() |
476 | .getLuid(), type, path, pg); | |
b2612f9d | 477 | } catch (IOException e) { |
62c63b07 | 478 | Instance.getTraceHandler().error(e); |
b2612f9d | 479 | } |
4d205683 | 480 | } |
b2612f9d NR |
481 | }); |
482 | } | |
4d205683 | 483 | } |
10d558d2 NR |
484 | } |
485 | }); | |
486 | ||
9843a5e5 NR |
487 | return export; |
488 | } | |
489 | ||
edd46289 NR |
490 | /** |
491 | * Create a {@link FileFilter} that accepts all files and return the given | |
492 | * description. | |
493 | * | |
494 | * @param desc | |
495 | * the description | |
496 | * | |
497 | * @return the filter | |
498 | */ | |
4d205683 NR |
499 | private FileFilter createAllFilter(final String desc) { |
500 | return new FileFilter() { | |
501 | @Override | |
502 | public String getDescription() { | |
503 | return desc; | |
504 | } | |
505 | ||
506 | @Override | |
507 | public boolean accept(File f) { | |
508 | return true; | |
509 | } | |
510 | }; | |
511 | } | |
512 | ||
513 | /** | |
514 | * Create the refresh (delete cache) menu item. | |
515 | * | |
516 | * @return the item | |
517 | */ | |
22848428 | 518 | private JMenuItem createMenuItemClearCache() { |
c428e675 NR |
519 | JMenuItem refresh = new JMenuItem( |
520 | GuiReader.trans(StringIdGui.MENU_EDIT_CLEAR_CACHE), | |
521 | KeyEvent.VK_C); | |
10d558d2 | 522 | refresh.addActionListener(new ActionListener() { |
211f7ddb | 523 | @Override |
10d558d2 | 524 | public void actionPerformed(ActionEvent e) { |
31e28683 | 525 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
10d558d2 | 526 | if (selectedBook != null) { |
2d56db73 | 527 | mainPanel.outOfUi(null, false, new Runnable() { |
211f7ddb | 528 | @Override |
10d558d2 | 529 | public void run() { |
79a99506 NR |
530 | reader.clearLocalReaderCache(selectedBook.getInfo() |
531 | .getMeta().getLuid()); | |
10d558d2 | 532 | selectedBook.setCached(false); |
df6e2d88 | 533 | GuiReaderCoverImager.clearIcon(selectedBook |
79a99506 | 534 | .getInfo()); |
3b2b638f | 535 | SwingUtilities.invokeLater(new Runnable() { |
211f7ddb | 536 | @Override |
3b2b638f | 537 | public void run() { |
10d558d2 | 538 | selectedBook.repaint(); |
3b2b638f NR |
539 | } |
540 | }); | |
541 | } | |
542 | }); | |
333f0e7b NR |
543 | } |
544 | } | |
545 | }); | |
10d558d2 | 546 | |
9843a5e5 NR |
547 | return refresh; |
548 | } | |
549 | ||
70c9b112 | 550 | /** |
c8d48938 | 551 | * Create the "move to" menu item. |
70c9b112 NR |
552 | * |
553 | * @return the item | |
554 | */ | |
0bb51c9c | 555 | private JMenuItem createMenuItemMoveTo() { |
c428e675 NR |
556 | JMenu changeTo = new JMenu( |
557 | GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO)); | |
c8d48938 | 558 | changeTo.setMnemonic(KeyEvent.VK_M); |
70c9b112 | 559 | |
c1b93db3 | 560 | Map<String, List<String>> groupedSources = new HashMap<String, List<String>>(); |
0bb51c9c | 561 | try { |
c1b93db3 | 562 | groupedSources = reader.getLibrary().getSourcesGrouped(); |
0bb51c9c NR |
563 | } catch (IOException e) { |
564 | error(e.getLocalizedMessage(), "IOException", e); | |
e6249b0f | 565 | } |
70c9b112 | 566 | |
c428e675 NR |
567 | JMenuItem item = new JMenuItem( |
568 | GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_TYPE)); | |
79a99506 | 569 | item.addActionListener(createMoveAction(ChangeAction.SOURCE, null)); |
c1b93db3 NR |
570 | changeTo.add(item); |
571 | changeTo.addSeparator(); | |
70c9b112 | 572 | |
c1b93db3 NR |
573 | for (final String type : groupedSources.keySet()) { |
574 | List<String> list = groupedSources.get(type); | |
575 | if (list.size() == 1 && list.get(0).isEmpty()) { | |
576 | item = new JMenuItem(type); | |
79a99506 NR |
577 | item.addActionListener(createMoveAction(ChangeAction.SOURCE, |
578 | type)); | |
c1b93db3 NR |
579 | changeTo.add(item); |
580 | } else { | |
581 | JMenu dir = new JMenu(type); | |
582 | for (String sub : list) { | |
583 | // " " instead of "" for the visual height | |
584 | String itemName = sub.isEmpty() ? " " : sub; | |
585 | String actualType = type; | |
586 | if (!sub.isEmpty()) { | |
587 | actualType += "/" + sub; | |
588 | } | |
70c9b112 | 589 | |
c1b93db3 | 590 | item = new JMenuItem(itemName); |
79a99506 NR |
591 | item.addActionListener(createMoveAction( |
592 | ChangeAction.SOURCE, actualType)); | |
c1b93db3 NR |
593 | dir.add(item); |
594 | } | |
595 | changeTo.add(dir); | |
c8d48938 NR |
596 | } |
597 | } | |
211f7ddb | 598 | |
c8d48938 NR |
599 | return changeTo; |
600 | } | |
70c9b112 | 601 | |
c8d48938 NR |
602 | /** |
603 | * Create the "set author" menu item. | |
604 | * | |
c8d48938 NR |
605 | * @return the item |
606 | */ | |
0bb51c9c | 607 | private JMenuItem createMenuItemSetAuthor() { |
c428e675 NR |
608 | JMenu changeTo = new JMenu( |
609 | GuiReader.trans(StringIdGui.MENU_FILE_SET_AUTHOR)); | |
c8d48938 | 610 | changeTo.setMnemonic(KeyEvent.VK_A); |
70c9b112 | 611 | |
c8d48938 | 612 | // New author |
c428e675 NR |
613 | JMenuItem newItem = new JMenuItem( |
614 | GuiReader.trans(StringIdGui.MENU_FILE_MOVE_TO_NEW_AUTHOR)); | |
c8d48938 NR |
615 | changeTo.add(newItem); |
616 | changeTo.addSeparator(); | |
79a99506 | 617 | newItem.addActionListener(createMoveAction(ChangeAction.AUTHOR, null)); |
70c9b112 | 618 | |
c8d48938 | 619 | // Existing authors |
0bb51c9c NR |
620 | Map<String, List<String>> groupedAuthors; |
621 | ||
622 | try { | |
623 | groupedAuthors = reader.getLibrary().getAuthorsGrouped(); | |
624 | } catch (IOException e) { | |
625 | error(e.getLocalizedMessage(), "IOException", e); | |
626 | groupedAuthors = new HashMap<String, List<String>>(); | |
627 | ||
628 | } | |
629 | ||
630 | if (groupedAuthors.size() > 1) { | |
631 | for (String key : groupedAuthors.keySet()) { | |
632 | JMenu group = new JMenu(key); | |
633 | for (String value : groupedAuthors.get(key)) { | |
116904b8 | 634 | JMenuItem item = new JMenuItem( |
c428e675 NR |
635 | value.isEmpty() ? GuiReader |
636 | .trans(StringIdGui.MENU_AUTHORS_UNKNOWN) | |
637 | : value); | |
79a99506 NR |
638 | item.addActionListener(createMoveAction( |
639 | ChangeAction.AUTHOR, value)); | |
0bb51c9c | 640 | group.add(item); |
c8d48938 | 641 | } |
0bb51c9c NR |
642 | changeTo.add(group); |
643 | } | |
644 | } else if (groupedAuthors.size() == 1) { | |
645 | for (String value : groupedAuthors.values().iterator().next()) { | |
646 | JMenuItem item = new JMenuItem( | |
647 | value.isEmpty() ? GuiReader | |
648 | .trans(StringIdGui.MENU_AUTHORS_UNKNOWN) | |
649 | : value); | |
650 | item.addActionListener(createMoveAction(ChangeAction.AUTHOR, | |
651 | value)); | |
652 | changeTo.add(item); | |
c8d48938 | 653 | } |
70c9b112 NR |
654 | } |
655 | ||
c8d48938 NR |
656 | return changeTo; |
657 | } | |
658 | ||
659 | /** | |
660 | * Create the "rename" menu item. | |
661 | * | |
c8d48938 NR |
662 | * @return the item |
663 | */ | |
0bb51c9c | 664 | private JMenuItem createMenuItemRename() { |
c428e675 NR |
665 | JMenuItem changeTo = new JMenuItem( |
666 | GuiReader.trans(StringIdGui.MENU_FILE_RENAME)); | |
c8d48938 | 667 | changeTo.setMnemonic(KeyEvent.VK_R); |
79a99506 | 668 | changeTo.addActionListener(createMoveAction(ChangeAction.TITLE, null)); |
c8d48938 NR |
669 | return changeTo; |
670 | } | |
671 | ||
79a99506 | 672 | private ActionListener createMoveAction(final ChangeAction what, |
14bb95fa | 673 | final String type) { |
c8d48938 NR |
674 | return new ActionListener() { |
675 | @Override | |
676 | public void actionPerformed(ActionEvent e) { | |
31e28683 | 677 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
c8d48938 | 678 | if (selectedBook != null) { |
c349fd48 NR |
679 | boolean refreshRequired = false; |
680 | ||
681 | if (what == ChangeAction.SOURCE) { | |
682 | refreshRequired = mainPanel.getCurrentType(); | |
683 | } else if (what == ChangeAction.TITLE) { | |
684 | refreshRequired = false; | |
685 | } else if (what == ChangeAction.AUTHOR) { | |
686 | refreshRequired = !mainPanel.getCurrentType(); | |
687 | } | |
688 | ||
c8d48938 NR |
689 | String changeTo = type; |
690 | if (type == null) { | |
79a99506 | 691 | MetaData meta = selectedBook.getInfo().getMeta(); |
c8d48938 | 692 | String init = ""; |
79a99506 NR |
693 | if (what == ChangeAction.SOURCE) { |
694 | init = meta.getSource(); | |
695 | } else if (what == ChangeAction.TITLE) { | |
696 | init = meta.getTitle(); | |
697 | } else if (what == ChangeAction.AUTHOR) { | |
698 | init = meta.getAuthor(); | |
c8d48938 NR |
699 | } |
700 | ||
701 | Object rep = JOptionPane.showInputDialog( | |
c428e675 NR |
702 | GuiReaderFrame.this, |
703 | GuiReader.trans(StringIdGui.SUBTITLE_MOVE_TO), | |
704 | GuiReader.trans(StringIdGui.TITLE_MOVE_TO), | |
705 | JOptionPane.QUESTION_MESSAGE, null, null, init); | |
c8d48938 NR |
706 | |
707 | if (rep == null) { | |
708 | return; | |
709 | } | |
710 | ||
711 | changeTo = rep.toString(); | |
712 | } | |
713 | ||
714 | final String fChangeTo = changeTo; | |
c349fd48 | 715 | mainPanel.outOfUi(null, refreshRequired, new Runnable() { |
c8d48938 NR |
716 | @Override |
717 | public void run() { | |
79a99506 NR |
718 | String luid = selectedBook.getInfo().getMeta() |
719 | .getLuid(); | |
720 | if (what == ChangeAction.SOURCE) { | |
721 | reader.changeSource(luid, fChangeTo); | |
722 | } else if (what == ChangeAction.TITLE) { | |
723 | reader.changeTitle(luid, fChangeTo); | |
724 | } else if (what == ChangeAction.AUTHOR) { | |
725 | reader.changeAuthor(luid, fChangeTo); | |
c8d48938 NR |
726 | } |
727 | ||
c349fd48 | 728 | mainPanel.getSelectedBook().repaint(); |
31e28683 | 729 | mainPanel.unsetSelectedBook(); |
c8d48938 NR |
730 | |
731 | SwingUtilities.invokeLater(new Runnable() { | |
732 | @Override | |
733 | public void run() { | |
0bb51c9c | 734 | createMenu(reader.getLibrary().getStatus()); |
c8d48938 NR |
735 | } |
736 | }); | |
737 | } | |
738 | }); | |
739 | } | |
740 | } | |
741 | }; | |
70c9b112 NR |
742 | } |
743 | ||
22848428 | 744 | /** |
79a99506 | 745 | * Create the re-download (then delete original) menu item. |
22848428 NR |
746 | * |
747 | * @return the item | |
748 | */ | |
749 | private JMenuItem createMenuItemRedownload() { | |
c428e675 NR |
750 | JMenuItem refresh = new JMenuItem( |
751 | GuiReader.trans(StringIdGui.MENU_EDIT_REDOWNLOAD), | |
752 | KeyEvent.VK_R); | |
22848428 | 753 | refresh.addActionListener(new ActionListener() { |
211f7ddb | 754 | @Override |
22848428 | 755 | public void actionPerformed(ActionEvent e) { |
31e28683 | 756 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
22848428 | 757 | if (selectedBook != null) { |
79a99506 | 758 | final MetaData meta = selectedBook.getInfo().getMeta(); |
0b39fb9f NR |
759 | mainPanel.imprt(meta.getUrl(), new StoryRunnable() { |
760 | @Override | |
761 | public void run(Story story) { | |
762 | MetaData newMeta = story.getMeta(); | |
763 | if (!newMeta.getSource().equals(meta.getSource())) { | |
764 | reader.changeSource(newMeta.getLuid(), | |
765 | meta.getSource()); | |
766 | } | |
767 | } | |
768 | }, GuiReader.trans(StringIdGui.PROGRESS_CHANGE_SOURCE)); | |
22848428 NR |
769 | } |
770 | } | |
771 | }); | |
772 | ||
773 | return refresh; | |
774 | } | |
775 | ||
4d205683 NR |
776 | /** |
777 | * Create the delete menu item. | |
778 | * | |
779 | * @return the item | |
780 | */ | |
9843a5e5 | 781 | private JMenuItem createMenuItemDelete() { |
c428e675 NR |
782 | JMenuItem delete = new JMenuItem( |
783 | GuiReader.trans(StringIdGui.MENU_EDIT_DELETE), KeyEvent.VK_D); | |
10d558d2 | 784 | delete.addActionListener(new ActionListener() { |
211f7ddb | 785 | @Override |
10d558d2 | 786 | public void actionPerformed(ActionEvent e) { |
31e28683 | 787 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
c349fd48 NR |
788 | if (selectedBook != null |
789 | && selectedBook.getInfo().getMeta() != null) { | |
790 | ||
791 | final MetaData meta = selectedBook.getInfo().getMeta(); | |
792 | int rep = JOptionPane.showConfirmDialog( | |
793 | GuiReaderFrame.this, | |
c428e675 NR |
794 | GuiReader.trans(StringIdGui.SUBTITLE_DELETE, |
795 | meta.getLuid(), meta.getTitle()), | |
796 | GuiReader.trans(StringIdGui.TITLE_DELETE), | |
c349fd48 NR |
797 | JOptionPane.OK_CANCEL_OPTION); |
798 | ||
799 | if (rep == JOptionPane.OK_OPTION) { | |
800 | mainPanel.outOfUi(null, true, new Runnable() { | |
801 | @Override | |
802 | public void run() { | |
803 | reader.delete(meta.getLuid()); | |
804 | mainPanel.unsetSelectedBook(); | |
805 | } | |
806 | }); | |
807 | } | |
10d558d2 NR |
808 | } |
809 | } | |
810 | }); | |
811 | ||
9843a5e5 NR |
812 | return delete; |
813 | } | |
10d558d2 | 814 | |
df6e2d88 NR |
815 | /** |
816 | * Create the properties menu item. | |
817 | * | |
818 | * @return the item | |
819 | */ | |
820 | private JMenuItem createMenuItemProperties() { | |
c428e675 NR |
821 | JMenuItem delete = new JMenuItem( |
822 | GuiReader.trans(StringIdGui.MENU_FILE_PROPERTIES), | |
823 | KeyEvent.VK_P); | |
df6e2d88 NR |
824 | delete.addActionListener(new ActionListener() { |
825 | @Override | |
826 | public void actionPerformed(ActionEvent e) { | |
31e28683 | 827 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
df6e2d88 | 828 | if (selectedBook != null) { |
2d56db73 | 829 | mainPanel.outOfUi(null, false, new Runnable() { |
df6e2d88 NR |
830 | @Override |
831 | public void run() { | |
5beb7cdc NR |
832 | new GuiReaderPropertiesFrame(reader.getLibrary(), |
833 | selectedBook.getInfo().getMeta()) | |
834 | .setVisible(true); | |
df6e2d88 NR |
835 | } |
836 | }); | |
837 | } | |
838 | } | |
839 | }); | |
840 | ||
841 | return delete; | |
842 | } | |
843 | ||
4d205683 | 844 | /** |
32224dda | 845 | * Create the open menu item for a book, a source/type or an author. |
4d205683 NR |
846 | * |
847 | * @return the item | |
848 | */ | |
14bb95fa | 849 | public JMenuItem createMenuItemOpenBook() { |
c428e675 NR |
850 | JMenuItem open = new JMenuItem( |
851 | GuiReader.trans(StringIdGui.MENU_FILE_OPEN), KeyEvent.VK_O); | |
9843a5e5 | 852 | open.addActionListener(new ActionListener() { |
211f7ddb | 853 | @Override |
9843a5e5 | 854 | public void actionPerformed(ActionEvent e) { |
31e28683 | 855 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
9843a5e5 | 856 | if (selectedBook != null) { |
8590da19 | 857 | if (selectedBook.getInfo().getMeta() == null) { |
31e28683 | 858 | mainPanel.removeBookPanes(); |
5beb7cdc NR |
859 | mainPanel.addBookPane(selectedBook.getInfo() |
860 | .getMainInfo(), mainPanel.getCurrentType()); | |
31e28683 | 861 | mainPanel.refreshBooks(); |
14b57448 | 862 | } else { |
31e28683 | 863 | mainPanel.openBook(selectedBook); |
14b57448 NR |
864 | } |
865 | } | |
866 | } | |
867 | }); | |
868 | ||
869 | return open; | |
870 | } | |
871 | ||
872 | /** | |
873 | * Create the SetCover menu item for a book to change the linked source | |
874 | * cover. | |
875 | * | |
876 | * @return the item | |
877 | */ | |
79a99506 | 878 | private JMenuItem createMenuItemSetCoverForSource() { |
c428e675 NR |
879 | JMenuItem open = new JMenuItem( |
880 | GuiReader.trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_SOURCE), | |
881 | KeyEvent.VK_C); | |
14b57448 NR |
882 | open.addActionListener(new ActionListener() { |
883 | @Override | |
0bb51c9c | 884 | public void actionPerformed(ActionEvent ae) { |
31e28683 | 885 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
14b57448 | 886 | if (selectedBook != null) { |
79a99506 NR |
887 | BasicLibrary lib = reader.getLibrary(); |
888 | String luid = selectedBook.getInfo().getMeta().getLuid(); | |
889 | String source = selectedBook.getInfo().getMeta() | |
890 | .getSource(); | |
891 | ||
0bb51c9c NR |
892 | try { |
893 | lib.setSourceCover(source, luid); | |
894 | } catch (IOException e) { | |
895 | error(e.getLocalizedMessage(), "IOException", e); | |
896 | } | |
79a99506 NR |
897 | |
898 | GuiReaderBookInfo sourceInfo = GuiReaderBookInfo | |
899 | .fromSource(lib, source); | |
900 | GuiReaderCoverImager.clearIcon(sourceInfo); | |
901 | } | |
902 | } | |
903 | }); | |
904 | ||
905 | return open; | |
906 | } | |
907 | ||
908 | /** | |
909 | * Create the SetCover menu item for a book to change the linked source | |
910 | * cover. | |
911 | * | |
912 | * @return the item | |
913 | */ | |
914 | private JMenuItem createMenuItemSetCoverForAuthor() { | |
c428e675 NR |
915 | JMenuItem open = new JMenuItem( |
916 | GuiReader.trans(StringIdGui.MENU_EDIT_SET_COVER_FOR_AUTHOR), | |
917 | KeyEvent.VK_A); | |
79a99506 NR |
918 | open.addActionListener(new ActionListener() { |
919 | @Override | |
0bb51c9c | 920 | public void actionPerformed(ActionEvent ae) { |
79a99506 NR |
921 | final GuiReaderBook selectedBook = mainPanel.getSelectedBook(); |
922 | if (selectedBook != null) { | |
923 | BasicLibrary lib = reader.getLibrary(); | |
924 | String luid = selectedBook.getInfo().getMeta().getLuid(); | |
925 | String author = selectedBook.getInfo().getMeta() | |
926 | .getAuthor(); | |
927 | ||
0bb51c9c NR |
928 | try { |
929 | lib.setAuthorCover(author, luid); | |
930 | } catch (IOException e) { | |
931 | error(e.getLocalizedMessage(), "IOException", e); | |
932 | } | |
79a99506 NR |
933 | |
934 | GuiReaderBookInfo authorInfo = GuiReaderBookInfo | |
935 | .fromAuthor(lib, author); | |
936 | GuiReaderCoverImager.clearIcon(authorInfo); | |
9843a5e5 NR |
937 | } |
938 | } | |
939 | }); | |
10d558d2 | 940 | |
9843a5e5 NR |
941 | return open; |
942 | } | |
10d558d2 | 943 | |
e6249b0f NR |
944 | /** |
945 | * Display an error message and log the linked {@link Exception}. | |
946 | * | |
947 | * @param message | |
948 | * the message | |
949 | * @param title | |
950 | * the title of the error message | |
951 | * @param e | |
952 | * the exception to log if any | |
953 | */ | |
14bb95fa | 954 | public void error(final String message, final String title, Exception e) { |
e6249b0f NR |
955 | Instance.getTraceHandler().error(title + ": " + message); |
956 | if (e != null) { | |
957 | Instance.getTraceHandler().error(e); | |
958 | } | |
959 | ||
960 | SwingUtilities.invokeLater(new Runnable() { | |
961 | @Override | |
962 | public void run() { | |
963 | JOptionPane.showMessageDialog(GuiReaderFrame.this, message, | |
964 | title, JOptionPane.ERROR_MESSAGE); | |
965 | } | |
966 | }); | |
967 | } | |
14bb95fa NR |
968 | |
969 | @Override | |
970 | public GuiReader getReader() { | |
971 | return reader; | |
972 | } | |
5bc9573b NR |
973 | |
974 | /** | |
975 | * Return the title of the application. | |
976 | * | |
977 | * @param libraryName | |
978 | * the name of the associated {@link BasicLibrary}, which can be | |
979 | * EMPTY | |
980 | * | |
981 | * @return the title | |
982 | */ | |
983 | static private String getAppTitle(String libraryName) { | |
984 | if (!libraryName.isEmpty()) { | |
985 | return GuiReader.trans(StringIdGui.TITLE_LIBRARY_WITH_NAME, Version | |
986 | .getCurrentVersion().toString(), libraryName); | |
987 | } | |
988 | ||
c428e675 NR |
989 | return GuiReader.trans(StringIdGui.TITLE_LIBRARY, Version |
990 | .getCurrentVersion().toString()); | |
5bc9573b | 991 | } |
a6395bef | 992 | } |