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