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