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