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