badf0b4b1dbe17b6cc0ef9ba78c8201ae4977a18
[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             print-length print-level)
407         (with-temp-buffer
408           (insert ";;; " (file-name-nondirectory file) "\n")
409           (insert "\n;; This file is generated automatically by "
410                   mime-view-version "\n\n")
411           (insert ";;; Code:\n\n")
412           (if mime-preview-situation-example-list
413               (pp `(setq mime-preview-situation-example-list
414                          ',mime-preview-situation-example-list)
415                   (current-buffer)))
416           (if mime-acting-situation-example-list
417               (pp `(setq mime-acting-situation-example-list
418                          ',mime-acting-situation-example-list)
419                   (current-buffer)))
420           (insert "\n;;; "
421                   (file-name-nondirectory file)
422                   " ends here.\n")
423           (static-cond
424            ((boundp 'buffer-file-coding-system)
425             (setq buffer-file-coding-system
426                   mime-situation-examples-file-coding-system))
427            ((boundp 'file-coding-system)
428             (setq file-coding-system
429                   mime-situation-examples-file-coding-system)))
430           ;; (setq buffer-file-coding-system
431           ;;       mime-situation-examples-file-coding-system)
432           (setq buffer-file-name file)
433           (save-buffer)))))
434
435 (add-hook 'kill-emacs-hook 'mime-save-situation-examples)
436
437 (defun mime-reduce-situation-examples (situation-examples)
438   (let ((len (length situation-examples))
439         i ir ic j jr jc ret
440         dest d-i d-j
441         (max-sim 0) sim
442         min-det-ret det-ret
443         min-det-org det-org
444         min-freq freq)
445     (setq i 0
446           ir situation-examples)
447     (while (< i len)
448       (setq ic (car ir)
449             j 0
450             jr situation-examples)
451       (while (< j len)
452         (unless (= i j)
453           (setq jc (car jr))
454           (setq ret (mime-compare-situation-with-example (car ic)(car jc))
455                 sim (car ret)
456                 det-ret (+ (length (car ic))(length (car jc)))
457                 det-org (length (cdr ret))
458                 freq (+ (cdr ic)(cdr jc)))
459           (cond ((< max-sim sim)
460                  (setq max-sim sim
461                        min-det-ret det-ret
462                        min-det-org det-org
463                        min-freq freq
464                        d-i i
465                        d-j j
466                        dest (cons (cdr ret) freq)))
467                 ((= max-sim sim)
468                  (cond ((> min-det-ret det-ret)
469                         (setq min-det-ret det-ret
470                               min-det-org det-org
471                               min-freq freq
472                               d-i i
473                               d-j j
474                               dest (cons (cdr ret) freq)))
475                        ((= min-det-ret det-ret)
476                         (cond ((> min-det-org det-org)
477                                (setq min-det-org det-org
478                                      min-freq freq
479                                      d-i i
480                                      d-j j
481                                      dest (cons (cdr ret) freq)))
482                               ((= min-det-org det-org)
483                                (cond ((> min-freq freq)
484                                       (setq min-freq freq
485                                             d-i i
486                                             d-j j
487                                             dest (cons (cdr ret) freq)))))))))))
488         (setq jr (cdr jr)
489               j (1+ j)))
490       (setq ir (cdr ir)
491             i (1+ i)))
492     (if (> d-i d-j)
493         (setq i d-i
494               d-i d-j
495               d-j i))
496     (setq jr (nthcdr (1- d-j) situation-examples))
497     (setcdr jr (cddr jr))
498     (if (= d-i 0)
499         (setq situation-examples
500               (cdr situation-examples))
501       (setq ir (nthcdr (1- d-i) situation-examples))
502       (setcdr ir (cddr ir)))
503     (if (setq ir (assoc (car dest) situation-examples))
504         (progn
505           (setcdr ir (+ (cdr ir)(cdr dest)))
506           situation-examples)
507       (cons dest situation-examples)
508       ;; situation-examples may be modified.
509       )))
510
511
512 ;;; @ presentation of preview
513 ;;;
514
515 ;;; @@ entity-button
516 ;;;
517
518 ;;; @@@ predicate function
519 ;;;
520
521 ;; (defun mime-view-entity-button-visible-p (entity)
522 ;;   "Return non-nil if header of ENTITY is visible.
523 ;; Please redefine this function if you want to change default setting."
524 ;;   (let ((media-type (mime-entity-media-type entity))
525 ;;         (media-subtype (mime-entity-media-subtype entity)))
526 ;;     (or (not (eq media-type 'application))
527 ;;         (and (not (eq media-subtype 'x-selection))
528 ;;              (or (not (eq media-subtype 'octet-stream))
529 ;;                  (let ((mother-entity (mime-entity-parent entity)))
530 ;;                    (or (not (eq (mime-entity-media-type mother-entity)
531 ;;                                 'multipart))
532 ;;                        (not (eq (mime-entity-media-subtype mother-entity)
533 ;;                                 'encrypted)))
534 ;;                    )
535 ;;                  )))))
536
537 ;;; @@@ entity button generator
538 ;;;
539
540 (defun mime-view-insert-entity-button (entity)
541   "Insert entity-button of ENTITY."
542   (let ((entity-node-id (mime-entity-node-id entity))
543         (params (mime-entity-parameters entity))
544         (subject (mime-view-entity-title entity)))
545     (mime-insert-button
546      (let ((access-type (assoc "access-type" params))
547            (num (or (cdr (assoc "x-part-number" params))
548                     (if (consp entity-node-id)
549                         (mapconcat (function
550                                     (lambda (num)
551                                       (format "%s" (1+ num))))
552                                    (reverse entity-node-id) ".")
553                       "0"))))
554        (cond (access-type
555               (let ((server (assoc "server" params)))
556                 (setq access-type (cdr access-type))
557                 (if server
558                     (format "%s %s ([%s] %s)"
559                             num subject access-type (cdr server))
560                 (let ((site (cdr (assoc "site" params)))
561                       (dir (cdr (assoc "directory" params)))
562                       (url (cdr (assoc "url" params))))
563                   (if url
564                       (format "%s %s ([%s] %s)"
565                               num subject access-type url)
566                     (format "%s %s ([%s] %s:%s)"
567                             num subject access-type site dir))))))
568            (t
569             (let ((media-type (mime-entity-media-type entity))
570                   (media-subtype (mime-entity-media-subtype entity))
571                   (charset (cdr (assoc "charset" params)))
572                   (encoding (mime-entity-encoding entity)))
573               (concat
574                num " " subject
575                (let ((rest
576                       (format " <%s/%s%s%s>"
577                               media-type media-subtype
578                               (if charset
579                                   (concat "; " charset)
580                                 "")
581                               (if encoding
582                                   (concat " (" encoding ")")
583                                 ""))))
584                  (if (>= (+ (current-column)(length rest))(window-width))
585                      "\n\t")
586                  rest))))))
587      (function mime-preview-play-current-entity))))
588
589
590 ;;; @@ entity-header
591 ;;;
592
593 (defvar mime-header-presentation-method-alist nil
594   "Alist of major mode vs. corresponding header-presentation-method functions.
595 Each element looks like (SYMBOL . FUNCTION).
596 SYMBOL must be major mode in raw-buffer or t.  t means default.
597 Interface of FUNCTION must be (ENTITY SITUATION).")
598
599 (defvar mime-view-ignored-field-list
600   '(".*Received:" ".*Path:" ".*Id:" "^References:"
601     "^Replied:" "^Errors-To:"
602     "^Lines:" "^Sender:" ".*Host:" "^Xref:"
603     "^Content-Type:" "^Precedence:"
604     "^Status:" "^X-VM-.*:")
605   "All fields that match this list will be hidden in MIME preview buffer.
606 Each elements are regexp of field-name.")
607
608 (defvar mime-view-visible-field-list '("^Dnas.*:" "^Message-Id:")
609   "All fields that match this list will be displayed in MIME preview buffer.
610 Each elements are regexp of field-name.")
611
612
613 ;;; @@ entity-body
614 ;;;
615
616 ;;; @@@ predicate function
617 ;;;
618
619 (in-calist-package 'mime-view)
620
621 (defun mime-calist::field-match-method-as-default-rule (calist
622                                                         field-type field-value)
623   (let ((s-field (assq field-type calist)))
624     (cond ((null s-field)
625            (cons (cons field-type field-value) calist))
626           (t calist))))
627
628 (define-calist-field-match-method
629   'header #'mime-calist::field-match-method-as-default-rule)
630
631 (define-calist-field-match-method
632   'body #'mime-calist::field-match-method-as-default-rule)
633
634
635 (defvar mime-preview-condition nil
636   "Condition-tree about how to display entity.")
637
638 (ctree-set-calist-strictly
639  'mime-preview-condition '((type . application)(subtype . octet-stream)
640                            (encoding . nil)
641                            (body . visible)))
642 (ctree-set-calist-strictly
643  'mime-preview-condition '((type . application)(subtype . octet-stream)
644                            (encoding . "7bit")
645                            (body . visible)))
646 (ctree-set-calist-strictly
647  'mime-preview-condition '((type . application)(subtype . octet-stream)
648                            (encoding . "8bit")
649                            (body . visible)))
650
651 (ctree-set-calist-strictly
652  'mime-preview-condition '((type . application)(subtype . pgp)
653                            (body . visible)))
654
655 (ctree-set-calist-strictly
656  'mime-preview-condition '((type . application)(subtype . x-latex)
657                            (body . visible)))
658
659 (ctree-set-calist-strictly
660  'mime-preview-condition '((type . application)(subtype . x-selection)
661                            (body . visible)))
662
663 (ctree-set-calist-strictly
664  'mime-preview-condition '((type . application)(subtype . x-comment)
665                            (body . visible)))
666
667 (ctree-set-calist-strictly
668  'mime-preview-condition '((type . message)(subtype . delivery-status)
669                            (body . visible)))
670
671 (ctree-set-calist-strictly
672  'mime-preview-condition
673  '((body . visible)
674    (body-presentation-method . mime-display-text/plain)))
675
676 (defvar mime-preview-fill-flowed-text
677   (module-installed-p 'flow-fill)
678   "If non-nil, fill RFC2646 \"flowed\" text.")
679
680 (autoload 'fill-flowed "flow-fill")
681
682 (defvar mime-preview-inline-fontify t
683   "If non-nil, fontify the inline part.")
684
685 (ctree-set-calist-strictly
686  'mime-preview-condition
687  '((type . nil)
688    (body . visible)
689    (body-presentation-method . mime-display-text/plain)))
690
691 (ctree-set-calist-strictly
692  'mime-preview-condition
693  '((type . text)(subtype . enriched)
694    (body . visible)
695    (body-presentation-method . mime-display-text/enriched)))
696
697 (ctree-set-calist-strictly
698  'mime-preview-condition
699  '((type . text)(subtype . richtext)
700    (body . visible)
701    (body-presentation-method . mime-display-text/richtext)))
702
703 (autoload 'mime-display-application/x-postpet "postpet")
704
705 (ctree-set-calist-strictly
706  'mime-preview-condition
707  '((type . application)(subtype . x-postpet)
708    (body . visible)
709    (body-presentation-method . mime-display-application/x-postpet)))
710
711 (ctree-set-calist-strictly
712  'mime-preview-condition
713  '((type . application)(subtype . emacs-lisp)
714    (body . visible)
715    (body-presentation-method . mime-display-application/emacs-lisp)))
716
717 (ctree-set-calist-strictly
718  'mime-preview-condition
719  '((type . text)(subtype . t)
720    (body . visible)
721    (body-presentation-method . mime-display-text/plain)))
722
723 (ctree-set-calist-strictly
724  'mime-preview-condition
725  '((type . multipart)(subtype . alternative)
726    (body . visible)
727    (body-presentation-method . mime-display-multipart/alternative)))
728
729 (ctree-set-calist-strictly
730  'mime-preview-condition
731  '((type . multipart)(subtype . related)
732    (body . visible)
733    (body-presentation-method . mime-display-multipart/related)))
734
735 (ctree-set-calist-strictly
736  'mime-preview-condition
737  '((type . multipart)(subtype . t)
738    (body . visible)
739    (body-presentation-method . mime-display-multipart/mixed)))
740
741 (ctree-set-calist-strictly
742  'mime-preview-condition
743  '((type . message)(subtype . partial)
744    (body . visible)
745    (body-presentation-method . mime-display-message/partial-button)))
746
747 (ctree-set-calist-strictly
748  'mime-preview-condition
749  '((type . message)(subtype . rfc822)
750    (body . visible)
751    (body-presentation-method . mime-display-multipart/mixed)
752    (childrens-situation (header . visible)
753                         (entity-button . invisible))))
754
755 (ctree-set-calist-strictly
756  'mime-preview-condition
757  '((type . message)(subtype . news)
758    (body . visible)
759    (body-presentation-method . mime-display-multipart/mixed)
760    (childrens-situation (header . visible)
761                         (entity-button . invisible))))
762
763
764 ;;; @@@ entity presentation
765 ;;;
766
767 (defun mime-display-text/plain (entity situation)
768   (save-restriction
769     (narrow-to-region (point-max)(point-max))
770     (condition-case nil
771         (if (and mime-preview-inline-fontify
772                  (mime-entity-filename entity)) ;should be an attachment.
773             (mime-view-insert-fontified-text-content entity situation)
774           (mime-view-insert-text-content entity situation))
775       (error (progn
776                (message "Can't decode current entity.")
777                (sit-for 1))))
778     (run-hooks 'mime-text-decode-hook)
779     (goto-char (point-max))
780     (if (not (eq (char-after (1- (point))) ?\n))
781         (insert "\n"))
782     (if (and mime-preview-fill-flowed-text
783              (equal (cdr (assoc "format" situation)) "flowed"))
784         (fill-flowed))
785     (mime-add-url-buttons)
786     (run-hooks 'mime-display-text/plain-hook)))
787
788 (defun mime-display-text/richtext (entity situation)
789   (save-restriction
790     (narrow-to-region (point-max)(point-max))
791     (mime-view-insert-text-content entity situation)
792     (run-hooks 'mime-text-decode-hook)
793     (let ((beg (point-min)))
794       (remove-text-properties beg (point-max) '(face nil))
795       (richtext-decode beg (point-max)))))
796
797 (defun mime-display-text/enriched (entity situation)
798   (save-restriction
799     (narrow-to-region (point-max)(point-max))
800     (mime-view-insert-text-content entity situation)
801     (run-hooks 'mime-text-decode-hook)
802     (let ((beg (point-min)))
803       (remove-text-properties beg (point-max) '(face nil))
804       (enriched-decode beg (point-max)))))
805
806 (defvar mime-view-announcement-for-message/partial
807   "This is message/partial style split message.")
808
809 (defun mime-display-message/partial-button (&optional entity situation)
810   (save-restriction
811     (goto-char (point-max))
812     (if (not (search-backward "\n\n" nil t))
813         (insert "\n"))
814     (goto-char (point-max))
815     (mime-insert-button mime-view-announcement-for-message/partial
816                         #'mime-preview-play-current-entity)))
817
818 (defun mime-display-multipart/mixed (entity situation)
819   (let ((children (mime-entity-children entity))
820         (original-major-mode-cell (assq 'major-mode situation))
821         (default-situation
822           (cdr (assq 'childrens-situation situation))))
823     (if original-major-mode-cell
824         (setq default-situation
825               (cons original-major-mode-cell default-situation)))
826     (while children
827       (mime-display-entity (car children) nil default-situation)
828       (setq children (cdr children)))))
829
830 (defcustom mime-view-type-subtype-score-alist
831   '(((text . enriched) . 3)
832     ((text . richtext) . 2)
833     ((text . plain)    . 1)
834     (t . 0))
835   "Alist MEDIA-TYPE vs corresponding score.
836 MEDIA-TYPE must be (TYPE . SUBTYPE), TYPE or t.  t means default."
837   :group 'mime-view
838   :type '(repeat (cons (choice :tag "Media-Type"
839                                (cons :tag "Type/Subtype"
840                                      (symbol :tag "Primary-type")
841                                      (symbol :tag "Subtype"))
842                                (symbol :tag "Type")
843                                (const :tag "Default" t))
844                        integer)))
845
846 (defun mime-display-multipart/alternative (entity situation)
847   (let* ((children (mime-entity-children entity))
848          (original-major-mode-cell (assq 'major-mode situation))
849          (default-situation
850            (cdr (assq 'childrens-situation situation)))
851          (i 0)
852          (p 0)
853          (max-score 0)
854          situations)
855     (if original-major-mode-cell
856         (setq default-situation
857               (cons original-major-mode-cell default-situation)))
858     (setq situations
859           (mapcar (function
860                    (lambda (child)
861                      (let ((situation
862                             (mime-find-entity-preview-situation
863                              child default-situation)))
864                        (if (cdr (assq 'body-presentation-method situation))
865                            (let ((score
866                                   (cdr
867                                    (or (assoc
868                                         (cons
869                                          (cdr (assq 'type situation))
870                                          (cdr (assq 'subtype situation)))
871                                         mime-view-type-subtype-score-alist)
872                                        (assq
873                                         (cdr (assq 'type situation))
874                                         mime-view-type-subtype-score-alist)
875                                        (assq
876                                         t
877                                         mime-view-type-subtype-score-alist)))))
878                              (if (> score max-score)
879                                  (setq p i
880                                        max-score score))))
881                        (setq i (1+ i))
882                        situation)))
883                   children))
884     (setq i 0)
885     (while children
886       (let ((child (car children))
887             (situation (car situations)))
888         (mime-display-entity child (if (= i p)
889                                        situation
890                                      (put-alist 'body 'invisible
891                                                 (copy-alist situation)))))
892       (setq children (cdr children)
893             situations (cdr situations)
894             i (1+ i)))))
895
896 (defun mime-display-multipart/related (entity situation)
897   (let* ((param-start (mime-parse-msg-id
898                        (std11-lexical-analyze
899                         (cdr (assoc "start"
900                                     (mime-content-type-parameters
901                                      (mime-entity-content-type entity)))))))
902          (start (or (and param-start (mime-find-entity-from-content-id
903                                       param-start
904                                       entity))
905                     (car (mime-entity-children entity))))
906          (original-major-mode-cell (assq 'major-mode situation))
907          (default-situation (cdr (assq 'childrens-situation situation))))
908     (when start
909       (if original-major-mode-cell
910           (setq default-situation
911                 (cons original-major-mode-cell default-situation)))
912       (mime-display-entity start nil default-situation))))
913
914 (defun mime-view-entity-content (entity situation)
915   (mime-decode-string
916    (mime-entity-body entity)
917    (mime-view-guess-encoding entity situation)))
918   
919 (defun mime-view-insert-text-content (entity situation)
920   (let (compression-info)
921     (cond
922      ((and (mime-entity-filename entity)
923            (featurep 'jka-compr)
924            (jka-compr-installed-p)
925            (setq compression-info (jka-compr-get-compression-info
926                                    (mime-entity-filename entity))))
927       (insert
928        (mime-view-filter-text-content
929         (mime-view-entity-content entity situation)
930         (jka-compr-info-uncompress-program compression-info)
931         (jka-compr-info-uncompress-args compression-info))))
932      ((or (assq '*encoding situation)   ;should be specified by user
933           (assq '*charset situation))   ;should be specified by user
934       (insert
935        (decode-mime-charset-string
936         (mime-view-entity-content entity situation)
937         (mime-view-guess-charset entity situation)
938         'CRLF)))
939      (t
940       (mime-insert-text-content entity)))))
941
942 ;;; stolen (and renamed) from `mime-display-gzipped' of EMY 1.13.
943 (defun mime-view-filter-text-content (content program args)
944   (with-temp-buffer
945     (static-cond
946      ((featurep 'xemacs)
947       (insert content)
948       (apply #'binary-to-text-funcall
949              mime-view-automatic-conversion
950              #'call-process-region (point-min)(point-max)
951              program t t args))
952      (t
953       (if (not (multibyte-string-p content))
954           (set-buffer-multibyte nil))
955       (insert content)
956       (apply #'binary-funcall
957              #'call-process-region (point-min)(point-max)
958              program t t args)
959       (set-buffer-multibyte t)
960       (decode-coding-region (point-min)(point-max)
961                             mime-view-automatic-conversion)))
962     (buffer-string)))
963
964 ;;; stolen (and renamed) from mm-view.el.
965 (defun mime-view-insert-fontified-text-content (entity situation
966                                                        &optional mode)
967   ;; XEmacs @#$@ version of font-lock refuses to fully turn itself
968   ;; on for buffers whose name begins with " ".  That's why we use
969   ;; save-current-buffer/get-buffer-create rather than
970   ;; with-temp-buffer.
971   (let ((buffer (generate-new-buffer "*fontification*"))
972         filename)
973     (unwind-protect
974         (progn
975           (save-current-buffer
976             (set-buffer buffer)
977             (buffer-disable-undo)
978             (kill-all-local-variables)
979             (mime-view-insert-text-content entity situation)
980             (require 'font-lock)
981             ;; Inhibit font-lock this time (*-mode-hook might run
982             ;; `turn-on-font-lock') so that jit-lock may not turn off
983             ;; font-lock immediately after this.
984             (let ((font-lock-mode t))
985               (cond (mode
986                      (funcall mode))
987                     ((setq filename (mime-entity-filename entity))
988                      (let ((buffer-file-name
989                             (expand-file-name (file-name-nondirectory filename)
990                                               temporary-file-directory)))
991                        (set-auto-mode)))))
992             (let ((font-lock-verbose nil))
993               ;; I find font-lock a bit too verbose.
994               (font-lock-fontify-buffer)
995               (when (and (boundp 'jit-lock-mode)
996                          jit-lock-mode)
997                 (jit-lock-fontify-now)))
998             ;; By default, XEmacs font-lock uses non-duplicable text
999             ;; properties.  This code forces all the text properties
1000             ;; to be copied along with the text.
1001             (static-when (fboundp 'extent-list)
1002               (map-extents (lambda (ext ignored)
1003                              (set-extent-property ext 'duplicable t)
1004                              nil)
1005                            nil nil nil nil nil 'text-prop)))
1006           (insert-buffer-substring buffer))
1007       (kill-buffer buffer))))
1008
1009 (defun mime-display-application/emacs-lisp (entity situation)
1010   (save-restriction
1011     (narrow-to-region (point-max)(point-max))
1012     (mime-view-insert-fontified-text-content entity situation 'emacs-lisp-mode)
1013     (run-hooks 'mime-text-decode-hook 'mime-display-text/plain-hook)))
1014
1015
1016 ;;; @ acting-condition
1017 ;;;
1018
1019 (defvar mime-acting-condition nil
1020   "Condition-tree about how to process entity.")
1021
1022 (defun mime-view-read-mailcap-files (&optional files)
1023   (or files
1024       (setq files mime-view-mailcap-files))
1025   (let (entries file)
1026     (while files
1027       (setq file (car files))
1028       (if (file-readable-p file)
1029           (setq entries (append entries (mime-parse-mailcap-file file))))
1030       (setq files (cdr files)))
1031     (while entries
1032       (let ((entry (car entries))
1033             view print shared)
1034         (while entry
1035           (let* ((field (car entry))
1036                  (field-type (car field)))
1037             (cond ((eq field-type 'view)  (setq view field))
1038                   ((eq field-type 'print) (setq print field))
1039                   ((memq field-type '(compose composetyped edit)))
1040                   (t (setq shared (cons field shared))))
1041             )
1042           (setq entry (cdr entry)))
1043         (setq shared (nreverse shared))
1044         (ctree-set-calist-with-default
1045          'mime-acting-condition
1046          (append shared (list '(mode . "play")(cons 'method (cdr view)))))
1047         (if print
1048             (ctree-set-calist-with-default
1049              'mime-acting-condition
1050              (append shared
1051                      (list '(mode . "print")(cons 'method (cdr view)))))))
1052       (setq entries (cdr entries)))))
1053
1054 (mime-view-read-mailcap-files)
1055
1056 (ctree-set-calist-strictly
1057  'mime-acting-condition
1058  '((type . application)(subtype . octet-stream)
1059    (mode . "play")
1060    (method . mime-detect-content)))
1061
1062 (ctree-set-calist-with-default
1063  'mime-acting-condition
1064  '((mode . "extract")
1065    (method . mime-save-content)))
1066
1067 (ctree-set-calist-strictly
1068  'mime-acting-condition
1069  '((type . text)(subtype . x-rot13-47)(mode . "play")
1070    (method . mime-view-caesar)))
1071 (ctree-set-calist-strictly
1072  'mime-acting-condition
1073  '((type . text)(subtype . x-rot13-47-48)(mode . "play")
1074    (method . mime-view-caesar)))
1075
1076 (ctree-set-calist-strictly
1077  'mime-acting-condition
1078  '((type . message)(subtype . rfc822)(mode . "play")
1079    (method . mime-view-message/rfc822)))
1080 (ctree-set-calist-strictly
1081  'mime-acting-condition
1082  '((type . message)(subtype . partial)(mode . "play")
1083    (method . mime-store-message/partial-piece)))
1084
1085 (ctree-set-calist-strictly
1086  'mime-acting-condition
1087  '((type . message)(subtype . external-body)
1088    ("access-type" . "anon-ftp")
1089    (method . mime-view-message/external-anon-ftp)))
1090
1091 (ctree-set-calist-strictly
1092  'mime-acting-condition
1093  '((type . message)(subtype . external-body)
1094    ("access-type" . "url")
1095    (method . mime-view-message/external-url)))
1096
1097 (ctree-set-calist-strictly
1098  'mime-acting-condition
1099  '((type . application)(subtype . octet-stream)
1100    (method . mime-save-content)))
1101
1102
1103 ;;; @ quitting method
1104 ;;;
1105
1106 (defvar mime-preview-quitting-method-alist
1107   '((mime-show-message-mode
1108      . mime-preview-quitting-method-for-mime-show-message-mode))
1109   "Alist of major-mode vs. quitting-method of mime-view.")
1110
1111 (defvar mime-preview-over-to-previous-method-alist nil
1112   "Alist of major-mode vs. over-to-previous-method of mime-view.")
1113
1114 (defvar mime-preview-over-to-next-method-alist nil
1115   "Alist of major-mode vs. over-to-next-method of mime-view.")
1116
1117
1118 ;;; @ following method
1119 ;;;
1120
1121 (defvar mime-preview-following-method-alist nil
1122   "Alist of major-mode vs. following-method of mime-view.")
1123
1124 (defvar mime-view-following-required-fields-list
1125   '("From"))
1126
1127
1128 ;;; @ buffer setup
1129 ;;;
1130
1131 (defun mime-display-entity (entity &optional situation
1132                                    default-situation preview-buffer)
1133   (or preview-buffer
1134       (setq preview-buffer (current-buffer)))
1135   (let* (e nb ne nhb nbb)
1136     (in-calist-package 'mime-view)
1137     (or situation
1138         (setq situation
1139               (mime-find-entity-preview-situation entity default-situation)))
1140     (let ((button-is-invisible
1141            (eq (cdr (or (assq '*entity-button situation)
1142                         (assq 'entity-button situation)))
1143                'invisible))
1144           (header-is-visible
1145            (eq (cdr (or (assq '*header situation)
1146                         (assq 'header situation)))
1147                'visible))
1148           (body-is-visible
1149            (eq (cdr (or (assq '*body situation)
1150                         (assq 'body situation)))
1151                'visible))
1152           (children (mime-entity-children entity)))
1153       (set-buffer preview-buffer)
1154       (setq nb (point))
1155       (narrow-to-region nb nb)
1156       (or button-is-invisible
1157           ;; (if (mime-view-entity-button-visible-p entity)
1158           (mime-view-insert-entity-button entity)
1159           ;;   )
1160           )
1161       (if header-is-visible
1162           (let ((header-presentation-method
1163                  (or (cdr (assq 'header-presentation-method situation))
1164                      (cdr (assq (cdr (assq 'major-mode situation))
1165                                 mime-header-presentation-method-alist)))))
1166             (setq nhb (point))
1167             (if header-presentation-method
1168                 (funcall header-presentation-method entity situation)
1169               (mime-insert-header entity
1170                                   mime-view-ignored-field-list
1171                                   mime-view-visible-field-list))
1172             (mime-add-url-buttons)
1173             (run-hooks 'mime-display-header-hook)
1174             (put-text-property nhb (point-max) 'mime-view-entity-header entity)
1175             (goto-char (point-max))
1176             (insert "\n")))
1177       (setq nbb (point))
1178       (unless children
1179         (if body-is-visible
1180             (let ((body-presentation-method
1181                    (cdr (assq 'body-presentation-method situation))))
1182               (if (functionp body-presentation-method)
1183                   (funcall body-presentation-method entity situation)
1184                 (mime-display-text/plain entity situation)))
1185           (when button-is-invisible
1186             (goto-char (point-max))
1187             (mime-view-insert-entity-button entity))
1188           (unless header-is-visible
1189             (goto-char (point-max))
1190             (insert "\n"))))
1191       (setq ne (point-max))
1192       (widen)
1193       (put-text-property nb ne 'mime-view-entity entity)
1194       (put-text-property nb ne 'mime-view-situation situation)
1195       (put-text-property nbb ne 'mime-view-entity-body entity)
1196       (goto-char ne)
1197       (if (and children body-is-visible)
1198           (let ((body-presentation-method
1199                  (cdr (assq 'body-presentation-method situation))))
1200             (if (functionp body-presentation-method)
1201                 (funcall body-presentation-method entity situation)
1202               (mime-display-multipart/mixed entity situation)))))))
1203
1204
1205 ;;; @ MIME viewer mode
1206 ;;;
1207
1208 (defconst mime-view-popup-menu-list
1209   '("MIME-View"
1210     ["Move to upper entity" mime-preview-move-to-upper]
1211     ["Move to previous entity" mime-preview-move-to-previous]
1212     ["Move to next entity" mime-preview-move-to-next]
1213     ["Scroll-down" mime-preview-scroll-down-entity]
1214     ["Scroll-up" mime-preview-scroll-up-entity]
1215     ["Play current entity" mime-preview-play-current-entity]
1216     ["Extract current entity" mime-preview-extract-current-entity]
1217     ["Print current entity" mime-preview-print-current-entity])
1218   "Menu for MIME Viewer")
1219
1220 (defun mime-view-popup-menu (event)
1221   "Popup the menu in the MIME Viewer buffer"
1222   (interactive "@e")
1223   (mime-popup-menu-popup mime-view-popup-menu-list event))
1224
1225 ;;; The current local map is taken precendence over `widget-keymap',
1226 ;;; because GNU Emacs' widget implementation doesn't set `local-map' property.
1227 ;;;  So we need to specify derivation.
1228 (defvar widget-keymap)
1229 (defun mime-view-maybe-inherit-widget-keymap ()
1230   (when (boundp 'widget-keymap)
1231     (set-keymap-parent (current-local-map) widget-keymap)))
1232
1233 (add-hook 'mime-view-mode-hook 'mime-view-maybe-inherit-widget-keymap)
1234           
1235 (defun mime-view-define-keymap (&optional default)
1236   (let ((mime-view-mode-map (if (keymapp default)
1237                                 (copy-keymap default)
1238                               (make-sparse-keymap))))
1239     (define-key mime-view-mode-map
1240       "u"        (function mime-preview-move-to-upper))
1241     (define-key mime-view-mode-map
1242       "p"        (function mime-preview-move-to-previous))
1243     (define-key mime-view-mode-map
1244       "n"        (function mime-preview-move-to-next))
1245     (define-key mime-view-mode-map
1246       "\e\t"     (function mime-preview-move-to-previous))
1247     (define-key mime-view-mode-map
1248       "\t"       (function mime-preview-move-to-next))
1249     (define-key mime-view-mode-map
1250       " "        (function mime-preview-scroll-up-entity))
1251     (define-key mime-view-mode-map
1252       "\M- "     (function mime-preview-scroll-down-entity))
1253     (define-key mime-view-mode-map
1254       "\177"     (function mime-preview-scroll-down-entity))
1255     (define-key mime-view-mode-map
1256       "\C-m"     (function mime-preview-next-line-entity))
1257     (define-key mime-view-mode-map
1258       "\C-\M-m"  (function mime-preview-previous-line-entity))
1259     (define-key mime-view-mode-map
1260       "v"        (function mime-preview-play-current-entity))
1261     (define-key mime-view-mode-map
1262       "e"        (function mime-preview-extract-current-entity))
1263     (define-key mime-view-mode-map
1264       "\C-c\C-p" (function mime-preview-print-current-entity))
1265
1266     (define-key mime-view-mode-map
1267       "\C-c\C-t\C-f" (function mime-preview-toggle-header))
1268     (define-key mime-view-mode-map
1269       "\C-c\C-th" (function mime-preview-toggle-header))
1270     (define-key mime-view-mode-map
1271       "\C-c\C-t\C-c" (function mime-preview-toggle-content))
1272
1273     (define-key mime-view-mode-map
1274       "\C-c\C-v\C-f" (function mime-preview-show-header))
1275     (define-key mime-view-mode-map
1276       "\C-c\C-vh" (function mime-preview-show-header))
1277     (define-key mime-view-mode-map
1278       "\C-c\C-v\C-c" (function mime-preview-show-content))
1279
1280     (define-key mime-view-mode-map
1281       "\C-c\C-d\C-f" (function mime-preview-hide-header))
1282     (define-key mime-view-mode-map
1283       "\C-c\C-dh" (function mime-preview-hide-header))
1284     (define-key mime-view-mode-map
1285       "\C-c\C-d\C-c" (function mime-preview-hide-content))
1286
1287     (define-key mime-view-mode-map
1288       "a"        (function mime-preview-follow-current-entity))
1289     (define-key mime-view-mode-map
1290       "q"        (function mime-preview-quit))
1291     (define-key mime-view-mode-map
1292       "\C-c\C-x" (function mime-preview-kill-buffer))
1293     ;; (define-key mime-view-mode-map
1294     ;;   "<"        (function beginning-of-buffer))
1295     ;; (define-key mime-view-mode-map
1296     ;;   ">"        (function end-of-buffer))
1297     (define-key mime-view-mode-map
1298       "?"        (function describe-mode))
1299     (define-key mime-view-mode-map
1300       [tab] (function mime-preview-move-to-next))
1301     (define-key mime-view-mode-map
1302       [delete] (function mime-preview-scroll-down-entity))
1303     (define-key mime-view-mode-map
1304       [backspace] (function mime-preview-scroll-down-entity))
1305     (if (functionp default)
1306         (if (featurep 'xemacs)
1307             (set-keymap-default-binding mime-view-mode-map default)
1308           (setq mime-view-mode-map
1309                 (append mime-view-mode-map (list (cons t default))))))
1310     (define-key mime-view-mode-map
1311       [down-mouse-3] (function mime-view-popup-menu))
1312     ;; (run-hooks 'mime-view-define-keymap-hook)
1313     mime-view-mode-map))
1314
1315 (defvar mime-view-mode-default-map (mime-view-define-keymap))
1316
1317
1318 (defsubst mime-maybe-hide-echo-buffer ()
1319   "Clear mime-echo buffer and delete window for it."
1320   (let ((buf (get-buffer mime-echo-buffer-name)))
1321     (if buf
1322         (save-excursion
1323           (set-buffer buf)
1324           (erase-buffer)
1325           (let ((win (get-buffer-window buf)))
1326             (if win
1327                 (delete-window win)))
1328           (bury-buffer buf)))))
1329
1330 (defvar mime-view-redisplay nil)
1331
1332 ;;;###autoload
1333 (defun mime-display-message (message &optional preview-buffer
1334                                      mother default-keymap-or-function
1335                                      original-major-mode keymap)
1336   "View MESSAGE in MIME-View mode.
1337
1338 Optional argument PREVIEW-BUFFER specifies the buffer of the
1339 presentation.  It must be either nil or a name of preview buffer.
1340
1341 Optional argument MOTHER specifies mother-buffer of the preview-buffer.
1342
1343 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
1344 function.  If it is a keymap, keymap of MIME-View mode will be added
1345 to it.  If it is a function, it will be bound as default binding of
1346 keymap of MIME-View mode.
1347
1348 Optional argument ORIGINAL-MAJOR-MODE is major-mode of representation
1349 buffer of MESSAGE.  If it is nil, current `major-mode' is used.
1350
1351 Optional argument KEYMAP is keymap of MIME-View mode.  If it is
1352 non-nil, DEFAULT-KEYMAP-OR-FUNCTION is ignored.  If it is nil,
1353 `mime-view-mode-default-map' is used."
1354   (mime-maybe-hide-echo-buffer)
1355   (let ((win-conf (current-window-configuration)))
1356     (or preview-buffer
1357         (setq preview-buffer
1358               (concat "*Preview-" (mime-entity-name message) "*")))
1359     (or original-major-mode
1360         (setq original-major-mode major-mode))
1361     (let ((inhibit-read-only t))
1362       (set-buffer (get-buffer-create preview-buffer))
1363       (widen)
1364       (erase-buffer)
1365       (if mother
1366           (setq mime-mother-buffer mother))
1367       (setq mime-preview-original-window-configuration win-conf)
1368       (setq major-mode 'mime-view-mode)
1369       (setq mode-name "MIME-View")
1370       (mime-display-entity message nil
1371                            `((entity-button . invisible)
1372                              (header . visible)
1373                              (major-mode . ,original-major-mode))
1374                            preview-buffer)
1375       (use-local-map
1376        (or keymap
1377            (if default-keymap-or-function
1378                (mime-view-define-keymap default-keymap-or-function)
1379              mime-view-mode-default-map)))
1380       (let ((point
1381              (next-single-property-change (point-min) 'mime-view-entity)))
1382         (if point
1383             (goto-char point)
1384           (goto-char (point-min))
1385           (search-forward "\n\n" nil t)))
1386       (run-hooks 'mime-view-mode-hook)
1387       (set-buffer-modified-p nil)
1388       (setq buffer-read-only t)
1389       preview-buffer)))
1390
1391 ;;;###autoload
1392 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
1393                                    default-keymap-or-function
1394                                    representation-type)
1395   "View RAW-BUFFER in MIME-View mode.
1396 Optional argument PREVIEW-BUFFER is either nil or a name of preview
1397 buffer.
1398 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
1399 function.  If it is a keymap, keymap of MIME-View mode will be added
1400 to it.  If it is a function, it will be bound as default binding of
1401 keymap of MIME-View mode.
1402 Optional argument REPRESENTATION-TYPE is representation-type of
1403 message.  It must be nil, `binary' or `cooked'.  If it is nil,
1404 `cooked' is used as default."
1405   (interactive)
1406   (or raw-buffer
1407       (setq raw-buffer (current-buffer)))
1408   (or representation-type
1409       (setq representation-type
1410             (save-excursion
1411               (set-buffer raw-buffer)
1412               (cdr (or (assq major-mode mime-raw-representation-type-alist)
1413                        (assq t mime-raw-representation-type-alist))))))
1414   (if (eq representation-type 'binary)
1415       (setq representation-type 'buffer))
1416   (setq preview-buffer (mime-display-message
1417                         (mime-open-entity representation-type raw-buffer)
1418                         preview-buffer mother default-keymap-or-function))
1419   (or (get-buffer-window preview-buffer)
1420       (let ((r-win (get-buffer-window raw-buffer)))
1421         (if r-win
1422             (set-window-buffer r-win preview-buffer)
1423           (let ((m-win (and mother (get-buffer-window mother))))
1424             (if m-win
1425                 (set-window-buffer m-win preview-buffer)
1426               (switch-to-buffer preview-buffer)))))))
1427
1428 (defun mime-view-mode (&optional mother ctl encoding
1429                                  raw-buffer preview-buffer
1430                                  default-keymap-or-function)
1431   "Major mode for viewing MIME message.
1432
1433 Here is a list of the standard keys for mime-view-mode.
1434
1435 key             feature
1436 ---             -------
1437
1438 u               Move to upper content
1439 p or M-TAB      Move to previous content
1440 n or TAB        Move to next content
1441 SPC             Scroll up or move to next content
1442 M-SPC or DEL    Scroll down or move to previous content
1443 RET             Move to next line
1444 M-RET           Move to previous line
1445 v               Decode current content as `play mode'
1446 e               Decode current content as `extract mode'
1447 C-c C-p         Decode current content as `print mode'
1448 a               Followup to current content.
1449 q               Quit
1450 button-2        Move to point under the mouse cursor
1451                 and decode current content as `play mode'
1452 "
1453   (interactive)
1454   (unless mime-view-redisplay
1455     (save-excursion
1456       (if raw-buffer (set-buffer raw-buffer))
1457       (let ((type
1458              (cdr
1459               (or (assq major-mode mime-raw-representation-type-alist)
1460                   (assq t mime-raw-representation-type-alist)))))
1461         (if (eq type 'binary)
1462             (setq type 'buffer))
1463         (setq mime-message-structure (mime-open-entity type raw-buffer))
1464         (or (mime-entity-content-type mime-message-structure)
1465             (mime-entity-set-content-type mime-message-structure ctl)))
1466       (or (mime-entity-encoding mime-message-structure)
1467           (mime-entity-set-encoding mime-message-structure encoding))))
1468   (mime-display-message mime-message-structure preview-buffer
1469                         mother default-keymap-or-function))
1470
1471
1472 ;;; @@ utility
1473 ;;;
1474
1475 (defun mime-preview-find-boundary-info (&optional with-children)
1476   "Return boundary information of current part.
1477 If WITH-CHILDREN, refer boundary surrounding current part and its branches."
1478   (let (entity
1479         p-beg p-end
1480         entity-node-id len)
1481     (while (and
1482             (null (setq entity
1483                         (get-text-property (point) 'mime-view-entity)))
1484             (> (point) (point-min)))
1485       (backward-char))
1486     (setq p-beg (previous-single-property-change (point) 'mime-view-entity))
1487     (setq entity-node-id (and entity (mime-entity-node-id entity)))
1488     (setq len (length entity-node-id))
1489     (cond ((null p-beg)
1490            (setq p-beg
1491                  (if (eq (next-single-property-change (point-min)
1492                                                       'mime-view-entity)
1493                          (point))
1494                      (point)
1495                    (point-min))))
1496           ((eq (next-single-property-change p-beg 'mime-view-entity)
1497                (point))
1498            (setq p-beg (point))))
1499     (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1500     (cond ((null p-end)
1501            (setq p-end (point-max)))
1502           ((null entity-node-id)
1503            (setq p-end (point-max)))
1504           (with-children
1505            (save-excursion
1506              (catch 'tag
1507                (let (e i)
1508                  (while (setq e
1509                               (next-single-property-change
1510                                (point) 'mime-view-entity))
1511                    (goto-char e)
1512                    (let ((rc (mime-entity-node-id
1513                               (get-text-property (point)
1514                                                  'mime-view-entity))))
1515                      (or (and (>= (setq i (- (length rc) len)) 0)
1516                               (equal entity-node-id (nthcdr i rc)))
1517                          (throw 'tag nil)))
1518                    (setq p-end (or (next-single-property-change
1519                                     (point) 'mime-view-entity)
1520                                    (point-max)))))
1521                (setq p-end (point-max))))))
1522     (vector p-beg p-end entity)))
1523
1524
1525 ;;; @@ playing
1526 ;;;
1527
1528 (autoload 'mime-preview-play-current-entity "mime-play"
1529   "Play current entity." t)
1530
1531 (defun mime-preview-extract-current-entity (&optional ignore-examples)
1532   "Extract current entity into file (maybe).
1533 It decodes current entity to call internal or external method as
1534 \"extract\" mode.  The method is selected from variable
1535 `mime-acting-condition'."
1536   (interactive "P")
1537   (mime-preview-play-current-entity ignore-examples "extract"))
1538
1539 (defun mime-preview-print-current-entity (&optional ignore-examples)
1540   "Print current entity (maybe).
1541 It decodes current entity to call internal or external method as
1542 \"print\" mode.  The method is selected from variable
1543 `mime-acting-condition'."
1544   (interactive "P")
1545   (mime-preview-play-current-entity ignore-examples "print"))
1546
1547
1548 ;;; @@ following
1549 ;;;
1550
1551 (defun mime-preview-follow-current-entity ()
1552   "Write follow message to current entity.
1553 It calls following-method selected from variable
1554 `mime-preview-following-method-alist'."
1555   (interactive)
1556   (let* ((boundary-info (mime-preview-find-boundary-info t))
1557          (p-beg (aref boundary-info 0))
1558          (p-end (aref boundary-info 1))
1559          (entity (aref boundary-info 2))
1560          pb-beg)
1561     (if (or (get-text-property p-beg 'mime-view-entity-body)
1562             (null entity))
1563         (setq pb-beg p-beg)
1564       (setq pb-beg
1565             (next-single-property-change
1566              p-beg 'mime-view-entity-body nil
1567              (or (next-single-property-change p-beg 'mime-view-entity)
1568                  p-end))))
1569     (let* ((mode (mime-preview-original-major-mode 'recursive))
1570            (entity-node-id (and entity (mime-entity-node-id entity)))
1571            (new-name
1572             (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1573            new-buf
1574            (the-buf (current-buffer))
1575            fields)
1576       (save-excursion
1577         (set-buffer (setq new-buf (get-buffer-create new-name)))
1578         (erase-buffer)
1579         (insert ?\n)
1580         (insert-buffer-substring the-buf pb-beg p-end)
1581         (goto-char (point-min))
1582         (let ((current-entity
1583                (if (and entity
1584                         (eq (mime-entity-media-type entity) 'message)
1585                         (eq (mime-entity-media-subtype entity) 'rfc822))
1586                    (car (mime-entity-children entity))
1587                  entity)))
1588           (while (and current-entity
1589                       (if (and (eq (mime-entity-media-type
1590                                     current-entity) 'message)
1591                                (eq (mime-entity-media-subtype
1592                                     current-entity) 'rfc822))
1593                           nil
1594                         (mime-insert-header current-entity fields)
1595                         t))
1596             (setq fields (std11-collect-field-names)
1597                   current-entity (mime-entity-parent current-entity))))
1598         (let ((rest mime-view-following-required-fields-list)
1599               field-name ret)
1600           (while rest
1601             (setq field-name (car rest))
1602             (or (std11-field-body field-name)
1603                 (progn
1604                   (save-excursion
1605                     (set-buffer the-buf)
1606                     (let ((entity (when mime-mother-buffer
1607                                     (set-buffer mime-mother-buffer)
1608                                     (get-text-property (point)
1609                                                        'mime-view-entity))))
1610                       (while (and entity
1611                                   (null (setq ret (mime-entity-fetch-field
1612                                                    entity field-name))))
1613                         (setq entity (mime-entity-parent entity)))))
1614                   (if ret
1615                       (insert (concat field-name ": " ret "\n")))))
1616             (setq rest (cdr rest)))))
1617       (let ((f (cdr (assq mode mime-preview-following-method-alist))))
1618         (if (functionp f)
1619             (funcall f new-buf)
1620           (message
1621            "Sorry, following method for %s is not implemented yet."
1622             mode))))))
1623
1624
1625 ;;; @@ moving
1626 ;;;
1627
1628 (defun mime-preview-move-to-upper ()
1629   "Move to upper entity.
1630 If there is no upper entity, call function `mime-preview-quit'."
1631   (interactive)
1632   (let (cinfo)
1633     (while (null (setq cinfo
1634                        (get-text-property (point) 'mime-view-entity)))
1635       (backward-char))
1636     (let ((r (mime-entity-parent cinfo))
1637           point)
1638       (catch 'tag
1639         (while (setq point (previous-single-property-change
1640                             (point) 'mime-view-entity))
1641           (goto-char point)
1642           (when (eq r (get-text-property (point) 'mime-view-entity))
1643             (if (or (eq mime-preview-move-scroll t)
1644                     (and mime-preview-move-scroll
1645                          (>= point
1646                              (save-excursion
1647                                (move-to-window-line -1)
1648                                (forward-line (* -1 next-screen-context-lines))
1649                                (beginning-of-line)
1650                                (point)))))
1651                 (recenter next-screen-context-lines))
1652             (throw 'tag t)))
1653         (mime-preview-quit)))))
1654
1655 (defun mime-preview-move-to-previous ()
1656   "Move to previous entity.
1657 If there is no previous entity, it calls function registered in
1658 variable `mime-preview-over-to-previous-method-alist'."
1659   (interactive)
1660   (while (and (not (bobp))
1661               (null (get-text-property (point) 'mime-view-entity)))
1662     (backward-char))
1663   (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1664     (if (and point
1665              (>= point (point-min)))
1666         (if (get-text-property (1- point) 'mime-view-entity)
1667             (progn (goto-char point)
1668                    (if
1669                     (or (eq mime-preview-move-scroll t)
1670                         (and mime-preview-move-scroll
1671                              (<= point
1672                                 (save-excursion
1673                                   (move-to-window-line 0)
1674                                   (forward-line next-screen-context-lines)
1675                                   (end-of-line)
1676                                   (point)))))
1677                         (recenter (* -1 next-screen-context-lines))))
1678           (goto-char (1- point))
1679           (mime-preview-move-to-previous))
1680       (let ((f (assq (mime-preview-original-major-mode)
1681                      mime-preview-over-to-previous-method-alist)))
1682         (if f
1683             (funcall (cdr f)))))))
1684
1685 (defun mime-preview-move-to-next ()
1686   "Move to next entity.
1687 If there is no previous entity, it calls function registered in
1688 variable `mime-preview-over-to-next-method-alist'."
1689   (interactive)
1690   (while (and (not (eobp))
1691               (null (get-text-property (point) 'mime-view-entity)))
1692     (forward-char))
1693   (let ((point (next-single-property-change (point) 'mime-view-entity)))
1694     (if (and point
1695              (<= point (point-max)))
1696         (progn
1697           (goto-char point)
1698           (if (null (get-text-property point 'mime-view-entity))
1699               (mime-preview-move-to-next)
1700             (and
1701              (or (eq mime-preview-move-scroll t)
1702                  (and mime-preview-move-scroll
1703                       (>= point
1704                          (save-excursion
1705                            (move-to-window-line -1)
1706                            (forward-line
1707                             (* -1 next-screen-context-lines))
1708                            (beginning-of-line)
1709                            (point)))))
1710                  (recenter next-screen-context-lines))))
1711       (let ((f (assq (mime-preview-original-major-mode)
1712                      mime-preview-over-to-next-method-alist)))
1713         (if f
1714             (funcall (cdr f)))))))
1715
1716 (defun mime-preview-scroll-up-entity (&optional h)
1717   "Scroll up current entity.
1718 If reached to (point-max), it calls function registered in variable
1719 `mime-preview-over-to-next-method-alist'."
1720   (interactive)
1721   (if (eobp)
1722       (let ((f (assq (mime-preview-original-major-mode)
1723                      mime-preview-over-to-next-method-alist)))
1724         (if f
1725             (funcall (cdr f))))
1726     (let ((point
1727            (or (next-single-property-change (point) 'mime-view-entity)
1728                (point-max)))
1729           (bottom (window-end (selected-window))))
1730       (if (and (not h)
1731                (> bottom point))
1732           (progn (goto-char point)
1733                  (recenter next-screen-context-lines))
1734         (condition-case nil
1735             (scroll-up h)
1736           (end-of-buffer
1737            (goto-char (point-max))))))))
1738
1739 (defun mime-preview-scroll-down-entity (&optional h)
1740   "Scroll down current entity.
1741 If reached to (point-min), it calls function registered in variable
1742 `mime-preview-over-to-previous-method-alist'."
1743   (interactive)
1744   (if (bobp)
1745       (let ((f (assq (mime-preview-original-major-mode)
1746                      mime-preview-over-to-previous-method-alist)))
1747         (if f
1748             (funcall (cdr f))))
1749     (let ((point
1750            (or (previous-single-property-change (point) 'mime-view-entity)
1751                (point-min)))
1752           (top (window-start (selected-window))))
1753       (if (and (not h)
1754                (< top point))
1755           (progn (goto-char point)
1756                  (recenter (* -1 next-screen-context-lines)))
1757         (condition-case nil
1758             (scroll-down h)
1759           (beginning-of-buffer
1760            (goto-char (point-min))))))))
1761
1762 (defun mime-preview-next-line-entity (&optional lines)
1763   "Scroll up one line (or prefix LINES lines).
1764 If LINES is negative, scroll down LINES lines."
1765   (interactive "p")
1766   (mime-preview-scroll-up-entity (or lines 1)))
1767
1768 (defun mime-preview-previous-line-entity (&optional lines)
1769   "Scrroll down one line (or prefix LINES lines).
1770 If LINES is negative, scroll up LINES lines."
1771   (interactive "p")
1772   (mime-preview-scroll-down-entity (or lines 1)))
1773
1774
1775 ;;; @@ display
1776 ;;;
1777
1778 (defun mime-view-guess-encoding (entity situation)
1779   (or (cdr (assq '*encoding situation))
1780       (cdr (assq 'encoding situation))
1781       (mime-entity-encoding entity)
1782       "7bit"))
1783
1784 (defun mime-view-read-encoding (entity situation)
1785   (let* ((default-encoding
1786            (mime-view-guess-encoding entity situation))
1787          (encoding
1788           (completing-read
1789            "Content Transfer Encoding: "
1790            (mime-encoding-alist) nil t default-encoding)))
1791     (unless (or (string= encoding "")
1792                 (string= encoding default-encoding))
1793       encoding)))
1794
1795 (defun mime-view-guess-charset (entity situation)
1796   (or (static-if (fboundp 'coding-system-to-mime-charset)
1797           ;; might be overridden by `universal-coding-system-argument'.
1798           (and coding-system-for-read
1799                (coding-system-to-mime-charset coding-system-for-read)))
1800       (cdr (assq '*charset situation))
1801       (cdr (assq 'charset situation))
1802       (let ((charset (cdr (assoc "charset" (mime-entity-parameters entity)))))
1803         (if charset
1804             (intern (downcase charset))))
1805       default-mime-charset))
1806
1807 (defun mime-view-read-charset (entity situation)
1808   (static-if (featurep 'mule)
1809       (let* ((default-charset
1810                (mime-view-guess-charset entity situation))
1811              (charset
1812               (intern (completing-read "MIME-charset: "
1813                                        (mapcar
1814                                         (lambda (sym)
1815                                           (list (symbol-name sym)))
1816                                         (mime-charset-list))
1817                                        nil t
1818                                        (symbol-name default-charset)))))
1819         (unless (eq charset default-charset)
1820           charset))
1821     default-charset))
1822
1823 (defun mime-preview-toggle-display (type &optional display)
1824   (let ((situation (mime-preview-find-boundary-info t))
1825         (sym (intern (concat "*" (symbol-name type))))
1826         entity p-beg p-end encoding charset)
1827     (setq p-beg (aref situation 0)
1828           p-end (aref situation 1)
1829           entity (aref situation 2)
1830           situation (get-text-property p-beg 'mime-view-situation))
1831     (cond ((eq display 'invisible)
1832            (setq display nil))
1833           (display)
1834           (t
1835            (setq display
1836                  (memq (cdr (or (assq sym situation)
1837                                 (assq type situation)))
1838                        '(nil invisible)))))
1839     (setq situation (put-alist sym (if display
1840                                        'visible
1841                                      'invisible)
1842                                situation))
1843     (when (and current-prefix-arg
1844                (eq (cdr (assq sym situation)) 'visible))
1845       (if (setq encoding (mime-view-read-encoding entity situation))
1846           (setq situation (put-alist '*encoding encoding situation)))
1847       (if (setq charset (mime-view-read-charset entity situation))
1848           (setq situation (put-alist '*charset charset situation))))
1849     (save-excursion
1850       (let ((inhibit-read-only t))
1851         (delete-region p-beg p-end)
1852         (mime-display-entity entity situation)))
1853     (let ((ret (assoc situation mime-preview-situation-example-list)))
1854       (if ret
1855           (setcdr ret (1+ (cdr ret)))
1856         (add-to-list 'mime-preview-situation-example-list
1857                      (cons situation 0))))))
1858
1859 (defun mime-preview-toggle-header (&optional force-visible)
1860   (interactive "P")
1861   (mime-preview-toggle-display 'header force-visible))
1862
1863 (defun mime-preview-toggle-content (&optional force-visible)
1864   (interactive "P")
1865   (mime-preview-toggle-display 'body force-visible))
1866
1867 (defun mime-preview-show-header ()
1868   (interactive)
1869   (mime-preview-toggle-display 'header 'visible))
1870
1871 (defun mime-preview-show-content ()
1872   (interactive)
1873   (mime-preview-toggle-display 'body 'visible))
1874
1875 (defun mime-preview-hide-header ()
1876   (interactive)
1877   (mime-preview-toggle-display 'header 'invisible))
1878
1879 (defun mime-preview-hide-content ()
1880   (interactive)
1881   (mime-preview-toggle-display 'body 'invisible))
1882
1883     
1884 ;;; @@ quitting
1885 ;;;
1886
1887 (defun mime-preview-quit ()
1888   "Quit from MIME-preview buffer.
1889 It calls function registered in variable
1890 `mime-preview-quitting-method-alist'."
1891   (interactive)
1892   (let ((r (assq (mime-preview-original-major-mode)
1893                  mime-preview-quitting-method-alist)))
1894     (if r
1895         (funcall (cdr r)))))
1896
1897 (defun mime-preview-kill-buffer ()
1898   (interactive)
1899   (kill-buffer (current-buffer)))
1900
1901
1902 ;;; @ end
1903 ;;;
1904
1905 (provide 'mime-view)
1906
1907 (eval-when-compile
1908   (setq mime-situation-examples-file nil)
1909   ;; to avoid to read situation-examples-file at compile time.
1910   )
1911
1912 (mime-view-read-situation-examples-file)
1913
1914 ;;; mime-view.el ends here