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: URITipHelper.java 1966 2010-1-11 01:15:14Z lanyjie $
21   */
22  package org.crosswire.bibledesktop.display.basic;
23  
24  import java.net.URI;
25  import java.util.Locale;
26  
27  import javax.swing.JToolTip;
28  import javax.swing.ToolTipManager;
29  import javax.xml.transform.TransformerException;
30  
31  import org.crosswire.bibledesktop.book.install.BookFont;
32  import org.crosswire.bibledesktop.desktop.Desktop;
33  import org.crosswire.bibledesktop.desktop.XSLTProperty;
34  import org.crosswire.bibledesktop.display.URIEvent;
35  import org.crosswire.bibledesktop.display.URIEventListener;
36  import org.crosswire.common.swing.GuiConvert;
37  import org.crosswire.common.swing.GuiUtil;
38  import org.crosswire.common.util.Reporter;
39  import org.crosswire.common.xml.Converter;
40  import org.crosswire.common.xml.SAXEventProvider;
41  import org.crosswire.common.xml.TransformingSAXEventProvider;
42  import org.crosswire.common.xml.XMLUtil;
43  import org.crosswire.jsword.book.Book;
44  import org.crosswire.jsword.book.BookCategory;
45  import org.crosswire.jsword.book.BookData;
46  import org.crosswire.jsword.book.BookException;
47  import org.crosswire.jsword.book.BookMetaData;
48  import org.crosswire.jsword.book.Books;
49  import org.crosswire.jsword.book.Defaults;
50  import org.crosswire.jsword.passage.NoSuchKeyException;
51  import org.xml.sax.SAXException;
52  
53  /**
54   * Implement URIEventListener to receive URIEvents whenever someone activates an
55   * URI.
56   * 
57   * @see gnu.gpl.License for license details.<br>
58   *      The copyright to this program is held by it's authors.
59   * @author Yingjie Lan [lanyjie at yahoo dot com]
60   */
61  public class URITipHelper implements URIEventListener {
62      /**
63       * ctor: after creation, add this as a listener to a BookDataDisplay, for
64       * example: basic/TextPaneBookDataDisplay.java
65       * 
66       * This class can also have a list of URIEvent content retrievers, who
67       * specialize in retrieving the content of a URI request and may also
68       * perform some kind of processing, such as converting to html (return a
69       * string if success, null o/w):
70       * 
71       * Such specialized retrievers will have two methods: public boolean
72       * handles(String protocol); public String retrieve(URIEvent evt);
73       */
74      public URITipHelper() {
75          tip = new FullHTMLTip();
76      }
77  
78      public JToolTip fetchToolTip() {
79          return tip;
80      }
81  
82      /**
83       * This is only called when the component needs to display the tip; note we
84       * delay this expensive operation until needed; a typical use is to have it
85       * in the getToolTipText() method of the managed component:
86       * 
87       * public String getToolTipText(){ return uritip.retrieve(); }
88       */
89      public String retrieve(Converter converter) {
90          if (event == null) {
91              return null;
92          }
93          if (txt != null) {
94              return txt; // return cached.
95          }
96          String protocol = event.getScheme();
97          Book book = null;
98          if (protocol.equals(Desktop.GREEK_DEF_PROTOCOL)) {
99              book = Defaults.getGreekDefinitions();
100         } else if (protocol.equals(Desktop.HEBREW_DEF_PROTOCOL)) {
101             book = Defaults.getHebrewDefinitions();
102         } else if (protocol.equals(Desktop.GREEK_MORPH_PROTOCOL)) {
103             book = Defaults.getGreekParse();
104         } else if (protocol.equals(Desktop.HEBREW_MORPH_PROTOCOL)) {
105             book = Defaults.getHebrewParse();
106         }
107 
108         if (book == null || Books.installed().getBook(book.getName()) == null) {
109             return "Book Unavailable!";
110         }
111 
112         BookData bdata = null;
113 
114         try {
115             bdata = new BookData(book, book.getKey(event.getURI()));
116         } catch (NoSuchKeyException ex) {
117             return ex.getDetailedMessage();
118         }
119 
120         assert book == bdata.getFirstBook();
121 
122         BookMetaData bmd = book.getBookMetaData();
123         if (bmd == null) {
124             return "Book Meta Data Unavailable!";
125         }
126 
127         // Make sure Hebrew displays from Right to Left
128         // Set the correct direction
129         boolean direction = bmd.isLeftToRight();
130         GuiUtil.applyOrientation(tip.getTextView(), direction);
131 
132         // The content of the module determines how the display
133         // should behave. It should not be the user's locale.
134         // Set the correct locale
135         tip.getTextView().setLocale(new Locale(bmd.getLanguage().getCode()));
136         String fontSpec = GuiConvert.font2String(BookFont.instance().getFont(book));
137         try {
138             SAXEventProvider osissep = bdata.getSAXEventProvider();
139             TransformingSAXEventProvider htmlsep = (TransformingSAXEventProvider) converter.convert(osissep);
140             XSLTProperty.DIRECTION.setState(direction ? "ltr" : "rtl");
141 
142             URI loc = bmd.getLocation();
143             XSLTProperty.BASE_URL.setState(loc == null ? "" : loc.getPath());
144 
145             if (bmd.getBookCategory() == BookCategory.BIBLE) {
146                 XSLTProperty.setProperties(htmlsep);
147             } else {
148                 XSLTProperty.CSS.setProperty(htmlsep);
149                 XSLTProperty.BASE_URL.setProperty(htmlsep);
150                 XSLTProperty.DIRECTION.setProperty(htmlsep);
151             }
152             // Override the default if needed
153             htmlsep.setParameter(XSLTProperty.FONT.getName(), fontSpec);
154 
155             txt = XMLUtil.writeToString(htmlsep);
156         } catch (SAXException e) {
157             Reporter.informUser(this, e);
158             e.printStackTrace();
159             txt = e.getMessage();
160         } catch (TransformerException e) {
161             Reporter.informUser(this, e);
162             e.printStackTrace();
163             txt = e.getMessage();
164         } catch (BookException e) {
165             Reporter.informUser(this, e);
166             e.printStackTrace();
167             txt = e.getMessage();
168         }
169         return txt;
170     }
171 
172     public String getTipTitle() {
173         if (event == null) {
174             return "Untitled Tip";
175         }
176         return event.getURI();
177     }
178 
179     /**
180      * This method is called to indicate that an URI can be processed.
181      * 
182      * @param ev Describes the URI
183      */
184     public void activateURI(URIEvent ev) {
185         // if(!interested(ev)) return;
186     }
187 
188     /**
189      * This method is called to indicate that the mouse has entered the URI.
190      * 
191      * @param ev
192      *            Describes the URI
193      */
194     public void enterURI(URIEvent ev) {
195         if (!interested(ev)) {
196             return;
197         }
198         // Get current delay
199         formerDismissDelay = ToolTipManager.sharedInstance().getDismissDelay();
200         // Set delay longer enough
201         ToolTipManager.sharedInstance().setDismissDelay(myDismissDelay);
202 
203         event = ev; // register event
204     }
205 
206     /**
207      * This method is called to indicate that the mouse has left the URI.
208      * 
209      * @param ev
210      *            Describes the URI
211      */
212     public void leaveURI(URIEvent ev) {
213 
214         if (!interested(ev)) {
215             return;
216         }
217 
218         ToolTipManager.sharedInstance().setDismissDelay(formerDismissDelay);
219 
220         event = null; // clear event
221         txt = null; // clear txt
222     }
223 
224     /**
225      * @param ev
226      *            if we are interested in this event
227      */
228     boolean interested(URIEvent ev) {
229         // tell if it is interested in ev
230         String protocol = ev.getScheme();
231         if (protocol.equals(Desktop.GREEK_DEF_PROTOCOL)) {
232             return true;
233         }
234         if (protocol.equals(Desktop.HEBREW_DEF_PROTOCOL)) {
235             return true;
236         }
237         if (protocol.equals(Desktop.GREEK_MORPH_PROTOCOL)) {
238             return true;
239         }
240         if (protocol.equals(Desktop.HEBREW_MORPH_PROTOCOL)) {
241             return true;
242         }
243         return false;
244     }
245 
246     private int formerDismissDelay;
247 
248     private int myDismissDelay = 60000;
249 
250     /**
251      * The tool tip to help with.
252      */
253     private FullHTMLTip tip;
254 
255     /**
256      * The most recent interested event, which is used for content retrieving.
257      */
258     private URIEvent event;
259     private String txt;
260 }
261