Fix some bad line breaks on HTML supports
[fanfix.git] / src / be / nikiroo / fanfix / reader / LocalReaderFrame.java
CommitLineData
a6395bef
NR
1package be.nikiroo.fanfix.reader;
2
d3c84ac3 3import java.awt.BorderLayout;
b4dc6ab5 4import java.awt.Color;
4d205683 5import java.awt.Frame;
5130ce84
NR
6import java.awt.Toolkit;
7import java.awt.datatransfer.DataFlavor;
a6395bef
NR
8import java.awt.event.ActionEvent;
9import java.awt.event.ActionListener;
333f0e7b 10import java.awt.event.KeyEvent;
9843a5e5 11import java.awt.event.MouseEvent;
333f0e7b
NR
12import java.awt.event.WindowEvent;
13import java.io.File;
a6395bef 14import java.io.IOException;
4d205683 15import java.net.URL;
4d205683 16import java.util.HashMap;
a6395bef 17import java.util.List;
4d205683
NR
18import java.util.Map;
19import java.util.Map.Entry;
a6395bef 20
4310bae9 21import javax.swing.BoxLayout;
10d558d2 22import javax.swing.JFileChooser;
a6395bef 23import javax.swing.JFrame;
333f0e7b
NR
24import javax.swing.JMenu;
25import javax.swing.JMenuBar;
26import javax.swing.JMenuItem;
27import javax.swing.JOptionPane;
28import javax.swing.JPanel;
9843a5e5 29import javax.swing.JPopupMenu;
d3c84ac3 30import javax.swing.JScrollPane;
3b2b638f 31import javax.swing.SwingUtilities;
4d205683
NR
32import javax.swing.filechooser.FileFilter;
33import javax.swing.filechooser.FileNameExtensionFilter;
a6395bef
NR
34
35import be.nikiroo.fanfix.Instance;
4d205683 36import be.nikiroo.fanfix.Library;
b4dc6ab5 37import be.nikiroo.fanfix.bundles.UiConfig;
a6395bef 38import be.nikiroo.fanfix.data.MetaData;
4d205683
NR
39import be.nikiroo.fanfix.data.Story;
40import be.nikiroo.fanfix.output.BasicOutput.OutputType;
92fb0719 41import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListener;
3b2b638f 42import be.nikiroo.utils.Progress;
39c3c689 43import be.nikiroo.utils.Version;
3b2b638f 44import be.nikiroo.utils.ui.ProgressBar;
a6395bef 45
4d205683
NR
46/**
47 * A {@link Frame} that will show a {@link LocalReaderBook} item for each
48 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
49 * way to copy them to the {@link LocalReader} cache ({@link LocalReader#lib}),
50 * read them, delete them...
51 *
52 * @author niki
53 */
a6395bef
NR
54class LocalReaderFrame extends JFrame {
55 private static final long serialVersionUID = 1L;
56 private LocalReader reader;
4310bae9
NR
57 private Map<LocalReaderGroup, String> booksByType;
58 private Map<LocalReaderGroup, String> booksByAuthor;
59 private JPanel pane;
b4dc6ab5 60 private Color color;
3b2b638f
NR
61 private ProgressBar pgBar;
62 private JMenuBar bar;
10d558d2 63 private LocalReaderBook selectedBook;
793f1071 64 private boolean words; // words or authors (secondary info on books)
a6395bef 65
4d205683
NR
66 /**
67 * Create a new {@link LocalReaderFrame}.
68 *
69 * @param reader
70 * the associated {@link LocalReader} to forward some commands
71 * and access its {@link Library}
72 * @param type
73 * the type of {@link Story} to load, or NULL for all types
74 */
333f0e7b 75 public LocalReaderFrame(LocalReader reader, String type) {
39c3c689 76 super(String.format("Fanfix %s Library", Version.getCurrentVersion()));
a6395bef
NR
77
78 this.reader = reader;
79
80 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
81 setSize(800, 600);
d3c84ac3 82 setLayout(new BorderLayout());
a6395bef 83
4310bae9
NR
84 pane = new JPanel();
85 pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
b4dc6ab5 86
3b2b638f 87 color = Instance.getUiConfig().getColor(UiConfig.BACKGROUND_COLOR);
b4dc6ab5
NR
88 if (color != null) {
89 setBackground(color);
4310bae9 90 pane.setBackground(color);
b4dc6ab5 91 }
d3c84ac3 92
4310bae9 93 JScrollPane scroll = new JScrollPane(pane);
b4dc6ab5
NR
94 scroll.getVerticalScrollBar().setUnitIncrement(16);
95 add(scroll, BorderLayout.CENTER);
a6395bef 96
3b2b638f
NR
97 pgBar = new ProgressBar();
98 add(pgBar, BorderLayout.SOUTH);
99
754a5bc2
NR
100 pgBar.addActionListener(new ActionListener() {
101 public void actionPerformed(ActionEvent e) {
102 invalidate();
103 pgBar.setProgress(null);
104 validate();
105 setEnabled(true);
106 }
107 });
108
109 pgBar.addUpdateListener(new ActionListener() {
110 public void actionPerformed(ActionEvent e) {
111 revalidate();
112 repaint();
113 }
114 });
115
333f0e7b
NR
116 setJMenuBar(createMenu());
117
4310bae9
NR
118 booksByType = new HashMap<LocalReaderGroup, String>();
119 booksByAuthor = new HashMap<LocalReaderGroup, String>();
120
121 addBookPane(type, true);
122 refreshBooks();
123
333f0e7b
NR
124 setVisible(true);
125 }
126
4d205683 127 /**
4310bae9
NR
128 * Add a new {@link LocalReaderGroup} on the frame to display the books of
129 * the selected type or author.
4d205683 130 *
4310bae9
NR
131 * @param value
132 * the author or the type
4d205683 133 * @param type
4310bae9 134 * TRUE for type, FALSE for author
4d205683 135 */
4310bae9
NR
136 private void addBookPane(String value, boolean type) {
137 if (value == null) {
138 if (type) {
139 for (String tt : Instance.getLibrary().getTypes()) {
140 if (tt != null) {
141 addBookPane(tt, type);
142 }
143 }
144 } else {
145 for (String tt : Instance.getLibrary().getAuthors()) {
146 if (tt != null) {
147 addBookPane(tt, type);
148 }
149 }
b4dc6ab5
NR
150 }
151
4310bae9
NR
152 return;
153 }
4d205683 154
4310bae9
NR
155 LocalReaderGroup bookPane = new LocalReaderGroup(reader, value, color);
156 if (type) {
157 booksByType.put(bookPane, value);
158 } else {
159 booksByAuthor.put(bookPane, value);
160 }
333f0e7b 161
4310bae9
NR
162 this.invalidate();
163 pane.invalidate();
164 pane.add(bookPane);
165 pane.validate();
166 this.validate();
9843a5e5 167
4310bae9
NR
168 bookPane.setActionListener(new BookActionListener() {
169 public void select(LocalReaderBook book) {
170 selectedBook = book;
171 }
172
173 public void popupRequested(LocalReaderBook book, MouseEvent e) {
174 JPopupMenu popup = new JPopupMenu();
175 popup.add(createMenuItemOpenBook());
176 popup.addSeparator();
177 popup.add(createMenuItemExport());
178 popup.add(createMenuItemClearCache());
179 popup.add(createMenuItemRedownload());
180 popup.addSeparator();
181 popup.add(createMenuItemDelete());
182 popup.show(e.getComponent(), e.getX(), e.getY());
183 }
184
185 public void action(final LocalReaderBook book) {
186 openBook(book);
187 }
188 });
189 }
a6395bef 190
4310bae9
NR
191 private void removeBookPanes() {
192 booksByType.clear();
193 booksByAuthor.clear();
194 pane.invalidate();
195 this.invalidate();
196 pane.removeAll();
197 pane.validate();
198 this.validate();
199 }
200
201 /**
202 * Refresh the list of {@link LocalReaderBook}s from disk.
203 *
4310bae9
NR
204 */
205 private void refreshBooks() {
206 for (LocalReaderGroup group : booksByType.keySet()) {
207 List<MetaData> stories = Instance.getLibrary().getListByType(
208 booksByType.get(group));
793f1071 209 group.refreshBooks(stories, words);
4310bae9
NR
210 }
211
212 for (LocalReaderGroup group : booksByAuthor.keySet()) {
213 List<MetaData> stories = Instance.getLibrary().getListByAuthor(
214 booksByAuthor.get(group));
793f1071 215 group.refreshBooks(stories, words);
a6395bef
NR
216 }
217
4310bae9
NR
218 pane.repaint();
219 this.repaint();
333f0e7b
NR
220 }
221
4d205683
NR
222 /**
223 * Create the main menu bar.
224 *
225 * @return the bar
226 */
333f0e7b 227 private JMenuBar createMenu() {
3b2b638f 228 bar = new JMenuBar();
333f0e7b
NR
229
230 JMenu file = new JMenu("File");
10d558d2 231 file.setMnemonic(KeyEvent.VK_F);
333f0e7b 232
22848428 233 JMenuItem imprt = new JMenuItem("Import URL...", KeyEvent.VK_U);
333f0e7b
NR
234 imprt.addActionListener(new ActionListener() {
235 public void actionPerformed(ActionEvent e) {
10d558d2
NR
236 imprt(true);
237 }
238 });
22848428 239 JMenuItem imprtF = new JMenuItem("Import File...", KeyEvent.VK_F);
10d558d2
NR
240 imprtF.addActionListener(new ActionListener() {
241 public void actionPerformed(ActionEvent e) {
242 imprt(false);
243 }
244 });
245 JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X);
246 exit.addActionListener(new ActionListener() {
247 public void actionPerformed(ActionEvent e) {
248 LocalReaderFrame.this.dispatchEvent(new WindowEvent(
249 LocalReaderFrame.this, WindowEvent.WINDOW_CLOSING));
250 }
251 });
3b2b638f 252
9843a5e5
NR
253 file.add(createMenuItemOpenBook());
254 file.add(createMenuItemExport());
255 file.addSeparator();
10d558d2
NR
256 file.add(imprt);
257 file.add(imprtF);
258 file.addSeparator();
259 file.add(exit);
3b2b638f 260
10d558d2
NR
261 bar.add(file);
262
263 JMenu edit = new JMenu("Edit");
264 edit.setMnemonic(KeyEvent.VK_E);
265
22848428
NR
266 edit.add(createMenuItemClearCache());
267 edit.add(createMenuItemRedownload());
9843a5e5
NR
268 edit.addSeparator();
269 edit.add(createMenuItemDelete());
270
271 bar.add(edit);
272
793f1071
NR
273 JMenu view = new JMenu("View");
274 view.setMnemonic(KeyEvent.VK_V);
275 JMenuItem vauthors = new JMenuItem("Author");
276 vauthors.setMnemonic(KeyEvent.VK_A);
277 vauthors.addActionListener(new ActionListener() {
278 public void actionPerformed(ActionEvent e) {
279 words = false;
280 refreshBooks();
281 }
282 });
283 view.add(vauthors);
284 JMenuItem vwords = new JMenuItem("Word count");
285 vwords.setMnemonic(KeyEvent.VK_W);
286 vwords.addActionListener(new ActionListener() {
287 public void actionPerformed(ActionEvent e) {
288 words = true;
289 refreshBooks();
290 }
291 });
292 view.add(vwords);
293 bar.add(view);
294
4310bae9
NR
295 JMenu sources = new JMenu("Sources");
296 sources.setMnemonic(KeyEvent.VK_S);
9843a5e5
NR
297
298 List<String> tt = Instance.getLibrary().getTypes();
299 tt.add(0, null);
300 for (final String type : tt) {
4310bae9 301 JMenuItem item = new JMenuItem(type == null ? "All" : type);
9843a5e5
NR
302 item.addActionListener(new ActionListener() {
303 public void actionPerformed(ActionEvent e) {
4310bae9
NR
304 removeBookPanes();
305 addBookPane(type, true);
306 refreshBooks();
9843a5e5
NR
307 }
308 });
4310bae9 309 sources.add(item);
9843a5e5
NR
310
311 if (type == null) {
4310bae9 312 sources.addSeparator();
9843a5e5
NR
313 }
314 }
10d558d2 315
4310bae9
NR
316 bar.add(sources);
317
318 JMenu authors = new JMenu("Authors");
319 authors.setMnemonic(KeyEvent.VK_A);
320
321 List<String> aa = Instance.getLibrary().getAuthors();
322 aa.add(0, null);
323 for (final String author : aa) {
324 JMenuItem item = new JMenuItem(author == null ? "All"
325 : author.isEmpty() ? "[unknown]" : author);
326 item.addActionListener(new ActionListener() {
327 public void actionPerformed(ActionEvent e) {
328 removeBookPanes();
329 addBookPane(author, false);
330 refreshBooks();
331 }
332 });
333 authors.add(item);
334
335 if (author == null || author.isEmpty()) {
336 authors.addSeparator();
337 }
338 }
339
340 bar.add(authors);
9843a5e5
NR
341
342 return bar;
343 }
344
4d205683
NR
345 /**
346 * Create the export menu item.
347 *
348 * @return the item
349 */
9843a5e5 350 private JMenuItem createMenuItemExport() {
4d205683
NR
351 final JFileChooser fc = new JFileChooser();
352 fc.setAcceptAllFileFilterUsed(false);
353
354 final Map<FileFilter, OutputType> filters = new HashMap<FileFilter, OutputType>();
355 for (OutputType type : OutputType.values()) {
356 String ext = type.getDefaultExtension(false);
357 String desc = type.getDesc(false);
b2612f9d 358
4d205683
NR
359 if (ext == null || ext.isEmpty()) {
360 filters.put(createAllFilter(desc), type);
361 } else {
362 filters.put(new FileNameExtensionFilter(desc, ext), type);
363 }
364 }
365
366 // First the "ALL" filters, then, the extension filters
367 for (Entry<FileFilter, OutputType> entry : filters.entrySet()) {
368 if (!(entry.getKey() instanceof FileNameExtensionFilter)) {
369 fc.addChoosableFileFilter(entry.getKey());
370 }
371 }
372 for (Entry<FileFilter, OutputType> entry : filters.entrySet()) {
373 if (entry.getKey() instanceof FileNameExtensionFilter) {
374 fc.addChoosableFileFilter(entry.getKey());
375 }
376 }
377 //
9843a5e5 378
4d205683 379 JMenuItem export = new JMenuItem("Save as...", KeyEvent.VK_S);
10d558d2
NR
380 export.addActionListener(new ActionListener() {
381 public void actionPerformed(ActionEvent e) {
4d205683
NR
382 if (selectedBook != null) {
383 fc.showDialog(LocalReaderFrame.this, "Save");
b2612f9d
NR
384 if (fc.getSelectedFile() != null) {
385 final OutputType type = filters.get(fc.getFileFilter());
386 final String path = fc.getSelectedFile()
387 .getAbsolutePath()
388 + type.getDefaultExtension(false);
389 final Progress pg = new Progress();
390 outOfUi(pg, new Runnable() {
391 public void run() {
392 try {
393 Instance.getLibrary().export(
394 selectedBook.getMeta().getLuid(),
395 type, path, pg);
396 } catch (IOException e) {
397 Instance.syserr(e);
398 }
4d205683 399 }
b2612f9d
NR
400 });
401 }
4d205683 402 }
10d558d2
NR
403 }
404 });
405
9843a5e5
NR
406 return export;
407 }
408
edd46289
NR
409 /**
410 * Create a {@link FileFilter} that accepts all files and return the given
411 * description.
412 *
413 * @param desc
414 * the description
415 *
416 * @return the filter
417 */
4d205683
NR
418 private FileFilter createAllFilter(final String desc) {
419 return new FileFilter() {
420 @Override
421 public String getDescription() {
422 return desc;
423 }
424
425 @Override
426 public boolean accept(File f) {
427 return true;
428 }
429 };
430 }
431
432 /**
433 * Create the refresh (delete cache) menu item.
434 *
435 * @return the item
436 */
22848428 437 private JMenuItem createMenuItemClearCache() {
4d205683 438 JMenuItem refresh = new JMenuItem("Clear cache", KeyEvent.VK_C);
10d558d2
NR
439 refresh.addActionListener(new ActionListener() {
440 public void actionPerformed(ActionEvent e) {
441 if (selectedBook != null) {
442 outOfUi(null, new Runnable() {
443 public void run() {
754a5bc2
NR
444 reader.clearLocalReaderCache(selectedBook.getMeta()
445 .getLuid());
10d558d2 446 selectedBook.setCached(false);
3b2b638f
NR
447 SwingUtilities.invokeLater(new Runnable() {
448 public void run() {
10d558d2 449 selectedBook.repaint();
3b2b638f
NR
450 }
451 });
452 }
453 });
333f0e7b
NR
454 }
455 }
456 });
10d558d2 457
9843a5e5
NR
458 return refresh;
459 }
460
22848428
NR
461 /**
462 * Create the redownload (then delete original) menu item.
463 *
464 * @return the item
465 */
466 private JMenuItem createMenuItemRedownload() {
467 JMenuItem refresh = new JMenuItem("Redownload", KeyEvent.VK_R);
468 refresh.addActionListener(new ActionListener() {
469 public void actionPerformed(ActionEvent e) {
470 if (selectedBook != null) {
754a5bc2
NR
471 final MetaData meta = selectedBook.getMeta();
472 imprt(meta.getUrl(), new Runnable() {
22848428 473 public void run() {
754a5bc2
NR
474 reader.delete(meta.getLuid());
475 LocalReaderFrame.this.selectedBook = null;
22848428 476 }
754a5bc2 477 }, "Removing old copy");
22848428
NR
478 }
479 }
480 });
481
482 return refresh;
483 }
484
4d205683
NR
485 /**
486 * Create the delete menu item.
487 *
488 * @return the item
489 */
9843a5e5 490 private JMenuItem createMenuItemDelete() {
10d558d2
NR
491 JMenuItem delete = new JMenuItem("Delete", KeyEvent.VK_D);
492 delete.addActionListener(new ActionListener() {
493 public void actionPerformed(ActionEvent e) {
494 if (selectedBook != null) {
495 outOfUi(null, new Runnable() {
496 public void run() {
22848428 497 reader.delete(selectedBook.getMeta().getLuid());
10d558d2 498 selectedBook = null;
10d558d2
NR
499 }
500 });
501 }
502 }
503 });
504
9843a5e5
NR
505 return delete;
506 }
10d558d2 507
4d205683
NR
508 /**
509 * Create the open menu item.
510 *
511 * @return the item
512 */
9843a5e5
NR
513 private JMenuItem createMenuItemOpenBook() {
514 JMenuItem open = new JMenuItem("Open", KeyEvent.VK_O);
515 open.addActionListener(new ActionListener() {
516 public void actionPerformed(ActionEvent e) {
517 if (selectedBook != null) {
518 openBook(selectedBook);
519 }
520 }
521 });
10d558d2 522
9843a5e5
NR
523 return open;
524 }
10d558d2 525
4d205683
NR
526 /**
527 * Open a {@link LocalReaderBook} item.
528 *
529 * @param book
530 * the {@link LocalReaderBook} to open
531 */
9843a5e5
NR
532 private void openBook(final LocalReaderBook book) {
533 final Progress pg = new Progress();
534 outOfUi(pg, new Runnable() {
535 public void run() {
536 try {
22848428 537 reader.open(book.getMeta().getLuid(), pg);
edd46289
NR
538 SwingUtilities.invokeLater(new Runnable() {
539 public void run() {
540 book.setCached(true);
9843a5e5 541 }
edd46289 542 });
9843a5e5 543 } catch (IOException e) {
edd46289 544 // TODO: error message?
9843a5e5 545 Instance.syserr(e);
333f0e7b 546 }
10d558d2 547 }
9843a5e5 548 });
a6395bef 549 }
3b2b638f 550
4d205683
NR
551 /**
552 * Process the given action out of the Swing UI thread and link the given
553 * {@link ProgressBar} to the action.
554 * <p>
555 * The code will make sure that the {@link ProgressBar} (if not NULL) is set
556 * to done when the action is done.
557 *
558 * @param pg
559 * the {@link ProgressBar} or NULL
560 * @param run
561 * the action to run
562 */
754a5bc2
NR
563 private void outOfUi(Progress progress, final Runnable run) {
564 final Progress pg = new Progress();
d5a7153c 565 final Progress reload = new Progress("Reload books");
754a5bc2
NR
566 if (progress == null) {
567 progress = new Progress();
568 }
3b2b638f 569
754a5bc2
NR
570 pg.addProgress(progress, 90);
571 pg.addProgress(reload, 10);
572
573 invalidate();
574 pgBar.setProgress(pg);
575 validate();
a4143cd7 576 setEnabled(false);
3b2b638f
NR
577
578 new Thread(new Runnable() {
579 public void run() {
580 run.run();
754a5bc2
NR
581 refreshBooks();
582 reload.setProgress(100);
583 if (!pg.isDone()) {
584 // will trigger pgBar ActionListener:
3b2b638f
NR
585 pg.setProgress(pg.getMax());
586 }
587 }
754a5bc2 588 }, "outOfUi thread").start();
3b2b638f
NR
589 }
590
4d205683
NR
591 /**
592 * Import a {@link Story} into the main {@link Library}.
22848428
NR
593 * <p>
594 * Should be called inside the UI thread.
4d205683
NR
595 *
596 * @param askUrl
597 * TRUE for an {@link URL}, false for a {@link File}
598 */
10d558d2
NR
599 private void imprt(boolean askUrl) {
600 JFileChooser fc = new JFileChooser();
601
5130ce84 602 Object url;
10d558d2 603 if (askUrl) {
5130ce84
NR
604 String clipboard = "";
605 try {
606 clipboard = ("" + Toolkit.getDefaultToolkit()
607 .getSystemClipboard().getData(DataFlavor.stringFlavor))
608 .trim();
609 } catch (Exception e) {
610 // No data will be handled
611 }
612
613 if (clipboard == null || !clipboard.startsWith("http")) {
614 clipboard = "";
615 }
616
10d558d2
NR
617 url = JOptionPane.showInputDialog(LocalReaderFrame.this,
618 "url of the story to import?", "Importing from URL",
5130ce84 619 JOptionPane.QUESTION_MESSAGE, null, null, clipboard);
10d558d2
NR
620 } else if (fc.showOpenDialog(this) != JFileChooser.CANCEL_OPTION) {
621 url = fc.getSelectedFile().getAbsolutePath();
622 } else {
623 url = null;
624 }
625
5130ce84 626 if (url != null && !url.toString().isEmpty()) {
754a5bc2 627 imprt(url.toString(), null, null);
22848428
NR
628 }
629 }
10d558d2 630
22848428
NR
631 /**
632 * Actually import the {@link Story} into the main {@link Library}.
633 * <p>
634 * Should be called inside the UI thread.
635 *
636 * @param url
637 * the {@link Story} to import by {@link URL}
638 * @param onSuccess
639 * Action to execute on success
640 */
754a5bc2
NR
641 private void imprt(final String url, final Runnable onSuccess,
642 String onSuccessPgName) {
643 final Progress pg = new Progress();
644 final Progress pgImprt = new Progress();
645 final Progress pgOnSuccess = new Progress(onSuccessPgName);
646 pg.addProgress(pgImprt, 95);
647 pg.addProgress(pgOnSuccess, 5);
648
22848428
NR
649 outOfUi(pg, new Runnable() {
650 public void run() {
651 Exception ex = null;
652 try {
754a5bc2
NR
653 Instance.getLibrary().imprt(BasicReader.getUrl(url),
654 pgImprt);
22848428
NR
655 } catch (IOException e) {
656 ex = e;
657 }
10d558d2 658
22848428
NR
659 final Exception e = ex;
660
661 final boolean ok = (e == null);
754a5bc2
NR
662
663 pgOnSuccess.setProgress(0);
664 if (!ok) {
665 Instance.syserr(e);
666 SwingUtilities.invokeLater(new Runnable() {
667 public void run() {
22848428
NR
668 JOptionPane.showMessageDialog(
669 LocalReaderFrame.this, "Cannot import: "
670 + url, e.getMessage(),
671 JOptionPane.ERROR_MESSAGE);
10d558d2 672 }
754a5bc2
NR
673 });
674 } else {
675 if (onSuccess != null) {
676 onSuccess.run();
22848428 677 }
754a5bc2
NR
678 }
679 pgOnSuccess.setProgress(100);
22848428
NR
680 }
681 });
10d558d2
NR
682 }
683
4d205683
NR
684 /**
685 * Enables or disables this component, depending on the value of the
686 * parameter <code>b</code>. An enabled component can respond to user input
687 * and generate events. Components are enabled initially by default.
688 * <p>
689 * Disabling this component will also affect its children.
690 *
691 * @param b
692 * If <code>true</code>, this component is enabled; otherwise
693 * this component is disabled
694 */
695 @Override
696 public void setEnabled(boolean b) {
4d205683 697 bar.setEnabled(b);
4310bae9
NR
698 for (LocalReaderGroup group : booksByType.keySet()) {
699 group.setEnabled(b);
700 }
701 for (LocalReaderGroup group : booksByAuthor.keySet()) {
702 group.setEnabled(b);
703 }
4d205683 704 super.setEnabled(b);
3b2b638f
NR
705 repaint();
706 }
a6395bef 707}