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