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