`mime-view-follow-content' -> `mime-view-follow-current-entity'.
[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.69 $
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.69 1997-03-18 08:24:07 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 (make-variable-buffer-local 'mime::article/content-info)
343
344 (defvar mime::article/preview-buffer nil)
345 (make-variable-buffer-local 'mime::article/preview-buffer)
346
347
348 ;;; @@@ in view buffer
349 ;;;
350
351 (make-variable-buffer-local 'mime::preview/mother-buffer)
352 (make-variable-buffer-local 'mime::preview/content-list)
353
354 (defvar mime::preview/article-buffer nil)
355 (make-variable-buffer-local 'mime::preview/article-buffer)
356
357 (make-variable-buffer-local 'mime::preview/original-major-mode)
358 (make-variable-buffer-local 'mime::preview/original-window-configuration)
359
360
361 ;;; @@ quitting method
362 ;;;
363
364 (defvar mime-view-quitting-method-alist
365   '((mime-show-message-mode
366      . mime-view-quitting-method-for-mime-show-message-mode))
367   "Alist of major-mode vs. quitting-method of mime-view.")
368
369 (defvar mime-view-over-to-previous-method-alist nil)
370 (defvar mime-view-over-to-next-method-alist nil)
371
372 (defvar mime-view-show-summary-method nil
373   "Alist of major-mode vs. show-summary-method.")
374
375
376 ;;; @@ following method
377 ;;;
378
379 (defvar mime-view-following-method-alist nil
380   "Alist of major-mode vs. following-method of mime-view.")
381
382 (defvar mime-view-following-required-fields-list
383   '("From"))
384
385
386 ;;; @@ X-Face
387 ;;;
388
389 ;; hack from Gnus 5.0.4.
390
391 (defvar mime-view-x-face-to-pbm-command
392   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm")
393
394 (defvar mime-view-x-face-command
395   (concat mime-view-x-face-to-pbm-command
396           " | xv -quit -")
397   "String to be executed to display an X-Face field.
398 The command will be executed in a sub-shell asynchronously.
399 The compressed face will be piped to this command.")
400
401 (defun mime-view-x-face-function ()
402   "Function to display X-Face field. You can redefine to customize."
403   ;; 1995/10/12 (c.f. tm-eng:130)
404   ;;    fixed by Eric Ding <ericding@San-Jose.ate.slb.com>
405   (save-restriction
406     (narrow-to-region (point-min) (re-search-forward "^$" nil t))
407     ;; end
408     (goto-char (point-min))
409     (if (re-search-forward "^X-Face:[ \t]*" nil t)
410         (let ((beg (match-end 0))
411               (end (std11-field-end))
412               )
413           (call-process-region beg end "sh" nil 0 nil
414                                "-c" mime-view-x-face-command)
415           ))))
416
417
418 ;;; @@ utility
419 ;;;
420
421 (defun mime-preview/get-original-major-mode ()
422   (if mime::preview/mother-buffer
423       (save-excursion
424         (set-buffer mime::preview/mother-buffer)
425         (mime-preview/get-original-major-mode)
426         )
427     mime::preview/original-major-mode))
428
429
430 ;;; @ data structures
431 ;;;
432
433 ;;; @@ preview-content-info
434 ;;;
435
436 (define-structure mime::preview-content-info
437   point-min point-max buffer content-info)
438
439
440 ;;; @ buffer setup
441 ;;;
442
443 (defun mime-view-setup-buffer (&optional ctl encoding ibuf obuf)
444   (if ibuf
445       (progn
446         (get-buffer ibuf)
447         (set-buffer ibuf)
448         ))
449   (or mime-view-redisplay
450       (setq mime::article/content-info (mime-parse-message ctl encoding))
451       )
452   (let ((ret (mime-view-make-preview-buffer obuf)))
453     (setq mime::article/preview-buffer (car ret))
454     ret))
455
456 (defun mime-view-make-preview-buffer (&optional obuf)
457   (let* ((cinfo mime::article/content-info)
458          (pcl (mime/flatten-content-info cinfo))
459          (dest (make-list (length pcl) nil))
460          (the-buf (current-buffer))
461          (mode major-mode)
462          )
463     (or obuf
464         (setq obuf (concat "*Preview-" (buffer-name the-buf) "*")))
465     (set-buffer (get-buffer-create obuf))
466     (setq buffer-read-only nil)
467     (widen)
468     (erase-buffer)
469     (setq mime::preview/article-buffer the-buf)
470     (setq mime::preview/original-major-mode mode)
471     (setq major-mode 'mime-view-mode)
472     (setq mode-name "MIME-View")
473     (let ((drest dest))
474       (while pcl
475         (setcar drest
476                 (mime-view-display-entity (car pcl) cinfo the-buf obuf))
477         (setq pcl (cdr pcl)
478               drest (cdr drest))
479         ))
480     (set-buffer-modified-p nil)
481     (setq buffer-read-only t)
482     (set-buffer the-buf)
483     (list obuf dest)
484     ))
485
486 (defun mime-view-display-entity (content cinfo ibuf obuf)
487   "Display entity from content-info CONTENT."
488   (let* ((beg (mime::content-info/point-min content))
489          (end (mime::content-info/point-max content))
490          (ctype (mime::content-info/type content))
491          (params (mime::content-info/parameters content))
492          (encoding (mime::content-info/encoding content))
493          (rcnum (mime::content-info/rcnum content))
494          he e nb ne subj)
495     (set-buffer ibuf)
496     (goto-char beg)
497     (setq he (if (re-search-forward "^$" nil t)
498                  (1+ (match-end 0))
499                end))
500     (if (> he end)
501         (setq he end)
502       )
503     (save-restriction
504       (narrow-to-region beg end)
505       (setq subj
506             (eword-decode-string
507              (mime-article/get-subject params encoding)))
508       )
509     (set-buffer obuf)
510     (setq nb (point))
511     (narrow-to-region nb nb)
512     (mime-view-entity-button-function rcnum cinfo ctype params subj encoding)
513     (if (mime-view-header-visible-p rcnum cinfo)
514         (mime-preview/display-header beg he)
515       )
516     (if (and (null rcnum)
517              (member
518               ctype mime-view-content-button-visible-ctype-list))
519         (save-excursion
520           (goto-char (point-max))
521           (mime-view-insert-entity-button
522            rcnum cinfo ctype params subj encoding)
523           ))
524     (cond ((mime-view-body-visible-p rcnum cinfo ctype)
525            (mime-preview/display-body he end
526                                       rcnum cinfo ctype params subj encoding)
527            )
528           ((equal ctype "message/partial")
529            (mime-preview/display-message/partial)
530            )
531           ((and (null rcnum)
532                 (null (mime::content-info/children cinfo))
533                 )
534            (goto-char (point-max))
535            (mime-view-insert-entity-button
536             rcnum cinfo ctype params subj encoding)
537            ))
538     (mime-view-entity-separator-function rcnum cinfo ctype params subj)
539     (prog1
540         (progn
541           (setq ne (point-max))
542           (widen)
543           (put-text-property nb ne 'mime-view-raw-buffer ibuf)
544           (put-text-property nb ne 'mime-view-cinfo content)
545           (mime::preview-content-info/create nb (1- ne) ibuf content)
546           )
547       (goto-char ne)
548       )))
549
550 (defun mime-preview/display-header (beg end)
551   (save-restriction
552     (narrow-to-region (point)(point))
553     (insert-buffer-substring mime::preview/article-buffer beg end)
554     (let ((f (cdr (assq mime::preview/original-major-mode
555                         mime-view-content-header-filter-alist))))
556       (if (functionp f)
557           (funcall f)
558         (mime-view-default-content-header-filter)
559         ))
560     (run-hooks 'mime-view-content-header-filter-hook)
561     ))
562
563 (defun mime-preview/display-body (beg end
564                                       rcnum cinfo ctype params subj encoding)
565   (save-restriction
566     (narrow-to-region (point-max)(point-max))
567     (insert-buffer-substring mime::preview/article-buffer beg end)
568     (let ((f (cdr (or (assoc ctype mime-view-content-filter-alist)
569                       (assq t mime-view-content-filter-alist)))))
570       (and (functionp f)
571            (funcall f ctype params encoding)
572            )
573       )))
574
575 (defun mime-preview/display-message/partial ()
576   (save-restriction
577     (goto-char (point-max))
578     (if (not (search-backward "\n\n" nil t))
579         (insert "\n")
580       )
581     (let ((be (point-max)))
582       (narrow-to-region be be)
583       (insert mime-view-announcement-for-message/partial)
584       (mime-add-button (point-min)(point-max)
585                        (function mime-view-play-current-entity))
586       )))
587
588 (defun mime-article/get-uu-filename (param &optional encoding)
589   (if (member (or encoding
590                   (cdr (assq 'encoding param))
591                   )
592               mime-view-uuencode-encoding-name-list)
593       (save-excursion
594         (or (if (re-search-forward "^begin [0-9]+ " nil t)
595                 (if (looking-at ".+$")
596                     (buffer-substring (match-beginning 0)(match-end 0))
597                   ))
598             ""))
599     ))
600
601 (defun mime-article/get-subject (param &optional encoding)
602   (or (std11-find-field-body '("Content-Description" "Subject"))
603       (let (ret)
604         (if (or (and (setq ret (mime/Content-Disposition))
605                      (setq ret (assoc "filename" (cdr ret)))
606                      )
607                 (setq ret (assoc "name" param))
608                 (setq ret (assoc "x-name" param))
609                 )
610             (std11-strip-quoted-string (cdr ret))
611           ))
612       (mime-article/get-uu-filename param encoding)
613       ""))
614
615
616 ;;; @ content information
617 ;;;
618
619 (defun mime-article/point-content-number (p &optional cinfo)
620   (or cinfo
621       (setq cinfo mime::article/content-info)
622       )
623   (let ((b (mime::content-info/point-min cinfo))
624         (e (mime::content-info/point-max cinfo))
625         (c (mime::content-info/children cinfo))
626         )
627     (if (and (<= b p)(<= p e))
628         (or (let (co ret (sn 0))
629               (catch 'tag
630                 (while c
631                   (setq co (car c))
632                   (setq ret (mime-article/point-content-number p co))
633                   (cond ((eq ret t) (throw 'tag (list sn)))
634                         (ret (throw 'tag (cons sn ret)))
635                         )
636                   (setq c (cdr c))
637                   (setq sn (1+ sn))
638                   )))
639             t))))
640
641 (defun mime-article/rcnum-to-cinfo (rcnum &optional cinfo)
642   (or cinfo
643       (setq cinfo mime::article/content-info)
644       )
645   (find-if (function
646             (lambda (ci)
647               (equal (mime::content-info/rcnum ci) rcnum)
648               ))
649            (mime/flatten-content-info cinfo)
650            ))
651
652 (defun mime-article/cnum-to-cinfo (cn &optional cinfo)
653   (or cinfo
654       (setq cinfo mime::article/content-info)
655       )
656   (if (eq cn t)
657       cinfo
658     (let ((sn (car cn)))
659       (if (null sn)
660           cinfo
661         (let ((rc (nth sn (mime::content-info/children cinfo))))
662           (if rc
663               (mime-article/cnum-to-cinfo (cdr cn) rc)
664             ))
665         ))))
666
667 (defun mime/flatten-content-info (&optional cinfo)
668   (or cinfo
669       (setq cinfo mime::article/content-info)
670       )
671   (let ((dest (list cinfo))
672         (rcl (mime::content-info/children cinfo))
673         )
674     (while rcl
675       (setq dest (nconc dest (mime/flatten-content-info (car rcl))))
676       (setq rcl (cdr rcl))
677       )
678     dest))
679
680
681 ;;; @ MIME viewer mode
682 ;;;
683
684 (defconst mime-view-menu-title "MIME-View")
685 (defconst mime-view-menu-list
686   '((up          "Move to upper content"      mime-view-move-to-upper)
687     (previous    "Move to previous content"   mime-view-move-to-previous)
688     (next        "Move to next content"       mime-view-move-to-next)
689     (scroll-down "Scroll to previous content" mime-view-scroll-down-entity)
690     (scroll-up   "Scroll to next content"     mime-view-scroll-up-entity)
691     (play        "Play Content"               mime-view-play-current-entity)
692     (extract     "Extract Content"            mime-view-extract-current-entity)
693     (print       "Print"                      mime-view-print-current-entity)
694     (x-face      "Show X Face"                mime-view-display-x-face)
695     )
696   "Menu for MIME Viewer")
697
698 (cond (running-xemacs
699        (defvar mime-view-xemacs-popup-menu
700          (cons mime-view-menu-title
701                (mapcar (function
702                         (lambda (item)
703                           (vector (nth 1 item)(nth 2 item) t)
704                           ))
705                        mime-view-menu-list)))
706        (defun mime-view-xemacs-popup-menu (event)
707          "Popup the menu in the MIME Viewer buffer"
708          (interactive "e")
709          (select-window (event-window event))
710          (set-buffer (event-buffer event))
711          (popup-menu 'mime-view-xemacs-popup-menu))
712        (defvar mouse-button-2 'button2)
713        )
714       (t
715        (defvar mouse-button-2 [mouse-2])
716        ))
717
718 (defun mime-view-define-keymap (&optional default)
719   (let ((mime-view-mode-map (if (keymapp default)
720                                 (copy-keymap default)
721                               (make-sparse-keymap)
722                               )))
723     (define-key mime-view-mode-map
724       "u"        (function mime-view-move-to-upper))
725     (define-key mime-view-mode-map
726       "p"        (function mime-view-move-to-previous))
727     (define-key mime-view-mode-map
728       "n"        (function mime-view-move-to-next))
729     (define-key mime-view-mode-map
730       "\e\t"     (function mime-view-move-to-previous))
731     (define-key mime-view-mode-map
732       "\t"       (function mime-view-move-to-next))
733     (define-key mime-view-mode-map
734       " "        (function mime-view-scroll-up-entity))
735     (define-key mime-view-mode-map
736       "\M- "     (function mime-view-scroll-down-entity))
737     (define-key mime-view-mode-map
738       "\177"     (function mime-view-scroll-down-entity))
739     (define-key mime-view-mode-map
740       "\C-m"     (function mime-view-next-line-content))
741     (define-key mime-view-mode-map
742       "\C-\M-m"  (function mime-view-previous-line-content))
743     (define-key mime-view-mode-map
744       "v"        (function mime-view-play-current-entity))
745     (define-key mime-view-mode-map
746       "e"        (function mime-view-extract-current-entity))
747     (define-key mime-view-mode-map
748       "\C-c\C-p" (function mime-view-print-current-entity))
749     (define-key mime-view-mode-map
750       "a"        (function mime-view-follow-current-entity))
751     (define-key mime-view-mode-map
752       "q"        (function mime-view-quit))
753     (define-key mime-view-mode-map
754       "h"        (function mime-view-show-summary))
755     (define-key mime-view-mode-map
756       "\C-c\C-x" (function mime-view-kill-buffer))
757     (define-key mime-view-mode-map
758       "<"        (function beginning-of-buffer))
759     (define-key mime-view-mode-map
760       ">"        (function end-of-buffer))
761     (define-key mime-view-mode-map
762       "?"        (function describe-mode))
763     (if (functionp default)
764         (setq mime-view-mode-map
765               (append mime-view-mode-map (list (cons t default)))
766               ))
767     (if mouse-button-2
768         (define-key mime-view-mode-map
769           mouse-button-2 (function mime-button-dispatcher))
770       )
771     (cond (running-xemacs
772            (define-key mime-view-mode-map
773              mouse-button-3 (function mime-view-xemacs-popup-menu))
774            )
775           ((>= emacs-major-version 19)
776            (define-key mime-view-mode-map [menu-bar mime-view]
777              (cons mime-view-menu-title
778                    (make-sparse-keymap mime-view-menu-title)))
779            (mapcar (function
780                     (lambda (item)
781                       (define-key mime-view-mode-map
782                         (vector 'menu-bar 'mime-view (car item))
783                         (cons (nth 1 item)(nth 2 item))
784                         )
785                       ))
786                    (reverse mime-view-menu-list)
787                    )
788            ))
789     (use-local-map mime-view-mode-map)
790     (run-hooks 'mime-view-define-keymap-hook)
791     ))
792
793 (defun mime-view-mode (&optional mother ctl encoding ibuf obuf
794                                  default-keymap-or-function)
795   "Major mode for viewing MIME message.
796
797 Here is a list of the standard keys for mime-view-mode.
798
799 key             feature
800 ---             -------
801
802 u               Move to upper content
803 p or M-TAB      Move to previous content
804 n or TAB        Move to next content
805 SPC             Scroll up or move to next content
806 M-SPC or DEL    Scroll down or move to previous content
807 RET             Move to next line
808 M-RET           Move to previous line
809 v               Decode current content as `play mode'
810 e               Decode current content as `extract mode'
811 C-c C-p         Decode current content as `print mode'
812 a               Followup to current content.
813 x               Display X-Face
814 q               Quit
815 button-2        Move to point under the mouse cursor
816                 and decode current content as `play mode'
817 "
818   (interactive)
819   (let ((buf (get-buffer mime/output-buffer-name)))
820     (if buf
821         (save-excursion
822           (set-buffer buf)
823           (erase-buffer)
824           )))
825   (let ((ret (mime-view-setup-buffer ctl encoding ibuf obuf))
826         (win-conf (current-window-configuration))
827         )
828     (prog1
829         (switch-to-buffer (car ret))
830       (setq mime::preview/original-window-configuration win-conf)
831       (if mother
832           (progn
833             (setq mime::preview/mother-buffer mother)
834             ))
835       (mime-view-define-keymap default-keymap-or-function)
836       (setq mime::preview/content-list (nth 1 ret))
837       (let ((point (next-single-property-change (point-min) 'mime-view-cinfo)))
838         (if point
839             (goto-char point)
840           (goto-char (point-min))
841           (search-forward "\n\n" nil t)
842           ))
843       (run-hooks 'mime-view-mode-hook)
844       )))
845
846 (autoload 'mime-view-play-current-entity "mime-play" "Play current entity." t)
847
848 (defun mime-view-extract-current-entity ()
849   "Extract current entity into file (maybe).
850 It decodes current entity to call internal or external method as
851 \"extract\" mode.  The method is selected from variable
852 `mime/content-decoding-condition'."
853   (interactive)
854   (mime-view-play-current-entity "extract")
855   )
856
857 (defun mime-view-print-current-entity ()
858   "Print current entity (maybe).
859 It decodes current entity to call internal or external method as
860 \"print\" mode.  The method is selected from variable
861 `mime/content-decoding-condition'."
862   (interactive)
863   (mime-view-play-current-entity "print")
864   )
865
866 (defun mime-view-follow-current-entity ()
867   "Write follow message to current entity.
868 It calls following-method selected from variable
869 `mime-view-following-method-alist'."
870   (interactive)
871   (let ((root-cinfo
872          (mime::preview-content-info/content-info
873           (car mime::preview/content-list)))
874         pc p-beg p-end cinfo rcnum)
875     (let ((rest mime::preview/content-list)
876           b e cell len rc)
877       (if (catch 'tag
878             (while (setq cell (car rest))
879               (setq b (mime::preview-content-info/point-min cell)
880                     e (mime::preview-content-info/point-max cell))
881               (setq rest (cdr rest))
882               (if (and (<= b (point))(<= (point) e))
883                   (throw 'tag cell)
884                 )
885               ))
886           (progn
887             (setq pc cell
888                   cinfo (mime::preview-content-info/content-info pc)
889                   rcnum (mime::content-info/rcnum cinfo))
890             (setq len (length rcnum))
891             (setq p-beg (mime::preview-content-info/point-min pc)
892                   p-end (mime::preview-content-info/point-max pc))
893             (while (and (setq cell (car rest))
894                         (progn
895                           (setq rc
896                                 (mime::content-info/rcnum
897                                  (mime::preview-content-info/content-info
898                                   cell)))
899                           (equal rcnum
900                                  (nthcdr (- (length rc) len) rc))
901                           ))
902               (setq p-end (mime::preview-content-info/point-max cell))
903               (setq rest (cdr rest))
904               ))))
905     (if pc
906         (let* ((mode (mime-preview/get-original-major-mode))
907                (new-name (format "%s-%s" (buffer-name) (reverse rcnum)))
908                new-buf
909                (the-buf (current-buffer))
910                (a-buf mime::preview/article-buffer)
911                fields)
912           (save-excursion
913             (set-buffer (setq new-buf (get-buffer-create new-name)))
914             (erase-buffer)
915             (insert-buffer-substring the-buf p-beg p-end)
916             (goto-char (point-min))
917             (if (mime-view-header-visible-p rcnum root-cinfo)
918                 (delete-region (goto-char (point-min))
919                                (if (re-search-forward "^$" nil t)
920                                    (match-end 0)
921                                  (point-min)))
922               )
923             (goto-char (point-min))
924             (insert "\n")
925             (goto-char (point-min))
926             (let ((rcnum (mime::content-info/rcnum cinfo)) ci str)
927               (while (progn
928                        (setq str
929                              (save-excursion
930                                (set-buffer a-buf)
931                                (setq ci (mime-article/rcnum-to-cinfo rcnum))
932                                (save-restriction
933                                  (narrow-to-region
934                                   (mime::content-info/point-min ci)
935                                   (mime::content-info/point-max ci)
936                                   )
937                                  (std11-header-string-except
938                                   (concat "^"
939                                           (apply (function regexp-or) fields)
940                                           ":") ""))))
941                        (if (string= (mime::content-info/type ci)
942                                     "message/rfc822")
943                            nil
944                          (if str
945                              (insert str)
946                            )
947                          rcnum))
948                 (setq fields (std11-collect-field-names)
949                       rcnum (cdr rcnum))
950                 )
951               )
952             (let ((rest mime-view-following-required-fields-list))
953               (while rest
954                 (let ((field-name (car rest)))
955                   (or (std11-field-body field-name)
956                       (insert
957                        (format
958                         (concat field-name
959                                 ": "
960                                 (save-excursion
961                                   (set-buffer the-buf)
962                                   (set-buffer mime::preview/mother-buffer)
963                                   (set-buffer mime::preview/article-buffer)
964                                   (std11-field-body field-name)
965                                   )
966                                 "\n")))
967                       ))
968                 (setq rest (cdr rest))
969                 ))
970             (eword-decode-header)
971             )
972           (let ((f (cdr (assq mode mime-view-following-method-alist))))
973             (if (functionp f)
974                 (funcall f new-buf)
975               (message
976                (format
977                 "Sorry, following method for %s is not implemented yet."
978                 mode))
979               ))
980           ))))
981
982 (defun mime-view-display-x-face ()
983   (interactive)
984   (save-window-excursion
985     (set-buffer mime::preview/article-buffer)
986     (mime-view-x-face-function)
987     ))
988
989 (defun mime-view-move-to-upper ()
990   "Move to upper entity.
991 If there is no upper entity, call function `mime-view-quit'."
992   (interactive)
993   (let (cinfo)
994     (while (null (setq cinfo (get-text-property (point) 'mime-view-cinfo)))
995       (backward-char)
996       )
997     (let ((r (mime-article/rcnum-to-cinfo
998               (cdr (mime::content-info/rcnum cinfo))
999               (get-text-property 1 'mime-view-cinfo)))
1000           point)
1001       (catch 'tag
1002         (while (setq point (previous-single-property-change
1003                             (point) 'mime-view-cinfo))
1004           (goto-char point)
1005           (if (eq r (get-text-property (point) 'mime-view-cinfo))
1006               (throw 'tag t)
1007             )
1008           )
1009         (mime-view-quit)
1010         ))))
1011
1012 (defun mime-view-move-to-previous ()
1013   "Move to previous entity.
1014 If there is no previous entity, it calls function registered in
1015 variable `mime-view-over-to-previous-method-alist'."
1016   (interactive)
1017   (while (null (get-text-property (point) 'mime-view-cinfo))
1018     (backward-char)
1019     )
1020   (let ((point (previous-single-property-change (point) 'mime-view-cinfo)))
1021     (if point
1022         (goto-char point)
1023       (let ((f (assq mime::preview/original-major-mode
1024                      mime-view-over-to-previous-method-alist)))
1025         (if f
1026             (funcall (cdr f))
1027           ))
1028       )))
1029
1030 (defun mime-view-move-to-next ()
1031   "Move to next entity.
1032 If there is no previous entity, it calls function registered in
1033 variable `mime-view-over-to-next-method-alist'."
1034   (interactive)
1035   (let ((point (next-single-property-change (point) 'mime-view-cinfo)))
1036     (if point
1037         (goto-char point)
1038       (let ((f (assq mime::preview/original-major-mode
1039                      mime-view-over-to-next-method-alist)))
1040         (if f
1041             (funcall (cdr f))
1042           ))
1043       )))
1044
1045 (defun mime-view-scroll-up-entity (&optional h)
1046   "Scroll up current entity.
1047 If reached to (point-max), it calls function registered in variable
1048 `mime-view-over-to-next-method-alist'."
1049   (interactive)
1050   (or h
1051       (setq h (1- (window-height)))
1052       )
1053   (if (= (point) (point-max))
1054       (let ((f (assq mime::preview/original-major-mode
1055                      mime-view-over-to-next-method-alist)))
1056         (if f
1057             (funcall (cdr f))
1058           ))
1059     (let ((point
1060            (or (next-single-property-change (point) 'mime-view-cinfo)
1061                (point-max))))
1062       (forward-line h)
1063       (if (> (point) point)
1064           (goto-char point)
1065         )
1066       )))
1067
1068 (defun mime-view-scroll-down-entity (&optional h)
1069   "Scroll down current entity.
1070 If reached to (point-min), it calls function registered in variable
1071 `mime-view-over-to-previous-method-alist'."
1072   (interactive)
1073   (or h
1074       (setq h (1- (window-height)))
1075       )
1076   (if (= (point) (point-min))
1077       (let ((f (assq mime::preview/original-major-mode
1078                      mime-view-over-to-previous-method-alist)))
1079         (if f
1080             (funcall (cdr f))
1081           ))
1082     (let (point)
1083       (save-excursion
1084         (catch 'tag
1085           (while (> (point) 1)
1086             (if (setq point
1087                       (previous-single-property-change (point)
1088                                                        'mime-view-cinfo))
1089                 (throw 'tag t)
1090               )
1091             (backward-char)
1092             )
1093           (setq point (point-min))
1094           ))
1095       (forward-line (- h))
1096       (if (< (point) point)
1097           (goto-char point)
1098         ))))
1099
1100 (defun mime-view-next-line-content ()
1101   (interactive)
1102   (mime-view-scroll-up-entity 1)
1103   )
1104
1105 (defun mime-view-previous-line-content ()
1106   (interactive)
1107   (mime-view-scroll-down-entity 1)
1108   )
1109
1110 (defun mime-view-quit ()
1111   "Quit from MIME-View buffer.
1112 It calls function registered in variable
1113 `mime-view-quitting-method-alist'."
1114   (interactive)
1115   (let ((r (assq mime::preview/original-major-mode
1116                  mime-view-quitting-method-alist)))
1117     (if r
1118         (funcall (cdr r))
1119       )))
1120
1121 (defun mime-view-show-summary ()
1122   "Show summary.
1123 It calls function registered in variable
1124 `mime-view-show-summary-method'."
1125   (interactive)
1126   (let ((r (assq mime::preview/original-major-mode
1127                  mime-view-show-summary-method)))
1128     (if r
1129         (funcall (cdr r))
1130       )))
1131
1132 (defun mime-view-kill-buffer ()
1133   (interactive)
1134   (kill-buffer (current-buffer))
1135   )
1136
1137
1138 ;;; @ end
1139 ;;;
1140
1141 (provide 'mime-view)
1142
1143 (run-hooks 'mime-view-load-hook)
1144
1145 ;;; mime-view.el ends here