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