Variable `mime-raw::text-decoder' was moved to mime-text.el.
[elisp/semi.git] / mime-view.el
1 ;;; mime-view.el --- interactive MIME viewer for GNU Emacs
2
3 ;; Copyright (C) 1995,1996,1997 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Created: 1994/7/13
7 ;;      Renamed: 1994/8/31 from tm-body.el
8 ;;      Renamed: 1997/02/19 from tm-view.el
9 ;; Version: $Revision: 0.25 $
10 ;; Keywords: MIME, multimedia, mail, news
11
12 ;; This file is part of SEMI (SEMI is Emacs MIME Interfaces).
13
14 ;; This program is free software; you can redistribute it and/or
15 ;; modify it under the terms of the GNU General Public License as
16 ;; published by the Free Software Foundation; either version 2, or (at
17 ;; your option) any later version.
18
19 ;; This program is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22 ;; General Public License for more details.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
28
29 ;;; Code:
30
31 (require 'cl)
32 (require 'std11)
33 (require 'mel)
34 (require 'eword-decode)
35 (require 'mime-parse)
36 (require 'mime-text)
37
38
39 ;;; @ version
40 ;;;
41
42 (defconst mime-view-RCS-ID
43   "$Id: mime-view.el,v 0.25 1997-03-15 23:52:58 morioka Exp $")
44
45 (defconst mime-view-version (get-version-string mime-view-RCS-ID))
46
47
48 ;;; @ variables
49 ;;;
50
51 (defvar mime/content-decoding-condition
52   '(((type . "text/plain")
53      (method "tm-plain" nil 'file 'type 'encoding 'mode 'name)
54      (mode "play" "print")
55      )
56     ((type . "text/html")
57      (method "tm-html" nil 'file 'type 'encoding 'mode 'name)
58      (mode . "play")
59      )
60     ((type . "text/x-rot13-47")
61      (method . mime-article/decode-caesar)
62      (mode . "play")
63      )
64     ((type . "audio/basic")
65      (method "tm-au"    nil 'file 'type 'encoding 'mode 'name)
66      (mode . "play")
67      )
68     
69     ((type . "image/jpeg")
70      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
71      (mode "play" "print")
72      )
73     ((type . "image/gif")
74      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
75      (mode "play" "print")
76      )
77     ((type . "image/tiff")
78      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
79      (mode "play" "print")
80      )
81     ((type . "image/x-tiff")
82      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
83      (mode "play" "print")
84      )
85     ((type . "image/x-xbm")
86      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
87      (mode "play" "print")
88      )
89     ((type . "image/x-pic")
90      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
91      (mode "play" "print")
92      )
93     ((type . "image/x-mag")
94      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
95      (mode "play" "print")
96      )
97     
98     ((type . "video/mpeg")
99      (method "tm-mpeg"  nil 'file 'type 'encoding 'mode 'name)
100      (mode . "play")
101      )
102     
103     ((type . "application/postscript")
104      (method "tm-ps" nil 'file 'type 'encoding 'mode 'name)
105      (mode "play" "print")
106      )
107     ((type . "application/octet-stream")
108      (method "tm-file"  nil 'file 'type 'encoding 'mode 'name)
109      (mode "play" "print")
110      )
111     
112     ;;((type . "message/external-body")
113     ;; (method "xterm" nil
114     ;;         "-e" "showexternal"
115     ;;         'file '"access-type" '"name" '"site" '"directory"))
116     ((type . "message/rfc822")
117      (method . mime-article/view-message/rfc822)
118      (mode . "play")
119      )
120     ((type . "message/partial")
121      (method . mime-article/decode-message/partial)
122      (mode . "play")
123      )
124     ((type . "message/external-body")
125      ("access-type" . "anon-ftp")
126      (method . mime-article/decode-message/external-ftp)
127      )
128     
129     ((method "metamail" t "-m" "tm" "-x" "-d" "-z" "-e" 'file)
130      (mode . "play")
131      )
132     ((method "tm-file"  nil 'file 'type 'encoding 'mode 'name)
133      (mode . "extract")
134      )
135     ))
136
137 (defvar mime-view-childrens-header-showing-Content-Type-list
138   '("message/rfc822" "message/news"))
139
140 (defvar mime-view-default-showing-Content-Type-list
141   '("text/plain" nil "text/richtext" "text/enriched"
142     "text/x-latex" "application/x-latex"
143     "message/delivery-status"
144     "application/pgp" "text/x-pgp"
145     "application/octet-stream"
146     "application/x-selection" "application/x-comment"))
147
148 (defvar mime-view-content-button-ignored-ctype-list
149   '("application/x-selection"))
150
151 (defvar mime-view-content-button-visible-ctype-list
152   '("application/pgp"))
153
154 (defvar mime-view-uuencode-encoding-name-list '("x-uue" "x-uuencode"))
155
156 (defvar mime-view-ignored-field-list
157   '(".*Received" ".*Path" ".*Id" "References"
158     "Replied" "Errors-To"
159     "Lines" "Sender" ".*Host" "Xref"
160     "Content-Type" "Precedence"
161     "Status" "X-VM-.*")
162   "All fields that match this list will be hidden in MIME preview buffer.
163 Each elements are regexp of field-name. [mime-view.el]")
164
165 (defvar mime-view-ignored-field-regexp
166   (concat "^"
167           (apply (function regexp-or) mime-view-ignored-field-list)
168           ":"))
169
170 (defvar mime-view-visible-field-list
171   '("Dnas.*" "Message-Id")
172   "All fields that match this list will be displayed in MIME preview buffer.
173 Each elements are regexp of field-name. [mime-view.el]")
174
175 (defvar mime-view-visible-field-regexp
176   (concat "^"
177           (apply (function regexp-or) mime-view-visible-field-list)
178           ":"))
179
180 (defvar mime-view-redisplay nil)
181
182 (defvar mime-view-announcement-for-message/partial
183   (if (and (>= emacs-major-version 19) window-system)
184       "\
185 \[[ This is message/partial style split message. ]]
186 \[[ Please press `v' key in this buffer          ]]
187 \[[ or click here by mouse button-2.             ]]"
188     "\
189 \[[ This is message/partial style split message. ]]
190 \[[ Please press `v' key in this buffer.         ]]"
191     ))
192
193
194 ;;; @@ predicate functions
195 ;;;
196
197 (defun mime-view-header-visible-p (rcnum cinfo &optional ctype)
198   (or (null rcnum)
199       (progn
200         (setq ctype
201               (mime::content-info/type
202                (mime-article/rcnum-to-cinfo (cdr rcnum) cinfo)
203                ))
204         (member ctype mime-view-childrens-header-showing-Content-Type-list)
205         )))
206
207 (defun mime-view-body-visible-p (rcnum cinfo &optional ctype)
208   (let (ccinfo)
209     (or ctype
210         (setq ctype
211               (mime::content-info/type
212                (setq ccinfo (mime-article/rcnum-to-cinfo rcnum cinfo))
213                ))
214         )
215     (and (member ctype mime-view-default-showing-Content-Type-list)
216          (if (string-equal ctype "application/octet-stream")
217              (progn
218                (or ccinfo
219                    (setq ccinfo (mime-article/rcnum-to-cinfo rcnum cinfo))
220                    )
221                (member (mime::content-info/encoding ccinfo)
222                        '(nil "7bit" "8bit"))
223                )
224            t))
225     ))
226
227
228 ;;; @@ content button
229 ;;;
230
231 (defun mime-preview/insert-content-button
232   (rcnum cinfo ctype params subj encoding)
233   (save-restriction
234     (narrow-to-region (point)(point))
235     (let ((access-type (assoc "access-type" params))
236           (charset (assoc "charset" params))
237           (num (or (cdr (assoc "x-part-number" params))
238                    (if (consp rcnum)
239                        (mapconcat (function
240                                    (lambda (num)
241                                      (format "%s" (1+ num))
242                                      ))
243                                   (reverse rcnum) ".")
244                      "0"))
245                ))
246       (cond (access-type
247              (let ((server (assoc "server" params)))
248                (setq access-type (cdr access-type))
249                (if server
250                    (insert (format "[%s %s ([%s] %s)]\n" num subj
251                                    access-type (cdr server)))
252                  (let ((site (cdr (assoc "site" params)))
253                        (dir (cdr (assoc "directory" params)))
254                        )
255                    (insert (format "[%s %s ([%s] %s:%s)]\n" num subj
256                                    access-type site dir))
257                    )))
258              )
259             (t
260              (insert (concat "[" num " " subj))
261              (let ((rest
262                     (if (setq charset (cdr charset))
263                         (if encoding
264                             (format " <%s; %s (%s)>]\n"
265                                     ctype charset encoding)
266                           (format " <%s; %s>]\n" ctype charset)
267                           )
268                       (format " <%s>]\n" ctype)
269                       )))
270                (if (>= (+ (current-column)(length rest))(window-width))
271                    (setq rest (concat "\n\t" rest))
272                  )
273                (insert rest)
274                ))))
275     (mime-add-button (point-min)(1- (point-max))
276                      (function mime-view-play-content))
277     ))
278
279 (defun mime-preview/default-content-button-function
280   (rcnum cinfo ctype params subj encoding)
281   (if (and (consp rcnum)
282            (not (member
283                  ctype
284                  mime-view-content-button-ignored-ctype-list)))
285       (mime-preview/insert-content-button
286        rcnum cinfo ctype params subj encoding)
287     ))
288
289 (defvar mime-preview/content-button-function
290   (function mime-preview/default-content-button-function))
291
292
293 ;;; @@ content header filter
294 ;;;
295
296 (defun mime-preview/cut-header ()
297   (goto-char (point-min))
298   (while (and
299           (re-search-forward mime-view-ignored-field-regexp nil t)
300           (let* ((beg (match-beginning 0))
301                  (end (match-end 0))
302                  (name (buffer-substring beg end))
303                  )
304             (if (not (string-match mime-view-visible-field-regexp name))
305                 (delete-region
306                  beg
307                  (save-excursion
308                    (and
309                     (re-search-forward "^\\([^ \t]\\|$\\)" nil t)
310                     (match-beginning 0)
311                     )))
312               )
313             t)))
314   )
315
316 (defun mime-view-default-content-header-filter ()
317   (mime-preview/cut-header)
318   (eword-decode-header)
319   )
320
321 (defvar mime-view-content-header-filter-alist nil)
322
323
324 ;;; @@ content filter
325 ;;;
326
327 (defvar mime-view-content-filter-alist
328   '(("text/enriched" . mime-preview/filter-for-text/enriched)
329     ("text/richtext" . mime-preview/filter-for-text/richtext)
330     (t . mime-preview/filter-for-text/plain)
331     ))
332
333
334 ;;; @@ content separator
335 ;;;
336
337 (defun mime-preview/default-content-separator (rcnum cinfo ctype params subj)
338   (if (and (not (mime-view-header-visible-p rcnum cinfo ctype))
339            (not (mime-view-body-visible-p rcnum cinfo ctype))
340            )
341       (progn
342         (goto-char (point-max))
343         (insert "\n")
344         )))
345
346
347 ;;; @@ buffer local variables
348 ;;;
349
350 ;;; @@@ in raw buffer
351 ;;;
352
353 (make-variable-buffer-local 'mime::article/content-info)
354
355 (defvar mime::article/preview-buffer nil)
356 (make-variable-buffer-local 'mime::article/preview-buffer)
357
358
359 ;;; @@@ in view buffer
360 ;;;
361
362 (make-variable-buffer-local 'mime::preview/mother-buffer)
363 (make-variable-buffer-local 'mime::preview/content-list)
364
365 (defvar mime::preview/article-buffer nil)
366 (make-variable-buffer-local 'mime::preview/article-buffer)
367
368 (make-variable-buffer-local 'mime::preview/original-major-mode)
369 (make-variable-buffer-local 'mime::preview/original-window-configuration)
370
371
372 ;;; @@ quitting method
373 ;;;
374
375 (defvar mime-view-quitting-method-alist
376   '((mime/show-message-mode
377      . mime-view-quitting-method-for-mime/show-message-mode)))
378
379 (defvar mime-view-over-to-previous-method-alist nil)
380 (defvar mime-view-over-to-next-method-alist nil)
381
382 (defvar mime-view-show-summary-method nil)
383
384
385 ;;; @@ following method
386 ;;;
387
388 (defvar mime-view-following-method-alist nil)
389
390 (defvar mime-view-following-required-fields-list
391   '("From"))
392
393
394 ;;; @@ X-Face
395 ;;;
396
397 ;; hack from Gnus 5.0.4.
398
399 (defvar mime-view-x-face-to-pbm-command
400   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm")
401
402 (defvar mime-view-x-face-command
403   (concat mime-view-x-face-to-pbm-command
404           " | xv -quit -")
405   "String to be executed to display an X-Face field.
406 The command will be executed in a sub-shell asynchronously.
407 The compressed face will be piped to this command.")
408
409 (defun mime-view-x-face-function ()
410   "Function to display X-Face field. You can redefine to customize."
411   ;; 1995/10/12 (c.f. tm-eng:130)
412   ;;    fixed by Eric Ding <ericding@San-Jose.ate.slb.com>
413   (save-restriction
414     (narrow-to-region (point-min) (re-search-forward "^$" nil t))
415     ;; end
416     (goto-char (point-min))
417     (if (re-search-forward "^X-Face:[ \t]*" nil t)
418         (let ((beg (match-end 0))
419               (end (std11-field-end))
420               )
421           (call-process-region beg end "sh" nil 0 nil
422                                "-c" mime-view-x-face-command)
423           ))))
424
425
426 ;;; @@ utility
427 ;;;
428
429 (defun mime-preview/get-original-major-mode ()
430   (if mime::preview/mother-buffer
431       (save-excursion
432         (set-buffer mime::preview/mother-buffer)
433         (mime-preview/get-original-major-mode)
434         )
435     mime::preview/original-major-mode))
436
437
438 ;;; @ data structures
439 ;;;
440
441 ;;; @@ preview-content-info
442 ;;;
443
444 (define-structure mime::preview-content-info
445   point-min point-max buffer content-info)
446
447
448 ;;; @ buffer setup
449 ;;;
450
451 (defun mime-view-setup-buffer (&optional ctl encoding ibuf obuf)
452   (if ibuf
453       (progn
454         (get-buffer ibuf)
455         (set-buffer ibuf)
456         ))
457   (or mime-view-redisplay
458       (setq mime::article/content-info (mime-parse-message ctl encoding))
459       )
460   (let ((ret (mime-view-make-preview-buffer obuf)))
461     (setq mime::article/preview-buffer (car ret))
462     ret))
463
464 (defun mime-view-make-preview-buffer (&optional obuf)
465   (let* ((cinfo mime::article/content-info)
466          (pcl (mime/flatten-content-info cinfo))
467          (dest (make-list (length pcl) nil))
468          (the-buf (current-buffer))
469          (mode major-mode)
470          )
471     (or obuf
472         (setq obuf (concat "*Preview-" (buffer-name the-buf) "*")))
473     (set-buffer (get-buffer-create obuf))
474     (setq buffer-read-only nil)
475     (widen)
476     (erase-buffer)
477     (setq mime::preview/article-buffer the-buf)
478     (setq mime::preview/original-major-mode mode)
479     (setq major-mode 'mime-view-mode)
480     (setq mode-name "MIME-View")
481     (let ((drest dest))
482       (while pcl
483         (setcar drest
484                 (mime-preview/display-content (car pcl) cinfo the-buf obuf))
485         (setq pcl (cdr pcl)
486               drest (cdr drest))
487         ))
488     (set-buffer-modified-p nil)
489     (setq buffer-read-only t)
490     (set-buffer the-buf)
491     (list obuf dest)
492     ))
493
494 (defun mime-preview/display-content (content cinfo ibuf obuf)
495   (let* ((beg (mime::content-info/point-min content))
496          (end (mime::content-info/point-max content))
497          (ctype (mime::content-info/type content))
498          (params (mime::content-info/parameters content))
499          (encoding (mime::content-info/encoding content))
500          (rcnum (mime::content-info/rcnum content))
501          he e nb ne subj)
502     (set-buffer ibuf)
503     (goto-char beg)
504     (setq he (if (re-search-forward "^$" nil t)
505                  (1+ (match-end 0))
506                end))
507     (if (> he end)
508         (setq he end)
509       )
510     (save-restriction
511       (narrow-to-region beg end)
512       (setq subj
513             (eword-decode-string
514              (mime-article/get-subject params encoding)))
515       )
516     (set-buffer obuf)
517     (setq nb (point))
518     (narrow-to-region nb nb)
519     (funcall mime-preview/content-button-function
520              rcnum cinfo ctype params subj encoding)
521     (if (mime-view-header-visible-p rcnum cinfo ctype)
522         (mime-preview/display-header beg he)
523       )
524     (if (and (null rcnum)
525              (member
526               ctype mime-view-content-button-visible-ctype-list))
527         (save-excursion
528           (goto-char (point-max))
529           (mime-preview/insert-content-button
530            rcnum cinfo ctype params subj encoding)
531           ))
532     (cond ((mime-view-body-visible-p rcnum cinfo ctype)
533            (mime-preview/display-body he end
534                                       rcnum cinfo ctype params subj encoding)
535            )
536           ((equal ctype "message/partial")
537            (mime-preview/display-message/partial)
538            )
539           ((and (null rcnum)
540                 (null (mime::content-info/children cinfo))
541                 )
542            (goto-char (point-max))
543            (mime-preview/insert-content-button
544             rcnum cinfo ctype params subj encoding)
545            ))
546     (mime-preview/default-content-separator rcnum cinfo ctype params subj)
547     (prog1
548         (progn
549           (setq ne (point-max))
550           (widen)
551           (mime::preview-content-info/create nb (1- ne) ibuf content)
552           )
553       (goto-char ne)
554       )))
555
556 (defun mime-preview/display-header (beg end)
557   (save-restriction
558     (narrow-to-region (point)(point))
559     (insert-buffer-substring mime::preview/article-buffer beg end)
560     (let ((f (cdr (assq mime::preview/original-major-mode
561                         mime-view-content-header-filter-alist))))
562       (if (functionp f)
563           (funcall f)
564         (mime-view-default-content-header-filter)
565         ))
566     (run-hooks 'mime-view-content-header-filter-hook)
567     ))
568
569 (defun mime-preview/display-body (beg end
570                                       rcnum cinfo ctype params subj encoding)
571   (save-restriction
572     (narrow-to-region (point-max)(point-max))
573     (insert-buffer-substring mime::preview/article-buffer beg end)
574     (let ((f (cdr (or (assoc ctype mime-view-content-filter-alist)
575                       (assq t mime-view-content-filter-alist)))))
576       (and (functionp f)
577            (funcall f ctype params encoding)
578            )
579       )))
580
581 (defun mime-preview/display-message/partial ()
582   (save-restriction
583     (goto-char (point-max))
584     (if (not (search-backward "\n\n" nil t))
585         (insert "\n")
586       )
587     (let ((be (point-max)))
588       (narrow-to-region be be)
589       (insert mime-view-announcement-for-message/partial)
590       (mime-add-button (point-min)(point-max)
591                        (function mime-view-play-content))
592       )))
593
594 (defun mime-article/get-uu-filename (param &optional encoding)
595   (if (member (or encoding
596                   (cdr (assq 'encoding param))
597                   )
598               mime-view-uuencode-encoding-name-list)
599       (save-excursion
600         (or (if (re-search-forward "^begin [0-9]+ " nil t)
601                 (if (looking-at ".+$")
602                     (buffer-substring (match-beginning 0)(match-end 0))
603                   ))
604             ""))
605     ))
606
607 (defun mime-article/get-subject (param &optional encoding)
608   (or (std11-find-field-body '("Content-Description" "Subject"))
609       (let (ret)
610         (if (or (and (setq ret (mime/Content-Disposition))
611                      (setq ret (assoc "filename" (cdr ret)))
612                      )
613                 (setq ret (assoc "name" param))
614                 (setq ret (assoc "x-name" param))
615                 )
616             (std11-strip-quoted-string (cdr ret))
617           ))
618       (mime-article/get-uu-filename param encoding)
619       ""))
620
621
622 ;;; @ content information
623 ;;;
624
625 (defun mime-article/point-content-number (p &optional cinfo)
626   (or cinfo
627       (setq cinfo mime::article/content-info)
628       )
629   (let ((b (mime::content-info/point-min cinfo))
630         (e (mime::content-info/point-max cinfo))
631         (c (mime::content-info/children cinfo))
632         )
633     (if (and (<= b p)(<= p e))
634         (or (let (co ret (sn 0))
635               (catch 'tag
636                 (while c
637                   (setq co (car c))
638                   (setq ret (mime-article/point-content-number p co))
639                   (cond ((eq ret t) (throw 'tag (list sn)))
640                         (ret (throw 'tag (cons sn ret)))
641                         )
642                   (setq c (cdr c))
643                   (setq sn (1+ sn))
644                   )))
645             t))))
646
647 (defun mime-article/rcnum-to-cinfo (rcnum &optional cinfo)
648   (or cinfo
649       (setq cinfo mime::article/content-info)
650       )
651   (find-if (function
652             (lambda (ci)
653               (equal (mime::content-info/rcnum ci) rcnum)
654               ))
655            (mime/flatten-content-info cinfo)
656            ))
657
658 (defun mime-article/cnum-to-cinfo (cn &optional cinfo)
659   (or cinfo
660       (setq cinfo mime::article/content-info)
661       )
662   (if (eq cn t)
663       cinfo
664     (let ((sn (car cn)))
665       (if (null sn)
666           cinfo
667         (let ((rc (nth sn (mime::content-info/children cinfo))))
668           (if rc
669               (mime-article/cnum-to-cinfo (cdr cn) rc)
670             ))
671         ))))
672
673 (defun mime/flatten-content-info (&optional cinfo)
674   (or cinfo
675       (setq cinfo mime::article/content-info)
676       )
677   (let ((dest (list cinfo))
678         (rcl (mime::content-info/children cinfo))
679         )
680     (while rcl
681       (setq dest (nconc dest (mime/flatten-content-info (car rcl))))
682       (setq rcl (cdr rcl))
683       )
684     dest))
685
686 (defun mime-preview/point-pcinfo (p &optional pcl)
687   (or pcl
688       (setq pcl mime::preview/content-list)
689       )
690   (catch 'tag
691     (let ((r pcl) cell)
692       (while r
693         (setq cell (car r))
694         (if (and (<= (mime::preview-content-info/point-min cell) p)
695                  (<= p (mime::preview-content-info/point-max cell))
696                  )
697             (throw 'tag cell)
698           )
699         (setq r (cdr r))
700         ))
701     (car (last pcl))
702     ))
703
704
705 ;;; @ MIME viewer mode
706 ;;;
707
708 (defconst mime-view-menu-title "MIME-View")
709 (defconst mime-view-menu-list
710   '((up          "Move to upper content"      mime-view-up-content)
711     (previous    "Move to previous content"   mime-view-previous-content)
712     (next        "Move to next content"       mime-view-next-content)
713     (scroll-down "Scroll to previous content" mime-view-scroll-down-content)
714     (scroll-up   "Scroll to next content"     mime-view-scroll-up-content)
715     (play        "Play Content"               mime-view-play-content)
716     (extract     "Extract Content"            mime-view-extract-content)
717     (print       "Print"                      mime-view-print-content)
718     (x-face      "Show X Face"                mime-view-display-x-face)
719     )
720   "Menu for MIME Viewer")
721
722 (cond (running-xemacs
723        (defvar mime-view-xemacs-popup-menu
724          (cons mime-view-menu-title
725                (mapcar (function
726                         (lambda (item)
727                           (vector (nth 1 item)(nth 2 item) t)
728                           ))
729                        mime-view-menu-list)))
730        (defun mime-view-xemacs-popup-menu (event)
731          "Popup the menu in the MIME Viewer buffer"
732          (interactive "e")
733          (select-window (event-window event))
734          (set-buffer (event-buffer event))
735          (popup-menu 'mime-view-xemacs-popup-menu))
736        (defvar mouse-button-2 'button2)
737        )
738       (t
739        (defvar mouse-button-2 [mouse-2])
740        ))
741
742 (defun mime-view-define-keymap (&optional default)
743   (let ((mime-view-mode-map (if (keymapp default)
744                                 (copy-keymap default)
745                               (make-sparse-keymap)
746                               )))
747     (define-key mime-view-mode-map
748       "u"        (function mime-view-up-content))
749     (define-key mime-view-mode-map
750       "p"        (function mime-view-previous-content))
751     (define-key mime-view-mode-map
752       "n"        (function mime-view-next-content))
753     (define-key mime-view-mode-map
754       "\e\t"     (function mime-view-previous-content))
755     (define-key mime-view-mode-map
756       "\t"       (function mime-view-next-content))
757     (define-key mime-view-mode-map
758       " "        (function mime-view-scroll-up-content))
759     (define-key mime-view-mode-map
760       "\M- "     (function mime-view-scroll-down-content))
761     (define-key mime-view-mode-map
762       "\177"     (function mime-view-scroll-down-content))
763     (define-key mime-view-mode-map
764       "\C-m"     (function mime-view-next-line-content))
765     (define-key mime-view-mode-map
766       "\C-\M-m"  (function mime-view-previous-line-content))
767     (define-key mime-view-mode-map
768       "v"        (function mime-view-play-content))
769     (define-key mime-view-mode-map
770       "e"        (function mime-view-extract-content))
771     (define-key mime-view-mode-map
772       "\C-c\C-p" (function mime-view-print-content))
773     (define-key mime-view-mode-map
774       "a"        (function mime-view-follow-content))
775     (define-key mime-view-mode-map
776       "q"        (function mime-view-quit))
777     (define-key mime-view-mode-map
778       "h"        (function mime-view-show-summary))
779     (define-key mime-view-mode-map
780       "\C-c\C-x" (function mime-view-kill-buffer))
781     (define-key mime-view-mode-map
782       "<"        (function beginning-of-buffer))
783     (define-key mime-view-mode-map
784       ">"        (function end-of-buffer))
785     (define-key mime-view-mode-map
786       "?"        (function describe-mode))
787     (if (functionp default)
788         (setq mime-view-mode-map
789               (append mime-view-mode-map (list (cons t default)))
790               ))
791     (if mouse-button-2
792         (define-key mime-view-mode-map
793           mouse-button-2 (function mime-button-dispatcher))
794       )
795     (cond (running-xemacs
796            (define-key mime-view-mode-map
797              mouse-button-3 (function mime-view-xemacs-popup-menu))
798            )
799           ((>= emacs-major-version 19)
800            (define-key mime-view-mode-map [menu-bar mime-view]
801              (cons mime-view-menu-title
802                    (make-sparse-keymap mime-view-menu-title)))
803            (mapcar (function
804                     (lambda (item)
805                       (define-key mime-view-mode-map
806                         (vector 'menu-bar 'mime-view (car item))
807                         (cons (nth 1 item)(nth 2 item))
808                         )
809                       ))
810                    (reverse mime-view-menu-list)
811                    )
812            ))
813     (use-local-map mime-view-mode-map)
814     (run-hooks 'mime-view-define-keymap-hook)
815     ))
816
817 (defun mime-view-mode (&optional mother ctl encoding ibuf obuf
818                                  default-keymap-or-function)
819   "Major mode for viewing MIME message.
820
821 Here is a list of the standard keys for mime-view-mode.
822
823 key             feature
824 ---             -------
825
826 u               Move to upper content
827 p or M-TAB      Move to previous content
828 n or TAB        Move to next content
829 SPC             Scroll up or move to next content
830 M-SPC or DEL    Scroll down or move to previous content
831 RET             Move to next line
832 M-RET           Move to previous line
833 v               Decode current content as `play mode'
834 e               Decode current content as `extract mode'
835 C-c C-p         Decode current content as `print mode'
836 a               Followup to current content.
837 x               Display X-Face
838 q               Quit
839 button-2        Move to point under the mouse cursor
840                 and decode current content as `play mode'
841 "
842   (interactive)
843   (let ((buf (get-buffer mime/output-buffer-name)))
844     (if buf
845         (save-excursion
846           (set-buffer buf)
847           (erase-buffer)
848           )))
849   (let ((ret (mime-view-setup-buffer ctl encoding ibuf obuf))
850         (win-conf (current-window-configuration))
851         )
852     (prog1
853         (switch-to-buffer (car ret))
854       (setq mime::preview/original-window-configuration win-conf)
855       (if mother
856           (progn
857             (setq mime::preview/mother-buffer mother)
858             ))
859       (mime-view-define-keymap default-keymap-or-function)
860       (setq mime::preview/content-list (nth 1 ret))
861       (goto-char
862        (let ((ce (mime::preview-content-info/point-max
863                   (car mime::preview/content-list)
864                   ))
865              e)
866          (goto-char (point-min))
867          (search-forward "\n\n" nil t)
868          (setq e (match-end 0))
869          (if (<= e ce)
870              e
871            ce)))
872       (run-hooks 'mime-view-mode-hook)
873       )))
874
875 (defun mime-preview/point-content-number (point)
876   (save-window-excursion
877     (let ((pc (mime-preview/point-pcinfo (point)))
878           cinfo)
879       (switch-to-buffer (mime::preview-content-info/buffer pc))
880       (setq cinfo (mime::preview-content-info/content-info pc))
881       (mime-article/point-content-number (mime::content-info/point-min cinfo))
882       )))
883
884 (defun mime-preview/cinfo-to-pcinfo (cinfo)
885   (let ((rpcl mime::preview/content-list) cell)
886     (catch 'tag
887       (while rpcl
888         (setq cell (car rpcl))
889         (if (eq cinfo (mime::preview-content-info/content-info cell))
890             (throw 'tag cell)
891           )
892         (setq rpcl (cdr rpcl))
893         ))))
894
895 (autoload 'mime-preview/decode-content "mime-play")
896
897 (defvar mime-view-decoding-mode "play" "MIME body decoding mode")
898
899 (defun mime-view-play-content ()
900   (interactive)
901   (let ((mime-view-decoding-mode "play"))
902     (mime-preview/decode-content)
903     ))
904
905 (defun mime-view-extract-content ()
906   (interactive)
907   (let ((mime-view-decoding-mode "extract"))
908     (mime-preview/decode-content)
909     ))
910
911 (defun mime-view-print-content ()
912   (interactive)
913   (let ((mime-view-decoding-mode "print"))
914     (mime-preview/decode-content)
915     ))
916
917 (defun mime-view-follow-content ()
918   (interactive)
919   (let ((root-cinfo
920          (mime::preview-content-info/content-info
921           (car mime::preview/content-list)))
922         pc p-beg p-end cinfo rcnum)
923     (let ((rest mime::preview/content-list)
924           b e cell len rc)
925       (if (catch 'tag
926             (while (setq cell (car rest))
927               (setq b (mime::preview-content-info/point-min cell)
928                     e (mime::preview-content-info/point-max cell))
929               (setq rest (cdr rest))
930               (if (and (<= b (point))(<= (point) e))
931                   (throw 'tag cell)
932                 )
933               ))
934           (progn
935             (setq pc cell
936                   cinfo (mime::preview-content-info/content-info pc)
937                   rcnum (mime::content-info/rcnum cinfo))
938             (setq len (length rcnum))
939             (setq p-beg (mime::preview-content-info/point-min pc)
940                   p-end (mime::preview-content-info/point-max pc))
941             (while (and (setq cell (car rest))
942                         (progn
943                           (setq rc
944                                 (mime::content-info/rcnum
945                                  (mime::preview-content-info/content-info
946                                   cell)))
947                           (equal rcnum
948                                  (nthcdr (- (length rc) len) rc))
949                           ))
950               (setq p-end (mime::preview-content-info/point-max cell))
951               (setq rest (cdr rest))
952               ))))
953     (if pc
954         (let* ((mode (mime-preview/get-original-major-mode))
955                (new-name (format "%s-%s" (buffer-name) (reverse rcnum)))
956                new-buf
957                (the-buf (current-buffer))
958                (a-buf mime::preview/article-buffer)
959                fields)
960           (save-excursion
961             (set-buffer (setq new-buf (get-buffer-create new-name)))
962             (erase-buffer)
963             (insert-buffer-substring the-buf p-beg p-end)
964             (goto-char (point-min))
965             (if (mime-view-header-visible-p rcnum root-cinfo)
966                 (delete-region (goto-char (point-min))
967                                (if (re-search-forward "^$" nil t)
968                                    (match-end 0)
969                                  (point-min)))
970               )
971             (goto-char (point-min))
972             (insert "\n")
973             (goto-char (point-min))
974             (let ((rcnum (mime::content-info/rcnum cinfo)) ci str)
975               (while (progn
976                        (setq str
977                              (save-excursion
978                                (set-buffer a-buf)
979                                (setq ci (mime-article/rcnum-to-cinfo rcnum))
980                                (save-restriction
981                                  (narrow-to-region
982                                   (mime::content-info/point-min ci)
983                                   (mime::content-info/point-max ci)
984                                   )
985                                  (std11-header-string-except
986                                   (concat "^"
987                                           (apply (function regexp-or) fields)
988                                           ":") ""))))
989                        (if (string= (mime::content-info/type ci)
990                                     "message/rfc822")
991                            nil
992                          (if str
993                              (insert str)
994                            )
995                          rcnum))
996                 (setq fields (std11-collect-field-names)
997                       rcnum (cdr rcnum))
998                 )
999               )
1000             (let ((rest mime-view-following-required-fields-list))
1001               (while rest
1002                 (let ((field-name (car rest)))
1003                   (or (std11-field-body field-name)
1004                       (insert
1005                        (format
1006                         (concat field-name
1007                                 ": "
1008                                 (save-excursion
1009                                   (set-buffer the-buf)
1010                                   (set-buffer mime::preview/mother-buffer)
1011                                   (set-buffer mime::preview/article-buffer)
1012                                   (std11-field-body field-name)
1013                                   )
1014                                 "\n")))
1015                       ))
1016                 (setq rest (cdr rest))
1017                 ))
1018             (eword-decode-header)
1019             )
1020           (let ((f (cdr (assq mode mime-view-following-method-alist))))
1021             (if (functionp f)
1022                 (funcall f new-buf)
1023               (message
1024                (format
1025                 "Sorry, following method for %s is not implemented yet."
1026                 mode))
1027               ))
1028           ))))
1029
1030 (defun mime-view-display-x-face ()
1031   (interactive)
1032   (save-window-excursion
1033     (set-buffer mime::preview/article-buffer)
1034     (mime-view-x-face-function)
1035     ))
1036
1037 (defun mime-view-up-content ()
1038   (interactive)
1039   (let* ((pc (mime-preview/point-pcinfo (point)))
1040          (cinfo (mime::preview-content-info/content-info pc))
1041          (rcnum (mime::content-info/rcnum cinfo))
1042          )
1043     (if rcnum
1044         (let ((r (save-excursion
1045                    (set-buffer (mime::preview-content-info/buffer pc))
1046                    (mime-article/rcnum-to-cinfo (cdr rcnum))
1047                    ))
1048               (rpcl mime::preview/content-list)
1049               cell)
1050           (while (and
1051                   (setq cell (car rpcl))
1052                   (not (eq r (mime::preview-content-info/content-info cell)))
1053                   )
1054             (setq rpcl (cdr rpcl))
1055             )
1056           (goto-char (mime::preview-content-info/point-min cell))
1057           )
1058       (mime-view-quit)
1059       )))
1060
1061 (defun mime-view-previous-content ()
1062   (interactive)
1063   (let* ((pcl mime::preview/content-list)
1064          (p (point))
1065          (i (- (length pcl) 1))
1066          beg)
1067     (catch 'tag
1068       (while (> i 0)
1069         (setq beg (mime::preview-content-info/point-min (nth i pcl)))
1070         (if (> p beg)
1071             (throw 'tag (goto-char beg))
1072           )
1073         (setq i (- i 1))
1074         )
1075       (let ((f (assq mime::preview/original-major-mode
1076                      mime-view-over-to-previous-method-alist)))
1077         (if f
1078             (funcall (cdr f))
1079           ))
1080       )
1081     ))
1082
1083 (defun mime-view-next-content ()
1084   (interactive)
1085   (let ((pcl mime::preview/content-list)
1086         (p (point))
1087         beg)
1088     (catch 'tag
1089       (while pcl
1090         (setq beg (mime::preview-content-info/point-min (car pcl)))
1091         (if (< p beg)
1092             (throw 'tag (goto-char beg))
1093           )
1094         (setq pcl (cdr pcl))
1095         )
1096       (let ((f (assq mime::preview/original-major-mode
1097                      mime-view-over-to-next-method-alist)))
1098         (if f
1099             (funcall (cdr f))
1100           ))
1101       )
1102     ))
1103
1104 (defun mime-view-scroll-up-content (&optional h)
1105   (interactive)
1106   (or h
1107       (setq h (- (window-height) 1))
1108       )
1109   (if (= (point) (point-max))
1110       (let ((f (assq mime::preview/original-major-mode
1111                      mime-view-over-to-next-method-alist)))
1112         (if f
1113             (funcall (cdr f))
1114           ))
1115     (let ((pcl mime::preview/content-list)
1116           (p (point))
1117           np beg)
1118       (setq np
1119             (or (catch 'tag
1120                   (while pcl
1121                     (setq beg (mime::preview-content-info/point-min (car pcl)))
1122                     (if (< p beg)
1123                         (throw 'tag beg)
1124                       )
1125                     (setq pcl (cdr pcl))
1126                     ))
1127                 (point-max)))
1128       (forward-line h)
1129       (if (> (point) np)
1130           (goto-char np)
1131         )
1132       ;;(show-subtree)
1133       ))
1134   )
1135
1136 (defun mime-view-scroll-down-content (&optional h)
1137   (interactive)
1138   (or h
1139       (setq h (- (window-height) 1))
1140       )
1141   (if (= (point) (point-min))
1142       (let ((f (assq mime::preview/original-major-mode
1143                      mime-view-over-to-previous-method-alist)))
1144         (if f
1145             (funcall (cdr f))
1146           ))
1147     (let ((pcl mime::preview/content-list)
1148           (p (point))
1149           pp beg)
1150       (setq pp
1151             (or (let ((i (- (length pcl) 1)))
1152                   (catch 'tag
1153                     (while (> i 0)
1154                       (setq beg (mime::preview-content-info/point-min
1155                                  (nth i pcl)))
1156                       (if (> p beg)
1157                           (throw 'tag beg)
1158                         )
1159                       (setq i (- i 1))
1160                       )))
1161                 (point-min)))
1162       (forward-line (- h))
1163       (if (< (point) pp)
1164           (goto-char pp)
1165         )))
1166   )
1167
1168 (defun mime-view-next-line-content ()
1169   (interactive)
1170   (mime-view-scroll-up-content 1)
1171   )
1172
1173 (defun mime-view-previous-line-content ()
1174   (interactive)
1175   (mime-view-scroll-down-content 1)
1176   )
1177
1178 (defun mime-view-quit ()
1179   (interactive)
1180   (let ((r (save-excursion
1181              (set-buffer (mime::preview-content-info/buffer
1182                           (mime-preview/point-pcinfo (point))))
1183              (assq major-mode mime-view-quitting-method-alist)
1184              )))
1185     (if r
1186         (funcall (cdr r))
1187       )))
1188
1189 (defun mime-view-show-summary ()
1190   (interactive)
1191   (let ((r (save-excursion
1192              (set-buffer
1193               (mime::preview-content-info/buffer
1194                (mime-preview/point-pcinfo (point)))
1195               )
1196              (assq major-mode mime-view-show-summary-method)
1197              )))
1198     (if r
1199         (funcall (cdr r))
1200       )))
1201
1202 (defun mime-view-kill-buffer ()
1203   (interactive)
1204   (kill-buffer (current-buffer))
1205   )
1206
1207
1208 ;;; @ end
1209 ;;;
1210
1211 (provide 'mime-view)
1212
1213 (run-hooks 'mime-view-load-hook)
1214
1215 ;;; mime-view.el ends here