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