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