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