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