1   /**
2    * Distribution License:
3    * BibleDesktop is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU General Public License, version 2 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/gpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: DesktopActions.java 2138 2011-03-16 22:10:11Z dmsmith $
21   */
22  package org.crosswire.bibledesktop.desktop;
23  
24  import java.awt.Component;
25  import java.awt.event.ActionEvent;
26  import java.io.IOException;
27  import java.net.URI;
28  
29  import javax.swing.JCheckBoxMenuItem;
30  import javax.swing.ToolTipManager;
31  
32  import org.crosswire.bibledesktop.BDMsg;
33  import org.crosswire.bibledesktop.book.BibleViewPane;
34  import org.crosswire.bibledesktop.book.install.InternetWarning;
35  import org.crosswire.bibledesktop.book.install.SitesPane;
36  import org.crosswire.bibledesktop.display.BookDataDisplay;
37  import org.crosswire.bibledesktop.display.basic.SplitBookDataDisplay;
38  import org.crosswire.bibledesktop.display.basic.TabbedBookDataDisplay;
39  import org.crosswire.common.config.swing.ConfigEditorFactory;
40  import org.crosswire.common.swing.ActionFactory;
41  import org.crosswire.common.swing.CWOptionPane;
42  import org.crosswire.common.swing.desktop.LayoutPersistence;
43  import org.crosswire.common.swing.desktop.ViewVisitor;
44  import org.crosswire.common.util.CWProject;
45  import org.crosswire.common.util.ClassUtil;
46  import org.crosswire.common.util.FileUtil;
47  import org.crosswire.common.util.Logger;
48  import org.crosswire.common.util.OSType;
49  import org.crosswire.common.util.ReflectionUtil;
50  import org.crosswire.common.util.Reporter;
51  import org.crosswire.jsword.passage.Key;
52  import org.crosswire.jsword.passage.NoSuchVerseException;
53  import org.crosswire.jsword.util.WebWarning;
54  
55  /**
56   * DesktopAction is nothing more than a holder of the behavior of the Desktop.
57   * It could easily be member methods in that class. It is here simply to
58   * simplify the Desktop class and minimize maintenance cost.
59   * 
60   * Previously each of the "do" methods was a separate class.
61   * 
62   * @see gnu.gpl.License for license details.<br>
63   *      The copyright to this program is held by it's authors.
64   * @author Joe Walker [joe at eireneh dot com]
65   * @author DM Smith [dmsmith555 at yahoo dot com]
66   */
67  public class DesktopActions {
68      /**
69       * Create the actions for the desktop
70       * 
71       * @param desktop
72       *            the desktop for which these actions apply
73       */
74      public DesktopActions(Desktop desktop) {
75          this.desktop = desktop;
76          actions = new ActionFactory(this);
77  
78          osxRegistered = macOSXRegistration();
79      }
80  
81      /**
82       * @return the desktop to which these actions apply
83       */
84      public Desktop getDesktop() {
85          return desktop;
86      }
87  
88      /**
89       * @return the action factory
90       */
91      public ActionFactory getActions() {
92          return actions;
93      }
94  
95      /**
96       * Register the application with Apple EAWT, which provides support for the
97       * Application Menu, with About, Preferences (Options) and Quit (Exit).
98       * 
99       * @return true on success
100      */
101     public boolean macOSXRegistration() {
102         if (OSType.MAC.equals(OSType.getOSType())) {
103             try {
104                 Class<?> osxAdapter = ClassUtil.forName("org.crosswire.common.aqua.OSXAdapter");
105                 Object[] registerOSXArgs = {
106                         actions, "About", "Options", "Exit"
107                 };
108                 ReflectionUtil.invoke(osxAdapter, osxAdapter, "registerMacOSXApplication", registerOSXArgs);
109 
110                 // To call a method taking a type of boolean, the type has to
111                 // match but the object has to be wrapped
112                 Class<?>[] enablePrefTypes = {
113                     boolean.class
114                 };
115                 Object[] enablePrefArgs = {
116                     Boolean.TRUE
117                 };
118                 ReflectionUtil.invoke(osxAdapter, osxAdapter, "enablePrefs", enablePrefArgs, enablePrefTypes);
119                 return true;
120             } catch (NoClassDefFoundError e) {
121                 // This is thrown when EAWT or MacOSXadapter is not present.
122                 log.error("This version of Mac OS X does not support the Apple EAWT.  Application Menu handling has been disabled (" + e + ")");
123             } catch (ClassNotFoundException e) {
124                 // Should not happen
125                 log.error("This version of Mac OS X does not support the Apple EAWT.  Application Menu handling has been disabled (" + e + ")");
126             } catch (Exception e) {
127                 // Everything else.
128                 log.error("Exception while loading the OSXAdapter:", e);
129             }
130         }
131         return false;
132     }
133 
134     /**
135      * Determines whether MacOSX has been registered.
136      * 
137      * @return true when there is full MacOSX integration.
138      */
139     public boolean isOSXRegistered() {
140         return osxRegistered;
141     }
142 
143     /**
144      * @return the Bible installer dialog
145      */
146     public SitesPane getSites() {
147         if (sites == null) {
148             sites = new SitesPane();
149         }
150         return sites;
151     }
152 
153     /**
154      * Open a new passage window from a file.
155      */
156     public void doOpen() {
157         try {
158             BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
159             view.open();
160         } catch (NoSuchVerseException e) {
161             Reporter.informUser(getDesktop(), e);
162         } catch (IOException e) {
163             Reporter.informUser(getDesktop(), e);
164         }
165     }
166 
167     /**
168      * Save the current passage window.
169      */
170     public void doSave() {
171         try {
172             BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
173             if (!view.maySave()) {
174                 // TRANSLATOR: The user is trying to save the passage in the visible
175                 // Bible View pane, but it is empty.
176                 Reporter.informUser(getDesktop(), BDMsg.gettext("No Passage to Save"));
177                 return;
178             }
179 
180             view.save();
181         } catch (IOException ex) {
182             Reporter.informUser(getDesktop(), ex);
183         }
184     }
185 
186     /**
187      * Save the current passage window under a new name.
188      */
189     public void doSaveAs() {
190         try {
191             BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
192             if (!view.maySave()) {
193                 // TRANSLATOR: The user is trying to save the passage in the visible
194                 // Bible View pane, but it is empty.
195                 Reporter.informUser(getDesktop(), BDMsg.gettext("No Passage to Save"));
196                 return;
197             }
198 
199             view.saveAs();
200         } catch (IOException ex) {
201             Reporter.informUser(getDesktop(), ex);
202         }
203     }
204 
205     /**
206      * Save all the passage windows.
207      */
208     public void doSaveAll() {
209         boolean ok = false;
210 
211         for (Component comp : getDesktop().getViews()) {
212             BibleViewPane view = (BibleViewPane) comp;
213             if (view.maySave()) {
214                 ok = true;
215             }
216         }
217 
218         if (!ok) {
219             // TRANSLATOR: The user is trying to save the passage in all the
220             // Bible View panes, but they are all empty.
221             Reporter.informUser(getDesktop(), BDMsg.gettext("No Passage to Save"));
222             return;
223         }
224 
225         for (Component comp : getDesktop().getViews()) {
226             try {
227                 BibleViewPane view = (BibleViewPane) comp;
228                 view.save();
229             } catch (IOException ex) {
230                 Reporter.informUser(getDesktop(), ex);
231             }
232         }
233     }
234 
235     /**
236      * Exits the VM.
237      */
238     public void doExit() {
239         LayoutPersistence.instance().saveLayout(desktop);
240         System.exit(0);
241     }
242 
243     /**
244      * Copy the selected text from the "active" display area to the clipboard.
245      */
246     public void doCopy() {
247         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
248         SplitBookDataDisplay da = view.getPassagePane();
249         da.copy();
250     }
251 
252     /**
253      * Go to previous passage.
254      */
255     public void doBack() {
256         getDesktop().selectHistory(-1);
257     }
258 
259     /**
260      * Go to next passage.
261      */
262     public void doForward() {
263         getDesktop().selectHistory(1);
264     }
265 
266     public void doStrongs(ActionEvent ev) {
267         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
268         XSLTProperty.STRONGS_NUMBERS.setState(toggle.isSelected());
269         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
270         SplitBookDataDisplay da = view.getPassagePane();
271         da.getBookDataDisplay().refresh();
272     }
273 
274     public void doMorph(ActionEvent ev) {
275         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
276         XSLTProperty.MORPH.setState(toggle.isSelected());
277         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
278         SplitBookDataDisplay da = view.getPassagePane();
279         da.getBookDataDisplay().refresh();
280     }
281 
282     public void doVLine(ActionEvent ev) {
283         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
284         XSLTProperty.START_VERSE_ON_NEWLINE.setState(toggle.isSelected());
285         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
286         SplitBookDataDisplay da = view.getPassagePane();
287         da.getBookDataDisplay().refresh();
288     }
289 
290     public void doVNum() {
291         XSLTProperty.VERSE_NUMBERS.setState(true);
292         XSLTProperty.CV.setState(false);
293         XSLTProperty.BCV.setState(false);
294         XSLTProperty.NO_VERSE_NUMBERS.setState(false);
295         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
296         SplitBookDataDisplay da = view.getPassagePane();
297         da.getBookDataDisplay().refresh();
298     }
299 
300     public void doTinyVNum(ActionEvent ev) {
301         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
302         XSLTProperty.TINY_VERSE_NUMBERS.setState(toggle.isSelected());
303         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
304         SplitBookDataDisplay da = view.getPassagePane();
305         da.getBookDataDisplay().refresh();
306     }
307 
308     public void doBCVNum() {
309         XSLTProperty.VERSE_NUMBERS.setState(false);
310         XSLTProperty.CV.setState(false);
311         XSLTProperty.BCV.setState(true);
312         XSLTProperty.NO_VERSE_NUMBERS.setState(false);
313         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
314         SplitBookDataDisplay da = view.getPassagePane();
315         da.getBookDataDisplay().refresh();
316     }
317 
318     public void doCVNum() {
319         XSLTProperty.VERSE_NUMBERS.setState(false);
320         XSLTProperty.CV.setState(true);
321         XSLTProperty.BCV.setState(false);
322         XSLTProperty.NO_VERSE_NUMBERS.setState(false);
323         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
324         SplitBookDataDisplay da = view.getPassagePane();
325         da.getBookDataDisplay().refresh();
326     }
327 
328     public void doNoVNum() {
329         XSLTProperty.VERSE_NUMBERS.setState(false);
330         XSLTProperty.CV.setState(false);
331         XSLTProperty.BCV.setState(false);
332         XSLTProperty.NO_VERSE_NUMBERS.setState(true);
333         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
334         SplitBookDataDisplay da = view.getPassagePane();
335         da.getBookDataDisplay().refresh();
336     }
337 
338     /**
339      * Show differences between Bible Book versions.
340      */
341     public void doCompareToggle(ActionEvent ev) {
342         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
343         getDesktop().setCompareShowing(toggle.getState());
344     }
345 
346     public void doHeadings(ActionEvent ev) {
347         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
348         XSLTProperty.HEADINGS.setState(toggle.isSelected());
349         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
350         SplitBookDataDisplay da = view.getPassagePane();
351         da.getBookDataDisplay().refresh();
352     }
353 
354     public void doNotes(ActionEvent ev) {
355         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
356         XSLTProperty.NOTES.setState(toggle.isSelected());
357         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
358         SplitBookDataDisplay da = view.getPassagePane();
359         da.getBookDataDisplay().refresh();
360     }
361 
362     public void doXRef(ActionEvent ev) {
363         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
364         XSLTProperty.XREF.setState(toggle.isSelected());
365         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
366         SplitBookDataDisplay da = view.getPassagePane();
367         da.getBookDataDisplay().refresh();
368     }
369 
370     /**
371      * View the HTML as interpreted by the current window. This HTML will not
372      * return the styling present in the viewer. That is all class="" are
373      * stripped out. Also you may find additional whitespace added to the
374      * original.
375      */
376     public void doViewSource() {
377         // Limit view source to the current tab.
378         BibleViewPane view = (BibleViewPane) getDesktop().getViews().getSelected();
379         SplitBookDataDisplay da = view.getPassagePane();
380         BookDataDisplay bdd = da.getBookDataDisplay();
381         if (bdd instanceof TabbedBookDataDisplay) {
382             bdd = ((TabbedBookDataDisplay) bdd).getInnerDisplayPane();
383         }
384 
385         Key key = bdd.getKey();
386 
387         if (key == null) {
388             // TRANSLATOR: The user is trying to view the source of the passage in the visible
389             // Bible View pane, but it is empty.
390             Reporter.informUser(getDesktop(), BDMsg.gettext("No current passage to view"));
391             return;
392         }
393 
394         ViewSourcePane viewer = new ViewSourcePane(da.getBooks(), key);
395         viewer.showInFrame(getDesktop());
396     }
397 
398     /**
399      * Opens the Book installer window (aka a SitesPane)
400      */
401     public void doBooks() {
402         int webAccess = InternetWarning.GRANTED;
403         if (WebWarning.instance().isShown()) {
404             webAccess = InternetWarning.showDialog(desktop, "?");
405         }
406 
407         if (webAccess == InternetWarning.GRANTED) {
408             getSites().showInDialog(getDesktop());
409         }
410     }
411 
412     /**
413      * Opens the Options window
414      */
415     public void doOptions() {
416         URI configUri = CWProject.instance().getWritableURI("desktop", FileUtil.EXTENSION_PROPERTIES);
417         ConfigEditorFactory.showDialog(desktop.getConfig(), desktop, configUri);
418     }
419 
420     /**
421      * For opening a help file.
422      */
423     public void doContents() {
424         StringBuilder buf = new StringBuilder(200);
425         // TRANSLATOR: Someday we'll have real help but for now this points them to the Bible Desktop web site.
426         buf.append(BDMsg.gettext("Currently on-line help is only available via the Bible Desktop's website:"));
427         buf.append("\nhttp://www.crosswire.org/bibledesktop");
428         CWOptionPane.showMessageDialog(getDesktop(), buf.toString());
429     }
430 
431     /**
432      * For opening the About window
433      */
434     public void doAbout() {
435         if (atp == null) {
436             atp = new AboutPane();
437         }
438 
439         atp.showInDialog(getDesktop());
440     }
441 
442     /**
443      * Show large or small tool bar icons.
444      */
445     public void doToolTipToggle(ActionEvent ev) {
446         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
447         ToolTipManager.sharedInstance().setEnabled(toggle.isSelected());
448     }
449 
450     /**
451      * Show large or small tool bar icons.
452      */
453     public void doStatusToggle(ActionEvent ev) {
454         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
455         desktop.showStatusBar(toggle.isSelected());
456     }
457 
458     /**
459      * Show large or small tool bar icons.
460      */
461     public void doSidebarToggle(ActionEvent ev) {
462         JCheckBoxMenuItem toggle = (JCheckBoxMenuItem) ev.getSource();
463         final boolean show = toggle.isSelected();
464         desktop.getViews().visit(new ShowSideBarVisitor(show));
465     }
466 
467     /**
468      *
469      */
470     private static final class ShowSideBarVisitor implements ViewVisitor {
471         /**
472          * @param show
473          */
474         public ShowSideBarVisitor(boolean show) {
475             this.show = show;
476         }
477 
478         /* (non-Javadoc)
479          * @see org.crosswire.common.swing.desktop.ViewVisitor#visitView(java.awt.Component)
480          */
481         public void visitView(Component component) {
482             if (component instanceof BibleViewPane) {
483                 BibleViewPane view = (BibleViewPane) component;
484                 SplitBookDataDisplay sbDisplay = view.getPassagePane();
485                 sbDisplay.showSidebar(show);
486             }
487         }
488 
489         private boolean show;
490     }
491 
492     // Enumeration of all the keys to known actions
493 //    static final String FILE = "File";
494 //    static final String EDIT = "Edit";
495 //    static final String GO = "Go";
496 //    static final String VIEW = "View";
497 //    static final String TOOLS = "Tools";
498 //    static final String HELP = "Help";
499 //    static final String OPEN = "Open";
500 //    static final String SAVE = "Save";
501 //    static final String SAVE_AS = "SaveAs";
502 //    static final String SAVE_ALL = "SaveAll";
503 //    static final String EXIT = "Exit";
504 //    static final String COPY = "Copy";
505 //    static final String BACK = "Back";
506 //    static final String FORWARD = "Forward";
507 //    static final String COMPARE_TOGGLE = "CompareToggle";
508 //    static final String TOOLTIP_TOGGLE = "ToolTipToggle";
509 //    static final String STATUS_TOGGLE = "StatusToggle";
510 //    static final String SIDEBAR_TOGGLE = "SidebarToggle";
511 //    static final String VERSE = "Verse";
512 //    static final String VIEW_SOURCE = "ViewSource";
513 //    static final String BOOKS = "Books";
514 //    static final String OPTIONS = "Options";
515 //    static final String CONTENTS = "Contents";
516 //    static final String ABOUT = "About";
517 
518     /**
519      * The desktop on which these actions work
520      */
521     protected Desktop desktop;
522 
523     /**
524      * The factory for actions that this class works with
525      */
526     private ActionFactory actions;
527 
528     /**
529      * Indicates whether there is MacOSX integration.
530      */
531     private boolean osxRegistered;
532 
533     /**
534      * The About window
535      */
536     private AboutPane atp;
537 
538     /**
539      * The Book installer window
540      */
541     private SitesPane sites;
542 
543     /**
544      * The log stream
545      */
546     protected static final Logger log = Logger.getLogger(DesktopActions.class);
547 }
548