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