* mime-bbdb.el: Add `mime-bbdb/define-keys' to `mime-view-mode-map'
[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             (let ((font-lock-verbose nil))
987               ;; I find font-lock a bit too verbose.
988               (font-lock-fontify-buffer))
989             ;; By default, XEmacs font-lock uses non-duplicable text
990             ;; properties.  This code forces all the text properties
991             ;; to be copied along with the text.
992             (static-when (fboundp 'extent-list)
993               (map-extents (lambda (ext ignored)
994                              (set-extent-property ext 'duplicable t)
995                              nil)
996                            nil nil nil nil nil 'text-prop)))
997           (insert-buffer-substring buffer))
998       (kill-buffer buffer))))
999
1000 (defun mime-display-application/emacs-lisp (entity situation)
1001   (save-restriction
1002     (narrow-to-region (point-max)(point-max))
1003     (mime-view-insert-fontified-text-content entity situation 'emacs-lisp-mode)
1004     (run-hooks 'mime-text-decode-hook 'mime-display-text/plain-hook)))
1005
1006
1007 ;;; @ acting-condition
1008 ;;;
1009
1010 (defvar mime-acting-condition nil
1011   "Condition-tree about how to process entity.")
1012
1013 (defun mime-view-read-mailcap-files (&optional files)
1014   (or files
1015       (setq files mime-view-mailcap-files))
1016   (let (entries file)
1017     (while files
1018       (setq file (car files))
1019       (if (file-readable-p file)
1020           (setq entries (append entries (mime-parse-mailcap-file file))))
1021       (setq files (cdr files)))
1022     (while entries
1023       (let ((entry (car entries))
1024             view print shared)
1025         (while entry
1026           (let* ((field (car entry))
1027                  (field-type (car field)))
1028             (cond ((eq field-type 'view)  (setq view field))
1029                   ((eq field-type 'print) (setq print field))
1030                   ((memq field-type '(compose composetyped edit)))
1031                   (t (setq shared (cons field shared))))
1032             )
1033           (setq entry (cdr entry)))
1034         (setq shared (nreverse shared))
1035         (ctree-set-calist-with-default
1036          'mime-acting-condition
1037          (append shared (list '(mode . "play")(cons 'method (cdr view)))))
1038         (if print
1039             (ctree-set-calist-with-default
1040              'mime-acting-condition
1041              (append shared
1042                      (list '(mode . "print")(cons 'method (cdr view)))))))
1043       (setq entries (cdr entries)))))
1044
1045 (mime-view-read-mailcap-files)
1046
1047 (ctree-set-calist-strictly
1048  'mime-acting-condition
1049  '((type . application)(subtype . octet-stream)
1050    (mode . "play")
1051    (method . mime-detect-content)))
1052
1053 (ctree-set-calist-with-default
1054  'mime-acting-condition
1055  '((mode . "extract")
1056    (method . mime-save-content)))
1057
1058 (ctree-set-calist-strictly
1059  'mime-acting-condition
1060  '((type . text)(subtype . x-rot13-47)(mode . "play")
1061    (method . mime-view-caesar)))
1062 (ctree-set-calist-strictly
1063  'mime-acting-condition
1064  '((type . text)(subtype . x-rot13-47-48)(mode . "play")
1065    (method . mime-view-caesar)))
1066
1067 (ctree-set-calist-strictly
1068  'mime-acting-condition
1069  '((type . message)(subtype . rfc822)(mode . "play")
1070    (method . mime-view-message/rfc822)))
1071 (ctree-set-calist-strictly
1072  'mime-acting-condition
1073  '((type . message)(subtype . partial)(mode . "play")
1074    (method . mime-store-message/partial-piece)))
1075
1076 (ctree-set-calist-strictly
1077  'mime-acting-condition
1078  '((type . message)(subtype . external-body)
1079    ("access-type" . "anon-ftp")
1080    (method . mime-view-message/external-anon-ftp)))
1081
1082 (ctree-set-calist-strictly
1083  'mime-acting-condition
1084  '((type . message)(subtype . external-body)
1085    ("access-type" . "url")
1086    (method . mime-view-message/external-url)))
1087
1088 (ctree-set-calist-strictly
1089  'mime-acting-condition
1090  '((type . application)(subtype . octet-stream)
1091    (method . mime-save-content)))
1092
1093
1094 ;;; @ quitting method
1095 ;;;
1096
1097 (defvar mime-preview-quitting-method-alist
1098   '((mime-show-message-mode
1099      . mime-preview-quitting-method-for-mime-show-message-mode))
1100   "Alist of major-mode vs. quitting-method of mime-view.")
1101
1102 (defvar mime-preview-over-to-previous-method-alist nil
1103   "Alist of major-mode vs. over-to-previous-method of mime-view.")
1104
1105 (defvar mime-preview-over-to-next-method-alist nil
1106   "Alist of major-mode vs. over-to-next-method of mime-view.")
1107
1108
1109 ;;; @ following method
1110 ;;;
1111
1112 (defvar mime-preview-following-method-alist nil
1113   "Alist of major-mode vs. following-method of mime-view.")
1114
1115 (defvar mime-view-following-required-fields-list
1116   '("From"))
1117
1118
1119 ;;; @ buffer setup
1120 ;;;
1121
1122 (defun mime-display-entity (entity &optional situation
1123                                    default-situation preview-buffer)
1124   (or preview-buffer
1125       (setq preview-buffer (current-buffer)))
1126   (let* (e nb ne nhb nbb)
1127     (in-calist-package 'mime-view)
1128     (or situation
1129         (setq situation
1130               (mime-find-entity-preview-situation entity default-situation)))
1131     (let ((button-is-invisible
1132            (eq (cdr (or (assq '*entity-button situation)
1133                         (assq 'entity-button situation)))
1134                'invisible))
1135           (header-is-visible
1136            (eq (cdr (or (assq '*header situation)
1137                         (assq 'header situation)))
1138                'visible))
1139           (body-is-visible
1140            (eq (cdr (or (assq '*body situation)
1141                         (assq 'body situation)))
1142                'visible))
1143           (children (mime-entity-children entity)))
1144       (set-buffer preview-buffer)
1145       (setq nb (point))
1146       (narrow-to-region nb nb)
1147       (or button-is-invisible
1148           ;; (if (mime-view-entity-button-visible-p entity)
1149           (mime-view-insert-entity-button entity)
1150           ;;   )
1151           )
1152       (if header-is-visible
1153           (let ((header-presentation-method
1154                  (or (cdr (assq 'header-presentation-method situation))
1155                      (cdr (assq (cdr (assq 'major-mode situation))
1156                                 mime-header-presentation-method-alist)))))
1157             (setq nhb (point))
1158             (if header-presentation-method
1159                 (funcall header-presentation-method entity situation)
1160               (mime-insert-header entity
1161                                   mime-view-ignored-field-list
1162                                   mime-view-visible-field-list))
1163             (mime-add-url-buttons)
1164             (run-hooks 'mime-display-header-hook)
1165             (put-text-property nhb (point-max) 'mime-view-entity-header entity)
1166             (goto-char (point-max))
1167             (insert "\n")))
1168       (setq nbb (point))
1169       (unless children
1170         (if body-is-visible
1171             (let ((body-presentation-method
1172                    (cdr (assq 'body-presentation-method situation))))
1173               (if (functionp body-presentation-method)
1174                   (funcall body-presentation-method entity situation)
1175                 (mime-display-text/plain entity situation)))
1176           (when button-is-invisible
1177             (goto-char (point-max))
1178             (mime-view-insert-entity-button entity))
1179           (unless header-is-visible
1180             (goto-char (point-max))
1181             (insert "\n"))))
1182       (setq ne (point-max))
1183       (widen)
1184       (put-text-property nb ne 'mime-view-entity entity)
1185       (put-text-property nb ne 'mime-view-situation situation)
1186       (put-text-property nbb ne 'mime-view-entity-body entity)
1187       (goto-char ne)
1188       (if (and children body-is-visible)
1189           (let ((body-presentation-method
1190                  (cdr (assq 'body-presentation-method situation))))
1191             (if (functionp body-presentation-method)
1192                 (funcall body-presentation-method entity situation)
1193               (mime-display-multipart/mixed entity situation)))))))
1194
1195
1196 ;;; @ MIME viewer mode
1197 ;;;
1198
1199 (defconst mime-view-popup-menu-list
1200   '("MIME-View"
1201     ["Move to upper entity" mime-preview-move-to-upper]
1202     ["Move to previous entity" mime-preview-move-to-previous]
1203     ["Move to next entity" mime-preview-move-to-next]
1204     ["Scroll-down" mime-preview-scroll-down-entity]
1205     ["Scroll-up" mime-preview-scroll-up-entity]
1206     ["Play current entity" mime-preview-play-current-entity]
1207     ["Extract current entity" mime-preview-extract-current-entity]
1208     ["Print current entity" mime-preview-print-current-entity])
1209   "Menu for MIME Viewer")
1210
1211 (defun mime-view-popup-menu (event)
1212   "Popup the menu in the MIME Viewer buffer"
1213   (interactive "@e")
1214   (mime-popup-menu-popup mime-view-popup-menu-list event))
1215
1216 ;;; The current local map is taken precendence over `widget-keymap',
1217 ;;; because GNU Emacs' widget implementation doesn't set `local-map' property.
1218 ;;;  So we need to specify derivation.
1219 (defvar widget-keymap)
1220 (defun mime-view-maybe-inherit-widget-keymap ()
1221   (when (boundp 'widget-keymap)
1222     (set-keymap-parent (current-local-map) widget-keymap)))
1223
1224 (add-hook 'mime-view-mode-hook 'mime-view-maybe-inherit-widget-keymap)
1225           
1226 (defun mime-view-define-keymap (&optional default)
1227   (let ((mime-view-mode-map (if (keymapp default)
1228                                 (copy-keymap default)
1229                               (make-sparse-keymap))))
1230     (define-key mime-view-mode-map
1231       "u"        (function mime-preview-move-to-upper))
1232     (define-key mime-view-mode-map
1233       "p"        (function mime-preview-move-to-previous))
1234     (define-key mime-view-mode-map
1235       "n"        (function mime-preview-move-to-next))
1236     (define-key mime-view-mode-map
1237       "\e\t"     (function mime-preview-move-to-previous))
1238     (define-key mime-view-mode-map
1239       "\t"       (function mime-preview-move-to-next))
1240     (define-key mime-view-mode-map
1241       " "        (function mime-preview-scroll-up-entity))
1242     (define-key mime-view-mode-map
1243       "\M- "     (function mime-preview-scroll-down-entity))
1244     (define-key mime-view-mode-map
1245       "\177"     (function mime-preview-scroll-down-entity))
1246     (define-key mime-view-mode-map
1247       "\C-m"     (function mime-preview-next-line-entity))
1248     (define-key mime-view-mode-map
1249       "\C-\M-m"  (function mime-preview-previous-line-entity))
1250     (define-key mime-view-mode-map
1251       "v"        (function mime-preview-play-current-entity))
1252     (define-key mime-view-mode-map
1253       "e"        (function mime-preview-extract-current-entity))
1254     (define-key mime-view-mode-map
1255       "\C-c\C-p" (function mime-preview-print-current-entity))
1256
1257     (define-key mime-view-mode-map
1258       "\C-c\C-t\C-f" (function mime-preview-toggle-header))
1259     (define-key mime-view-mode-map
1260       "\C-c\C-th" (function mime-preview-toggle-header))
1261     (define-key mime-view-mode-map
1262       "\C-c\C-t\C-c" (function mime-preview-toggle-content))
1263
1264     (define-key mime-view-mode-map
1265       "\C-c\C-v\C-f" (function mime-preview-show-header))
1266     (define-key mime-view-mode-map
1267       "\C-c\C-vh" (function mime-preview-show-header))
1268     (define-key mime-view-mode-map
1269       "\C-c\C-v\C-c" (function mime-preview-show-content))
1270
1271     (define-key mime-view-mode-map
1272       "\C-c\C-d\C-f" (function mime-preview-hide-header))
1273     (define-key mime-view-mode-map
1274       "\C-c\C-dh" (function mime-preview-hide-header))
1275     (define-key mime-view-mode-map
1276       "\C-c\C-d\C-c" (function mime-preview-hide-content))
1277
1278     (define-key mime-view-mode-map
1279       "a"        (function mime-preview-follow-current-entity))
1280     (define-key mime-view-mode-map
1281       "q"        (function mime-preview-quit))
1282     (define-key mime-view-mode-map
1283       "\C-c\C-x" (function mime-preview-kill-buffer))
1284     ;; (define-key mime-view-mode-map
1285     ;;   "<"        (function beginning-of-buffer))
1286     ;; (define-key mime-view-mode-map
1287     ;;   ">"        (function end-of-buffer))
1288     (define-key mime-view-mode-map
1289       "?"        (function describe-mode))
1290     (define-key mime-view-mode-map
1291       [tab] (function mime-preview-move-to-next))
1292     (define-key mime-view-mode-map
1293       [delete] (function mime-preview-scroll-down-entity))
1294     (define-key mime-view-mode-map
1295       [backspace] (function mime-preview-scroll-down-entity))
1296     (if (functionp default)
1297         (if (featurep 'xemacs)
1298             (set-keymap-default-binding mime-view-mode-map default)
1299           (setq mime-view-mode-map
1300                 (append mime-view-mode-map (list (cons t default))))))
1301     (define-key mime-view-mode-map
1302       [down-mouse-3] (function mime-view-popup-menu))
1303     ;; (run-hooks 'mime-view-define-keymap-hook)
1304     mime-view-mode-map))
1305
1306 (defvar mime-view-mode-default-map (mime-view-define-keymap))
1307
1308
1309 (defsubst mime-maybe-hide-echo-buffer ()
1310   "Clear mime-echo buffer and delete window for it."
1311   (let ((buf (get-buffer mime-echo-buffer-name)))
1312     (if buf
1313         (save-excursion
1314           (set-buffer buf)
1315           (erase-buffer)
1316           (let ((win (get-buffer-window buf)))
1317             (if win
1318                 (delete-window win)))
1319           (bury-buffer buf)))))
1320
1321 (defvar mime-view-redisplay nil)
1322
1323 ;;;###autoload
1324 (defun mime-display-message (message &optional preview-buffer
1325                                      mother default-keymap-or-function
1326                                      original-major-mode keymap)
1327   "View MESSAGE in MIME-View mode.
1328
1329 Optional argument PREVIEW-BUFFER specifies the buffer of the
1330 presentation.  It must be either nil or a name of preview buffer.
1331
1332 Optional argument MOTHER specifies mother-buffer of the preview-buffer.
1333
1334 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
1335 function.  If it is a keymap, keymap of MIME-View mode will be added
1336 to it.  If it is a function, it will be bound as default binding of
1337 keymap of MIME-View mode.
1338
1339 Optional argument ORIGINAL-MAJOR-MODE is major-mode of representation
1340 buffer of MESSAGE.  If it is nil, current `major-mode' is used.
1341
1342 Optional argument KEYMAP is keymap of MIME-View mode.  If it is
1343 non-nil, DEFAULT-KEYMAP-OR-FUNCTION is ignored.  If it is nil,
1344 `mime-view-mode-default-map' is used."
1345   (mime-maybe-hide-echo-buffer)
1346   (let ((win-conf (current-window-configuration)))
1347     (or preview-buffer
1348         (setq preview-buffer
1349               (concat "*Preview-" (mime-entity-name message) "*")))
1350     (or original-major-mode
1351         (setq original-major-mode major-mode))
1352     (let ((inhibit-read-only t))
1353       (set-buffer (get-buffer-create preview-buffer))
1354       (widen)
1355       (erase-buffer)
1356       (if mother
1357           (setq mime-mother-buffer mother))
1358       (setq mime-preview-original-window-configuration win-conf)
1359       (setq major-mode 'mime-view-mode)
1360       (setq mode-name "MIME-View")
1361       (mime-display-entity message nil
1362                            `((entity-button . invisible)
1363                              (header . visible)
1364                              (major-mode . ,original-major-mode))
1365                            preview-buffer)
1366       (use-local-map
1367        (or keymap
1368            (if default-keymap-or-function
1369                (mime-view-define-keymap default-keymap-or-function)
1370              mime-view-mode-default-map)))
1371       (let ((point
1372              (next-single-property-change (point-min) 'mime-view-entity)))
1373         (if point
1374             (goto-char point)
1375           (goto-char (point-min))
1376           (search-forward "\n\n" nil t)))
1377       (run-hooks 'mime-view-mode-hook)
1378       (set-buffer-modified-p nil)
1379       (setq buffer-read-only t)
1380       preview-buffer)))
1381
1382 ;;;###autoload
1383 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
1384                                    default-keymap-or-function
1385                                    representation-type)
1386   "View RAW-BUFFER in MIME-View mode.
1387 Optional argument PREVIEW-BUFFER is either nil or a name of preview
1388 buffer.
1389 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
1390 function.  If it is a keymap, keymap of MIME-View mode will be added
1391 to it.  If it is a function, it will be bound as default binding of
1392 keymap of MIME-View mode.
1393 Optional argument REPRESENTATION-TYPE is representation-type of
1394 message.  It must be nil, `binary' or `cooked'.  If it is nil,
1395 `cooked' is used as default."
1396   (interactive)
1397   (or raw-buffer
1398       (setq raw-buffer (current-buffer)))
1399   (or representation-type
1400       (setq representation-type
1401             (save-excursion
1402               (set-buffer raw-buffer)
1403               (cdr (or (assq major-mode mime-raw-representation-type-alist)
1404                        (assq t mime-raw-representation-type-alist))))))
1405   (if (eq representation-type 'binary)
1406       (setq representation-type 'buffer))
1407   (setq preview-buffer (mime-display-message
1408                         (mime-open-entity representation-type raw-buffer)
1409                         preview-buffer mother default-keymap-or-function))
1410   (or (get-buffer-window preview-buffer)
1411       (let ((r-win (get-buffer-window raw-buffer)))
1412         (if r-win
1413             (set-window-buffer r-win preview-buffer)
1414           (let ((m-win (and mother (get-buffer-window mother))))
1415             (if m-win
1416                 (set-window-buffer m-win preview-buffer)
1417               (switch-to-buffer preview-buffer)))))))
1418
1419 (defun mime-view-mode (&optional mother ctl encoding
1420                                  raw-buffer preview-buffer
1421                                  default-keymap-or-function)
1422   "Major mode for viewing MIME message.
1423
1424 Here is a list of the standard keys for mime-view-mode.
1425
1426 key             feature
1427 ---             -------
1428
1429 u               Move to upper content
1430 p or M-TAB      Move to previous content
1431 n or TAB        Move to next content
1432 SPC             Scroll up or move to next content
1433 M-SPC or DEL    Scroll down or move to previous content
1434 RET             Move to next line
1435 M-RET           Move to previous line
1436 v               Decode current content as `play mode'
1437 e               Decode current content as `extract mode'
1438 C-c C-p         Decode current content as `print mode'
1439 a               Followup to current content.
1440 q               Quit
1441 button-2        Move to point under the mouse cursor
1442                 and decode current content as `play mode'
1443 "
1444   (interactive)
1445   (unless mime-view-redisplay
1446     (save-excursion
1447       (if raw-buffer (set-buffer raw-buffer))
1448       (let ((type
1449              (cdr
1450               (or (assq major-mode mime-raw-representation-type-alist)
1451                   (assq t mime-raw-representation-type-alist)))))
1452         (if (eq type 'binary)
1453             (setq type 'buffer))
1454         (setq mime-message-structure (mime-open-entity type raw-buffer))
1455         (or (mime-entity-content-type mime-message-structure)
1456             (mime-entity-set-content-type mime-message-structure ctl)))
1457       (or (mime-entity-encoding mime-message-structure)
1458           (mime-entity-set-encoding mime-message-structure encoding))))
1459   (mime-display-message mime-message-structure preview-buffer
1460                         mother default-keymap-or-function))
1461
1462
1463 ;;; @@ utility
1464 ;;;
1465
1466 (defun mime-preview-find-boundary-info (&optional get-mother)
1467   (let (entity
1468         p-beg p-end
1469         entity-node-id len)
1470     (while (null (setq entity
1471                        (get-text-property (point) 'mime-view-entity)))
1472       (backward-char))
1473     (setq p-beg (previous-single-property-change (point) 'mime-view-entity))
1474     (setq entity-node-id (mime-entity-node-id entity))
1475     (setq len (length entity-node-id))
1476     (cond ((null p-beg)
1477            (setq p-beg
1478                  (if (eq (next-single-property-change (point-min)
1479                                                       'mime-view-entity)
1480                          (point))
1481                      (point)
1482                    (point-min))))
1483           ((eq (next-single-property-change p-beg 'mime-view-entity)
1484                (point))
1485            (setq p-beg (point))))
1486     (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1487     (cond ((null p-end)
1488            (setq p-end (point-max)))
1489           ((null entity-node-id)
1490            (setq p-end (point-max)))
1491           (get-mother
1492            (save-excursion
1493              (goto-char p-end)
1494              (catch 'tag
1495                (let (e i)
1496                  (while (setq e
1497                               (next-single-property-change
1498                                (point) 'mime-view-entity))
1499                    (goto-char e)
1500                    (let ((rc (mime-entity-node-id
1501                               (get-text-property (1- (point))
1502                                                  'mime-view-entity))))
1503                      (or (and (>= (setq i (- (length rc) len)) 0)
1504                               (equal entity-node-id (nthcdr i rc)))
1505                          (throw 'tag nil)))
1506                    (setq p-end e)))
1507                (setq p-end (point-max))))))
1508     (vector p-beg p-end entity)))
1509
1510
1511 ;;; @@ playing
1512 ;;;
1513
1514 (autoload 'mime-preview-play-current-entity "mime-play"
1515   "Play current entity." t)
1516
1517 (defun mime-preview-extract-current-entity (&optional ignore-examples)
1518   "Extract current entity into file (maybe).
1519 It decodes current entity to call internal or external method as
1520 \"extract\" mode.  The method is selected from variable
1521 `mime-acting-condition'."
1522   (interactive "P")
1523   (mime-preview-play-current-entity ignore-examples "extract"))
1524
1525 (defun mime-preview-print-current-entity (&optional ignore-examples)
1526   "Print current entity (maybe).
1527 It decodes current entity to call internal or external method as
1528 \"print\" mode.  The method is selected from variable
1529 `mime-acting-condition'."
1530   (interactive "P")
1531   (mime-preview-play-current-entity ignore-examples "print"))
1532
1533
1534 ;;; @@ following
1535 ;;;
1536
1537 (defun mime-preview-follow-current-entity ()
1538   "Write follow message to current entity.
1539 It calls following-method selected from variable
1540 `mime-preview-following-method-alist'."
1541   (interactive)
1542   (let ((entity (mime-preview-find-boundary-info t))
1543         p-beg p-end
1544         pb-beg)
1545     (setq p-beg (aref entity 0)
1546           p-end (aref entity 1)
1547           entity (aref entity 2))
1548     (if (get-text-property p-beg 'mime-view-entity-body)
1549         (setq pb-beg p-beg)
1550       (setq pb-beg
1551             (next-single-property-change
1552              p-beg 'mime-view-entity-body nil
1553              (or (next-single-property-change p-beg 'mime-view-entity)
1554                  p-end))))
1555     (let* ((mode (mime-preview-original-major-mode 'recursive))
1556            (entity-node-id (mime-entity-node-id entity))
1557            (new-name
1558             (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1559            new-buf
1560            (the-buf (current-buffer))
1561            fields)
1562       (save-excursion
1563         (set-buffer (setq new-buf (get-buffer-create new-name)))
1564         (erase-buffer)
1565         (insert ?\n)
1566         (insert-buffer-substring the-buf pb-beg p-end)
1567         (goto-char (point-min))
1568         (let ((current-entity
1569                (if (and (eq (mime-entity-media-type entity) 'message)
1570                         (eq (mime-entity-media-subtype entity) 'rfc822))
1571                    (car (mime-entity-children entity))
1572                  entity)))
1573           (while (and current-entity
1574                       (if (and (eq (mime-entity-media-type
1575                                     current-entity) 'message)
1576                                (eq (mime-entity-media-subtype
1577                                     current-entity) 'rfc822))
1578                           nil
1579                         (mime-insert-header current-entity fields)
1580                         t))
1581             (setq fields (std11-collect-field-names)
1582                   current-entity (mime-entity-parent current-entity))))
1583         (let ((rest mime-view-following-required-fields-list)
1584               field-name ret)
1585           (while rest
1586             (setq field-name (car rest))
1587             (or (std11-field-body field-name)
1588                 (progn
1589                   (save-excursion
1590                     (set-buffer the-buf)
1591                     (let ((entity (when mime-mother-buffer
1592                                     (set-buffer mime-mother-buffer)
1593                                     (get-text-property (point)
1594                                                        'mime-view-entity))))
1595                       (while (and entity
1596                                   (null (setq ret (mime-entity-fetch-field
1597                                                    entity field-name))))
1598                         (setq entity (mime-entity-parent entity)))))
1599                   (if ret
1600                       (insert (concat field-name ": " ret "\n")))))
1601             (setq rest (cdr rest)))))
1602       (let ((f (cdr (assq mode mime-preview-following-method-alist))))
1603         (if (functionp f)
1604             (funcall f new-buf)
1605           (message
1606            (format
1607             "Sorry, following method for %s is not implemented yet."
1608             mode)))))))
1609
1610
1611 ;;; @@ moving
1612 ;;;
1613
1614 (defun mime-preview-move-to-upper ()
1615   "Move to upper entity.
1616 If there is no upper entity, call function `mime-preview-quit'."
1617   (interactive)
1618   (let (cinfo)
1619     (while (null (setq cinfo
1620                        (get-text-property (point) 'mime-view-entity)))
1621       (backward-char))
1622     (let ((r (mime-entity-parent cinfo))
1623           point)
1624       (catch 'tag
1625         (while (setq point (previous-single-property-change
1626                             (point) 'mime-view-entity))
1627           (goto-char point)
1628           (when (eq r (get-text-property (point) 'mime-view-entity))
1629             (if (or (eq mime-preview-move-scroll t)
1630                     (and mime-preview-move-scroll
1631                          (>= point
1632                              (save-excursion
1633                                (move-to-window-line -1)
1634                                (forward-line (* -1 next-screen-context-lines))
1635                                (beginning-of-line)
1636                                (point)))))
1637                 (recenter next-screen-context-lines))
1638             (throw 'tag t)))
1639         (mime-preview-quit)))))
1640
1641 (defun mime-preview-move-to-previous ()
1642   "Move to previous entity.
1643 If there is no previous entity, it calls function registered in
1644 variable `mime-preview-over-to-previous-method-alist'."
1645   (interactive)
1646   (while (and (not (bobp))
1647               (null (get-text-property (point) 'mime-view-entity)))
1648     (backward-char))
1649   (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1650     (if (and point
1651              (>= point (point-min)))
1652         (if (get-text-property (1- point) 'mime-view-entity)
1653             (progn (goto-char point)
1654                    (if
1655                     (or (eq mime-preview-move-scroll t)
1656                         (and mime-preview-move-scroll
1657                              (<= point
1658                                 (save-excursion
1659                                   (move-to-window-line 0)
1660                                   (forward-line next-screen-context-lines)
1661                                   (end-of-line)
1662                                   (point)))))
1663                         (recenter (* -1 next-screen-context-lines))))
1664           (goto-char (1- point))
1665           (mime-preview-move-to-previous))
1666       (let ((f (assq (mime-preview-original-major-mode)
1667                      mime-preview-over-to-previous-method-alist)))
1668         (if f
1669             (funcall (cdr f)))))))
1670
1671 (defun mime-preview-move-to-next ()
1672   "Move to next entity.
1673 If there is no previous entity, it calls function registered in
1674 variable `mime-preview-over-to-next-method-alist'."
1675   (interactive)
1676   (while (and (not (eobp))
1677               (null (get-text-property (point) 'mime-view-entity)))
1678     (forward-char))
1679   (let ((point (next-single-property-change (point) 'mime-view-entity)))
1680     (if (and point
1681              (<= point (point-max)))
1682         (progn
1683           (goto-char point)
1684           (if (null (get-text-property point 'mime-view-entity))
1685               (mime-preview-move-to-next)
1686             (and
1687              (or (eq mime-preview-move-scroll t)
1688                  (and mime-preview-move-scroll
1689                       (>= point
1690                          (save-excursion
1691                            (move-to-window-line -1)
1692                            (forward-line
1693                             (* -1 next-screen-context-lines))
1694                            (beginning-of-line)
1695                            (point)))))
1696                  (recenter next-screen-context-lines))))
1697       (let ((f (assq (mime-preview-original-major-mode)
1698                      mime-preview-over-to-next-method-alist)))
1699         (if f
1700             (funcall (cdr f)))))))
1701
1702 (defun mime-preview-scroll-up-entity (&optional h)
1703   "Scroll up current entity.
1704 If reached to (point-max), it calls function registered in variable
1705 `mime-preview-over-to-next-method-alist'."
1706   (interactive)
1707   (if (eobp)
1708       (let ((f (assq (mime-preview-original-major-mode)
1709                      mime-preview-over-to-next-method-alist)))
1710         (if f
1711             (funcall (cdr f))))
1712     (let ((point
1713            (or (next-single-property-change (point) 'mime-view-entity)
1714                (point-max)))
1715           (bottom (window-end (selected-window))))
1716       (if (and (not h)
1717                (> bottom point))
1718           (progn (goto-char point)
1719                  (recenter next-screen-context-lines))
1720         (condition-case nil
1721             (scroll-up h)
1722           (end-of-buffer
1723            (goto-char (point-max))))))))
1724
1725 (defun mime-preview-scroll-down-entity (&optional h)
1726   "Scroll down current entity.
1727 If reached to (point-min), it calls function registered in variable
1728 `mime-preview-over-to-previous-method-alist'."
1729   (interactive)
1730   (if (bobp)
1731       (let ((f (assq (mime-preview-original-major-mode)
1732                      mime-preview-over-to-previous-method-alist)))
1733         (if f
1734             (funcall (cdr f))))
1735     (let ((point
1736            (or (previous-single-property-change (point) 'mime-view-entity)
1737                (point-min)))
1738           (top (window-start (selected-window))))
1739       (if (and (not h)
1740                (< top point))
1741           (progn (goto-char point)
1742                  (recenter (* -1 next-screen-context-lines)))
1743         (condition-case nil
1744             (scroll-down h)
1745           (beginning-of-buffer
1746            (goto-char (point-min))))))))
1747
1748 (defun mime-preview-next-line-entity (&optional lines)
1749   "Scroll up one line (or prefix LINES lines).
1750 If LINES is negative, scroll down LINES lines."
1751   (interactive "p")
1752   (mime-preview-scroll-up-entity (or lines 1)))
1753
1754 (defun mime-preview-previous-line-entity (&optional lines)
1755   "Scrroll down one line (or prefix LINES lines).
1756 If LINES is negative, scroll up LINES lines."
1757   (interactive "p")
1758   (mime-preview-scroll-down-entity (or lines 1)))
1759
1760
1761 ;;; @@ display
1762 ;;;
1763
1764 (defun mime-view-guess-encoding (entity situation)
1765   (or (cdr (assq '*encoding situation))
1766       (cdr (assq 'encoding situation))
1767       (mime-entity-encoding entity)
1768       "7bit"))
1769
1770 (defun mime-view-read-encoding (entity situation)
1771   (let* ((default-encoding
1772            (mime-view-guess-encoding entity situation))
1773          (encoding
1774           (completing-read
1775            "Content Transfer Encoding: "
1776            (mime-encoding-alist) nil t default-encoding)))
1777     (unless (or (string= encoding "")
1778                 (string= encoding default-encoding))
1779       encoding)))
1780
1781 (defun mime-view-guess-charset (entity situation)
1782   (or (static-if (fboundp 'coding-system-to-mime-charset)
1783           ;; might be overridden by `universal-coding-system-argument'.
1784           (and coding-system-for-read
1785                (coding-system-to-mime-charset coding-system-for-read)))
1786       (cdr (assq '*charset situation))
1787       (cdr (assq 'charset situation))
1788       (let ((charset (cdr (assoc "charset" (mime-entity-parameters entity)))))
1789         (if charset
1790             (intern (downcase charset))))
1791       default-mime-charset))
1792
1793 (defun mime-view-read-charset (entity situation)
1794   (static-if (featurep 'mule)
1795       (let* ((default-charset
1796                (mime-view-guess-charset entity situation))
1797              (charset
1798               (intern (completing-read "MIME-charset: "
1799                                        (mapcar
1800                                         (lambda (sym)
1801                                           (list (symbol-name sym)))
1802                                         (mime-charset-list))
1803                                        #'mime-charset-p t
1804                                        (symbol-name default-charset)))))
1805         (unless (eq charset default-charset)
1806           charset))
1807     default-charset))
1808
1809 (defun mime-preview-toggle-display (type &optional display)
1810   (let ((situation (mime-preview-find-boundary-info))
1811         (sym (intern (concat "*" (symbol-name type))))
1812         entity p-beg p-end encoding charset)
1813     (setq p-beg (aref situation 0)
1814           p-end (aref situation 1)
1815           entity (aref situation 2)
1816           situation (get-text-property p-beg 'mime-view-situation))
1817     (cond ((eq display 'invisible)
1818            (setq display nil))
1819           (display)
1820           (t
1821            (setq display
1822                  (memq (cdr (or (assq sym situation)
1823                                 (assq type situation)))
1824                        '(nil invisible)))))
1825     (setq situation (put-alist sym (if display
1826                                        'visible
1827                                      'invisible)
1828                                situation))
1829     (when (and current-prefix-arg
1830                (eq (cdr (assq sym situation)) 'visible))
1831       (if (setq encoding (mime-view-read-encoding entity situation))
1832           (setq situation (put-alist '*encoding encoding situation)))
1833       (if (setq charset (mime-view-read-charset entity situation))
1834           (setq situation (put-alist '*charset charset situation))))
1835     (save-excursion
1836       (let ((inhibit-read-only t))
1837         (delete-region p-beg p-end)
1838         (mime-display-entity entity situation)))
1839     (let ((ret (assoc situation mime-preview-situation-example-list)))
1840       (if ret
1841           (setcdr ret (1+ (cdr ret)))
1842         (add-to-list 'mime-preview-situation-example-list
1843                      (cons situation 0))))))
1844
1845 (defun mime-preview-toggle-header (&optional force-visible)
1846   (interactive "P")
1847   (mime-preview-toggle-display 'header force-visible))
1848
1849 (defun mime-preview-toggle-content (&optional force-visible)
1850   (interactive "P")
1851   (mime-preview-toggle-display 'body force-visible))
1852
1853 (defun mime-preview-show-header ()
1854   (interactive)
1855   (mime-preview-toggle-display 'header 'visible))
1856
1857 (defun mime-preview-show-content ()
1858   (interactive)
1859   (mime-preview-toggle-display 'body 'visible))
1860
1861 (defun mime-preview-hide-header ()
1862   (interactive)
1863   (mime-preview-toggle-display 'header 'invisible))
1864
1865 (defun mime-preview-hide-content ()
1866   (interactive)
1867   (mime-preview-toggle-display 'body 'invisible))
1868
1869     
1870 ;;; @@ quitting
1871 ;;;
1872
1873 (defun mime-preview-quit ()
1874   "Quit from MIME-preview buffer.
1875 It calls function registered in variable
1876 `mime-preview-quitting-method-alist'."
1877   (interactive)
1878   (let ((r (assq (mime-preview-original-major-mode)
1879                  mime-preview-quitting-method-alist)))
1880     (if r
1881         (funcall (cdr r)))))
1882
1883 (defun mime-preview-kill-buffer ()
1884   (interactive)
1885   (kill-buffer (current-buffer)))
1886
1887
1888 ;;; @ end
1889 ;;;
1890
1891 (provide 'mime-view)
1892
1893 (eval-when-compile
1894   (setq mime-situation-examples-file nil)
1895   ;; to avoid to read situation-examples-file at compile time.
1896   )
1897
1898 (mime-view-read-situation-examples-file)
1899
1900 ;;; mime-view.el ends here