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: 2007
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: org.eclipse.jdt.ui.prefs 1178 2006-11-06 12:48:02Z lanyjie $
21   */
22  
23  package org.crosswire.bibledesktop.display.basic;
24  
25  import java.awt.Color;
26  import java.awt.Dimension;
27  import java.awt.Point;
28  import java.awt.Toolkit;
29  import java.awt.event.MouseAdapter;
30  import java.awt.event.MouseEvent;
31  import java.net.URI;
32  import java.util.Locale;
33  
34  import javax.swing.JScrollPane;
35  import javax.swing.JTextPane;
36  import javax.swing.Popup;
37  import javax.swing.PopupFactory;
38  import javax.swing.border.TitledBorder;
39  import javax.xml.transform.TransformerException;
40  
41  import org.crosswire.bibledesktop.book.install.BookFont;
42  import org.crosswire.bibledesktop.desktop.Desktop;
43  import org.crosswire.bibledesktop.desktop.XSLTProperty;
44  import org.crosswire.bibledesktop.display.URIEvent;
45  import org.crosswire.bibledesktop.display.URIEventListener;
46  import org.crosswire.common.swing.AntiAliasedTextPane;
47  import org.crosswire.common.swing.GuiConvert;
48  import org.crosswire.common.swing.GuiUtil;
49  import org.crosswire.common.util.Reporter;
50  import org.crosswire.common.xml.Converter;
51  import org.crosswire.common.xml.SAXEventProvider;
52  import org.crosswire.common.xml.TransformingSAXEventProvider;
53  import org.crosswire.common.xml.XMLUtil;
54  import org.crosswire.jsword.book.Book;
55  import org.crosswire.jsword.book.BookCategory;
56  import org.crosswire.jsword.book.BookData;
57  import org.crosswire.jsword.book.BookException;
58  import org.crosswire.jsword.book.BookMetaData;
59  import org.crosswire.jsword.book.Books;
60  import org.crosswire.jsword.book.Defaults;
61  import org.crosswire.jsword.passage.NoSuchKeyException;
62  import org.crosswire.jsword.util.ConverterFactory;
63  import org.xml.sax.SAXException;
64  
65  /**
66   * How it works: 1. When mouse clicked with the right button on a link, show the
67   * popup tip. 2. Hide the tip when clicked anywhere else.
68   * 
69   * @see gnu.gpl.License for license details. The copyright to this program is
70   *      held by it's authors.
71   * @author Yingjie Lan [lanyjie at yahoo dot com]
72   */
73  public class ActiveURITip extends MouseAdapter implements URIEventListener {
74  
75      public ActiveURITip(JTextPane own, Dimension dim) {
76          converter = ConverterFactory.getConverter();
77          owner = own;
78          lazykit = (LazyHTMLEditorKit) own.getEditorKit();
79          txtView = new AntiAliasedTextPane();
80          txtView.setEditable(false);
81          txtView.setEditorKit(new LazyHTMLEditorKit());
82          title = new TitledBorder((String) null);
83          scrView = new JScrollPane(txtView);
84          scrView.setBackground(Color.yellow);
85          scrView.setBorder(title);
86          scrView.getViewport().setPreferredSize(dim);
87          // listen to mouse events of the managed component
88          // own.addMouseMotionListener(this);
89          own.addMouseListener(this);
90          // also hide popup if clicked inside popup
91          // txtView.addMouseListener(this);
92      }
93  
94      public void updateText(URIEvent event) {
95          if (event == null) {
96              return;
97          }
98          String txt = null;
99          String protocol = event.getScheme();
100         Book book = null;
101         if (protocol.equals(Desktop.GREEK_DEF_PROTOCOL)) {
102             book = Defaults.getGreekDefinitions();
103             title.setTitle("Greek Definition");
104         } else if (protocol.equals(Desktop.HEBREW_DEF_PROTOCOL)) {
105             book = Defaults.getHebrewDefinitions();
106             title.setTitle("Hebrew Definition");
107         } else if (protocol.equals(Desktop.GREEK_MORPH_PROTOCOL)) {
108             book = Defaults.getGreekParse();
109             title.setTitle("Greek Morphology");
110         } else if (protocol.equals(Desktop.HEBREW_MORPH_PROTOCOL)) {
111             book = Defaults.getHebrewParse();
112             title.setTitle("Hebrew Morphology");
113         }
114 
115         if (book == null || Books.installed().getBook(book.getName()) == null) {
116             txtView.setText("Book Unavailable!");
117             title.setTitle("Exception");
118             return;
119         }
120 
121         BookData bdata = null;
122 
123         try {
124             bdata = new BookData(book, book.getKey(event.getURI()));
125         } catch (NoSuchKeyException ex) {
126             txtView.setText(ex.getDetailedMessage());
127             title.setTitle("Exception");
128             return;
129         }
130 
131         assert book == bdata.getFirstBook();
132 
133         BookMetaData bmd = book.getBookMetaData();
134         if (bmd == null) {
135             txtView.setText("Book Meta Data Unavailable!");
136             title.setTitle("Exception");
137             return;
138         }
139 
140         // Make sure Hebrew displays from Right to Left
141         boolean direction = bmd.isLeftToRight();
142         String fontSpec = GuiConvert.font2String(BookFont.instance().getFont(book));
143         try {
144             SAXEventProvider osissep = bdata.getSAXEventProvider();
145             TransformingSAXEventProvider htmlsep = (TransformingSAXEventProvider) converter.convert(osissep);
146             XSLTProperty.DIRECTION.setState(direction ? "ltr" : "rtl");
147 
148             URI loc = bmd.getLocation();
149             XSLTProperty.BASE_URL.setState(loc == null ? "" : loc.getPath());
150 
151             if (bmd.getBookCategory() == BookCategory.BIBLE) {
152                 XSLTProperty.setProperties(htmlsep);
153             } else {
154                 XSLTProperty.CSS.setProperty(htmlsep);
155                 XSLTProperty.BASE_URL.setProperty(htmlsep);
156                 XSLTProperty.DIRECTION.setProperty(htmlsep);
157             }
158             // Override the default if needed
159             htmlsep.setParameter(XSLTProperty.FONT.getName(), fontSpec);
160 
161             txt = XMLUtil.writeToString(htmlsep);
162         } catch (SAXException e) {
163             Reporter.informUser(this, e);
164             e.printStackTrace();
165             txtView.setText(e.getMessage());
166             title.setTitle("Exception");
167         } catch (TransformerException e) {
168             Reporter.informUser(this, e);
169             e.printStackTrace();
170             txtView.setText(e.getMessage());
171             title.setTitle("Exception");
172         } catch (BookException e) {
173             Reporter.informUser(this, e);
174             e.printStackTrace();
175             txtView.setText(e.getMessage());
176             title.setTitle("Exception");
177         }
178         // Set the correct direction
179         GuiUtil.applyOrientation(txtView, direction);
180 
181         // The content of the module determines how the display
182         // should behave. It should not be the user's locale.
183         // Set the correct locale
184         txtView.setLocale(new Locale(bmd.getLanguage().getCode()));
185         txtView.setText(txt);
186         txtView.setCaretPosition(0);
187     }
188 
189     void showTip() { // when it is time to do so
190         Point p = owner.getLocationOnScreen();
191         Dimension d = scrView.getPreferredSize();
192         Dimension s = Toolkit.getDefaultToolkit().getScreenSize();
193         int x = p.x + lastx;
194         int y = p.y + lasty;
195         int horizDist = 30; // these numbers are hard coded
196         int vertiDist = 10; // but they depend on font size.
197         // always show tips in the 'better half'
198         if (x + x > s.width) {
199             x -= horizDist + d.width;
200         } else {
201             x += horizDist;
202         }
203         if (y + y > s.height) {
204             y -= vertiDist + d.height;
205         } else {
206             y += vertiDist;
207         }
208         popup = PopupFactory.getSharedInstance().getPopup(owner, scrView, x, y);
209         popup.show();
210     }
211 
212     void hideTip() {
213         if (popup != null) {
214             popup.hide();
215         }
216         popup = null;
217     }
218 
219     /**
220      * @param ev
221      *            if we are interested in this event
222      */
223     boolean interested(URIEvent ev) {
224         // tell if it is interested in ev
225         String protocol = ev.getScheme();
226         if (protocol.equals(Desktop.GREEK_DEF_PROTOCOL)) {
227             return true;
228         }
229         if (protocol.equals(Desktop.HEBREW_DEF_PROTOCOL)) {
230             return true;
231         }
232         if (protocol.equals(Desktop.GREEK_MORPH_PROTOCOL)) {
233             return true;
234         }
235         if (protocol.equals(Desktop.HEBREW_MORPH_PROTOCOL)) {
236             return true;
237         }
238         return false;
239     }
240 
241     /* (non-Javadoc)
242      * @see org.crosswire.bibledesktop.display.URIEventListener#activateURI(org.crosswire.bibledesktop.display.URIEvent)
243      */
244     public void activateURI(URIEvent ev) {
245     }
246 
247     /* (non-Javadoc)
248      * @see org.crosswire.bibledesktop.display.URIEventListener#enterURI(org.crosswire.bibledesktop.display.URIEvent)
249      */
250     public void enterURI(URIEvent ev) {
251         System.out.println(ev);
252         if (interested(ev) && lastb == MouseEvent.BUTTON3) {
253             updateText(ev);
254             this.showTip();
255         }
256     }
257 
258     /* (non-Javadoc)
259      * @see org.crosswire.bibledesktop.display.URIEventListener#leaveURI(org.crosswire.bibledesktop.display.URIEvent)
260      */
261     public void leaveURI(URIEvent ev) {
262         this.hideTip();
263     }
264 
265     @Override
266     public void mousePressed(MouseEvent e) {
267         // System.out.println(e);
268         // hideTip();
269         lastx = e.getX();
270         lasty = e.getY();
271         lastb = e.getButton();
272         // mouseMoved would cause URI enter/exit events
273         lazykit.getLinkCtrl().mouseMoved(e);
274         // mouseClicked would cause URI activated events
275         // lazykit.getLinkCtrl().mouseClicked(e);
276     }
277 
278     private JTextPane owner;
279     private LazyHTMLEditorKit lazykit;
280     private JTextPane txtView;
281     private JScrollPane scrView;
282     private TitledBorder title;
283     private Popup popup;
284 
285     private int lastx;
286     private int lasty;
287     private int lastb;
288 
289     private Converter converter;
290 }
291