Sync with semi-1_14
[elisp/semi.git] / mime-view.el
1 ;;; mime-view.el --- interactive MIME viewer for GNU Emacs
2
3 ;; Copyright (C) 1995,96,97,98,99,2000 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <tomo@m17n.org>
6 ;; Created: 1994/07/13
7 ;;      Renamed: 1994/08/31 from tm-body.el
8 ;;      Renamed: 1997/02/19 from tm-view.el
9 ;; Keywords: MIME, multimedia, mail, news
10
11 ;; This file is part of SEMI (Sample of Elastic MIME Interfaces).
12
13 ;; This program is free software; you can redistribute it and/or
14 ;; modify it under the terms of the GNU General Public License as
15 ;; published by the Free Software Foundation; either version 2, or (at
16 ;; your option) any later version.
17
18 ;; This program is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 ;; General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Code:
29
30 (require 'mime)
31 (require 'semi-def)
32 (require 'calist)
33 (require 'alist)
34 (require 'mime-conf)
35 (require 'mcharset)
36
37 (eval-when-compile (require 'static))
38
39
40 ;;; @ version
41 ;;;
42
43 (defconst mime-view-version
44   (concat (mime-product-name mime-user-interface-product) " MIME-View "
45           (mapconcat #'number-to-string
46                      (mime-product-version mime-user-interface-product) ".")
47           " (" (mime-product-code-name mime-user-interface-product) ")"))
48
49
50 ;;; @ variables
51 ;;;
52
53 (defgroup mime-view nil
54   "MIME view mode"
55   :group 'mime)
56
57 (defcustom mime-situation-examples-file "~/.mime-example"
58   "*File name of situation-examples demonstrated by user."
59   :group 'mime-view
60   :type 'file)
61
62 (defcustom mime-preview-move-scroll nil
63   "*Decides whether to scroll when moving to next entity.
64 When t, scroll the buffer. Non-nil but not t means scroll when
65 the next entity is within next-screen-context-lines from top or
66 buttom. Nil means don't scroll at all."
67   :group 'mime-view
68   :type '(choice (const :tag "Off" nil)
69                  (const :tag "On" t)
70                  (sexp :tag "Situation" 1)))
71
72 (defcustom mime-view-mailcap-files
73   (let ((files '("/etc/mailcap" "/usr/etc/mailcap" "~/.mailcap")))
74     (or (member mime-mailcap-file files)
75         (setq files (cons mime-mailcap-file files)))
76     files)
77   "List of mailcap files."
78   :group 'mime-view
79   :type '(repeat file))
80
81 (defvar mime-view-automatic-conversion 'undecided)
82
83
84 ;;; @ in raw-buffer (representation space)
85 ;;;
86
87 (defvar mime-preview-buffer nil
88   "MIME-preview buffer corresponding with the (raw) buffer.")
89 (make-variable-buffer-local 'mime-preview-buffer)
90
91
92 (defvar mime-raw-representation-type-alist
93   '((mime-show-message-mode     . binary)
94     (mime-temp-message-mode     . binary)
95     (t                          . cooked))
96   "Alist of major-mode vs. representation-type of mime-raw-buffer.
97 Each element looks like (SYMBOL . REPRESENTATION-TYPE).  SYMBOL is
98 major-mode or t.  t means default.  REPRESENTATION-TYPE must be
99 `binary' or `cooked'.")
100
101
102 ;;; @ in preview-buffer (presentation space)
103 ;;;
104
105 (defvar mime-mother-buffer nil
106   "Mother buffer corresponding with the (MIME-preview) buffer.
107 If current MIME-preview buffer is generated by other buffer, such as
108 message/partial, it is called `mother-buffer'.")
109 (make-variable-buffer-local 'mime-mother-buffer)
110
111 ;; (defvar mime-raw-buffer nil
112 ;;   "Raw buffer corresponding with the (MIME-preview) buffer.")
113 ;; (make-variable-buffer-local 'mime-raw-buffer)
114
115 (defvar mime-preview-original-window-configuration nil
116   "Window-configuration before mime-view-mode is called.")
117 (make-variable-buffer-local 'mime-preview-original-window-configuration)
118
119 (defun mime-preview-original-major-mode (&optional recursive point)
120   "Return major-mode of original buffer.
121 If optional argument RECURSIVE is non-nil and current buffer has
122 mime-mother-buffer, it returns original major-mode of the
123 mother-buffer."
124   (if (and recursive mime-mother-buffer)
125       (save-excursion
126         (set-buffer mime-mother-buffer)
127         (mime-preview-original-major-mode recursive))
128     (cdr (assq 'major-mode
129                (get-text-property (or point
130                                       (if (> (point) (buffer-size))
131                                           (max (1- (point-max)) (point-min))
132                                         (point)))
133                                   'mime-view-situation)))))
134
135
136 ;;; @ entity information
137 ;;;
138
139 (defun mime-entity-situation (entity &optional situation)
140   "Return situation of ENTITY."
141   (let (rest param name)
142     ;; Content-Type
143     (unless (assq 'type situation)
144       (setq rest (or (mime-entity-content-type entity)
145                      (make-mime-content-type 'text 'plain))
146             situation (cons (car rest) situation)
147             rest (cdr rest)))
148     (unless (assq 'subtype situation)
149       (or rest
150           (setq rest (or (cdr (mime-entity-content-type entity))
151                          '((subtype . plain)))))
152       (setq situation (cons (car rest) situation)
153             rest (cdr rest)))
154     (while rest
155       (setq param (car rest))
156       (or (assoc (car param) situation)
157           (setq situation (cons param situation)))
158       (setq rest (cdr rest)))
159     
160     ;; Content-Disposition
161     (setq rest nil)
162     (unless (assq 'disposition-type situation)
163       (setq rest (mime-entity-content-disposition entity))
164       (if rest
165           (setq situation (cons (cons 'disposition-type
166                                       (mime-content-disposition-type rest))
167                                 situation)
168                 rest (mime-content-disposition-parameters rest))))
169     (while rest
170       (setq param (car rest)
171             name (car param))
172       (if (cond ((string= name "filename")
173                  (if (assq 'filename situation)
174                      nil
175                    (setq name 'filename)))
176                 ((string= name "creation-date")
177                  (if (assq 'creation-date situation)
178                      nil
179                    (setq name 'creation-date)))
180                 ((string= name "modification-date")
181                  (if (assq 'modification-date situation)
182                      nil
183                    (setq name 'modification-date)))
184                 ((string= name "read-date")
185                  (if (assq 'read-date situation)
186                      nil
187                    (setq name 'read-date)))
188                 ((string= name "size")
189                  (if (assq 'size situation)
190                      nil
191                    (setq name 'size)))
192                 (t (setq name (cons 'disposition name))
193                    (if (assoc name situation)
194                        nil
195                      name)))
196           (setq situation
197                 (cons (cons name (cdr param))
198                       situation)))
199       (setq rest (cdr rest)))
200     
201     ;; Content-Transfer-Encoding
202     (or (assq 'encoding situation)
203         (setq situation
204               (cons (cons 'encoding (or (mime-entity-encoding entity)
205                                         "7bit"))
206                     situation)))
207     
208     situation))
209
210 (defsubst mime-delq-null-situation (situations field
211                                                &rest ignored-values)
212   (let (dest)
213     (while situations
214       (let* ((situation (car situations))
215              (cell (assq field situation)))
216         (if cell
217             (or (memq (cdr cell) ignored-values)
218                 (setq dest (cons situation dest)))))
219       (setq situations (cdr situations)))
220     dest))
221
222 (defun mime-compare-situation-with-example (situation example)
223   (let ((example (copy-alist example))
224         (match 0))
225     (while situation
226       (let* ((cell (car situation))
227              (key (car cell))
228              (ecell (assoc key example)))
229         (when ecell
230           (if (equal cell ecell)
231               (setq match (1+ match))
232             (setq example (delq ecell example)))))
233       (setq situation (cdr situation)))
234     (cons match example)))
235
236 (defun mime-sort-situation (situation)
237   (sort situation
238         #'(lambda (a b)
239             (let ((a-t (car a))
240                   (b-t (car b))
241                   (order '((type . 1)
242                            (subtype . 2)
243                            (mode . 3)
244                            (method . 4)
245                            (major-mode . 5)
246                            (disposition-type . 6)))
247                   a-order b-order)
248               (if (symbolp a-t)
249                   (let ((ret (assq a-t order)))
250                     (if ret
251                         (setq a-order (cdr ret))
252                       (setq a-order 7)))
253                 (setq a-order 8))
254               (if (symbolp b-t)
255                   (let ((ret (assq b-t order)))
256                     (if ret
257                         (setq b-order (cdr ret))
258                       (setq b-order 7)))
259                 (setq b-order 8))
260               (if (= a-order b-order)
261                   (string< (format "%s" a-t)(format "%s" b-t))
262                 (< a-order b-order))))))
263
264 (defun mime-unify-situations (entity-situation
265                               condition situation-examples
266                               &optional required-name ignored-value
267                               every-situations)
268   (let (ret)
269     (in-calist-package 'mime-view)
270     (setq ret
271           (ctree-find-calist condition entity-situation
272                              every-situations))
273     (if required-name
274         (setq ret (mime-delq-null-situation ret required-name
275                                             ignored-value t)))
276     (or (assq 'ignore-examples entity-situation)
277         (if (cdr ret)
278             (let ((rest ret)
279                   (max-score 0)
280                   (max-escore 0)
281                   max-examples
282                   max-situations)
283               (while rest
284                 (let ((situation (car rest))
285                       (examples situation-examples))
286                   (while examples
287                     (let* ((ret
288                             (mime-compare-situation-with-example
289                              situation (caar examples)))
290                            (ret-score (car ret)))
291                       (cond ((> ret-score max-score)
292                              (setq max-score ret-score
293                                    max-escore (cdar examples)
294                                    max-examples (list (cdr ret))
295                                    max-situations (list situation)))
296                             ((= ret-score max-score)
297                              (cond ((> (cdar examples) max-escore)
298                                     (setq max-escore (cdar examples)
299                                           max-examples (list (cdr ret))
300                                           max-situations (list situation)))
301                                    ((= (cdar examples) max-escore)
302                                     (setq max-examples
303                                           (cons (cdr ret) max-examples))
304                                     (or (member situation max-situations)
305                                         (setq max-situations
306                                               (cons situation max-situations))))))))
307                     (setq examples (cdr examples))))
308                 (setq rest (cdr rest)))
309               (when max-situations
310                 (setq ret max-situations)
311                 (while max-examples
312                   (let* ((example (car max-examples))
313                          (cell
314                           (assoc example situation-examples)))
315                     (if cell
316                         (setcdr cell (1+ (cdr cell)))
317                       (setq situation-examples
318                             (cons (cons example 0)
319                                   situation-examples))))
320                   (setq max-examples (cdr max-examples)))))))
321     (cons ret situation-examples)
322     ;; ret: list of situations
323     ;; situation-examples: new examples (notoce that contents of
324     ;;                     argument `situation-examples' has bees modified)
325     ))
326
327 (defun mime-view-entity-title (entity)
328   (or (mime-entity-read-field entity 'Content-Description)
329       (mime-entity-read-field entity 'Subject)
330       (mime-entity-filename entity)
331       ""))
332
333 (defvar mime-preview-situation-example-list nil)
334 (defvar mime-preview-situation-example-list-max-size 16)
335 ;; (defvar mime-preview-situation-example-condition nil)
336
337 (defun mime-find-entity-preview-situation (entity
338                                            &optional default-situation)
339   (or (let ((ret
340              (mime-unify-situations
341               (append (mime-entity-situation entity)
342                       default-situation)
343               mime-preview-condition
344               mime-preview-situation-example-list)))
345         (setq mime-preview-situation-example-list
346               (cdr ret))
347         (caar ret))
348       default-situation))
349
350   
351 (defvar mime-acting-situation-example-list nil)
352 (defvar mime-acting-situation-example-list-max-size 16)
353 (defvar mime-situation-examples-file-coding-system nil)
354
355 (defun mime-view-read-situation-examples-file (&optional file)
356   (or file
357       (setq file mime-situation-examples-file))
358   (if (and file
359            (file-readable-p file))
360       (with-temp-buffer
361         (insert-file-contents file)
362         (setq mime-situation-examples-file-coding-system
363               (static-cond
364                ((boundp 'buffer-file-coding-system)
365                 (symbol-value 'buffer-file-coding-system))
366                ((boundp 'file-coding-system)
367                 (symbol-value 'file-coding-system))
368                (t nil))
369               ;; (and (boundp 'buffer-file-coding-system)
370               ;;      buffer-file-coding-system)
371               )
372         (condition-case error
373             (eval-buffer)
374           (error (message "%s is broken: %s" file (cdr error))))
375         ;; format check
376         (condition-case nil
377             (let ((i 0))
378               (while (and (> (length mime-preview-situation-example-list)
379                              mime-preview-situation-example-list-max-size)
380                           (< i 16))
381                 (setq mime-preview-situation-example-list
382                       (mime-reduce-situation-examples
383                        mime-preview-situation-example-list))
384                 (setq i (1+ i))))
385           (error (setq mime-preview-situation-example-list nil)))
386         ;; (let ((rest mime-preview-situation-example-list))
387         ;;   (while rest
388         ;;     (ctree-set-calist-strictly 'mime-preview-condition
389         ;;                                (caar rest))
390         ;;     (setq rest (cdr rest))))
391         (condition-case nil
392             (let ((i 0))
393               (while (and (> (length mime-acting-situation-example-list)
394                              mime-acting-situation-example-list-max-size)
395                           (< i 16))
396                 (setq mime-acting-situation-example-list
397                       (mime-reduce-situation-examples
398                        mime-acting-situation-example-list))
399                 (setq i (1+ i))))
400           (error (setq mime-acting-situation-example-list nil))))))
401
402 (defun mime-save-situation-examples ()
403   (if (or mime-preview-situation-example-list
404           mime-acting-situation-example-list)
405       (let ((file mime-situation-examples-file))
406         (with-temp-buffer
407           (insert ";;; " (file-name-nondirectory file) "\n")
408           (insert "\n;; This file is generated automatically by "
409                   mime-view-version "\n\n")
410           (insert ";;; Code:\n\n")
411           (if mime-preview-situation-example-list
412               (pp `(setq mime-preview-situation-example-list
413                          ',mime-preview-situation-example-list)
414                   (current-buffer)))
415           (if mime-acting-situation-example-list
416               (pp `(setq mime-acting-situation-example-list
417                          ',mime-acting-situation-example-list)
418                   (current-buffer)))
419           (insert "\n;;; "
420                   (file-name-nondirectory file)
421                   " ends here.\n")
422           (static-cond
423            ((boundp 'buffer-file-coding-system)
424             (setq buffer-file-coding-system
425                   mime-situation-examples-file-coding-system))
426            ((boundp 'file-coding-system)
427             (setq file-coding-system
428                   mime-situation-examples-file-coding-system)))
429           ;; (setq buffer-file-coding-system
430           ;;       mime-situation-examples-file-coding-system)
431           (setq buffer-file-name file)
432           (save-buffer)))))
433
434 (add-hook 'kill-emacs-hook 'mime-save-situation-examples)
435
436 (defun mime-reduce-situation-examples (situation-examples)
437   (let ((len (length situation-examples))
438         i ir ic j jr jc ret
439         dest d-i d-j
440         (max-sim 0) sim
441         min-det-ret det-ret
442         min-det-org det-org
443         min-freq freq)
444     (setq i 0
445           ir situation-examples)
446     (while (< i len)
447       (setq ic (car ir)
448             j 0
449             jr situation-examples)
450       (while (< j len)
451         (unless (= i j)
452           (setq jc (car jr))
453           (setq ret (mime-compare-situation-with-example (car ic)(car jc))
454                 sim (car ret)
455                 det-ret (+ (length (car ic))(length (car jc)))
456                 det-org (length (cdr ret))
457                 freq (+ (cdr ic)(cdr jc)))
458           (cond ((< max-sim sim)
459                  (setq max-sim sim
460                        min-det-ret det-ret
461                        min-det-org det-org
462                        min-freq freq
463                        d-i i
464                        d-j j
465                        dest (cons (cdr ret) freq)))
466                 ((= max-sim sim)
467                  (cond ((> min-det-ret det-ret)
468                         (setq min-det-ret det-ret
469                               min-det-org det-org
470                               min-freq freq
471                               d-i i
472                               d-j j
473                               dest (cons (cdr ret) freq)))
474                        ((= min-det-ret det-ret)
475                         (cond ((> min-det-org det-org)
476                                (setq min-det-org det-org
477                                      min-freq freq
478                                      d-i i
479                                      d-j j
480                                      dest (cons (cdr ret) freq)))
481                               ((= min-det-org det-org)
482                                (cond ((> min-freq freq)
483                                       (setq min-freq freq
484                                             d-i i
485                                             d-j j
486                                             dest (cons (cdr ret) freq)))))))))))
487         (setq jr (cdr jr)
488               j (1+ j)))
489       (setq ir (cdr ir)
490             i (1+ i)))
491     (if (> d-i d-j)
492         (setq i d-i
493               d-i d-j
494               d-j i))
495     (setq jr (nthcdr (1- d-j) situation-examples))
496     (setcdr jr (cddr jr))
497     (if (= d-i 0)
498         (setq situation-examples
499               (cdr situation-examples))
500       (setq ir (nthcdr (1- d-i) situation-examples))
501       (setcdr ir (cddr ir)))
502     (if (setq ir (assoc (car dest) situation-examples))
503         (progn
504           (setcdr ir (+ (cdr ir)(cdr dest)))
505           situation-examples)
506       (cons dest situation-examples)
507       ;; situation-examples may be modified.
508       )))
509
510
511 ;;; @ presentation of preview
512 ;;;
513
514 ;;; @@ entity-button
515 ;;;
516
517 ;;; @@@ predicate function
518 ;;;
519
520 ;; (defun mime-view-entity-button-visible-p (entity)
521 ;;   "Return non-nil if header of ENTITY is visible.
522 ;; Please redefine this function if you want to change default setting."
523 ;;   (let ((media-type (mime-entity-media-type entity))
524 ;;         (media-subtype (mime-entity-media-subtype entity)))
525 ;;     (or (not (eq media-type 'application))
526 ;;         (and (not (eq media-subtype 'x-selection))
527 ;;              (or (not (eq media-subtype 'octet-stream))
528 ;;                  (let ((mother-entity (mime-entity-parent entity)))
529 ;;                    (or (not (eq (mime-entity-media-type mother-entity)
530 ;;                                 'multipart))
531 ;;                        (not (eq (mime-entity-media-subtype mother-entity)
532 ;;                                 'encrypted)))
533 ;;                    )
534 ;;                  )))))
535
536 ;;; @@@ entity button generator
537 ;;;
538
539 (defun mime-view-insert-entity-button (entity)
540   "Insert entity-button of ENTITY."
541   (let ((entity-node-id (mime-entity-node-id entity))
542         (params (mime-entity-parameters entity))
543         (subject (mime-view-entity-title entity)))
544     (mime-insert-button
545      (let ((access-type (assoc "access-type" params))
546            (num (or (cdr (assoc "x-part-number" params))
547                     (if (consp entity-node-id)
548                         (mapconcat (function
549                                     (lambda (num)
550                                       (format "%s" (1+ num))))
551                                    (reverse entity-node-id) ".")
552                       "0"))))
553        (cond (access-type
554               (let ((server (assoc "server" params)))
555                 (setq access-type (cdr access-type))
556                 (if server
557                     (format "%s %s ([%s] %s)"
558                             num subject access-type (cdr server))
559                 (let ((site (cdr (assoc "site" params)))
560                       (dir (cdr (assoc "directory" params)))
561                       (url (cdr (assoc "url" params))))
562                   (if url
563                       (format "%s %s ([%s] %s)"
564                               num subject access-type url)
565                     (format "%s %s ([%s] %s:%s)"
566                             num subject access-type site dir))))))
567            (t
568             (let ((media-type (mime-entity-media-type entity))
569                   (media-subtype (mime-entity-media-subtype entity))
570                   (charset (cdr (assoc "charset" params)))
571                   (encoding (mime-entity-encoding entity)))
572               (concat
573                num " " subject
574                (let ((rest
575                       (format " <%s/%s%s%s>"
576                               media-type media-subtype
577                               (if charset
578                                   (concat "; " charset)
579                                 "")
580                               (if encoding
581                                   (concat " (" encoding ")")
582                                 ""))))
583                  (if (>= (+ (current-column)(length rest))(window-width))
584                      "\n\t")
585                  rest))))))
586      (function mime-preview-play-current-entity))))
587
588
589 ;;; @@ entity-header
590 ;;;
591
592 (defvar mime-header-presentation-method-alist nil
593   "Alist of major mode vs. corresponding header-presentation-method functions.
594 Each element looks like (SYMBOL . FUNCTION).
595 SYMBOL must be major mode in raw-buffer or t.  t means default.
596 Interface of FUNCTION must be (ENTITY SITUATION).")
597
598 (defvar mime-view-ignored-field-list
599   '(".*Received:" ".*Path:" ".*Id:" "^References:"
600     "^Replied:" "^Errors-To:"
601     "^Lines:" "^Sender:" ".*Host:" "^Xref:"
602     "^Content-Type:" "^Precedence:"
603     "^Status:" "^X-VM-.*:")
604   "All fields that match this list will be hidden in MIME preview buffer.
605 Each elements are regexp of field-name.")
606
607 (defvar mime-view-visible-field-list '("^Dnas.*:" "^Message-Id:")
608   "All fields that match this list will be displayed in MIME preview buffer.
609 Each elements are regexp of field-name.")
610
611
612 ;;; @@ entity-body
613 ;;;
614
615 ;;; @@@ predicate function
616 ;;;
617
618 (in-calist-package 'mime-view)
619
620 (defun mime-calist::field-match-method-as-default-rule (calist
621                                                         field-type field-value)
622   (let ((s-field (assq field-type calist)))
623     (cond ((null s-field)
624            (cons (cons field-type field-value) calist))
625           (t calist))))
626
627 (define-calist-field-match-method
628   'header #'mime-calist::field-match-method-as-default-rule)
629
630 (define-calist-field-match-method
631   'body #'mime-calist::field-match-method-as-default-rule)
632
633
634 (defvar mime-preview-condition nil
635   "Condition-tree about how to display entity.")
636
637 (ctree-set-calist-strictly
638  'mime-preview-condition '((type . application)(subtype . octet-stream)
639                            (encoding . nil)
640                            (body . visible)))
641 (ctree-set-calist-strictly
642  'mime-preview-condition '((type . application)(subtype . octet-stream)
643                            (encoding . "7bit")
644                            (body . visible)))
645 (ctree-set-calist-strictly
646  'mime-preview-condition '((type . application)(subtype . octet-stream)
647                            (encoding . "8bit")
648                            (body . visible)))
649
650 (ctree-set-calist-strictly
651  'mime-preview-condition '((type . application)(subtype . pgp)
652                            (body . visible)))
653
654 (ctree-set-calist-strictly
655  'mime-preview-condition '((type . application)(subtype . x-latex)
656                            (body . visible)))
657
658 (ctree-set-calist-strictly
659  'mime-preview-condition '((type . application)(subtype . x-selection)
660                            (body . visible)))
661
662 (ctree-set-calist-strictly
663  'mime-preview-condition '((type . application)(subtype . x-comment)
664                            (body . visible)))
665
666 (ctree-set-calist-strictly
667  'mime-preview-condition '((type . message)(subtype . delivery-status)
668                            (body . visible)))
669
670 (ctree-set-calist-strictly
671  'mime-preview-condition
672  '((body . visible)
673    (body-presentation-method . mime-display-text/plain)))
674
675 (defvar mime-preview-fill-flowed-text
676   (module-installed-p 'flow-fill)
677   "If non-nil, fill RFC2646 \"flowed\" text.")
678
679 (autoload 'fill-flowed "flow-fill")
680
681 (defvar mime-preview-inline-fontify t
682   "If non-nil, fontify the inline part.")
683
684 (ctree-set-calist-strictly
685  'mime-preview-condition
686  '((type . nil)
687    (body . visible)
688    (body-presentation-method . mime-display-text/plain)))
689
690 (ctree-set-calist-strictly
691  'mime-preview-condition
692  '((type . text)(subtype . enriched)
693    (body . visible)
694    (body-presentation-method . mime-display-text/enriched)))
695
696 (ctree-set-calist-strictly
697  'mime-preview-condition
698  '((type . text)(subtype . richtext)
699    (body . visible)
700    (body-presentation-method . mime-display-text/richtext)))
701
702 (autoload 'mime-display-application/x-postpet "postpet")
703
704 (ctree-set-calist-strictly
705  'mime-preview-condition
706  '((type . application)(subtype . x-postpet)
707    (body . visible)
708    (body-presentation-method . mime-display-application/x-postpet)))
709
710 (ctree-set-calist-strictly
711  'mime-preview-condition
712  '((type . application)(subtype . emacs-lisp)
713    (body . visible)
714    (body-presentation-method . mime-display-application/emacs-lisp)))
715
716 (ctree-set-calist-strictly
717  'mime-preview-condition
718  '((type . text)(subtype . t)
719    (body . visible)
720    (body-presentation-method . mime-display-text/plain)))
721
722 (ctree-set-calist-strictly
723  'mime-preview-condition
724  '((type . multipart)(subtype . alternative)
725    (body . visible)
726    (body-presentation-method . mime-display-multipart/alternative)))
727
728 (ctree-set-calist-strictly
729  'mime-preview-condition
730  '((type . multipart)(subtype . related)
731    (body . visible)
732    (body-presentation-method . mime-display-multipart/related)))
733
734 (ctree-set-calist-strictly
735  'mime-preview-condition
736  '((type . multipart)(subtype . t)
737    (body . visible)
738    (body-presentation-method . mime-display-multipart/mixed)))
739
740 (ctree-set-calist-strictly
741  'mime-preview-condition
742  '((type . message)(subtype . partial)
743    (body . visible)
744    (body-presentation-method . mime-display-message/partial-button)))
745
746 (ctree-set-calist-strictly
747  'mime-preview-condition
748  '((type . message)(subtype . rfc822)
749    (body . visible)
750    (body-presentation-method . mime-display-multipart/mixed)
751    (childrens-situation (header . visible)
752                         (entity-button . invisible))))
753
754 (ctree-set-calist-strictly
755  'mime-preview-condition
756  '((type . message)(subtype . news)
757    (body . visible)
758    (body-presentation-method . mime-display-multipart/mixed)
759    (childrens-situation (header . visible)
760                         (entity-button . invisible))))
761
762
763 ;;; @@@ entity presentation
764 ;;;
765
766 (defun mime-display-text/plain (entity situation)
767   (save-restriction
768     (narrow-to-region (point-max)(point-max))
769     (condition-case nil
770         (if (and mime-preview-inline-fontify
771                  (mime-entity-filename entity)) ;should be an attachment.
772             (mime-view-insert-fontified-text-content entity situation)
773           (mime-view-insert-text-content entity situation))
774       (error (progn
775                (message "Can't decode current entity.")
776                (sit-for 1))))
777     (run-hooks 'mime-text-decode-hook)
778     (goto-char (point-max))
779     (if (not (eq (char-after (1- (point))) ?\n))
780         (insert "\n"))
781     (if (and mime-preview-fill-flowed-text
782              (equal (cdr (assoc "format" situation)) "flowed"))
783         (fill-flowed))
784     (mime-add-url-buttons)
785     (run-hooks 'mime-display-text/plain-hook)))
786
787 (defun mime-display-text/richtext (entity situation)
788   (save-restriction
789     (narrow-to-region (point-max)(point-max))
790     (mime-view-insert-text-content entity situation)
791     (run-hooks 'mime-text-decode-hook)
792     (let ((beg (point-min)))
793       (remove-text-properties beg (point-max) '(face nil))
794       (richtext-decode beg (point-max)))))
795
796 (defun mime-display-text/enriched (entity situation)
797   (save-restriction
798     (narrow-to-region (point-max)(point-max))
799     (mime-view-insert-text-content entity situation)
800     (run-hooks 'mime-text-decode-hook)
801     (let ((beg (point-min)))
802       (remove-text-properties beg (point-max) '(face nil))
803       (enriched-decode beg (point-max)))))
804
805 (defvar mime-view-announcement-for-message/partial
806   "This is message/partial style split message.")
807
808 (defun mime-display-message/partial-button (&optional entity situation)
809   (save-restriction
810     (goto-char (point-max))
811     (if (not (search-backward "\n\n" nil t))
812         (insert "\n"))
813     (goto-char (point-max))
814     (mime-insert-button mime-view-announcement-for-message/partial
815                         #'mime-preview-play-current-entity)))
816
817 (defun mime-display-multipart/mixed (entity situation)
818   (let ((children (mime-entity-children entity))
819         (original-major-mode-cell (assq 'major-mode situation))
820         (default-situation
821           (cdr (assq 'childrens-situation situation))))
822     (if original-major-mode-cell
823         (setq default-situation
824               (cons original-major-mode-cell default-situation)))
825     (while children
826       (mime-display-entity (car children) nil default-situation)
827       (setq children (cdr children)))))
828
829 (defcustom mime-view-type-subtype-score-alist
830   '(((text . enriched) . 3)
831     ((text . richtext) . 2)
832     ((text . plain)    . 1)
833     (t . 0))
834   "Alist MEDIA-TYPE vs corresponding score.
835 MEDIA-TYPE must be (TYPE . SUBTYPE), TYPE or t.  t means default."
836   :group 'mime-view
837   :type '(repeat (cons (choice :tag "Media-Type"
838                                (cons :tag "Type/Subtype"
839                                      (symbol :tag "Primary-type")
840                                      (symbol :tag "Subtype"))
841                                (symbol :tag "Type")
842                                (const :tag "Default" t))
843                        integer)))
844
845 (defun mime-display-multipart/alternative (entity situation)
846   (let* ((children (mime-entity-children entity))
847          (original-major-mode-cell (assq 'major-mode situation))
848          (default-situation
849            (cdr (assq 'childrens-situation situation)))
850          (i 0)
851          (p 0)
852          (max-score 0)
853          situations)
854     (if original-major-mode-cell
855         (setq default-situation
856               (cons original-major-mode-cell default-situation)))
857     (setq situations
858           (mapcar (function
859                    (lambda (child)
860                      (let ((situation
861                             (mime-find-entity-preview-situation
862                              child default-situation)))
863                        (if (cdr (assq 'body-presentation-method situation))
864                            (let ((score
865                                   (cdr
866                                    (or (assoc
867                                         (cons
868                                          (cdr (assq 'type situation))
869                                          (cdr (assq 'subtype situation)))
870                                         mime-view-type-subtype-score-alist)
871                                        (assq
872                                         (cdr (assq 'type situation))
873                                         mime-view-type-subtype-score-alist)
874                                        (assq
875                                         t
876                                         mime-view-type-subtype-score-alist)))))
877                              (if (> score max-score)
878                                  (setq p i
879                                        max-score score))))
880                        (setq i (1+ i))
881                        situation)))
882                   children))
883     (setq i 0)
884     (while children
885       (let ((child (car children))
886             (situation (car situations)))
887         (mime-display-entity child (if (= i p)
888                                        situation
889                                      (put-alist 'body 'invisible
890                                                 (copy-alist situation)))))
891       (setq children (cdr children)
892             situations (cdr situations)
893             i (1+ i)))))
894
895 (defun mime-display-multipart/related (entity situation)
896   (let* ((param-start (mime-parse-msg-id
897                        (std11-lexical-analyze
898                         (cdr (assoc "start"
899                                     (mime-content-type-parameters
900                                      (mime-entity-content-type entity)))))))
901          (start (or (and param-start (mime-find-entity-from-content-id
902                                       param-start
903                                       entity))
904                     (car (mime-entity-children entity))))
905          (original-major-mode-cell (assq 'major-mode situation))
906          (default-situation (cdr (assq 'childrens-situation situation))))
907     (if original-major-mode-cell
908         (setq default-situation
909               (cons original-major-mode-cell default-situation)))
910     (mime-display-entity start nil default-situation)))
911
912 (defun mime-view-entity-content (entity situation)
913   (mime-decode-string
914    (mime-entity-body entity)
915    (mime-view-guess-encoding entity situation)))
916   
917 (defun mime-view-insert-text-content (entity situation)
918   (let (compression-info)
919     (cond
920      ((and (mime-entity-filename entity)
921            (featurep 'jka-compr)
922            (jka-compr-installed-p)
923            (setq compression-info (jka-compr-get-compression-info
924                                    (mime-entity-filename entity))))
925       (insert
926        (mime-view-filter-text-content
927         (mime-view-entity-content entity situation)
928         (jka-compr-info-uncompress-program compression-info)
929         (jka-compr-info-uncompress-args compression-info))))
930      ((or (assq '*encoding situation)   ;should be specified by user
931           (assq '*charset situation))   ;should be specified by user
932       (insert
933        (decode-mime-charset-string
934         (mime-view-entity-content entity situation)
935         (mime-view-guess-charset entity situation)
936         'CRLF)))
937      (t
938       (mime-insert-text-content entity)))))
939
940 ;;; stolen (and renamed) from `mime-display-gzipped' of EMY 1.13.
941 (defun mime-view-filter-text-content (content program args)
942   (with-temp-buffer
943     (static-cond
944      ((featurep 'xemacs)
945       (insert content)
946       (apply #'binary-to-text-funcall
947              mime-view-automatic-conversion
948              #'call-process-region (point-min)(point-max)
949              program t t args))
950      (t
951       (if (not (multibyte-string-p content))
952           (set-buffer-multibyte nil))
953       (insert content)
954       (apply #'binary-funcall
955              #'call-process-region (point-min)(point-max)
956              program t t args)
957       (set-buffer-multibyte t)
958       (decode-coding-region (point-min)(point-max)
959                             mime-view-automatic-conversion)))
960     (buffer-string)))
961
962 ;;; stolen (and renamed) from mm-view.el.
963 (defun mime-view-insert-fontified-text-content (entity situation
964                                                        &optional mode)
965   ;; XEmacs @#$@ version of font-lock refuses to fully turn itself
966   ;; on for buffers whose name begins with " ".  That's why we use
967   ;; save-current-buffer/get-buffer-create rather than
968   ;; with-temp-buffer.
969   (let ((buffer (generate-new-buffer "*fontification*"))
970         filename)
971     (unwind-protect
972         (progn
973           (save-current-buffer
974             (set-buffer buffer)
975             (buffer-disable-undo)
976             (kill-all-local-variables)
977             (mime-view-insert-text-content entity situation)
978             (if mode
979                 (funcall mode)
980               (if (setq filename (mime-entity-filename entity))
981                   (unwind-protect
982                       (progn
983                         (setq buffer-file-name filename)
984                         (set-auto-mode))
985                     (setq buffer-file-name nil))))
986             (require 'font-lock)
987             (let ((font-lock-verbose nil))
988               ;; I find font-lock a bit too verbose.
989               (font-lock-fontify-buffer))
990             ;; By default, XEmacs font-lock uses non-duplicable text
991             ;; properties.  This code forces all the text properties
992             ;; to be copied along with the text.
993             (static-when (fboundp 'extent-list)
994               (map-extents (lambda (ext ignored)
995                              (set-extent-property ext 'duplicable t)
996                              nil)
997                            nil nil nil nil nil 'text-prop)))
998           (insert-buffer-substring buffer))
999       (kill-buffer buffer))))
1000
1001 (defun mime-display-application/emacs-lisp (entity situation)
1002   (save-restriction
1003     (narrow-to-region (point-max)(point-max))
1004     (mime-view-insert-fontified-text-content entity situation 'emacs-lisp-mode)
1005     (run-hooks 'mime-text-decode-hook 'mime-display-text/plain-hook)))
1006
1007
1008 ;;; @ acting-condition
1009 ;;;
1010
1011 (defvar mime-acting-condition nil
1012   "Condition-tree about how to process entity.")
1013
1014 (defun mime-view-read-mailcap-files (&optional files)
1015   (or files
1016       (setq files mime-view-mailcap-files))
1017   (let (entries file)
1018     (while files
1019       (setq file (car files))
1020       (if (file-readable-p file)
1021           (setq entries (append entries (mime-parse-mailcap-file file))))
1022       (setq files (cdr files)))
1023     (while entries
1024       (let ((entry (car entries))
1025             view print shared)
1026         (while entry
1027           (let* ((field (car entry))
1028                  (field-type (car field)))
1029             (cond ((eq field-type 'view)  (setq view field))
1030                   ((eq field-type 'print) (setq print field))
1031                   ((memq field-type '(compose composetyped edit)))
1032                   (t (setq shared (cons field shared))))
1033             )
1034           (setq entry (cdr entry)))
1035         (setq shared (nreverse shared))
1036         (ctree-set-calist-with-default
1037          'mime-acting-condition
1038          (append shared (list '(mode . "play")(cons 'method (cdr view)))))
1039         (if print
1040             (ctree-set-calist-with-default
1041              'mime-acting-condition
1042              (append shared
1043                      (list '(mode . "print")(cons 'method (cdr view)))))))
1044       (setq entries (cdr entries)))))
1045
1046 (mime-view-read-mailcap-files)
1047
1048 (ctree-set-calist-strictly
1049  'mime-acting-condition
1050  '((type . application)(subtype . octet-stream)
1051    (mode . "play")
1052    (method . mime-detect-content)))
1053
1054 (ctree-set-calist-with-default
1055  'mime-acting-condition
1056  '((mode . "extract")
1057    (method . mime-save-content)))
1058
1059 (ctree-set-calist-strictly
1060  'mime-acting-condition
1061  '((type . text)(subtype . x-rot13-47)(mode . "play")
1062    (method . mime-view-caesar)))
1063 (ctree-set-calist-strictly
1064  'mime-acting-condition
1065  '((type . text)(subtype . x-rot13-47-48)(mode . "play")
1066    (method . mime-view-caesar)))
1067
1068 (ctree-set-calist-strictly
1069  'mime-acting-condition
1070  '((type . message)(subtype . rfc822)(mode . "play")
1071    (method . mime-view-message/rfc822)))
1072 (ctree-set-calist-strictly
1073  'mime-acting-condition
1074  '((type . message)(subtype . partial)(mode . "play")
1075    (method . mime-store-message/partial-piece)))
1076
1077 (ctree-set-calist-strictly
1078  'mime-acting-condition
1079  '((type . message)(subtype . external-body)
1080    ("access-type" . "anon-ftp")
1081    (method . mime-view-message/external-anon-ftp)))
1082
1083 (ctree-set-calist-strictly
1084  'mime-acting-condition
1085  '((type . message)(subtype . external-body)
1086    ("access-type" . "url")
1087    (method . mime-view-message/external-url)))
1088
1089 (ctree-set-calist-strictly
1090  'mime-acting-condition
1091  '((type . application)(subtype . octet-stream)
1092    (method . mime-save-content)))
1093
1094
1095 ;;; @ quitting method
1096 ;;;
1097
1098 (defvar mime-preview-quitting-method-alist
1099   '((mime-show-message-mode
1100      . mime-preview-quitting-method-for-mime-show-message-mode))
1101   "Alist of major-mode vs. quitting-method of mime-view.")
1102
1103 (defvar mime-preview-over-to-previous-method-alist nil
1104   "Alist of major-mode vs. over-to-previous-method of mime-view.")
1105
1106 (defvar mime-preview-over-to-next-method-alist nil
1107   "Alist of major-mode vs. over-to-next-method of mime-view.")
1108
1109
1110 ;;; @ following method
1111 ;;;
1112
1113 (defvar mime-preview-following-method-alist nil
1114   "Alist of major-mode vs. following-method of mime-view.")
1115
1116 (defvar mime-view-following-required-fields-list
1117   '("From"))
1118
1119
1120 ;;; @ buffer setup
1121 ;;;
1122
1123 (defun mime-display-entity (entity &optional situation
1124                                    default-situation preview-buffer)
1125   (or preview-buffer
1126       (setq preview-buffer (current-buffer)))
1127   (let* (e nb ne nhb nbb)
1128     (in-calist-package 'mime-view)
1129     (or situation
1130         (setq situation
1131               (mime-find-entity-preview-situation entity default-situation)))
1132     (let ((button-is-invisible
1133            (eq (cdr (or (assq '*entity-button situation)
1134                         (assq 'entity-button situation)))
1135                'invisible))
1136           (header-is-visible
1137            (eq (cdr (or (assq '*header situation)
1138                         (assq 'header situation)))
1139                'visible))
1140           (body-is-visible
1141            (eq (cdr (or (assq '*body situation)
1142                         (assq 'body situation)))
1143                'visible))
1144           (children (mime-entity-children entity)))
1145       (set-buffer preview-buffer)
1146       (setq nb (point))
1147       (narrow-to-region nb nb)
1148       (or button-is-invisible
1149           ;; (if (mime-view-entity-button-visible-p entity)
1150           (mime-view-insert-entity-button entity)
1151           ;;   )
1152           )
1153       (if header-is-visible
1154           (let ((header-presentation-method
1155                  (or (cdr (assq 'header-presentation-method situation))
1156                      (cdr (assq (cdr (assq 'major-mode situation))
1157                                 mime-header-presentation-method-alist)))))
1158             (setq nhb (point))
1159             (if header-presentation-method
1160                 (funcall header-presentation-method entity situation)
1161               (mime-insert-header entity
1162                                   mime-view-ignored-field-list
1163                                   mime-view-visible-field-list))
1164             (mime-add-url-buttons)
1165             (run-hooks 'mime-display-header-hook)
1166             (put-text-property nhb (point-max) 'mime-view-entity-header entity)
1167             (goto-char (point-max))
1168             (insert "\n")))
1169       (setq nbb (point))
1170       (unless children
1171         (if body-is-visible
1172             (let ((body-presentation-method
1173                    (cdr (assq 'body-presentation-method situation))))
1174               (if (functionp body-presentation-method)
1175                   (funcall body-presentation-method entity situation)
1176                 (mime-display-text/plain entity situation)))
1177           (when button-is-invisible
1178             (goto-char (point-max))
1179             (mime-view-insert-entity-button entity))
1180           (unless header-is-visible
1181             (goto-char (point-max))
1182             (insert "\n"))))
1183       (setq ne (point-max))
1184       (widen)
1185       (put-text-property nb ne 'mime-view-entity entity)
1186       (put-text-property nb ne 'mime-view-situation situation)
1187       (put-text-property nbb ne 'mime-view-entity-body entity)
1188       (goto-char ne)
1189       (if (and children body-is-visible)
1190           (let ((body-presentation-method
1191                  (cdr (assq 'body-presentation-method situation))))
1192             (if (functionp body-presentation-method)
1193                 (funcall body-presentation-method entity situation)
1194               (mime-display-multipart/mixed entity situation)))))))
1195
1196
1197 ;;; @ MIME viewer mode
1198 ;;;
1199
1200 (defconst mime-view-popup-menu-list
1201   '("MIME-View"
1202     ["Move to upper entity" mime-preview-move-to-upper]
1203     ["Move to previous entity" mime-preview-move-to-previous]
1204     ["Move to next entity" mime-preview-move-to-next]
1205     ["Scroll-down" mime-preview-scroll-down-entity]
1206     ["Scroll-up" mime-preview-scroll-up-entity]
1207     ["Play current entity" mime-preview-play-current-entity]
1208     ["Extract current entity" mime-preview-extract-current-entity]
1209     ["Print current entity" mime-preview-print-current-entity])
1210   "Menu for MIME Viewer")
1211
1212 (defun mime-view-popup-menu (event)
1213   "Popup the menu in the MIME Viewer buffer"
1214   (interactive "@e")
1215   (mime-popup-menu-popup mime-view-popup-menu-list event))
1216
1217 ;;; The current local map is taken precendence over `widget-keymap',
1218 ;;; because GNU Emacs' widget implementation doesn't set `local-map' property.
1219 ;;;  So we need to specify derivation.
1220 (defvar widget-keymap)
1221 (defun mime-view-maybe-inherit-widget-keymap ()
1222   (when (boundp 'widget-keymap)
1223     (set-keymap-parent (current-local-map) widget-keymap)))
1224
1225 (add-hook 'mime-view-mode-hook 'mime-view-maybe-inherit-widget-keymap)
1226           
1227 (defun mime-view-define-keymap (&optional default)
1228   (let ((mime-view-mode-map (if (keymapp default)
1229                                 (copy-keymap default)
1230                               (make-sparse-keymap))))
1231     (define-key mime-view-mode-map
1232       "u"        (function mime-preview-move-to-upper))
1233     (define-key mime-view-mode-map
1234       "p"        (function mime-preview-move-to-previous))
1235     (define-key mime-view-mode-map
1236       "n"        (function mime-preview-move-to-next))
1237     (define-key mime-view-mode-map
1238       "\e\t"     (function mime-preview-move-to-previous))
1239     (define-key mime-view-mode-map
1240       "\t"       (function mime-preview-move-to-next))
1241     (define-key mime-view-mode-map
1242       " "        (function mime-preview-scroll-up-entity))
1243     (define-key mime-view-mode-map
1244       "\M- "     (function mime-preview-scroll-down-entity))
1245     (define-key mime-view-mode-map
1246       "\177"     (function mime-preview-scroll-down-entity))
1247     (define-key mime-view-mode-map
1248       "\C-m"     (function mime-preview-next-line-entity))
1249     (define-key mime-view-mode-map
1250       "\C-\M-m"  (function mime-preview-previous-line-entity))
1251     (define-key mime-view-mode-map
1252       "v"        (function mime-preview-play-current-entity))
1253     (define-key mime-view-mode-map
1254       "e"        (function mime-preview-extract-current-entity))
1255     (define-key mime-view-mode-map
1256       "\C-c\C-p" (function mime-preview-print-current-entity))
1257
1258     (define-key mime-view-mode-map
1259       "\C-c\C-t\C-f" (function mime-preview-toggle-header))
1260     (define-key mime-view-mode-map
1261       "\C-c\C-th" (function mime-preview-toggle-header))
1262     (define-key mime-view-mode-map
1263       "\C-c\C-t\C-c" (function mime-preview-toggle-content))
1264
1265     (define-key mime-view-mode-map
1266       "\C-c\C-v\C-f" (function mime-preview-show-header))
1267     (define-key mime-view-mode-map
1268       "\C-c\C-vh" (function mime-preview-show-header))
1269     (define-key mime-view-mode-map
1270       "\C-c\C-v\C-c" (function mime-preview-show-content))
1271
1272     (define-key mime-view-mode-map
1273       "\C-c\C-d\C-f" (function mime-preview-hide-header))
1274     (define-key mime-view-mode-map
1275       "\C-c\C-dh" (function mime-preview-hide-header))
1276     (define-key mime-view-mode-map
1277       "\C-c\C-d\C-c" (function mime-preview-hide-content))
1278
1279     (define-key mime-view-mode-map
1280       "a"        (function mime-preview-follow-current-entity))
1281     (define-key mime-view-mode-map
1282       "q"        (function mime-preview-quit))
1283     (define-key mime-view-mode-map
1284       "\C-c\C-x" (function mime-preview-kill-buffer))
1285     ;; (define-key mime-view-mode-map
1286     ;;   "<"        (function beginning-of-buffer))
1287     ;; (define-key mime-view-mode-map
1288     ;;   ">"        (function end-of-buffer))
1289     (define-key mime-view-mode-map
1290       "?"        (function describe-mode))
1291     (define-key mime-view-mode-map
1292       [tab] (function mime-preview-move-to-next))
1293     (define-key mime-view-mode-map
1294       [delete] (function mime-preview-scroll-down-entity))
1295     (define-key mime-view-mode-map
1296       [backspace] (function mime-preview-scroll-down-entity))
1297     (if (functionp default)
1298         (if (featurep 'xemacs)
1299             (set-keymap-default-binding mime-view-mode-map default)
1300           (setq mime-view-mode-map
1301                 (append mime-view-mode-map (list (cons t default))))))
1302     (define-key mime-view-mode-map
1303       [down-mouse-3] (function mime-view-popup-menu))
1304     ;; (run-hooks 'mime-view-define-keymap-hook)
1305     mime-view-mode-map))
1306
1307 (defvar mime-view-mode-default-map (mime-view-define-keymap))
1308
1309
1310 (defsubst mime-maybe-hide-echo-buffer ()
1311   "Clear mime-echo buffer and delete window for it."
1312   (let ((buf (get-buffer mime-echo-buffer-name)))
1313     (if buf
1314         (save-excursion
1315           (set-buffer buf)
1316           (erase-buffer)
1317           (let ((win (get-buffer-window buf)))
1318             (if win
1319                 (delete-window win)))
1320           (bury-buffer buf)))))
1321
1322 (defvar mime-view-redisplay nil)
1323
1324 ;;;###autoload
1325 (defun mime-display-message (message &optional preview-buffer
1326                                      mother default-keymap-or-function
1327                                      original-major-mode keymap)
1328   "View MESSAGE in MIME-View mode.
1329
1330 Optional argument PREVIEW-BUFFER specifies the buffer of the
1331 presentation.  It must be either nil or a name of preview buffer.
1332
1333 Optional argument MOTHER specifies mother-buffer of the preview-buffer.
1334
1335 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
1336 function.  If it is a keymap, keymap of MIME-View mode will be added
1337 to it.  If it is a function, it will be bound as default binding of
1338 keymap of MIME-View mode.
1339
1340 Optional argument ORIGINAL-MAJOR-MODE is major-mode of representation
1341 buffer of MESSAGE.  If it is nil, current `major-mode' is used.
1342
1343 Optional argument KEYMAP is keymap of MIME-View mode.  If it is
1344 non-nil, DEFAULT-KEYMAP-OR-FUNCTION is ignored.  If it is nil,
1345 `mime-view-mode-default-map' is used."
1346   (mime-maybe-hide-echo-buffer)
1347   (let ((win-conf (current-window-configuration)))
1348     (or preview-buffer
1349         (setq preview-buffer
1350               (concat "*Preview-" (mime-entity-name message) "*")))
1351     (or original-major-mode
1352         (setq original-major-mode major-mode))
1353     (let ((inhibit-read-only t))
1354       (set-buffer (get-buffer-create preview-buffer))
1355       (widen)
1356       (erase-buffer)
1357       (if mother
1358           (setq mime-mother-buffer mother))
1359       (setq mime-preview-original-window-configuration win-conf)
1360       (setq major-mode 'mime-view-mode)
1361       (setq mode-name "MIME-View")
1362       (mime-display-entity message nil
1363                            `((entity-button . invisible)
1364                              (header . visible)
1365                              (major-mode . ,original-major-mode))
1366                            preview-buffer)
1367       (use-local-map
1368        (or keymap
1369            (if default-keymap-or-function
1370                (mime-view-define-keymap default-keymap-or-function)
1371              mime-view-mode-default-map)))
1372       (let ((point
1373              (next-single-property-change (point-min) 'mime-view-entity)))
1374         (if point
1375             (goto-char point)
1376           (goto-char (point-min))
1377           (search-forward "\n\n" nil t)))
1378       (run-hooks 'mime-view-mode-hook)
1379       (set-buffer-modified-p nil)
1380       (setq buffer-read-only t)
1381       preview-buffer)))
1382
1383 ;;;###autoload
1384 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
1385                                    default-keymap-or-function
1386                                    representation-type)
1387   "View RAW-BUFFER in MIME-View mode.
1388 Optional argument PREVIEW-BUFFER is either nil or a name of preview
1389 buffer.
1390 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
1391 function.  If it is a keymap, keymap of MIME-View mode will be added
1392 to it.  If it is a function, it will be bound as default binding of
1393 keymap of MIME-View mode.
1394 Optional argument REPRESENTATION-TYPE is representation-type of
1395 message.  It must be nil, `binary' or `cooked'.  If it is nil,
1396 `cooked' is used as default."
1397   (interactive)
1398   (or raw-buffer
1399       (setq raw-buffer (current-buffer)))
1400   (or representation-type
1401       (setq representation-type
1402             (save-excursion
1403               (set-buffer raw-buffer)
1404               (cdr (or (assq major-mode mime-raw-representation-type-alist)
1405                        (assq t mime-raw-representation-type-alist))))))
1406   (if (eq representation-type 'binary)
1407       (setq representation-type 'buffer))
1408   (setq preview-buffer (mime-display-message
1409                         (mime-open-entity representation-type raw-buffer)
1410                         preview-buffer mother default-keymap-or-function))
1411   (or (get-buffer-window preview-buffer)
1412       (let ((r-win (get-buffer-window raw-buffer)))
1413         (if r-win
1414             (set-window-buffer r-win preview-buffer)
1415           (let ((m-win (and mother (get-buffer-window mother))))
1416             (if m-win
1417                 (set-window-buffer m-win preview-buffer)
1418               (switch-to-buffer preview-buffer)))))))
1419
1420 (defun mime-view-mode (&optional mother ctl encoding
1421                                  raw-buffer preview-buffer
1422                                  default-keymap-or-function)
1423   "Major mode for viewing MIME message.
1424
1425 Here is a list of the standard keys for mime-view-mode.
1426
1427 key             feature
1428 ---             -------
1429
1430 u               Move to upper content
1431 p or M-TAB      Move to previous content
1432 n or TAB        Move to next content
1433 SPC             Scroll up or move to next content
1434 M-SPC or DEL    Scroll down or move to previous content
1435 RET             Move to next line
1436 M-RET           Move to previous line
1437 v               Decode current content as `play mode'
1438 e               Decode current content as `extract mode'
1439 C-c C-p         Decode current content as `print mode'
1440 a               Followup to current content.
1441 q               Quit
1442 button-2        Move to point under the mouse cursor
1443                 and decode current content as `play mode'
1444 "
1445   (interactive)
1446   (unless mime-view-redisplay
1447     (save-excursion
1448       (if raw-buffer (set-buffer raw-buffer))
1449       (let ((type
1450              (cdr
1451               (or (assq major-mode mime-raw-representation-type-alist)
1452                   (assq t mime-raw-representation-type-alist)))))
1453         (if (eq type 'binary)
1454             (setq type 'buffer))
1455         (setq mime-message-structure (mime-open-entity type raw-buffer))
1456         (or (mime-entity-content-type mime-message-structure)
1457             (mime-entity-set-content-type mime-message-structure ctl)))
1458       (or (mime-entity-encoding mime-message-structure)
1459           (mime-entity-set-encoding mime-message-structure encoding))))
1460   (mime-display-message mime-message-structure preview-buffer
1461                         mother default-keymap-or-function))
1462
1463
1464 ;;; @@ utility
1465 ;;;
1466
1467 (defun mime-preview-find-boundary-info (&optional with-children)
1468   "Return boundary information of current part.
1469 If WITH-CHILDREN, refer boundary surrounding current part and its branches."
1470   (let (entity
1471         p-beg p-end
1472         entity-node-id len)
1473     (while (null (setq entity
1474                        (get-text-property (point) 'mime-view-entity)))
1475       (backward-char))
1476     (setq p-beg (previous-single-property-change (point) 'mime-view-entity))
1477     (setq entity-node-id (mime-entity-node-id entity))
1478     (setq len (length entity-node-id))
1479     (cond ((null p-beg)
1480            (setq p-beg
1481                  (if (eq (next-single-property-change (point-min)
1482                                                       'mime-view-entity)
1483                          (point))
1484                      (point)
1485                    (point-min))))
1486           ((eq (next-single-property-change p-beg 'mime-view-entity)
1487                (point))
1488            (setq p-beg (point))))
1489     (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1490     (cond ((null p-end)
1491            (setq p-end (point-max)))
1492           ((null entity-node-id)
1493            (setq p-end (point-max)))
1494           (with-children
1495            (save-excursion
1496              (catch 'tag
1497                (let (e i)
1498                  (while (setq e
1499                               (next-single-property-change
1500                                (point) 'mime-view-entity))
1501                    (goto-char e)
1502                    (let ((rc (mime-entity-node-id
1503                               (get-text-property (point)
1504                                                  'mime-view-entity))))
1505                      (or (and (>= (setq i (- (length rc) len)) 0)
1506                               (equal entity-node-id (nthcdr i rc)))
1507                          (throw 'tag nil)))
1508                    (setq p-end (or (next-single-property-change
1509                                     (point) 'mime-view-entity)
1510                                    (point-max)))))
1511                (setq p-end (point-max))))))
1512     (vector p-beg p-end entity)))
1513
1514
1515 ;;; @@ playing
1516 ;;;
1517
1518 (autoload 'mime-preview-play-current-entity "mime-play"
1519   "Play current entity." t)
1520
1521 (defun mime-preview-extract-current-entity (&optional ignore-examples)
1522   "Extract current entity into file (maybe).
1523 It decodes current entity to call internal or external method as
1524 \"extract\" mode.  The method is selected from variable
1525 `mime-acting-condition'."
1526   (interactive "P")
1527   (mime-preview-play-current-entity ignore-examples "extract"))
1528
1529 (defun mime-preview-print-current-entity (&optional ignore-examples)
1530   "Print current entity (maybe).
1531 It decodes current entity to call internal or external method as
1532 \"print\" mode.  The method is selected from variable
1533 `mime-acting-condition'."
1534   (interactive "P")
1535   (mime-preview-play-current-entity ignore-examples "print"))
1536
1537
1538 ;;; @@ following
1539 ;;;
1540
1541 (defun mime-preview-follow-current-entity ()
1542   "Write follow message to current entity.
1543 It calls following-method selected from variable
1544 `mime-preview-following-method-alist'."
1545   (interactive)
1546   (let ((entity (mime-preview-find-boundary-info t))
1547         p-beg p-end
1548         pb-beg)
1549     (setq p-beg (aref entity 0)
1550           p-end (aref entity 1)
1551           entity (aref entity 2))
1552     (if (get-text-property p-beg 'mime-view-entity-body)
1553         (setq pb-beg p-beg)
1554       (setq pb-beg
1555             (next-single-property-change
1556              p-beg 'mime-view-entity-body nil
1557              (or (next-single-property-change p-beg 'mime-view-entity)
1558                  p-end))))
1559     (let* ((mode (mime-preview-original-major-mode 'recursive))
1560            (entity-node-id (mime-entity-node-id entity))
1561            (new-name
1562             (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1563            new-buf
1564            (the-buf (current-buffer))
1565            fields)
1566       (save-excursion
1567         (set-buffer (setq new-buf (get-buffer-create new-name)))
1568         (erase-buffer)
1569         (insert ?\n)
1570         (insert-buffer-substring the-buf pb-beg p-end)
1571         (goto-char (point-min))
1572         (let ((current-entity
1573                (if (and (eq (mime-entity-media-type entity) 'message)
1574                         (eq (mime-entity-media-subtype entity) 'rfc822))
1575                    (car (mime-entity-children entity))
1576                  entity)))
1577           (while (and current-entity
1578                       (if (and (eq (mime-entity-media-type
1579                                     current-entity) 'message)
1580                                (eq (mime-entity-media-subtype
1581                                     current-entity) 'rfc822))
1582                           nil
1583                         (mime-insert-header current-entity fields)
1584                         t))
1585             (setq fields (std11-collect-field-names)
1586                   current-entity (mime-entity-parent current-entity))))
1587         (let ((rest mime-view-following-required-fields-list)
1588               field-name ret)
1589           (while rest
1590             (setq field-name (car rest))
1591             (or (std11-field-body field-name)
1592                 (progn
1593                   (save-excursion
1594                     (set-buffer the-buf)
1595                     (let ((entity (when mime-mother-buffer
1596                                     (set-buffer mime-mother-buffer)
1597                                     (get-text-property (point)
1598                                                        'mime-view-entity))))
1599                       (while (and entity
1600                                   (null (setq ret (mime-entity-fetch-field
1601                                                    entity field-name))))
1602                         (setq entity (mime-entity-parent entity)))))
1603                   (if ret
1604                       (insert (concat field-name ": " ret "\n")))))
1605             (setq rest (cdr rest)))))
1606       (let ((f (cdr (assq mode mime-preview-following-method-alist))))
1607         (if (functionp f)
1608             (funcall f new-buf)
1609           (message
1610            (format
1611             "Sorry, following method for %s is not implemented yet."
1612             mode)))))))
1613
1614
1615 ;;; @@ moving
1616 ;;;
1617
1618 (defun mime-preview-move-to-upper ()
1619   "Move to upper entity.
1620 If there is no upper entity, call function `mime-preview-quit'."
1621   (interactive)
1622   (let (cinfo)
1623     (while (null (setq cinfo
1624                        (get-text-property (point) 'mime-view-entity)))
1625       (backward-char))
1626     (let ((r (mime-entity-parent cinfo))
1627           point)
1628       (catch 'tag
1629         (while (setq point (previous-single-property-change
1630                             (point) 'mime-view-entity))
1631           (goto-char point)
1632           (when (eq r (get-text-property (point) 'mime-view-entity))
1633             (if (or (eq mime-preview-move-scroll t)
1634                     (and mime-preview-move-scroll
1635                          (>= point
1636                              (save-excursion
1637                                (move-to-window-line -1)
1638                                (forward-line (* -1 next-screen-context-lines))
1639                                (beginning-of-line)
1640                                (point)))))
1641                 (recenter next-screen-context-lines))
1642             (throw 'tag t)))
1643         (mime-preview-quit)))))
1644
1645 (defun mime-preview-move-to-previous ()
1646   "Move to previous entity.
1647 If there is no previous entity, it calls function registered in
1648 variable `mime-preview-over-to-previous-method-alist'."
1649   (interactive)
1650   (while (and (not (bobp))
1651               (null (get-text-property (point) 'mime-view-entity)))
1652     (backward-char))
1653   (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1654     (if (and point
1655              (>= point (point-min)))
1656         (if (get-text-property (1- point) 'mime-view-entity)
1657             (progn (goto-char point)
1658                    (if
1659                     (or (eq mime-preview-move-scroll t)
1660                         (and mime-preview-move-scroll
1661                              (<= point
1662                                 (save-excursion
1663                                   (move-to-window-line 0)
1664                                   (forward-line next-screen-context-lines)
1665                                   (end-of-line)
1666                                   (point)))))
1667                         (recenter (* -1 next-screen-context-lines))))
1668           (goto-char (1- point))
1669           (mime-preview-move-to-previous))
1670       (let ((f (assq (mime-preview-original-major-mode)
1671                      mime-preview-over-to-previous-method-alist)))
1672         (if f
1673             (funcall (cdr f)))))))
1674
1675 (defun mime-preview-move-to-next ()
1676   "Move to next entity.
1677 If there is no previous entity, it calls function registered in
1678 variable `mime-preview-over-to-next-method-alist'."
1679   (interactive)
1680   (while (and (not (eobp))
1681               (null (get-text-property (point) 'mime-view-entity)))
1682     (forward-char))
1683   (let ((point (next-single-property-change (point) 'mime-view-entity)))
1684     (if (and point
1685              (<= point (point-max)))
1686         (progn
1687           (goto-char point)
1688           (if (null (get-text-property point 'mime-view-entity))
1689               (mime-preview-move-to-next)
1690             (and
1691              (or (eq mime-preview-move-scroll t)
1692                  (and mime-preview-move-scroll
1693                       (>= point
1694                          (save-excursion
1695                            (move-to-window-line -1)
1696                            (forward-line
1697                             (* -1 next-screen-context-lines))
1698                            (beginning-of-line)
1699                            (point)))))
1700                  (recenter next-screen-context-lines))))
1701       (let ((f (assq (mime-preview-original-major-mode)
1702                      mime-preview-over-to-next-method-alist)))
1703         (if f
1704             (funcall (cdr f)))))))
1705
1706 (defun mime-preview-scroll-up-entity (&optional h)
1707   "Scroll up current entity.
1708 If reached to (point-max), it calls function registered in variable
1709 `mime-preview-over-to-next-method-alist'."
1710   (interactive)
1711   (if (eobp)
1712       (let ((f (assq (mime-preview-original-major-mode)
1713                      mime-preview-over-to-next-method-alist)))
1714         (if f
1715             (funcall (cdr f))))
1716     (let ((point
1717            (or (next-single-property-change (point) 'mime-view-entity)
1718                (point-max)))
1719           (bottom (window-end (selected-window))))
1720       (if (and (not h)
1721                (> bottom point))
1722           (progn (goto-char point)
1723                  (recenter next-screen-context-lines))
1724         (condition-case nil
1725             (scroll-up h)
1726           (end-of-buffer
1727            (goto-char (point-max))))))))
1728
1729 (defun mime-preview-scroll-down-entity (&optional h)
1730   "Scroll down current entity.
1731 If reached to (point-min), it calls function registered in variable
1732 `mime-preview-over-to-previous-method-alist'."
1733   (interactive)
1734   (if (bobp)
1735       (let ((f (assq (mime-preview-original-major-mode)
1736                      mime-preview-over-to-previous-method-alist)))
1737         (if f
1738             (funcall (cdr f))))
1739     (let ((point
1740            (or (previous-single-property-change (point) 'mime-view-entity)
1741                (point-min)))
1742           (top (window-start (selected-window))))
1743       (if (and (not h)
1744                (< top point))
1745           (progn (goto-char point)
1746                  (recenter (* -1 next-screen-context-lines)))
1747         (condition-case nil
1748             (scroll-down h)
1749           (beginning-of-buffer
1750            (goto-char (point-min))))))))
1751
1752 (defun mime-preview-next-line-entity (&optional lines)
1753   "Scroll up one line (or prefix LINES lines).
1754 If LINES is negative, scroll down LINES lines."
1755   (interactive "p")
1756   (mime-preview-scroll-up-entity (or lines 1)))
1757
1758 (defun mime-preview-previous-line-entity (&optional lines)
1759   "Scrroll down one line (or prefix LINES lines).
1760 If LINES is negative, scroll up LINES lines."
1761   (interactive "p")
1762   (mime-preview-scroll-down-entity (or lines 1)))
1763
1764
1765 ;;; @@ display
1766 ;;;
1767
1768 (defun mime-view-guess-encoding (entity situation)
1769   (or (cdr (assq '*encoding situation))
1770       (cdr (assq 'encoding situation))
1771       (mime-entity-encoding entity)
1772       "7bit"))
1773
1774 (defun mime-view-read-encoding (entity situation)
1775   (let* ((default-encoding
1776            (mime-view-guess-encoding entity situation))
1777          (encoding
1778           (completing-read
1779            "Content Transfer Encoding: "
1780            (mime-encoding-alist) nil t default-encoding)))
1781     (unless (or (string= encoding "")
1782                 (string= encoding default-encoding))
1783       encoding)))
1784
1785 (defun mime-view-guess-charset (entity situation)
1786   (or (static-if (fboundp 'coding-system-to-mime-charset)
1787           ;; might be overridden by `universal-coding-system-argument'.
1788           (and coding-system-for-read
1789                (coding-system-to-mime-charset coding-system-for-read)))
1790       (cdr (assq '*charset situation))
1791       (cdr (assq 'charset situation))
1792       (let ((charset (cdr (assoc "charset" (mime-entity-parameters entity)))))
1793         (if charset
1794             (intern (downcase charset))))
1795       default-mime-charset))
1796
1797 (defun mime-view-read-charset (entity situation)
1798   (static-if (featurep 'mule)
1799       (let* ((default-charset
1800                (mime-view-guess-charset entity situation))
1801              (charset
1802               (intern (completing-read "MIME-charset: "
1803                                        (mapcar
1804                                         (lambda (sym)
1805                                           (list (symbol-name sym)))
1806                                         (mime-charset-list))
1807                                        nil t
1808                                        (symbol-name default-charset)))))
1809         (unless (eq charset default-charset)
1810           charset))
1811     default-charset))
1812
1813 (defun mime-preview-toggle-display (type &optional display)
1814   (let ((situation (mime-preview-find-boundary-info))
1815         (sym (intern (concat "*" (symbol-name type))))
1816         entity p-beg p-end encoding charset)
1817     (setq p-beg (aref situation 0)
1818           p-end (aref situation 1)
1819           entity (aref situation 2)
1820           situation (get-text-property p-beg 'mime-view-situation))
1821     (cond ((eq display 'invisible)
1822            (setq display nil))
1823           (display)
1824           (t
1825            (setq display
1826                  (memq (cdr (or (assq sym situation)
1827                                 (assq type situation)))
1828                        '(nil invisible)))))
1829     (setq situation (put-alist sym (if display
1830                                        'visible
1831                                      'invisible)
1832                                situation))
1833     (when (and current-prefix-arg
1834                (eq (cdr (assq sym situation)) 'visible))
1835       (if (setq encoding (mime-view-read-encoding entity situation))
1836           (setq situation (put-alist '*encoding encoding situation)))
1837       (if (setq charset (mime-view-read-charset entity situation))
1838           (setq situation (put-alist '*charset charset situation))))
1839     (save-excursion
1840       (let ((inhibit-read-only t))
1841         (delete-region p-beg p-end)
1842         (mime-display-entity entity situation)))
1843     (let ((ret (assoc situation mime-preview-situation-example-list)))
1844       (if ret
1845           (setcdr ret (1+ (cdr ret)))
1846         (add-to-list 'mime-preview-situation-example-list
1847                      (cons situation 0))))))
1848
1849 (defun mime-preview-toggle-header (&optional force-visible)
1850   (interactive "P")
1851   (mime-preview-toggle-display 'header force-visible))
1852
1853 (defun mime-preview-toggle-content (&optional force-visible)
1854   (interactive "P")
1855   (mime-preview-toggle-display 'body force-visible))
1856
1857 (defun mime-preview-show-header ()
1858   (interactive)
1859   (mime-preview-toggle-display 'header 'visible))
1860
1861 (defun mime-preview-show-content ()
1862   (interactive)
1863   (mime-preview-toggle-display 'body 'visible))
1864
1865 (defun mime-preview-hide-header ()
1866   (interactive)
1867   (mime-preview-toggle-display 'header 'invisible))
1868
1869 (defun mime-preview-hide-content ()
1870   (interactive)
1871   (mime-preview-toggle-display 'body 'invisible))
1872
1873     
1874 ;;; @@ quitting
1875 ;;;
1876
1877 (defun mime-preview-quit ()
1878   "Quit from MIME-preview buffer.
1879 It calls function registered in variable
1880 `mime-preview-quitting-method-alist'."
1881   (interactive)
1882   (let ((r (assq (mime-preview-original-major-mode)
1883                  mime-preview-quitting-method-alist)))
1884     (if r
1885         (funcall (cdr r)))))
1886
1887 (defun mime-preview-kill-buffer ()
1888   (interactive)
1889   (kill-buffer (current-buffer)))
1890
1891
1892 ;;; @ end
1893 ;;;
1894
1895 (provide 'mime-view)
1896
1897 (eval-when-compile
1898   (setq mime-situation-examples-file nil)
1899   ;; to avoid to read situation-examples-file at compile time.
1900   )
1901
1902 (mime-view-read-situation-examples-file)
1903
1904 ;;; mime-view.el ends here