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