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