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