2001-07-13 12:00:00 ShengHuo ZHU <zsh@cs.rochester.edu>
[gnus] / lisp / mm-view.el
1 ;;; mm-view.el --- Functions for viewing MIME objects
2 ;; Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
5 ;; This file is part of GNU Emacs.
6
7 ;; GNU Emacs is free software; you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation; either version 2, or (at your option)
10 ;; any later version.
11
12 ;; GNU Emacs is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ;; GNU General Public License for more details.
16
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
19 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 ;; Boston, MA 02111-1307, USA.
21
22 ;;; Commentary:
23
24 ;;; Code:
25
26 (eval-when-compile (require 'cl))
27 (require 'mail-parse)
28 (require 'mailcap)
29 (require 'mm-bodies)
30 (require 'mm-decode)
31
32 (eval-and-compile
33   (autoload 'gnus-article-prepare-display "gnus-art")
34   (autoload 'vcard-parse-string "vcard")
35   (autoload 'vcard-format-string "vcard")
36   (autoload 'fill-flowed "flow-fill")
37   (autoload 'diff-mode "diff-mode"))
38
39 ;;;
40 ;;; Functions for displaying various formats inline
41 ;;;
42 (defun mm-inline-image-emacs (handle)
43   (let ((b (point-marker))
44         buffer-read-only)
45     (insert "\n")
46     (put-image (mm-get-image handle) b)
47     (mm-handle-set-undisplayer
48      handle
49      `(lambda () (remove-images ,b (1+ ,b))))))
50
51 (defun mm-inline-image-xemacs (handle)
52   (insert "\n")
53   (forward-char -1)
54   (let ((b (point))
55         (annot (make-annotation (mm-get-image handle) nil 'text))
56         buffer-read-only)
57     (mm-handle-set-undisplayer
58      handle
59      `(lambda ()
60         (let (buffer-read-only)
61           (delete-annotation ,annot)
62           (delete-region ,(set-marker (make-marker) b)
63                          ,(set-marker (make-marker) (point))))))
64     (set-extent-property annot 'mm t)
65     (set-extent-property annot 'duplicable t)))
66
67 (eval-and-compile
68   (if (featurep 'xemacs)
69       (defalias 'mm-inline-image 'mm-inline-image-xemacs)
70     (defalias 'mm-inline-image 'mm-inline-image-emacs)))
71
72 (defvar mm-w3-setup nil)
73 (defun mm-setup-w3 ()
74   (unless mm-w3-setup
75     (require 'w3)
76     (w3-do-setup)
77     (require 'url)
78     (require 'w3-vars)
79     (require 'url-vars)
80     (setq mm-w3-setup t)))
81
82 (defun mm-inline-text (handle)
83   (let ((type (mm-handle-media-subtype handle))
84         text buffer-read-only)
85     (cond
86      ((equal type "html")
87       (mm-setup-w3)
88       (setq text (mm-get-part handle))
89       (let ((b (point))
90             (url-standalone-mode t)
91             (url-current-object
92              (url-generic-parse-url (format "cid:%s" (mm-handle-id handle))))
93             (width (window-width))
94             (charset (mail-content-type-get
95                       (mm-handle-type handle) 'charset)))
96         (save-excursion
97           (insert text)
98           (save-restriction
99             (narrow-to-region b (point))
100             (goto-char (point-min))
101             (if (or (and (boundp 'w3-meta-content-type-charset-regexp)
102                          (re-search-forward
103                           w3-meta-content-type-charset-regexp nil t))
104                     (and (boundp 'w3-meta-charset-content-type-regexp)
105                          (re-search-forward
106                           w3-meta-charset-content-type-regexp nil t)))
107                 (setq charset
108                      (or (let ((bsubstr (buffer-substring-no-properties
109                                          (match-beginning 2)
110                                          (match-end 2))))
111                            (if (fboundp 'w3-coding-system-for-mime-charset)
112                                (w3-coding-system-for-mime-charset bsubstr)
113                              (mm-charset-to-coding-system bsubstr)))
114                          charset)))
115             (delete-region (point-min) (point-max))
116             (insert (mm-decode-string text charset))
117             (save-window-excursion
118               (save-restriction
119                 (let ((w3-strict-width width)
120                       ;; Don't let w3 set the global version of
121                       ;; this variable.
122                       (fill-column fill-column)
123                       (url-standalone-mode t))
124                   (condition-case var
125                       (w3-region (point-min) (point-max))
126                     (error
127                      (delete-region (point-min) (point-max))
128                      (let ((b (point))
129                            (charset (mail-content-type-get
130                                      (mm-handle-type handle) 'charset)))
131                        (if (or (eq charset 'gnus-decoded)
132                                (eq mail-parse-charset 'gnus-decoded))
133                            (save-restriction
134                              (narrow-to-region (point) (point))
135                              (mm-insert-part handle)
136                              (goto-char (point-max)))
137                          (insert (mm-decode-string (mm-get-part handle) 
138                                                    charset))))
139                      (message
140                       "Error while rendering html; showing as text/plain"))))))
141             (mm-handle-set-undisplayer
142              handle
143              `(lambda ()
144                 (let (buffer-read-only)
145                   (if (functionp 'remove-specifier)
146                       (mapcar (lambda (prop)
147                                 (remove-specifier
148                                  (face-property 'default prop)
149                                  (current-buffer)))
150                               '(background background-pixmap foreground)))
151                   (delete-region ,(point-min-marker)
152                                  ,(point-max-marker)))))))))
153      ((or (equal type "enriched")
154           (equal type "richtext"))
155       (save-excursion
156         (mm-with-unibyte-buffer
157           (mm-insert-part handle)
158           (save-window-excursion
159             (enriched-decode (point-min) (point-max))
160             (setq text (buffer-string)))))
161       (mm-insert-inline handle text))
162      ((equal type "x-vcard")
163       (mm-insert-inline
164        handle
165        (concat "\n-- \n"
166                (if (fboundp 'vcard-pretty-print)
167                    (vcard-pretty-print (mm-get-part handle))
168                  (vcard-format-string
169                   (vcard-parse-string (mm-get-part handle)
170                                       'vcard-standard-filter))))))
171      (t
172       (let ((b (point))
173             (charset (mail-content-type-get
174                       (mm-handle-type handle) 'charset)))
175         (if (or (eq charset 'gnus-decoded)
176                 ;; This is probably not entirely correct, but
177                 ;; makes rfc822 parts with embedded multiparts work.
178                 (eq mail-parse-charset 'gnus-decoded))
179             (save-restriction
180               (narrow-to-region (point) (point))
181               (mm-insert-part handle)
182               (goto-char (point-max)))
183           (insert (mm-decode-string (mm-get-part handle) charset)))
184         (when (and (equal type "plain")
185                    (equal (cdr (assoc 'format (mm-handle-type handle)))
186                           "flowed"))
187           (save-restriction
188             (narrow-to-region b (point))
189             (goto-char b)
190             (fill-flowed)
191             (goto-char (point-max))))
192         (save-restriction
193           (narrow-to-region b (point))
194           (set-text-properties (point-min) (point-max) nil)
195           (mm-handle-set-undisplayer
196            handle
197            `(lambda ()
198               (let (buffer-read-only)
199                 (delete-region ,(point-min-marker)
200                                ,(point-max-marker)))))))))))
201
202 (defun mm-insert-inline (handle text)
203   "Insert TEXT inline from HANDLE."
204   (let ((b (point)))
205     (insert text)
206     (mm-handle-set-undisplayer
207      handle
208      `(lambda ()
209         (let (buffer-read-only)
210           (delete-region ,(set-marker (make-marker) b)
211                          ,(set-marker (make-marker) (point))))))))
212
213 (defun mm-inline-audio (handle)
214   (message "Not implemented"))
215
216 (defun mm-view-sound-file ()
217   (message "Not implemented"))
218
219 (defun mm-w3-prepare-buffer ()
220   (require 'w3)
221   (let ((url-standalone-mode t))
222     (w3-prepare-buffer)))
223
224 (defun mm-view-message ()
225   (mm-enable-multibyte)
226   (let (handles)
227     (let (gnus-article-mime-handles)
228       ;; Double decode problem may happen.  See mm-inline-message.
229       (run-hooks 'gnus-article-decode-hook)
230       (gnus-article-prepare-display)
231       (setq handles gnus-article-mime-handles))
232     (when handles
233       (setq gnus-article-mime-handles
234             (mm-merge-handles gnus-article-mime-handles handles))))
235   (fundamental-mode)
236   (goto-char (point-min)))
237
238 (defun mm-inline-message (handle)
239   (let ((b (point))
240         (bolp (bolp))
241         (charset (mail-content-type-get
242                   (mm-handle-type handle) 'charset))
243         gnus-displaying-mime handles)
244     (when (and charset
245                (stringp charset))
246       (setq charset (intern (downcase charset)))
247       (when (eq charset 'us-ascii)
248         (setq charset nil)))
249     (save-excursion
250       (save-restriction
251         (narrow-to-region b b)
252         (mm-insert-part handle)
253         (let (gnus-article-mime-handles
254               ;; disable prepare hook
255               gnus-article-prepare-hook
256               (gnus-newsgroup-charset
257                (or charset gnus-newsgroup-charset)))
258           (run-hooks 'gnus-article-decode-hook)
259           (gnus-article-prepare-display)
260           (setq handles gnus-article-mime-handles))
261         (goto-char (point-min))
262         (unless bolp
263           (insert "\n"))
264         (goto-char (point-max))
265         (unless (bolp)
266           (insert "\n"))
267         (insert "----------\n\n")
268         (when handles
269           (setq gnus-article-mime-handles
270                 (mm-merge-handles gnus-article-mime-handles handles)))
271         (mm-handle-set-undisplayer
272          handle
273          `(lambda ()
274             (let (buffer-read-only)
275               (if (fboundp 'remove-specifier)
276                   ;; This is only valid on XEmacs.
277                   (mapcar (lambda (prop)
278                             (remove-specifier
279                              (face-property 'default prop) (current-buffer)))
280                           '(background background-pixmap foreground)))
281               (delete-region ,(point-min-marker) ,(point-max-marker)))))))))
282
283 (defun mm-display-inline-fontify (handle mode)
284   (let (text)
285     ;; XEmacs @#$@ version of font-lock refuses to fully turn itself
286     ;; on for buffers whose name begins with " ".  That's why we use
287     ;; save-current-buffer/get-buffer-create rather than
288     ;; with-temp-buffer.
289     (save-current-buffer
290       (set-buffer (generate-new-buffer "*fontification*"))
291       (unwind-protect
292           (progn
293             (buffer-disable-undo)
294             (mm-insert-part handle)
295             (funcall mode)
296             (let ((font-lock-verbose nil))
297               ;; I find font-lock a bit too verbose.
298               (font-lock-fontify-buffer))
299             ;; By default, XEmacs font-lock uses non-duplicable text
300             ;; properties.  This code forces all the text properties
301             ;; to be copied along with the text.
302             (when (fboundp 'extent-list)
303               (map-extents (lambda (ext ignored)
304                              (set-extent-property ext 'duplicable t)
305                              nil)
306                            nil nil nil nil nil 'text-prop))
307             (setq text (buffer-string)))
308         (kill-buffer (current-buffer))))
309     (mm-insert-inline handle text)))
310
311 ;; Shouldn't these functions check whether the user even wants to use
312 ;; font-lock?  At least under XEmacs, this fontification is pretty
313 ;; much unconditional.  Also, it would be nice to change for the size
314 ;; of the fontified region.
315
316 (defun mm-display-patch-inline (handle)
317   (mm-display-inline-fontify handle 'diff-mode))
318
319 (defun mm-display-elisp-inline (handle)
320   (mm-display-inline-fontify handle 'emacs-lisp-mode))
321
322 (provide 'mm-view)
323
324 ;;; mm-view.el ends here