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