KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > mail > gui > message > viewer > TextViewer


1 // The contents of this file are subject to the Mozilla Public License Version
2
// 1.1
3
//(the "License"); you may not use this file except in compliance with the
4
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
5
//
6
//Software distributed under the License is distributed on an "AS IS" basis,
7
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
8
//for the specific language governing rights and
9
//limitations under the License.
10
//
11
//The Original Code is "The Columba Project"
12
//
13
//The Initial Developers of the Original Code are Frederik Dietz and Timo
14
// Stich.
15
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2006.
16
//
17
//All Rights Reserved.
18
package org.columba.mail.gui.message.viewer;
19
20 import java.awt.BorderLayout JavaDoc;
21 import java.awt.Font JavaDoc;
22 import java.awt.event.MouseEvent JavaDoc;
23 import java.awt.event.MouseListener JavaDoc;
24 import java.io.ByteArrayInputStream JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileOutputStream JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.net.MalformedURLException JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.nio.charset.Charset JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Observable JavaDoc;
34 import java.util.Observer JavaDoc;
35 import java.util.logging.Logger JavaDoc;
36 import java.util.regex.Matcher JavaDoc;
37 import java.util.regex.Pattern JavaDoc;
38
39 import javax.swing.BorderFactory JavaDoc;
40 import javax.swing.JComponent JavaDoc;
41 import javax.swing.JEditorPane JavaDoc;
42 import javax.swing.JOptionPane JavaDoc;
43 import javax.swing.JPanel JavaDoc;
44 import javax.swing.JPopupMenu JavaDoc;
45 import javax.swing.SwingUtilities JavaDoc;
46 import javax.swing.event.CaretEvent JavaDoc;
47 import javax.swing.event.CaretListener JavaDoc;
48 import javax.swing.text.AttributeSet JavaDoc;
49 import javax.swing.text.BadLocationException JavaDoc;
50 import javax.swing.text.Element JavaDoc;
51 import javax.swing.text.html.HTML JavaDoc;
52 import javax.swing.text.html.HTMLDocument JavaDoc;
53
54 import org.columba.api.plugin.IExtension;
55 import org.columba.api.plugin.IExtensionHandler;
56 import org.columba.api.plugin.IExtensionHandlerKeys;
57 import org.columba.api.plugin.PluginException;
58 import org.columba.api.plugin.PluginHandlerNotFoundException;
59 import org.columba.core.charset.CharsetOwnerInterface;
60 import org.columba.core.config.Config;
61 import org.columba.core.desktop.ColumbaDesktop;
62 import org.columba.core.gui.frame.DefaultContainer;
63 import org.columba.core.gui.htmlviewer.api.IHTMLViewerPlugin;
64 import org.columba.core.gui.util.FontProperties;
65 import org.columba.core.io.StreamUtils;
66 import org.columba.core.logging.Logging;
67 import org.columba.core.plugin.PluginManager;
68 import org.columba.core.util.TempFileStore;
69 import org.columba.core.xml.XmlElement;
70 import org.columba.mail.config.MailConfig;
71 import org.columba.mail.config.OptionsItem;
72 import org.columba.mail.folder.IMailbox;
73 import org.columba.mail.gui.composer.ComposerController;
74 import org.columba.mail.gui.composer.ComposerModel;
75 import org.columba.mail.gui.frame.MailFrameMediator;
76 import org.columba.mail.gui.message.IMessageController;
77 import org.columba.mail.gui.message.action.AddToAddressbookAction;
78 import org.columba.mail.gui.message.action.ComposeMessageAction;
79 import org.columba.mail.gui.message.action.CopyLinkLocationAction;
80 import org.columba.mail.gui.message.action.OpenAction;
81 import org.columba.mail.gui.message.action.OpenWithAction;
82 import org.columba.mail.gui.message.util.ColumbaURL;
83 import org.columba.mail.parser.text.HtmlParser;
84 import org.columba.ristretto.coder.Base64DecoderInputStream;
85 import org.columba.ristretto.coder.FallbackCharsetDecoderInputStream;
86 import org.columba.ristretto.coder.QuotedPrintableDecoderInputStream;
87 import org.columba.ristretto.message.MimeHeader;
88 import org.columba.ristretto.message.MimePart;
89 import org.columba.ristretto.message.MimeTree;
90
91 /**
92  * Display message body text.
93  *
94  * @author fdietz
95  */

96 public class TextViewer extends JPanel JavaDoc implements IMimePartViewer, Observer JavaDoc,
97         CaretListener JavaDoc {
98
99     /** JDK 1.4+ logging framework logger, used for logging. */
100     private static final Logger JavaDoc LOG = Logger
101             .getLogger("org.columba.mail.gui.message.viewer");
102
103     private static final Pattern JavaDoc CIDPattern = Pattern.compile("cid:([^\"]+)",
104             Pattern.CASE_INSENSITIVE);
105
106     // stylesheet is created dynamically because
107
// user configurable fonts are used
108
private String JavaDoc css = "";
109
110     // enable/disable smilies configuration
111
private XmlElement smilies;
112
113     private boolean enableSmilies;
114
115     // name of font
116
private String JavaDoc name;
117
118     /*
119      * private String body;
120      *
121      * private URL url;
122      */

123
124     private String JavaDoc body;
125
126     /**
127      * if true, a html message is shown. Otherwise, plain/text
128      */

129     private boolean htmlMessage;
130
131     private IMessageController mediator;
132
133     private IHTMLViewerPlugin viewerPlugin;
134
135     private IMailbox folder;
136
137     private Object JavaDoc uid;
138
139     private boolean usingJDIC;
140
141     public TextViewer(IMessageController mediator) {
142         super();
143
144         this.mediator = mediator;
145
146         initHTMLViewerPlugin();
147
148         setLayout(new BorderLayout JavaDoc());
149
150         add(viewerPlugin.getContainer(), BorderLayout.CENTER);
151
152         initConfiguration();
153
154         initStyleSheet();
155
156         if (!usingJDIC)
157             viewerPlugin.getComponent()
158                     .addMouseListener(new URLMouseListener());
159
160         setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
161     }
162
163     private void initHTMLViewerPlugin() {
164         OptionsItem optionsItem = MailConfig.getInstance().getOptionsItem();
165         String JavaDoc selectedBrowser = optionsItem.getStringWithDefault(
166                 OptionsItem.MESSAGEVIEWER, OptionsItem.SELECTED_BROWSER,
167                 "Default");
168
169         try {
170             viewerPlugin = createHTMLViewerPluginInstance(selectedBrowser);
171             // in case of an error -> fall-back to Swing's built-in JTextPane
172
if (viewerPlugin == null || !viewerPlugin.initialized()) {
173                 JOptionPane.showMessageDialog(this,
174                         "Error while trying to load html viewer");
175
176                 LOG
177                         .severe("Error while trying to load html viewer -> falling back to default");
178
179                 viewerPlugin = createHTMLViewerPluginInstance("Default");
180             }
181         } catch (Exception JavaDoc e) {
182             viewerPlugin = createHTMLViewerPluginInstance("Default");
183
184             if (Logging.DEBUG)
185                 e.printStackTrace();
186         } catch (Error JavaDoc e) {
187             viewerPlugin = createHTMLViewerPluginInstance("Default");
188
189             if (Logging.DEBUG)
190                 e.printStackTrace();
191         }
192
193     }
194
195     private JPopupMenu JavaDoc createPopupMenu(ColumbaURL url) {
196         JPopupMenu JavaDoc menu = new JPopupMenu JavaDoc();
197         menu.add(new CopyLinkLocationAction(url));
198         menu.addSeparator();
199         menu.add(new OpenAction(url));
200         menu.add(new OpenWithAction(url));
201         menu.addSeparator();
202         menu.add(new AddToAddressbookAction(url));
203         menu.add(new ComposeMessageAction(url));
204
205         return menu;
206     }
207
208     private IHTMLViewerPlugin createHTMLViewerPluginInstance(String JavaDoc pluginId) {
209         IHTMLViewerPlugin plugin = null;
210         try {
211
212             IExtensionHandler handler = PluginManager.getInstance()
213                     .getExtensionHandler(
214                             IExtensionHandlerKeys.ORG_COLUMBA_CORE_HTMLVIEWER);
215
216             IExtension extension = handler.getExtension(pluginId);
217             if (extension == null)
218                 return null;
219
220             plugin = (IHTMLViewerPlugin) extension.instanciateExtension(null);
221
222             return plugin;
223         } catch (PluginHandlerNotFoundException e) {
224             LOG.severe("Error while loading viewer plugin: " + e.getMessage());
225             if (Logging.DEBUG)
226                 e.printStackTrace();
227         } catch (PluginException e) {
228             LOG.severe("Error while loading viewer plugin: " + e.getMessage());
229             if (Logging.DEBUG)
230                 e.printStackTrace();
231         }
232
233         return null;
234     }
235
236     /**
237      *
238      */

239     private void initConfiguration() {
240         XmlElement gui = MailConfig.getInstance().get("options").getElement(
241                 "/options/gui");
242         XmlElement messageviewer = gui.getElement("messageviewer");
243
244         if (messageviewer == null) {
245             messageviewer = gui.addSubElement("messageviewer");
246         }
247
248         messageviewer.addObserver(this);
249
250         smilies = messageviewer.getElement("smilies");
251
252         if (smilies == null) {
253             smilies = messageviewer.addSubElement("smilies");
254         }
255
256         // register as configuration change listener
257
smilies.addObserver(this);
258
259         String JavaDoc enable = smilies.getAttribute("enabled", "true");
260
261         if (enable.equals("true")) {
262             enableSmilies = true;
263         } else {
264             enableSmilies = false;
265         }
266
267         XmlElement quote = messageviewer.getElement("quote");
268
269         if (quote == null) {
270             quote = messageviewer.addSubElement("quote");
271         }
272
273         // register as configuration change listener
274
quote.addObserver(this);
275
276         // register for configuration changes
277
Font JavaDoc font = FontProperties.getTextFont();
278         name = font.getName();
279
280         XmlElement options = Config.getInstance().get("options").getElement(
281                 "/options");
282         XmlElement gui1 = options.getElement("gui");
283         XmlElement fonts = gui1.getElement("fonts");
284
285         if (fonts == null) {
286             fonts = gui1.addSubElement("fonts");
287         }
288
289         // register interest on configuratin changes
290
fonts.addObserver(this);
291
292         // XmlElement selectedBrowser =
293
// messageviewer.getElement(OptionsItem.SELECTED_BROWSER);
294
// selectedBrowser.addObserver(this);
295

296     }
297
298     /**
299      * @see org.columba.mail.gui.message.viewer.IMimePartViewer#view(org.columba.mail.folder.IMailbox,
300      * java.lang.Object, java.lang.Integer[],
301      * org.columba.mail.gui.frame.MailFrameMediator)
302      */

303     public void view(IMailbox folder, Object JavaDoc uid, Integer JavaDoc[] address,
304             MailFrameMediator mediator) throws Exception JavaDoc {
305
306         this.folder = folder;
307         this.uid = uid;
308
309         MimePart bodyPart = null;
310         InputStream JavaDoc bodyStream;
311
312         MimeTree mimePartTree = folder.getMimePartTree(uid);
313
314         bodyPart = mimePartTree.getFromAddress(address);
315
316         if (bodyPart == null) {
317             bodyStream = new ByteArrayInputStream JavaDoc("<No Message-Text>"
318                     .getBytes());
319         } else {
320             // Shall we use the HTML-IViewer?
321
htmlMessage = bodyPart.getHeader().getMimeType().getSubtype()
322                     .equals("html");
323
324             bodyStream = folder.getMimePartBodyStream(uid, bodyPart
325                     .getAddress());
326         }
327
328         bodyStream = MessageParser.decodeBodyStream(bodyPart, bodyStream);
329
330         // Which Charset shall we use ?
331
if (!htmlMessage) {
332             Charset JavaDoc charset = ((CharsetOwnerInterface) mediator).getCharset();
333             charset = MessageParser.extractCharset(charset, bodyPart);
334
335             bodyStream = new FallbackCharsetDecoderInputStream(bodyStream,
336                     charset);
337         }
338
339         // Read Stream in String
340
StringBuffer JavaDoc text = StreamUtils.readCharacterStream(bodyStream);
341
342         // if HTML stripping is enabled
343
if (isHTMLStrippingEnabled()) {
344             // strip HTML message -> remove all HTML tags
345
text = new StringBuffer JavaDoc(HtmlParser.stripHtmlTags(text.toString(),
346                     true));
347
348             htmlMessage = false;
349         }
350
351         if (htmlMessage) {
352             // this is a HTML message
353
body = text.toString();
354
355             // Download any CIDs in the html mail
356
body = downloadCIDParts(body, mimePartTree);
357
358         } else {
359             // this is a text/plain message
360

361             body = MessageParser.transformTextToHTML(text.toString(), css,
362                     enableSmilies);
363
364             // setText(body);
365

366         }
367
368     }
369
370     private boolean isHTMLStrippingEnabled() {
371         XmlElement html = MailConfig.getInstance().getMainFrameOptionsConfig()
372                 .getRoot().getElement("/options/html");
373
374         return Boolean.valueOf(html.getAttribute("disable")).booleanValue();
375     }
376
377     /**
378      *
379      * read text-properties from configuration and create a stylesheet for the
380      * html-document
381      *
382      */

383     private void initStyleSheet() {
384         // read configuration from options.xml file
385
// create css-stylesheet string
386
// set font of html-element <P>
387

388         /*
389          * css = "<style type=\"text/css\">\n" + "body {font-family:\"" + name +
390          * "\"; font-size:\"" + size + "pt; \"} \n" + "a { color: blue;
391          * text-decoration: underline }\n" + "font.quoting {color:#949494;} \n" + "</style>\n";
392          */

393
394         css = "<style type=\"text/css\">\n" + "body {font-family:\"" + name
395                 + "\";} \n" + "a { color: blue; text-decoration: underline }\n"
396                 + "font.quoting {color:#949494;} \n" + "</style>\n";
397
398     }
399
400     /*
401      * (non-Javadoc)
402      *
403      * @see org.columba.mail.gui.config.general.MailOptionsDialog
404      *
405      * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
406      */

407     public void update(Observable JavaDoc arg0, Object JavaDoc arg1) {
408         Font JavaDoc font = FontProperties.getTextFont();
409         name = font.getName();
410
411         initStyleSheet();
412
413         // remove old renderer
414
remove(viewerPlugin.getContainer());
415
416         // init new renderer
417
initHTMLViewerPlugin();
418
419         // add new renderer
420
add(viewerPlugin.getContainer(), BorderLayout.CENTER);
421     }
422
423     public String JavaDoc getSelectedText() {
424         return viewerPlugin.getSelectedText();
425     }
426
427     public String JavaDoc getText() {
428         return viewerPlugin.getText();
429     }
430
431     public void setCaretPosition(int position) {
432         viewerPlugin.setCaretPosition(position);
433     }
434
435     public void moveCaretPosition(int position) {
436         viewerPlugin.moveCaretPosition(position);
437     }
438
439     /**
440      * @see org.columba.mail.gui.message.viewer.IViewer#getView()
441      */

442     public JComponent JavaDoc getView() {
443         return viewerPlugin.getContainer();
444     }
445
446     /**
447      * @see org.columba.mail.gui.message.viewer.IViewer#updateGUI()
448      */

449     public void updateGUI() throws Exception JavaDoc {
450         viewerPlugin.view(body);
451     }
452
453     /**
454      * @see javax.swing.event.CaretListener#caretUpdate(javax.swing.event.CaretEvent)
455      */

456     public void caretUpdate(CaretEvent JavaDoc arg0) {
457         // FocusManager.getInstance().updateActions();
458
}
459
460     private String JavaDoc downloadCIDParts(String JavaDoc body, MimeTree mimeTree) {
461         Matcher JavaDoc matcher = CIDPattern.matcher(body);
462
463         if (!matcher.find()) {
464             return body;
465         }
466
467         StringBuffer JavaDoc modifiedBody = new StringBuffer JavaDoc(body.length());
468         File JavaDoc mimePartFile;
469         List JavaDoc mimeParts = mimeTree.getAllLeafs();
470
471         MimePart CIDPart = findMimePart(mimeParts, matcher.group(1));
472         if (CIDPart != null) {
473             mimePartFile = TempFileStore.createTempFile();
474             try {
475                 downloadMimePart(CIDPart, mimePartFile);
476
477                 matcher.appendReplacement(modifiedBody, mimePartFile.toURL()
478                         .toString());
479             } catch (Exception JavaDoc e) {
480                 matcher.appendReplacement(modifiedBody, "missing");
481             }
482         } else {
483             matcher.appendReplacement(modifiedBody, "missing");
484         }
485
486         while (matcher.find()) {
487             CIDPart = findMimePart(mimeParts, matcher.group(1));
488             if (CIDPart != null) {
489                 mimePartFile = TempFileStore.createTempFile();
490                 try {
491                     downloadMimePart(CIDPart, mimePartFile);
492
493                     matcher.appendReplacement(modifiedBody, mimePartFile
494                             .toURL().toString());
495                 } catch (Exception JavaDoc e) {
496                     matcher.appendReplacement(modifiedBody, "missing");
497                 }
498             } else {
499                 matcher.appendReplacement(modifiedBody, "missing");
500             }
501         }
502
503         matcher.appendTail(modifiedBody);
504
505         return modifiedBody.toString();
506     }
507
508     private MimePart findMimePart(List JavaDoc mimeParts, String JavaDoc findCid) {
509         MimePart result;
510         Iterator JavaDoc it = mimeParts.iterator();
511         while (it.hasNext()) {
512             result = (MimePart) it.next();
513
514             String JavaDoc cid = result.getHeader().getContentID();
515             if (cid != null
516                     && cid.substring(1, cid.length() - 1).equalsIgnoreCase(
517                             findCid)) {
518                 return result;
519             }
520         }
521
522         return null;
523     }
524
525     private void downloadMimePart(MimePart part, File JavaDoc destFile)
526             throws Exception JavaDoc {
527         MimeHeader header = part.getHeader();
528
529         InputStream JavaDoc bodyStream = folder.getMimePartBodyStream(uid, part
530                 .getAddress());
531
532         int encoding = header.getContentTransferEncoding();
533
534         switch (encoding) {
535         case MimeHeader.QUOTED_PRINTABLE:
536             bodyStream = new QuotedPrintableDecoderInputStream(bodyStream);
537             break;
538
539         case MimeHeader.BASE64:
540             bodyStream = new Base64DecoderInputStream(bodyStream);
541             break;
542         default:
543         }
544
545         FileOutputStream JavaDoc fileStream = new FileOutputStream JavaDoc(destFile);
546         StreamUtils.streamCopy(bodyStream, fileStream);
547         fileStream.close();
548         bodyStream.close();
549     }
550
551     /**
552      * @return Returns the htmlMessage.
553      */

554     public boolean isHtmlMessage() {
555         return htmlMessage;
556     }
557
558     protected URL JavaDoc extractURL(MouseEvent JavaDoc event) {
559         JEditorPane JavaDoc pane = (JEditorPane JavaDoc) event.getSource();
560         HTMLDocument JavaDoc doc = (HTMLDocument JavaDoc) pane.getDocument();
561
562         Element JavaDoc e = doc.getCharacterElement(pane.viewToModel(event.getPoint()));
563         AttributeSet JavaDoc a = e.getAttributes();
564         AttributeSet JavaDoc anchor = (AttributeSet JavaDoc) a.getAttribute(HTML.Tag.A);
565
566         if (anchor == null) {
567             return null;
568         }
569
570         URL JavaDoc url = null;
571
572         try {
573             url = new URL JavaDoc((String JavaDoc) anchor.getAttribute(HTML.Attribute.HREF));
574         } catch (MalformedURLException JavaDoc mue) {
575             return null;
576         }
577
578         return url;
579     }
580
581     /**
582      * this method extracts any url, but if URL's protocol is mailto: then this
583      * method also extracts the corresponding recipient name whatever it may be.
584      * <br>
585      * This "kind of" superseeds the previous extractURL(MouseEvent) method.
586      */

587     private ColumbaURL extractMailToURL(MouseEvent JavaDoc event) {
588
589         ColumbaURL url = new ColumbaURL(extractURL(event));
590         if (url.getRealURL() == null)
591             return null;
592
593         if (!url.getRealURL().getProtocol().equalsIgnoreCase("mailto"))
594             return url;
595
596         JEditorPane JavaDoc pane = (JEditorPane JavaDoc) event.getSource();
597         HTMLDocument JavaDoc doc = (HTMLDocument JavaDoc) pane.getDocument();
598
599         Element JavaDoc e = doc.getCharacterElement(pane.viewToModel(event.getPoint()));
600         try {
601             url.setSender(doc.getText(e.getStartOffset(), (e.getEndOffset() - e
602                     .getStartOffset())));
603         } catch (BadLocationException JavaDoc e1) {
604             url.setSender("");
605         }
606
607         return url;
608     }
609
610     class URLMouseListener implements MouseListener JavaDoc {
611
612         public void mousePressed(MouseEvent JavaDoc event) {
613             if (event.isPopupTrigger()) {
614                 processPopup(event);
615             }
616         }
617
618         public void mouseReleased(MouseEvent JavaDoc event) {
619             if (event.isPopupTrigger()) {
620                 processPopup(event);
621             }
622         }
623
624         public void mouseEntered(MouseEvent JavaDoc event) {
625         }
626
627         public void mouseExited(MouseEvent JavaDoc event) {
628         }
629
630         public void mouseClicked(MouseEvent JavaDoc event) {
631             if (!SwingUtilities.isLeftMouseButton(event)) {
632                 return;
633             }
634
635             URL JavaDoc url = extractURL(event);
636
637             if (url == null) {
638                 return;
639             }
640
641             if (url.getProtocol().equalsIgnoreCase("mailto")) {
642                 // open composer
643
ComposerController controller = new ComposerController();
644                 new DefaultContainer(controller);
645
646                 ComposerModel model = new ComposerModel();
647                 model.setTo(url.getFile());
648
649                 // apply model
650
controller.setComposerModel(model);
651
652                 controller.updateComponents(true);
653             } else {
654                 ColumbaDesktop.getInstance().browse(url);
655             }
656         }
657     }
658
659     protected void processPopup(MouseEvent JavaDoc ev) {
660         // final URL url = extractURL(ev);
661
final ColumbaURL mailto = extractMailToURL(ev);
662
663         final MouseEvent JavaDoc event = ev;
664         // open context-menu
665
// -> this has to happen in the awt-event dispatcher thread
666
SwingUtilities.invokeLater(new Runnable JavaDoc() {
667
668             public void run() {
669                 createPopupMenu(mailto).show(event.getComponent(),
670                         event.getX(), event.getY());
671             }
672         });
673     }
674 }
Popular Tags