1
22 package org.crosswire.bibledesktop.book.install;
23
24 import java.awt.BorderLayout;
25 import java.awt.Component;
26 import java.awt.Font;
27 import java.awt.GridLayout;
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30
31 import javax.swing.BorderFactory;
32 import javax.swing.JButton;
33 import javax.swing.JLabel;
34 import javax.swing.JOptionPane;
35 import javax.swing.JPanel;
36 import javax.swing.JScrollPane;
37 import javax.swing.JSplitPane;
38 import javax.swing.JTree;
39 import javax.swing.ToolTipManager;
40 import javax.swing.event.TreeSelectionEvent;
41 import javax.swing.event.TreeSelectionListener;
42 import javax.swing.tree.DefaultMutableTreeNode;
43 import javax.swing.tree.DefaultTreeModel;
44 import javax.swing.tree.TreeModel;
45 import javax.swing.tree.TreeNode;
46 import javax.swing.tree.TreePath;
47 import javax.swing.tree.TreeSelectionModel;
48
49 import org.crosswire.bibledesktop.BDMsg;
50 import org.crosswire.common.swing.ActionFactory;
51 import org.crosswire.common.swing.CWAction;
52 import org.crosswire.common.swing.CWLabel;
53 import org.crosswire.common.swing.CWOptionPane;
54 import org.crosswire.common.swing.CWScrollPane;
55 import org.crosswire.common.swing.FixedSplitPane;
56 import org.crosswire.common.swing.FontChooser;
57 import org.crosswire.common.util.Language;
58 import org.crosswire.common.util.Reporter;
59 import org.crosswire.jsword.book.Book;
60 import org.crosswire.jsword.book.BookCategory;
61 import org.crosswire.jsword.book.BookException;
62 import org.crosswire.jsword.book.BookList;
63 import org.crosswire.jsword.book.BookMetaData;
64 import org.crosswire.jsword.book.BookSet;
65 import org.crosswire.jsword.book.Books;
66 import org.crosswire.jsword.book.BooksEvent;
67 import org.crosswire.jsword.book.BooksListener;
68 import org.crosswire.jsword.book.install.InstallException;
69 import org.crosswire.jsword.book.install.Installer;
70 import org.crosswire.jsword.index.IndexManager;
71 import org.crosswire.jsword.index.IndexManagerFactory;
72 import org.crosswire.jsword.util.WebWarning;
73
74
83 public class SitePane extends JPanel {
84
87 public SitePane() {
88 this(null, BDMsg.gettext("Installed Books:"));
90 }
91
92
95 public SitePane(Installer bookListInstaller) {
96 this(bookListInstaller, BDMsg.gettext("Available Books:"));
98 }
99
100
103 private SitePane(Installer bookListInstaller, String labelAcronymn) {
104 installer = bookListInstaller;
105
106 actions = new ActionFactory(this);
107
108 BookList bl = installer;
109 if (bl == null) {
110 bl = Books.installed();
111 bl.addBooksListener(new CustomBooksListener());
112 }
113
114 initialize(labelAcronymn, bl);
115 }
116
117
120 private void initialize(String labelAcronymn, BookList books) {
121 lblDesc = new JLabel();
122 lblDesc.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
123
124 Component left = createAvailablePanel(labelAcronymn, books);
125 Component right = createSelectedPanel();
126 this.setLayout(new BorderLayout());
127 this.add(lblDesc, BorderLayout.NORTH);
128 this.add(createSplitPane(left, right), BorderLayout.CENTER);
129
130 updateDescription();
131 }
132
133
136 private void updateDescription() {
137 String desc = "#ERROR#";
138
139 if (installer == null) {
140 int bookCount = Books.installed().getBooks().size();
141 desc = BDMsg.gettext("{0} books installed.", Integer.valueOf(bookCount));
143 } else {
144 int bookCount = installer.getBooks().size();
145 if (bookCount == 0) {
146 StringBuilder buf = new StringBuilder(200);
147 buf.append("<html><b>");
148 buf.append(BDMsg.gettext("Click 'Update Available Books' to download an up to date book list."));
150 buf.append("</b>");
151 desc = buf.toString();
152 } else {
153 desc = BDMsg.gettext("{0} books available for download.", Integer.valueOf(bookCount));
155 }
156 }
157
158 lblDesc.setText(desc);
159 }
160
161
164 private Component createSplitPane(Component left, Component right) {
165 JSplitPane split = new FixedSplitPane();
166 split.setDividerLocation(0.3D);
167 split.setResizeWeight(0.3D);
168 split.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
169 split.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
170 split.setDividerSize(10);
171 split.setLeftComponent(left);
172 split.setRightComponent(right);
173 return split;
174 }
175
176
179 private Component createAvailablePanel(String labelAcronymn, BookList books) {
180 JLabel lblAvailable = CWLabel.createJLabel(labelAcronymn);
181
182 JPanel panel = new JPanel();
183 panel.setLayout(new BorderLayout());
184 panel.add(lblAvailable, BorderLayout.PAGE_START);
185 panel.add(createScrolledTree(books), BorderLayout.CENTER);
186 panel.add(createPanelActions(), BorderLayout.PAGE_END);
187
188 lblAvailable.setLabelFor(treAvailable);
190
191 return panel;
192 }
193
194
197 private Component createSelectedPanel() {
198
199 JLabel lblSelected = CWLabel.createJLabel(BDMsg.gettext("Selected Book:"));
201 display = new TextPaneBookMetaDataDisplay();
202 lblSelected.setLabelFor(display.getComponent());
203
204 JScrollPane scrSelected = new CWScrollPane();
205 JPanel panel = new JPanel();
206 panel.setLayout(new BorderLayout());
207 panel.add(lblSelected, BorderLayout.PAGE_START);
208 panel.add(scrSelected, BorderLayout.CENTER);
209 scrSelected.getViewport().add(display.getComponent());
210 return panel;
211 }
212
213
216 private Component createScrolledTree(BookList books) {
217 treAvailable = new JTree();
218 ToolTipManager.sharedInstance().registerComponent(treAvailable);
220 treAvailable.setCellRenderer(new BookTreeCellRenderer());
221
222 setTreeModel(books);
223 treAvailable.putClientProperty("JTree.lineStyle", "Angled");
225 treAvailable.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
226 treAvailable.setCellEditor(null);
227 treAvailable.setRootVisible(false);
228 treAvailable.setShowsRootHandles(true);
229 treAvailable.addTreeSelectionListener(new TreeSelectionListener() {
230 public void valueChanged(TreeSelectionEvent ev) {
231 selected();
232 }
233 });
234
235 return new CWScrollPane(treAvailable);
236 }
237
238 private TreeModel createTreeModel(BookList books) {
239 BookSet bmds = new BookSet(books.getBooks());
241 TreeNode bookRoot = new BookNode("root", bmds, 0, BookMetaData.KEY_CATEGORY, BookMetaData.KEY_XML_LANG);
242 return new DefaultTreeModel(bookRoot);
243 }
244
245 private Book getBook(Object anObj) {
246 Object obj = anObj;
247 if (obj instanceof DefaultMutableTreeNode) {
248 obj = ((DefaultMutableTreeNode) obj).getUserObject();
249 }
250 if (obj instanceof Book) {
251 return (Book) obj;
252 }
253 return null;
254 }
255
256 private Language getLanguage(Object anObj) {
257 Object obj = anObj;
258 if (obj instanceof DefaultMutableTreeNode) {
259 obj = ((DefaultMutableTreeNode) obj).getUserObject();
260 }
261 if (obj instanceof Language) {
262 return (Language) obj;
263 }
264 return null;
265 }
266
267
270 private Component createPanelActions() {
271 JPanel panel = new JPanel();
272 CWAction action;
273 if (installer != null) {
274 panel.setLayout(new GridLayout(1, 2, 3, 3));
275
276 action = actions.addAction("Install", BDMsg.gettext("Install"));
278 action.setTooltip(BDMsg.gettext("Install the selected book"));
280 action.enable(false);
281 panel.add(new JButton(action));
282
283
289 action = actions.addAction("Refresh", BDMsg.gettext("Update Available Books"));
292 action.setTooltip(BDMsg.gettext("Download a current listing of books."));
293 panel.add(new JButton(action));
294 } else {
295 panel.setLayout(new GridLayout(3, 2, 3, 3));
296
297 action = actions.addAction("Delete", BDMsg.gettext("Delete Book"));
299 action.setTooltip(BDMsg.gettext("Delete the selected book"));
301 action.enable(false);
302 panel.add(new JButton(action));
303
304 action = actions.addAction("Unindex", BDMsg.gettext("Remove Search Index"));
306 action.setTooltip(BDMsg.gettext("Remove the search index of the selected book"));
308 action.enable(false);
309 panel.add(new JButton(action));
310
311 action = actions.addAction("ChooseFont", BDMsg.gettext("Font..."));
313 action.setTooltip(BDMsg.gettext("Choose a font for the language or book"));
315 action.enable(false);
316 panel.add(new JButton(action));
317
318 action = actions.addAction("Unlock", BDMsg.gettext("Unlock"));
320 action.setTooltip(BDMsg.gettext("Unlock the selected book"));
322 action.enable(false);
323 panel.add(new JButton(action));
324
325 action = actions.addAction("ResetFont", BDMsg.gettext("Reset Font"));
328 action.setTooltip(BDMsg.gettext("Reset the custom font set for this language or book"));
330 action.enable(false);
331 panel.add(new JButton(action));
332
333 }
334 return panel;
335 }
336
337
340 public void doDelete() {
341 TreePath path = treAvailable.getSelectionPath();
342 if (path == null) {
343 return;
344 }
345
346 Object last = path.getLastPathComponent();
347 Book book = getBook(last);
348
349 try {
350 String msg = BDMsg.gettext("Are you sure you want to delete {0}?", book.getName());
352 if (CWOptionPane.showConfirmDialog(this, msg, BDMsg.gettext("Delete Book"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
354 book.getDriver().delete(book);
355
356 IndexManager imanager = IndexManagerFactory.getIndexManager();
357 if (imanager.isIndexed(book)) {
358 imanager.deleteIndex(book);
359 }
360 }
361 } catch (BookException e) {
362 Reporter.informUser(this, e);
363 }
364 }
365
366
369 public void doUnlock() {
370 TreePath path = treAvailable.getSelectionPath();
371 if (path == null) {
372 return;
373 }
374
375 Object last = path.getLastPathComponent();
376 Book book = getBook(last);
377
378 String title = BDMsg.gettext("Unlock Book");
380 StringBuilder msg = new StringBuilder(200);
381 msg.append(BDMsg.gettext("Please enter the unlock key for:"));
384 msg.append('\n');
386 msg.append(book.getName());
387 String unlockKey = (String) CWOptionPane.showInputDialog(this, msg.toString(), title, JOptionPane.QUESTION_MESSAGE, null, null, book.getUnlockKey());
388
389 if (unlockKey != null && unlockKey.length() > 0) {
390 book.unlock(unlockKey);
391 Books.installed().addBook(book);
392 }
393 }
394
395
398 public void doUnindex() {
399 TreePath path = treAvailable.getSelectionPath();
400 if (path == null) {
401 return;
402 }
403
404 Object last = path.getLastPathComponent();
405 Book book = getBook(last);
406
407 try {
408 IndexManager imanager = IndexManagerFactory.getIndexManager();
409 if (imanager.isIndexed(book)) {
410 String formattedMsg = BDMsg.gettext("Are you sure you want to remove the index for {0}?", book.getName());
413 if (CWOptionPane.showConfirmDialog(this, formattedMsg, BDMsg.gettext("Remove Index for Book"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
416 imanager.deleteIndex(book);
417 }
418 }
419 actions.findAction("Unindex").setEnabled(imanager.isIndexed(book));
420 } catch (BookException e) {
421 Reporter.informUser(this, e);
422 }
423 }
424
425
428 public void doRefresh() {
429 if (installer != null) {
430 try {
431 int webAccess = InternetWarning.GRANTED;
432 if (WebWarning.instance().isShown()) {
433 webAccess = InternetWarning.showDialog(this, "?");
434 }
435
436 if (webAccess == InternetWarning.GRANTED) {
437 installer.reloadBookList();
438 setTreeModel(installer);
439 }
440 } catch (InstallException ex) {
441 Reporter.informUser(this, ex);
442 }
443 }
444 }
445
446
449 public void doInstall() {
450 if (installer == null) {
451 return;
452 }
453
454 TreePath path = treAvailable.getSelectionPath();
455 if (path == null) {
456 return;
457 }
458
459 int webAccess = InternetWarning.GRANTED;
460 if (WebWarning.instance().isShown()) {
461 webAccess = InternetWarning.showDialog(this, "?");
462 }
463
464 if (webAccess != InternetWarning.GRANTED) {
465 return;
466 }
467
468 Object last = path.getLastPathComponent();
469 Book name = getBook(last);
470
471 try {
472 Book book = Books.installed().getBook(name.getName());
474 if (book != null && !installer.isNewer(name)) {
475 Reporter.informUser(this, BDMsg.gettext("Book already installed: {0}", name.getName()));
478 return;
479 }
480
481 float size = installer.getSize(name) / 1024.0F;
482
483 String formattedMsg = "";
484 if (size > 1024.0F) {
485 size /= 1024.0F;
486 formattedMsg = BDMsg.gettext("{0} is {1,number,###,###,###.#}MB. Continue?", name.getName(), Float.valueOf(size));
493 } else {
494 formattedMsg = BDMsg.gettext("{0} is {1,number,###,###,###.#}KB. Continue?", name.getName(), Float.valueOf(size));
501 }
502
503 if (CWOptionPane.showConfirmDialog(this, formattedMsg, BDMsg.gettext("Download Book"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
505 installer.install(name);
506 }
507 } catch (InstallException ex) {
508 Reporter.informUser(this, ex);
509 }
510 }
511
512
515 public void doInstallSearch() {
516 doInstall();
517
518 TreePath path = treAvailable.getSelectionPath();
519 if (path != null) {
520 Object last = path.getLastPathComponent();
521 Book book = getBook(last);
522 IndexResolver.scheduleIndex(book, this);
523 }
524 }
525
526
529 public void doChooseFont() {
530 TreePath path = treAvailable.getSelectionPath();
531 if (path == null) {
532 return;
533 }
534
535 Object last = path.getLastPathComponent();
536 Book book = getBook(last);
537 if (book != null) {
538 Font picked = FontChooser.showDialog(this, BDMsg.gettext("Choose Font"), BookFont.instance().getFont(book));
540 BookFont.instance().setFont(book, picked);
541 }
542
543 Language language = getLanguage(last);
544 if (language != null) {
545 Font picked = FontChooser.showDialog(this, BDMsg.gettext("Choose Font"), BookFont.instance().getFont(language));
547 BookFont.instance().setFont(language, picked);
548 }
549 actions.findAction("ResetFont").setEnabled(BookFont.instance().isSet(book, language));
550 }
551
552
555 public void doResetFont() {
556 TreePath path = treAvailable.getSelectionPath();
557 if (path == null) {
558 return;
559 }
560
561 Object last = path.getLastPathComponent();
562 Book book = getBook(last);
563 Language language = getLanguage(last);
564 BookFont.instance().resetFont(book, language);
565 actions.findAction("ResetFont").setEnabled(false);
566 }
567
568
571 protected void selected() {
572 TreePath path = treAvailable.getSelectionPath();
573
574 Book book = null;
575 Language lang = null;
576 if (path != null) {
577 Object last = path.getLastPathComponent();
578 book = getBook(last);
579 lang = getLanguage(last);
580 }
581
582 display.setBook(book);
583
584 actions.findAction("Delete").setEnabled(book != null && book.getDriver().isDeletable(book));
585 actions.findAction("Unlock").setEnabled(book != null && book.isEnciphered());
586 actions.findAction("Unindex").setEnabled(book != null && IndexManagerFactory.getIndexManager().isIndexed(book));
587 actions.findAction("Install").setEnabled(book != null && book.isSupported());
588 actions.findAction("InstallSearch").setEnabled(book != null && book.isSupported() && book.getBookCategory() == BookCategory.BIBLE);
589 actions.findAction("ChooseFont").setEnabled(book != null || lang != null);
590 actions.findAction("ResetFont").setEnabled(BookFont.instance().isSet(book, lang));
591 }
592
593 public void setTreeModel(BookList books) {
594 treAvailable.setModel(createTreeModel(books));
595 }
596
597
600 final class CustomBooksListener implements BooksListener {
601
604 public void bookAdded(BooksEvent ev) {
605 setTreeModel((BookList) ev.getSource());
606 }
607
608
611 public void bookRemoved(BooksEvent ev) {
612 setTreeModel((BookList) ev.getSource());
613 }
614 }
615
616
623 private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
624 installer = null;
626 display = null;
627 actions = new ActionFactory(this);
628 is.defaultReadObject();
629 }
630
631
632
635 protected transient Installer installer;
636
637
640 private transient ActionFactory actions;
641
642
645 private JTree treAvailable;
646 private transient TextPaneBookMetaDataDisplay display;
647 private JLabel lblDesc;
648
649
652 private static final long serialVersionUID = 3616445692051075634L;
653 }
654