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