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