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