Structure `mime::preview-content-info' was abolished.
[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.71 $
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.71 1997-03-18 14:32:53 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/external-body")
117      ("access-type" . "anon-ftp")
118      (method . mime-article/decode-message/external-ftp)
119      )
120     ((type . "message/rfc822")
121      (method . mime-article/view-message/rfc822)
122      (mode . "play")
123      )
124     ((type . "message/partial")
125      (method . mime-article/decode-message/partial)
126      (mode . "play")
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-visible-media-type-list
141   '("text/plain" nil "text/richtext" "text/enriched"
142     "text/rfc822-headers"
143     "text/x-latex" "application/x-latex"
144     "message/delivery-status"
145     "application/pgp" "text/x-pgp"
146     "application/octet-stream"
147     "application/x-selection" "application/x-comment")
148   "*List of media-types to be able to display in MIME-View buffer.
149 Each elements are string of TYPE/SUBTYPE, e.g. \"text/plain\".")
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 '("Dnas.*" "Message-Id")
171   "All fields that match this list will be displayed in MIME preview buffer.
172 Each elements are regexp of field-name.")
173
174 (defvar mime-view-redisplay nil)
175
176 (defvar mime-view-announcement-for-message/partial
177   (if (and (>= emacs-major-version 19) window-system)
178       "\
179 \[[ This is message/partial style split message. ]]
180 \[[ Please press `v' key in this buffer          ]]
181 \[[ or click here by mouse button-2.             ]]"
182     "\
183 \[[ This is message/partial style split message. ]]
184 \[[ Please press `v' key in this buffer.         ]]"
185     ))
186
187
188 ;;; @@ predicate functions
189 ;;;
190
191 (defun mime-view-header-visible-p (rcnum cinfo)
192   "Return non-nil if header of current entity is visible."
193   (or (null rcnum)
194       (member (mime::content-info/type
195                (mime-article/rcnum-to-cinfo (cdr rcnum) cinfo))
196               mime-view-childrens-header-showing-Content-Type-list)
197       ))
198
199 (defun mime-view-body-visible-p (rcnum cinfo &optional ctype)
200   (let (ccinfo)
201     (or ctype
202         (setq ctype
203               (mime::content-info/type
204                (setq ccinfo (mime-article/rcnum-to-cinfo rcnum cinfo))
205                ))
206         )
207     (and (member ctype mime-view-visible-media-type-list)
208          (if (string-equal ctype "application/octet-stream")
209              (progn
210                (or ccinfo
211                    (setq ccinfo (mime-article/rcnum-to-cinfo rcnum cinfo))
212                    )
213                (member (mime::content-info/encoding ccinfo)
214                        '(nil "7bit" "8bit"))
215                )
216            t))
217     ))
218
219
220 ;;; @@ entity button
221 ;;;
222
223 (defun mime-view-insert-entity-button (rcnum cinfo ctype params subj encoding)
224   "Insert entity-button."
225   (save-restriction
226     (narrow-to-region (point)(point))
227     (let ((access-type (assoc "access-type" params))
228           (num (or (cdr (assoc "x-part-number" params))
229                    (if (consp rcnum)
230                        (mapconcat (function
231                                    (lambda (num)
232                                      (format "%s" (1+ num))
233                                      ))
234                                   (reverse rcnum) ".")
235                      "0"))
236                ))
237       (cond (access-type
238              (let ((server (assoc "server" params)))
239                (setq access-type (cdr access-type))
240                (if server
241                    (insert (format "[%s %s ([%s] %s)]\n" num subj
242                                    access-type (cdr server)))
243                  (let ((site (cdr (assoc "site" params)))
244                        (dir (cdr (assoc "directory" params)))
245                        )
246                    (insert (format "[%s %s ([%s] %s:%s)]\n" num subj
247                                    access-type site dir))
248                    )))
249              )
250             (t
251              (let ((charset (cdr (assoc "charset" params))))
252                (insert (concat "[" num " " subj))
253                (let ((rest
254                       (concat " <" ctype
255                               (if charset
256                                   (concat "; " charset)
257                                 (if encoding (concat " (" encoding ")"))
258                                 )
259                               ">]\n")))
260                  (if (>= (+ (current-column)(length rest))(window-width))
261                      (insert "\n\t")
262                    )
263                  (insert rest)
264                  ))))
265       )
266     (mime-add-button (point-min)(1- (point-max))
267                      (function mime-view-play-current-entity))
268     ))
269
270 (defun mime-view-entity-button-function
271   (rcnum cinfo ctype params subj encoding)
272   "Insert entity button conditionally.
273 Please redefine this function if you want to change default setting."
274   (or (null rcnum)
275       (string= ctype "application/x-selection")
276       (and (string= ctype "application/octet-stream")
277            (string= (mime::content-info/type
278                      (mime-article/rcnum-to-cinfo (cdr rcnum) cinfo))
279                     "multipart/encrypted"))
280       (mime-view-insert-entity-button rcnum cinfo ctype params subj encoding)
281       ))
282
283
284 ;;; @@ content header filter
285 ;;;
286
287 (defsubst mime-view-cut-header ()
288   (goto-char (point-min))
289   (while (re-search-forward mime-view-ignored-field-regexp nil t)
290     (let* ((beg (match-beginning 0))
291            (end (match-end 0))
292            (name (buffer-substring beg end))
293            )
294       (or (member-if (function
295                       (lambda (regexp)
296                         (string-match regexp name)
297                         )) mime-view-visible-field-list)
298           (delete-region beg
299                          (if (re-search-forward "^\\([^ \t]\\|$\\)" nil t)
300                              (match-beginning 0)
301                            (point-max)))
302           ))))
303
304 (defun mime-view-default-content-header-filter ()
305   (mime-view-cut-header)
306   (eword-decode-header)
307   )
308
309 (defvar mime-view-content-header-filter-alist nil)
310
311
312 ;;; @@ content filter
313 ;;;
314
315 (defvar mime-view-content-filter-alist
316   '(("text/enriched" . mime-preview/filter-for-text/enriched)
317     ("text/richtext" . mime-preview/filter-for-text/richtext)
318     (t . mime-preview/filter-for-text/plain)
319     ))
320
321
322 ;;; @@ entity separator
323 ;;;
324
325 (defun mime-view-entity-separator-function (rcnum cinfo ctype params subj)
326   "Insert entity separator conditionally.
327 Please redefine this function if you want to change default setting."
328   (or (mime-view-header-visible-p rcnum cinfo)
329       (mime-view-body-visible-p rcnum cinfo ctype)
330       (progn
331         (goto-char (point-max))
332         (insert "\n")
333         )))
334
335
336 ;;; @@ buffer local variables
337 ;;;
338
339 ;;; @@@ in raw buffer
340 ;;;
341
342 (defvar mime::article/content-info
343   "Information about structure of message.
344 Please use reference function `mime::content-info/SLOT-NAME' to
345 reference slot of content-info.  Their argument is only content-info.
346
347 Following is a list of slots of the structure:
348
349 rcnum           reversed content-number (list)
350 point-min       beginning point of region in raw-buffer
351 point-max       end point of region in raw-buffer
352 type            media-type/subtype (string or nil)
353 parameters      parameter of Content-Type field (association list)
354 encoding        Content-Transfer-Encoding (string or nil)
355 children        entities included in this entity (list of content-infos)
356
357 If a entity includes other entities in its body, such as multipart or
358 message/rfc822, content-infos of other entities are included in
359 `children', so content-info become a tree.")
360 (make-variable-buffer-local 'mime::article/content-info)
361
362 (defvar mime::article/preview-buffer nil)
363 (make-variable-buffer-local 'mime::article/preview-buffer)
364
365
366 ;;; @@@ in view buffer
367 ;;;
368
369 (make-variable-buffer-local 'mime::preview/mother-buffer)
370
371 (defvar mime::preview/article-buffer nil)
372 (make-variable-buffer-local 'mime::preview/article-buffer)
373
374 (make-variable-buffer-local 'mime::preview/original-major-mode)
375 (make-variable-buffer-local 'mime::preview/original-window-configuration)
376
377
378 ;;; @@ quitting method
379 ;;;
380
381 (defvar mime-view-quitting-method-alist
382   '((mime-show-message-mode
383      . mime-view-quitting-method-for-mime-show-message-mode))
384   "Alist of major-mode vs. quitting-method of mime-view.")
385
386 (defvar mime-view-over-to-previous-method-alist nil)
387 (defvar mime-view-over-to-next-method-alist nil)
388
389 (defvar mime-view-show-summary-method nil
390   "Alist of major-mode vs. show-summary-method.")
391
392
393 ;;; @@ following method
394 ;;;
395
396 (defvar mime-view-following-method-alist nil
397   "Alist of major-mode vs. following-method of mime-view.")
398
399 (defvar mime-view-following-required-fields-list
400   '("From"))
401
402
403 ;;; @@ X-Face
404 ;;;
405
406 ;; hack from Gnus 5.0.4.
407
408 (defvar mime-view-x-face-to-pbm-command
409   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm")
410
411 (defvar mime-view-x-face-command
412   (concat mime-view-x-face-to-pbm-command
413           " | xv -quit -")
414   "String to be executed to display an X-Face field.
415 The command will be executed in a sub-shell asynchronously.
416 The compressed face will be piped to this command.")
417
418 (defun mime-view-x-face-function ()
419   "Function to display X-Face field. You can redefine to customize."
420   ;; 1995/10/12 (c.f. tm-eng:130)
421   ;;    fixed by Eric Ding <ericding@San-Jose.ate.slb.com>
422   (save-restriction
423     (narrow-to-region (point-min) (re-search-forward "^$" nil t))
424     ;; end
425     (goto-char (point-min))
426     (if (re-search-forward "^X-Face:[ \t]*" nil t)
427         (let ((beg (match-end 0))
428               (end (std11-field-end))
429               )
430           (call-process-region beg end "sh" nil 0 nil
431                                "-c" mime-view-x-face-command)
432           ))))
433
434
435 ;;; @@ utility
436 ;;;
437
438 (defun mime-preview/get-original-major-mode ()
439   (if mime::preview/mother-buffer
440       (save-excursion
441         (set-buffer mime::preview/mother-buffer)
442         (mime-preview/get-original-major-mode)
443         )
444     mime::preview/original-major-mode))
445
446
447 ;;; @ buffer setup
448 ;;;
449
450 (defun mime-view-setup-buffer (&optional ctl encoding ibuf obuf)
451   (if ibuf
452       (progn
453         (get-buffer ibuf)
454         (set-buffer ibuf)
455         ))
456   (or mime-view-redisplay
457       (setq mime::article/content-info (mime-parse-message ctl encoding))
458       )
459   (setq mime::article/preview-buffer (mime-view-make-preview-buffer obuf))
460   )
461
462 (defun mime-view-make-preview-buffer (&optional obuf)
463   (let* ((cinfo mime::article/content-info)
464          (pcl (mime/flatten-content-info cinfo))
465          (the-buf (current-buffer))
466          (mode major-mode)
467          )
468     (or obuf
469         (setq obuf (concat "*Preview-" (buffer-name the-buf) "*")))
470     (set-buffer (get-buffer-create obuf))
471     (setq buffer-read-only nil)
472     (widen)
473     (erase-buffer)
474     (setq mime::preview/article-buffer the-buf)
475     (setq mime::preview/original-major-mode mode)
476     (setq major-mode 'mime-view-mode)
477     (setq mode-name "MIME-View")
478     (while pcl
479       (mime-view-display-entity (car pcl) cinfo the-buf obuf)
480       (setq pcl (cdr pcl))
481       )
482     (set-buffer-modified-p nil)
483     (setq buffer-read-only t)
484     (set-buffer the-buf)
485     obuf))
486
487 (defun mime-view-display-entity (content cinfo ibuf obuf)
488   "Display entity from content-info CONTENT."
489   (let* ((beg (mime::content-info/point-min content))
490          (end (mime::content-info/point-max content))
491          (ctype (mime::content-info/type content))
492          (params (mime::content-info/parameters content))
493          (encoding (mime::content-info/encoding content))
494          (rcnum (mime::content-info/rcnum content))
495          he e nb ne subj)
496     (set-buffer ibuf)
497     (goto-char beg)
498     (setq he (if (re-search-forward "^$" nil t)
499                  (1+ (match-end 0))
500                end))
501     (if (> he end)
502         (setq he end)
503       )
504     (save-restriction
505       (narrow-to-region beg end)
506       (setq subj
507             (eword-decode-string
508              (mime-article/get-subject params encoding)))
509       )
510     (set-buffer obuf)
511     (setq nb (point))
512     (narrow-to-region nb nb)
513     (mime-view-entity-button-function rcnum cinfo ctype params subj encoding)
514     (if (mime-view-header-visible-p rcnum cinfo)
515         (mime-preview/display-header beg he)
516       )
517     (if (and (null rcnum)
518              (member
519               ctype mime-view-content-button-visible-ctype-list))
520         (save-excursion
521           (goto-char (point-max))
522           (mime-view-insert-entity-button
523            rcnum cinfo ctype params subj encoding)
524           ))
525     (cond ((mime-view-body-visible-p rcnum cinfo ctype)
526            (mime-preview/display-body he end
527                                       rcnum cinfo ctype params subj encoding)
528            )
529           ((equal ctype "message/partial")
530            (mime-preview/display-message/partial)
531            )
532           ((and (null rcnum)
533                 (null (mime::content-info/children cinfo))
534                 )
535            (goto-char (point-max))
536            (mime-view-insert-entity-button
537             rcnum cinfo ctype params subj encoding)
538            ))
539     (mime-view-entity-separator-function rcnum cinfo ctype params subj)
540     (setq ne (point-max))
541     (widen)
542     (put-text-property nb ne 'mime-view-raw-buffer ibuf)
543     (put-text-property nb ne 'mime-view-cinfo content)
544     (goto-char ne)
545     ))
546
547 (defun mime-preview/display-header (beg end)
548   (save-restriction
549     (narrow-to-region (point)(point))
550     (insert-buffer-substring mime::preview/article-buffer beg end)
551     (let ((f (cdr (assq mime::preview/original-major-mode
552                         mime-view-content-header-filter-alist))))
553       (if (functionp f)
554           (funcall f)
555         (mime-view-default-content-header-filter)
556         ))
557     (run-hooks 'mime-view-content-header-filter-hook)
558     ))
559
560 (defun mime-preview/display-body (beg end
561                                       rcnum cinfo ctype params subj encoding)
562   (save-restriction
563     (narrow-to-region (point-max)(point-max))
564     (insert-buffer-substring mime::preview/article-buffer beg end)
565     (let ((f (cdr (or (assoc ctype mime-view-content-filter-alist)
566                       (assq t mime-view-content-filter-alist)))))
567       (and (functionp f)
568            (funcall f ctype params encoding)
569            )
570       )))
571
572 (defun mime-preview/display-message/partial ()
573   (save-restriction
574     (goto-char (point-max))
575     (if (not (search-backward "\n\n" nil t))
576         (insert "\n")
577       )
578     (let ((be (point-max)))
579       (narrow-to-region be be)
580       (insert mime-view-announcement-for-message/partial)
581       (mime-add-button (point-min)(point-max)
582                        (function mime-view-play-current-entity))
583       )))
584
585 (defun mime-article/get-uu-filename (param &optional encoding)
586   (if (member (or encoding
587                   (cdr (assq 'encoding param))
588                   )
589               mime-view-uuencode-encoding-name-list)
590       (save-excursion
591         (or (if (re-search-forward "^begin [0-9]+ " nil t)
592                 (if (looking-at ".+$")
593                     (buffer-substring (match-beginning 0)(match-end 0))
594                   ))
595             ""))
596     ))
597
598 (defun mime-article/get-subject (param &optional encoding)
599   (or (std11-find-field-body '("Content-Description" "Subject"))
600       (let (ret)
601         (if (or (and (setq ret (mime/Content-Disposition))
602                      (setq ret (assoc "filename" (cdr ret)))
603                      )
604                 (setq ret (assoc "name" param))
605                 (setq ret (assoc "x-name" param))
606                 )
607             (std11-strip-quoted-string (cdr ret))
608           ))
609       (mime-article/get-uu-filename param encoding)
610       ""))
611
612
613 ;;; @ content information
614 ;;;
615
616 (defun mime-article/point-content-number (p &optional cinfo)
617   (or cinfo
618       (setq cinfo mime::article/content-info)
619       )
620   (let ((b (mime::content-info/point-min cinfo))
621         (e (mime::content-info/point-max cinfo))
622         (c (mime::content-info/children cinfo))
623         )
624     (if (and (<= b p)(<= p e))
625         (or (let (co ret (sn 0))
626               (catch 'tag
627                 (while c
628                   (setq co (car c))
629                   (setq ret (mime-article/point-content-number p co))
630                   (cond ((eq ret t) (throw 'tag (list sn)))
631                         (ret (throw 'tag (cons sn ret)))
632                         )
633                   (setq c (cdr c))
634                   (setq sn (1+ sn))
635                   )))
636             t))))
637
638 (defun mime-article/rcnum-to-cinfo (rcnum &optional cinfo)
639   (or cinfo
640       (setq cinfo mime::article/content-info)
641       )
642   (find-if (function
643             (lambda (ci)
644               (equal (mime::content-info/rcnum ci) rcnum)
645               ))
646            (mime/flatten-content-info cinfo)
647            ))
648
649 (defun mime-article/cnum-to-cinfo (cn &optional cinfo)
650   (or cinfo
651       (setq cinfo mime::article/content-info)
652       )
653   (if (eq cn t)
654       cinfo
655     (let ((sn (car cn)))
656       (if (null sn)
657           cinfo
658         (let ((rc (nth sn (mime::content-info/children cinfo))))
659           (if rc
660               (mime-article/cnum-to-cinfo (cdr cn) rc)
661             ))
662         ))))
663
664 (defun mime/flatten-content-info (&optional cinfo)
665   (or cinfo
666       (setq cinfo mime::article/content-info)
667       )
668   (let ((dest (list cinfo))
669         (rcl (mime::content-info/children cinfo))
670         )
671     (while rcl
672       (setq dest (nconc dest (mime/flatten-content-info (car rcl))))
673       (setq rcl (cdr rcl))
674       )
675     dest))
676
677
678 ;;; @ MIME viewer mode
679 ;;;
680
681 (defconst mime-view-menu-title "MIME-View")
682 (defconst mime-view-menu-list
683   '((up          "Move to upper content"      mime-view-move-to-upper)
684     (previous    "Move to previous content"   mime-view-move-to-previous)
685     (next        "Move to next content"       mime-view-move-to-next)
686     (scroll-down "Scroll to previous content" mime-view-scroll-down-entity)
687     (scroll-up   "Scroll to next content"     mime-view-scroll-up-entity)
688     (play        "Play Content"               mime-view-play-current-entity)
689     (extract     "Extract Content"            mime-view-extract-current-entity)
690     (print       "Print"                      mime-view-print-current-entity)
691     (x-face      "Show X Face"                mime-view-display-x-face)
692     )
693   "Menu for MIME Viewer")
694
695 (cond (running-xemacs
696        (defvar mime-view-xemacs-popup-menu
697          (cons mime-view-menu-title
698                (mapcar (function
699                         (lambda (item)
700                           (vector (nth 1 item)(nth 2 item) t)
701                           ))
702                        mime-view-menu-list)))
703        (defun mime-view-xemacs-popup-menu (event)
704          "Popup the menu in the MIME Viewer buffer"
705          (interactive "e")
706          (select-window (event-window event))
707          (set-buffer (event-buffer event))
708          (popup-menu 'mime-view-xemacs-popup-menu))
709        (defvar mouse-button-2 'button2)
710        )
711       (t
712        (defvar mouse-button-2 [mouse-2])
713        ))
714
715 (defun mime-view-define-keymap (&optional default)
716   (let ((mime-view-mode-map (if (keymapp default)
717                                 (copy-keymap default)
718                               (make-sparse-keymap)
719                               )))
720     (define-key mime-view-mode-map
721       "u"        (function mime-view-move-to-upper))
722     (define-key mime-view-mode-map
723       "p"        (function mime-view-move-to-previous))
724     (define-key mime-view-mode-map
725       "n"        (function mime-view-move-to-next))
726     (define-key mime-view-mode-map
727       "\e\t"     (function mime-view-move-to-previous))
728     (define-key mime-view-mode-map
729       "\t"       (function mime-view-move-to-next))
730     (define-key mime-view-mode-map
731       " "        (function mime-view-scroll-up-entity))
732     (define-key mime-view-mode-map
733       "\M- "     (function mime-view-scroll-down-entity))
734     (define-key mime-view-mode-map
735       "\177"     (function mime-view-scroll-down-entity))
736     (define-key mime-view-mode-map
737       "\C-m"     (function mime-view-next-line-content))
738     (define-key mime-view-mode-map
739       "\C-\M-m"  (function mime-view-previous-line-content))
740     (define-key mime-view-mode-map
741       "v"        (function mime-view-play-current-entity))
742     (define-key mime-view-mode-map
743       "e"        (function mime-view-extract-current-entity))
744     (define-key mime-view-mode-map
745       "\C-c\C-p" (function mime-view-print-current-entity))
746     (define-key mime-view-mode-map
747       "a"        (function mime-view-follow-current-entity))
748     (define-key mime-view-mode-map
749       "q"        (function mime-view-quit))
750     (define-key mime-view-mode-map
751       "h"        (function mime-view-show-summary))
752     (define-key mime-view-mode-map
753       "\C-c\C-x" (function mime-view-kill-buffer))
754     (define-key mime-view-mode-map
755       "<"        (function beginning-of-buffer))
756     (define-key mime-view-mode-map
757       ">"        (function end-of-buffer))
758     (define-key mime-view-mode-map
759       "?"        (function describe-mode))
760     (if (functionp default)
761         (setq mime-view-mode-map
762               (append mime-view-mode-map (list (cons t default)))
763               ))
764     (if mouse-button-2
765         (define-key mime-view-mode-map
766           mouse-button-2 (function mime-button-dispatcher))
767       )
768     (cond (running-xemacs
769            (define-key mime-view-mode-map
770              mouse-button-3 (function mime-view-xemacs-popup-menu))
771            )
772           ((>= emacs-major-version 19)
773            (define-key mime-view-mode-map [menu-bar mime-view]
774              (cons mime-view-menu-title
775                    (make-sparse-keymap mime-view-menu-title)))
776            (mapcar (function
777                     (lambda (item)
778                       (define-key mime-view-mode-map
779                         (vector 'menu-bar 'mime-view (car item))
780                         (cons (nth 1 item)(nth 2 item))
781                         )
782                       ))
783                    (reverse mime-view-menu-list)
784                    )
785            ))
786     (use-local-map mime-view-mode-map)
787     (run-hooks 'mime-view-define-keymap-hook)
788     ))
789
790 (defun mime-view-mode (&optional mother ctl encoding ibuf obuf
791                                  default-keymap-or-function)
792   "Major mode for viewing MIME message.
793
794 Here is a list of the standard keys for mime-view-mode.
795
796 key             feature
797 ---             -------
798
799 u               Move to upper content
800 p or M-TAB      Move to previous content
801 n or TAB        Move to next content
802 SPC             Scroll up or move to next content
803 M-SPC or DEL    Scroll down or move to previous content
804 RET             Move to next line
805 M-RET           Move to previous line
806 v               Decode current content as `play mode'
807 e               Decode current content as `extract mode'
808 C-c C-p         Decode current content as `print mode'
809 a               Followup to current content.
810 x               Display X-Face
811 q               Quit
812 button-2        Move to point under the mouse cursor
813                 and decode current content as `play mode'
814 "
815   (interactive)
816   (let ((buf (get-buffer mime/output-buffer-name)))
817     (if buf
818         (save-excursion
819           (set-buffer buf)
820           (erase-buffer)
821           )))
822   (let ((ret (mime-view-setup-buffer ctl encoding ibuf obuf))
823         (win-conf (current-window-configuration))
824         )
825     (prog1
826         (switch-to-buffer ret)
827       (setq mime::preview/original-window-configuration win-conf)
828       (if mother
829           (progn
830             (setq mime::preview/mother-buffer mother)
831             ))
832       (mime-view-define-keymap default-keymap-or-function)
833       (let ((point (next-single-property-change (point-min) 'mime-view-cinfo)))
834         (if point
835             (goto-char point)
836           (goto-char (point-min))
837           (search-forward "\n\n" nil t)
838           ))
839       (run-hooks 'mime-view-mode-hook)
840       )))
841
842 (autoload 'mime-view-play-current-entity "mime-play" "Play current entity." t)
843
844 (defun mime-view-extract-current-entity ()
845   "Extract current entity into file (maybe).
846 It decodes current entity to call internal or external method as
847 \"extract\" mode.  The method is selected from variable
848 `mime/content-decoding-condition'."
849   (interactive)
850   (mime-view-play-current-entity "extract")
851   )
852
853 (defun mime-view-print-current-entity ()
854   "Print current entity (maybe).
855 It decodes current entity to call internal or external method as
856 \"print\" mode.  The method is selected from variable
857 `mime/content-decoding-condition'."
858   (interactive)
859   (mime-view-play-current-entity "print")
860   )
861
862 (defun mime-view-follow-current-entity ()
863   "Write follow message to current entity.
864 It calls following-method selected from variable
865 `mime-view-following-method-alist'."
866   (interactive)
867   (let ((root-cinfo (get-text-property (point-min) 'mime-view-cinfo))
868         cinfo)
869     (while (null (setq cinfo (get-text-property (point) 'mime-view-cinfo)))
870       (backward-char)
871       )
872     (let* ((p-beg (previous-single-property-change (point) 'mime-view-cinfo))
873            p-end
874            (rcnum (mime::content-info/rcnum cinfo))
875            (len (length rcnum))
876            rc)
877       (cond ((null p-beg)
878              (setq p-beg
879                    (if (eq (next-single-property-change (point-min)
880                                                         'mime-view-cinfo)
881                            (point))
882                        (point)
883                      (point-min)))
884              )
885             ((eq (next-single-property-change p-beg 'mime-view-cinfo)
886                  (point))
887              (setq p-beg (point))
888              ))
889       (setq p-end (next-single-property-change p-beg 'mime-view-cinfo))
890       (cond ((null p-end)
891              (setq p-end (point-max))
892              )
893             ((null rcnum)
894              (setq p-end (point-max))
895              )
896             (t
897              (save-excursion
898                (goto-char p-end)
899                (catch 'tag
900                  (let (e)
901                    (while (setq e
902                                 (next-single-property-change
903                                  (point) 'mime-view-cinfo))
904                      (goto-char e)
905                      (let ((rc (mime::content-info/rcnum
906                                 (get-text-property (point)
907                                                    'mime-view-cinfo))))
908                        (or (equal rcnum (nthcdr (- (length rc) len) rc))
909                            (throw 'tag nil)
910                            ))
911                      (setq p-end e)
912                      ))
913                  (setq p-end (point-max))
914                  ))
915              ))
916       (let* ((mode (mime-preview/get-original-major-mode))
917              (new-name (format "%s-%s" (buffer-name) (reverse rcnum)))
918              new-buf
919              (the-buf (current-buffer))
920              (a-buf mime::preview/article-buffer)
921              fields)
922         (save-excursion
923           (set-buffer (setq new-buf (get-buffer-create new-name)))
924           (erase-buffer)
925           (insert-buffer-substring the-buf p-beg p-end)
926           (goto-char (point-min))
927           (if (mime-view-header-visible-p rcnum root-cinfo)
928               (delete-region (goto-char (point-min))
929                              (if (re-search-forward "^$" nil t)
930                                  (match-end 0)
931                                (point-min)))
932             )
933           (goto-char (point-min))
934           (insert "\n")
935           (goto-char (point-min))
936           (let ((rcnum (mime::content-info/rcnum cinfo)) ci str)
937             (while (progn
938                      (setq str
939                            (save-excursion
940                              (set-buffer a-buf)
941                              (setq ci (mime-article/rcnum-to-cinfo rcnum))
942                              (save-restriction
943                                (narrow-to-region
944                                 (mime::content-info/point-min ci)
945                                 (mime::content-info/point-max ci)
946                                 )
947                                (std11-header-string-except
948                                 (concat "^"
949                                         (apply (function regexp-or) fields)
950                                         ":") ""))))
951                      (if (string= (mime::content-info/type ci)
952                                   "message/rfc822")
953                          nil
954                        (if str
955                            (insert str)
956                          )
957                        rcnum))
958               (setq fields (std11-collect-field-names)
959                     rcnum (cdr rcnum))
960               )
961             )
962           (let ((rest mime-view-following-required-fields-list))
963             (while rest
964               (let ((field-name (car rest)))
965                 (or (std11-field-body field-name)
966                     (insert
967                      (format
968                       (concat field-name
969                               ": "
970                               (save-excursion
971                                 (set-buffer the-buf)
972                                 (set-buffer mime::preview/mother-buffer)
973                                 (set-buffer mime::preview/article-buffer)
974                                 (std11-field-body field-name)
975                                 )
976                               "\n")))
977                     ))
978               (setq rest (cdr rest))
979               ))
980           (eword-decode-header)
981           )
982         (let ((f (cdr (assq mode mime-view-following-method-alist))))
983           (if (functionp f)
984               (funcall f new-buf)
985             (message
986              (format
987               "Sorry, following method for %s is not implemented yet."
988               mode))
989             ))
990         ))))
991
992 (defun mime-view-display-x-face ()
993   (interactive)
994   (save-window-excursion
995     (set-buffer mime::preview/article-buffer)
996     (mime-view-x-face-function)
997     ))
998
999 (defun mime-view-move-to-upper ()
1000   "Move to upper entity.
1001 If there is no upper entity, call function `mime-view-quit'."
1002   (interactive)
1003   (let (cinfo)
1004     (while (null (setq cinfo (get-text-property (point) 'mime-view-cinfo)))
1005       (backward-char)
1006       )
1007     (let ((r (mime-article/rcnum-to-cinfo
1008               (cdr (mime::content-info/rcnum cinfo))
1009               (get-text-property 1 'mime-view-cinfo)))
1010           point)
1011       (catch 'tag
1012         (while (setq point (previous-single-property-change
1013                             (point) 'mime-view-cinfo))
1014           (goto-char point)
1015           (if (eq r (get-text-property (point) 'mime-view-cinfo))
1016               (throw 'tag t)
1017             )
1018           )
1019         (mime-view-quit)
1020         ))))
1021
1022 (defun mime-view-move-to-previous ()
1023   "Move to previous entity.
1024 If there is no previous entity, it calls function registered in
1025 variable `mime-view-over-to-previous-method-alist'."
1026   (interactive)
1027   (while (null (get-text-property (point) 'mime-view-cinfo))
1028     (backward-char)
1029     )
1030   (let ((point (previous-single-property-change (point) 'mime-view-cinfo)))
1031     (if point
1032         (goto-char point)
1033       (let ((f (assq mime::preview/original-major-mode
1034                      mime-view-over-to-previous-method-alist)))
1035         (if f
1036             (funcall (cdr f))
1037           ))
1038       )))
1039
1040 (defun mime-view-move-to-next ()
1041   "Move to next entity.
1042 If there is no previous entity, it calls function registered in
1043 variable `mime-view-over-to-next-method-alist'."
1044   (interactive)
1045   (let ((point (next-single-property-change (point) 'mime-view-cinfo)))
1046     (if point
1047         (goto-char point)
1048       (let ((f (assq mime::preview/original-major-mode
1049                      mime-view-over-to-next-method-alist)))
1050         (if f
1051             (funcall (cdr f))
1052           ))
1053       )))
1054
1055 (defun mime-view-scroll-up-entity (&optional h)
1056   "Scroll up current entity.
1057 If reached to (point-max), it calls function registered in variable
1058 `mime-view-over-to-next-method-alist'."
1059   (interactive)
1060   (or h
1061       (setq h (1- (window-height)))
1062       )
1063   (if (= (point) (point-max))
1064       (let ((f (assq mime::preview/original-major-mode
1065                      mime-view-over-to-next-method-alist)))
1066         (if f
1067             (funcall (cdr f))
1068           ))
1069     (let ((point
1070            (or (next-single-property-change (point) 'mime-view-cinfo)
1071                (point-max))))
1072       (forward-line h)
1073       (if (> (point) point)
1074           (goto-char point)
1075         )
1076       )))
1077
1078 (defun mime-view-scroll-down-entity (&optional h)
1079   "Scroll down current entity.
1080 If reached to (point-min), it calls function registered in variable
1081 `mime-view-over-to-previous-method-alist'."
1082   (interactive)
1083   (or h
1084       (setq h (1- (window-height)))
1085       )
1086   (if (= (point) (point-min))
1087       (let ((f (assq mime::preview/original-major-mode
1088                      mime-view-over-to-previous-method-alist)))
1089         (if f
1090             (funcall (cdr f))
1091           ))
1092     (let (point)
1093       (save-excursion
1094         (catch 'tag
1095           (while (> (point) 1)
1096             (if (setq point
1097                       (previous-single-property-change (point)
1098                                                        'mime-view-cinfo))
1099                 (throw 'tag t)
1100               )
1101             (backward-char)
1102             )
1103           (setq point (point-min))
1104           ))
1105       (forward-line (- h))
1106       (if (< (point) point)
1107           (goto-char point)
1108         ))))
1109
1110 (defun mime-view-next-line-content ()
1111   (interactive)
1112   (mime-view-scroll-up-entity 1)
1113   )
1114
1115 (defun mime-view-previous-line-content ()
1116   (interactive)
1117   (mime-view-scroll-down-entity 1)
1118   )
1119
1120 (defun mime-view-quit ()
1121   "Quit from MIME-View buffer.
1122 It calls function registered in variable
1123 `mime-view-quitting-method-alist'."
1124   (interactive)
1125   (let ((r (assq mime::preview/original-major-mode
1126                  mime-view-quitting-method-alist)))
1127     (if r
1128         (funcall (cdr r))
1129       )))
1130
1131 (defun mime-view-show-summary ()
1132   "Show summary.
1133 It calls function registered in variable
1134 `mime-view-show-summary-method'."
1135   (interactive)
1136   (let ((r (assq mime::preview/original-major-mode
1137                  mime-view-show-summary-method)))
1138     (if r
1139         (funcall (cdr r))
1140       )))
1141
1142 (defun mime-view-kill-buffer ()
1143   (interactive)
1144   (kill-buffer (current-buffer))
1145   )
1146
1147
1148 ;;; @ end
1149 ;;;
1150
1151 (provide 'mime-view)
1152
1153 (run-hooks 'mime-view-load-hook)
1154
1155 ;;; mime-view.el ends here