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