1 ;;; mime-view.el --- interactive MIME viewer for GNU Emacs
3 ;; Copyright (C) 1995,1996,1997,1998 Free Software Foundation, Inc.
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
7 ;; Renamed: 1994/08/31 from tm-body.el
8 ;; Renamed: 1997/02/19 from tm-view.el
9 ;; Keywords: MIME, multimedia, mail, news
11 ;; This file is part of SEMI (Sophisticated Emacs MIME Interfaces).
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.
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.
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.
40 (defconst mime-view-version-string
41 `,(concat (car mime-user-interface-version) " MIME-View "
42 (mapconcat #'number-to-string
43 (cddr mime-user-interface-version) ".")
44 " (" (cadr mime-user-interface-version) ")"))
50 (defgroup mime-view nil
54 (defcustom mime-view-find-every-acting-situation t
55 "*Find every available acting-situation if non-nil."
59 (defcustom mime-acting-situation-examples-file "~/.mime-example"
60 "*File name of example about acting-situation demonstrated by user."
65 ;;; @ in raw-buffer (representation space)
68 (defvar mime-preview-buffer nil
69 "MIME-preview buffer corresponding with the (raw) buffer.")
70 (make-variable-buffer-local 'mime-preview-buffer)
73 (defvar mime-raw-representation-type nil
74 "Representation-type of mime-raw-buffer.
75 It must be nil, `binary' or `cooked'.
76 If it is nil, `mime-raw-representation-type-alist' is used as default
78 Notice that this variable is usually used as buffer local variable in
81 (make-variable-buffer-local 'mime-raw-representation-type)
83 (defvar mime-raw-representation-type-alist
84 '((mime-show-message-mode . binary)
85 (mime-temp-message-mode . binary)
88 "Alist of major-mode vs. representation-type of mime-raw-buffer.
89 Each element looks like (SYMBOL . REPRESENTATION-TYPE). SYMBOL is
90 major-mode or t. t means default. REPRESENTATION-TYPE must be
92 This value is overridden by buffer local variable
93 `mime-raw-representation-type' if it is not nil.")
96 (defun mime-raw-find-entity-from-point (point &optional message-info)
97 "Return entity from POINT in mime-raw-buffer.
98 If optional argument MESSAGE-INFO is not specified,
99 `mime-message-structure' is used."
101 (setq message-info mime-message-structure))
102 (if (and (<= (mime-entity-point-min message-info) point)
103 (<= point (mime-entity-point-max message-info)))
104 (let ((children (mime-entity-children message-info)))
108 (mime-raw-find-entity-from-point point (car children))))
112 (setq children (cdr children)))
116 ;;; @ in preview-buffer (presentation space)
119 (defvar mime-mother-buffer nil
120 "Mother buffer corresponding with the (MIME-preview) buffer.
121 If current MIME-preview buffer is generated by other buffer, such as
122 message/partial, it is called `mother-buffer'.")
123 (make-variable-buffer-local 'mime-mother-buffer)
125 (defvar mime-raw-buffer nil
126 "Raw buffer corresponding with the (MIME-preview) buffer.")
127 (make-variable-buffer-local 'mime-raw-buffer)
129 (defvar mime-preview-original-window-configuration nil
130 "Window-configuration before mime-view-mode is called.")
131 (make-variable-buffer-local 'mime-preview-original-window-configuration)
133 (defun mime-preview-original-major-mode (&optional recursive)
134 "Return major-mode of original buffer.
135 If optional argument RECURSIVE is non-nil and current buffer has
136 mime-mother-buffer, it returns original major-mode of the
138 (if (and recursive mime-mother-buffer)
140 (set-buffer mime-mother-buffer)
141 (mime-preview-original-major-mode recursive)
146 (get-text-property (point-min) 'mime-view-entity)))
150 ;;; @ entity information
153 (defsubst mime-entity-representation-type (entity)
154 (with-current-buffer (mime-entity-buffer entity)
155 (or mime-raw-representation-type
156 (cdr (or (assq major-mode mime-raw-representation-type-alist)
157 (assq t mime-raw-representation-type-alist))))))
159 (defsubst mime-entity-cooked-p (entity)
160 (eq (mime-entity-representation-type entity) 'cooked))
162 (defun mime-entity-situation (entity)
163 "Return situation of ENTITY."
164 (append (or (mime-entity-content-type entity)
165 (make-mime-content-type 'text 'plain))
166 (let ((d (mime-entity-content-disposition entity)))
167 (cons (cons 'disposition-type
168 (mime-content-disposition-type d))
171 (let ((name (car param)))
172 (cons (cond ((string= name "filename")
174 ((string= name "creation-date")
176 ((string= name "modification-date")
178 ((string= name "read-date")
180 ((string= name "size")
182 (t (cons 'disposition (car param))))
184 (mime-content-disposition-parameters d))
186 (list (cons 'encoding (mime-entity-encoding entity))
189 (set-buffer (mime-entity-buffer entity))
194 (defun mime-view-entity-title (entity)
195 (or (mime-read-field 'Content-Description entity)
196 (mime-read-field 'Subject entity)
197 (mime-entity-filename entity)
201 (defsubst mime-raw-point-to-entity-node-id (point &optional message-info)
202 "Return entity-node-id from POINT in mime-raw-buffer.
203 If optional argument MESSAGE-INFO is not specified,
204 `mime-message-structure' is used."
205 (mime-entity-node-id (mime-raw-find-entity-from-point point message-info)))
207 (defsubst mime-raw-point-to-entity-number (point &optional message-info)
208 "Return entity-number from POINT in mime-raw-buffer.
209 If optional argument MESSAGE-INFO is not specified,
210 `mime-message-structure' is used."
211 (mime-entity-number (mime-raw-find-entity-from-point point message-info)))
213 (defun mime-raw-flatten-message-info (&optional message-info)
214 "Return list of entity in mime-raw-buffer.
215 If optional argument MESSAGE-INFO is not specified,
216 `mime-message-structure' is used."
218 (setq message-info mime-message-structure))
219 (let ((dest (list message-info))
220 (rcl (mime-entity-children message-info)))
222 (setq dest (nconc dest (mime-raw-flatten-message-info (car rcl))))
223 (setq rcl (cdr rcl)))
227 ;;; @ presentation of preview
233 ;;; @@@ predicate function
236 (defun mime-view-entity-button-visible-p (entity)
237 "Return non-nil if header of ENTITY is visible.
238 Please redefine this function if you want to change default setting."
239 (let ((media-type (mime-entity-media-type entity))
240 (media-subtype (mime-entity-media-subtype entity)))
241 (or (not (eq media-type 'application))
242 (and (not (eq media-subtype 'x-selection))
243 (or (not (eq media-subtype 'octet-stream))
244 (let ((mother-entity (mime-entity-parent entity)))
245 (or (not (eq (mime-entity-media-type mother-entity)
247 (not (eq (mime-entity-media-subtype mother-entity)
252 ;;; @@@ entity button generator
255 (defun mime-view-insert-entity-button (entity)
256 "Insert entity-button of ENTITY."
257 (let ((entity-node-id (mime-entity-node-id entity))
258 (params (mime-entity-parameters entity))
259 (subject (mime-view-entity-title entity)))
261 (let ((access-type (assoc "access-type" params))
262 (num (or (cdr (assoc "x-part-number" params))
263 (if (consp entity-node-id)
266 (format "%s" (1+ num))
268 (reverse entity-node-id) ".")
272 (let ((server (assoc "server" params)))
273 (setq access-type (cdr access-type))
275 (format "%s %s ([%s] %s)"
276 num subject access-type (cdr server))
277 (let ((site (cdr (assoc "site" params)))
278 (dir (cdr (assoc "directory" params)))
280 (format "%s %s ([%s] %s:%s)"
281 num subject access-type site dir)
285 (let ((media-type (mime-entity-media-type entity))
286 (media-subtype (mime-entity-media-subtype entity))
287 (charset (cdr (assoc "charset" params)))
288 (encoding (mime-entity-encoding entity)))
292 (format " <%s/%s%s%s>"
293 media-type media-subtype
295 (concat "; " charset)
298 (concat " (" encoding ")")
300 (if (>= (+ (current-column)(length rest))(window-width))
304 (function mime-preview-play-current-entity))
311 (defvar mime-header-presentation-method-alist nil
312 "Alist of major mode vs. corresponding header-presentation-method functions.
313 Each element looks like (SYMBOL . FUNCTION).
314 SYMBOL must be major mode in raw-buffer or t. t means default.
315 Interface of FUNCTION must be (ENTITY SITUATION).")
317 (defvar mime-view-ignored-field-list
318 '(".*Received" ".*Path" ".*Id" "References"
319 "Replied" "Errors-To"
320 "Lines" "Sender" ".*Host" "Xref"
321 "Content-Type" "Precedence"
323 "All fields that match this list will be hidden in MIME preview buffer.
324 Each elements are regexp of field-name.")
326 (defvar mime-view-visible-field-list '("Dnas.*" "Message-Id")
327 "All fields that match this list will be displayed in MIME preview buffer.
328 Each elements are regexp of field-name.")
334 ;;; @@@ predicate function
337 (defun mime-calist::field-match-method-as-default-rule (calist
338 field-type field-value)
339 (let ((s-field (assq field-type calist)))
340 (cond ((null s-field)
341 (cons (cons field-type field-value) calist)
345 (define-calist-field-match-method
346 'header #'mime-calist::field-match-method-as-default-rule)
348 (define-calist-field-match-method
349 'body #'mime-calist::field-match-method-as-default-rule)
352 (defvar mime-preview-condition nil
353 "Condition-tree about how to display entity.")
355 (ctree-set-calist-strictly
356 'mime-preview-condition '((type . application)(subtype . octet-stream)
359 (ctree-set-calist-strictly
360 'mime-preview-condition '((type . application)(subtype . octet-stream)
363 (ctree-set-calist-strictly
364 'mime-preview-condition '((type . application)(subtype . octet-stream)
368 (ctree-set-calist-strictly
369 'mime-preview-condition '((type . application)(subtype . pgp)
372 (ctree-set-calist-strictly
373 'mime-preview-condition '((type . application)(subtype . x-latex)
376 (ctree-set-calist-strictly
377 'mime-preview-condition '((type . application)(subtype . x-selection)
380 (ctree-set-calist-strictly
381 'mime-preview-condition '((type . application)(subtype . x-comment)
384 (ctree-set-calist-strictly
385 'mime-preview-condition '((type . message)(subtype . delivery-status)
388 (ctree-set-calist-strictly
389 'mime-preview-condition
391 (body-presentation-method . mime-display-text/plain)))
393 (ctree-set-calist-strictly
394 'mime-preview-condition
397 (body-presentation-method . mime-display-text/plain)))
399 (ctree-set-calist-strictly
400 'mime-preview-condition
401 '((type . text)(subtype . enriched)
403 (body-presentation-method . mime-display-text/enriched)))
405 (ctree-set-calist-strictly
406 'mime-preview-condition
407 '((type . text)(subtype . richtext)
409 (body-presentation-method . mime-display-text/richtext)))
411 (ctree-set-calist-strictly
412 'mime-preview-condition
413 '((type . text)(subtype . t)
415 (body-presentation-method . mime-display-text/plain)))
417 (ctree-set-calist-strictly
418 'mime-preview-condition
419 '((type . multipart)(subtype . alternative)
421 (body-presentation-method . mime-display-multipart/alternative)))
423 (ctree-set-calist-strictly
424 'mime-preview-condition '((type . message)(subtype . partial)
425 (body-presentation-method
426 . mime-display-message/partial-button)))
428 (ctree-set-calist-strictly
429 'mime-preview-condition '((type . message)(subtype . rfc822)
430 (body-presentation-method . nil)
431 (childrens-situation (header . visible)
432 (entity-button . invisible))))
434 (ctree-set-calist-strictly
435 'mime-preview-condition '((type . message)(subtype . news)
436 (body-presentation-method . nil)
437 (childrens-situation (header . visible)
438 (entity-button . invisible))))
441 ;;; @@@ entity presentation
444 (autoload 'mime-display-text/plain "mime-text")
445 (autoload 'mime-display-text/enriched "mime-text")
446 (autoload 'mime-display-text/richtext "mime-text")
448 (defvar mime-view-announcement-for-message/partial
449 (if (and (>= emacs-major-version 19) window-system)
451 \[[ This is message/partial style split message. ]]
452 \[[ Please press `v' key in this buffer ]]
453 \[[ or click here by mouse button-2. ]]"
455 \[[ This is message/partial style split message. ]]
456 \[[ Please press `v' key in this buffer. ]]"
459 (defun mime-display-message/partial-button (&optional entity situation)
461 (goto-char (point-max))
462 (if (not (search-backward "\n\n" nil t))
465 (goto-char (point-max))
466 (narrow-to-region (point-max)(point-max))
467 (insert mime-view-announcement-for-message/partial)
468 (mime-add-button (point-min)(point-max)
469 #'mime-preview-play-current-entity)
472 (defun mime-display-multipart/mixed (entity situation)
473 (let ((children (mime-entity-children entity))
475 (cdr (assq 'childrens-situation situation))))
477 (mime-display-entity (car children) nil default-situation)
478 (setq children (cdr children))
481 (defcustom mime-view-type-subtype-score-alist
482 '(((text . enriched) . 3)
483 ((text . richtext) . 2)
486 "Alist MEDIA-TYPE vs corresponding score.
487 MEDIA-TYPE must be (TYPE . SUBTYPE), TYPE or t. t means default."
489 :type '(repeat (cons (choice :tag "Media-Type"
490 (item :tag "Type/Subtype"
491 (cons symbol symbol))
492 (item :tag "Type" symbol)
493 (item :tag "Default" t))
496 (defun mime-display-multipart/alternative (entity situation)
497 (let* ((children (mime-entity-children entity))
499 (cdr (assq 'childrens-situation situation)))
507 (or (ctree-match-calist
508 mime-preview-condition
509 (append (mime-entity-situation child)
512 (if (cdr (assq 'body-presentation-method situation))
517 (cdr (assq 'type situation))
518 (cdr (assq 'subtype situation)))
519 mime-view-type-subtype-score-alist)
521 (cdr (assq 'type situation))
522 mime-view-type-subtype-score-alist)
525 mime-view-type-subtype-score-alist)
527 (if (> score max-score)
537 (let ((child (car children))
538 (situation (car situations)))
539 (mime-display-entity child (if (= i p)
541 (del-alist 'body-presentation-method
542 (copy-alist situation))))
544 (setq children (cdr children)
545 situations (cdr situations)
550 ;;; @ acting-condition
553 (defvar mime-acting-condition nil
554 "Condition-tree about how to process entity.")
556 (if (file-readable-p mailcap-file)
557 (let ((entries (mailcap-parse-file)))
559 (let ((entry (car entries))
562 (let* ((field (car entry))
563 (field-type (car field)))
564 (cond ((eq field-type 'view) (setq view field))
565 ((eq field-type 'print) (setq print field))
566 ((memq field-type '(compose composetyped edit)))
567 (t (setq shared (cons field shared))))
569 (setq entry (cdr entry))
571 (setq shared (nreverse shared))
572 (ctree-set-calist-with-default
573 'mime-acting-condition
574 (append shared (list '(mode . "play")(cons 'method (cdr view)))))
576 (ctree-set-calist-with-default
577 'mime-acting-condition
579 (list '(mode . "print")(cons 'method (cdr view))))
582 (setq entries (cdr entries))
585 (ctree-set-calist-strictly
586 'mime-acting-condition
587 '((type . application)(subtype . octet-stream)
589 (method . mime-detect-content)
592 (ctree-set-calist-with-default
593 'mime-acting-condition
595 (method . mime-save-content)))
597 (ctree-set-calist-strictly
598 'mime-acting-condition
599 '((type . text)(subtype . x-rot13-47)(mode . "play")
600 (method . mime-view-caesar)
602 (ctree-set-calist-strictly
603 'mime-acting-condition
604 '((type . text)(subtype . x-rot13-47-48)(mode . "play")
605 (method . mime-view-caesar)
608 (ctree-set-calist-strictly
609 'mime-acting-condition
610 '((type . message)(subtype . rfc822)(mode . "play")
611 (method . mime-view-message/rfc822)
613 (ctree-set-calist-strictly
614 'mime-acting-condition
615 '((type . message)(subtype . partial)(mode . "play")
616 (method . mime-store-message/partial-piece)
619 (ctree-set-calist-strictly
620 'mime-acting-condition
621 '((type . message)(subtype . external-body)
622 ("access-type" . "anon-ftp")
623 (method . mime-view-message/external-anon-ftp)
626 (ctree-set-calist-strictly
627 'mime-acting-condition
628 '((type . message)(subtype . external-body)
629 ("access-type" . "url")
630 (method . mime-view-message/external-url)
633 (ctree-set-calist-strictly
634 'mime-acting-condition
635 '((type . application)(subtype . octet-stream)
636 (method . mime-save-content)
640 ;;; @ quitting method
643 (defvar mime-preview-quitting-method-alist
644 '((mime-show-message-mode
645 . mime-preview-quitting-method-for-mime-show-message-mode))
646 "Alist of major-mode vs. quitting-method of mime-view.")
648 (defvar mime-preview-over-to-previous-method-alist nil
649 "Alist of major-mode vs. over-to-previous-method of mime-view.")
651 (defvar mime-preview-over-to-next-method-alist nil
652 "Alist of major-mode vs. over-to-next-method of mime-view.")
655 ;;; @ following method
658 (defvar mime-preview-following-method-alist nil
659 "Alist of major-mode vs. following-method of mime-view.")
661 (defvar mime-view-following-required-fields-list
668 (defun mime-display-entity (entity &optional situation
669 default-situation preview-buffer)
671 (setq preview-buffer (current-buffer)))
672 (let* ((raw-buffer (mime-entity-buffer entity))
673 (start (mime-entity-point-min entity))
675 (set-buffer raw-buffer)
679 (or (ctree-match-calist mime-preview-condition
680 (append (mime-entity-situation entity)
683 (let ((button-is-invisible
684 (eq (cdr (assq 'entity-button situation)) 'invisible))
686 (eq (cdr (assq 'header situation)) 'visible))
687 (header-presentation-method
688 (or (cdr (assq 'header-presentation-method situation))
689 (cdr (assq major-mode mime-header-presentation-method-alist))))
690 (body-presentation-method
691 (cdr (assq 'body-presentation-method situation)))
692 (children (mime-entity-children entity)))
693 (set-buffer preview-buffer)
695 (narrow-to-region nb nb)
696 (or button-is-invisible
697 (if (mime-view-entity-button-visible-p entity)
698 (mime-view-insert-entity-button entity)
700 (when header-is-visible
701 (if header-presentation-method
702 (funcall header-presentation-method entity situation)
703 (mime-insert-decoded-header entity
704 mime-view-ignored-field-list
705 mime-view-visible-field-list
706 (if (mime-entity-cooked-p entity)
708 default-mime-charset))
710 (goto-char (point-max))
712 (run-hooks 'mime-display-header-hook)
715 ((functionp body-presentation-method)
716 (funcall body-presentation-method entity situation)
719 (when button-is-invisible
720 (goto-char (point-max))
721 (mime-view-insert-entity-button entity)
723 (or header-is-visible
725 (goto-char (point-max))
729 (setq ne (point-max))
731 (put-text-property nb ne 'mime-view-entity entity)
734 (if (functionp body-presentation-method)
735 (funcall body-presentation-method entity situation)
736 (mime-display-multipart/mixed entity situation)
741 ;;; @ MIME viewer mode
744 (defconst mime-view-menu-title "MIME-View")
745 (defconst mime-view-menu-list
746 '((up "Move to upper entity" mime-preview-move-to-upper)
747 (previous "Move to previous entity" mime-preview-move-to-previous)
748 (next "Move to next entity" mime-preview-move-to-next)
749 (scroll-down "Scroll-down" mime-preview-scroll-down-entity)
750 (scroll-up "Scroll-up" mime-preview-scroll-up-entity)
751 (play "Play current entity" mime-preview-play-current-entity)
752 (extract "Extract current entity" mime-preview-extract-current-entity)
753 (print "Print current entity" mime-preview-print-current-entity)
755 "Menu for MIME Viewer")
757 (cond (running-xemacs
758 (defvar mime-view-xemacs-popup-menu
759 (cons mime-view-menu-title
762 (vector (nth 1 item)(nth 2 item) t)
764 mime-view-menu-list)))
765 (defun mime-view-xemacs-popup-menu (event)
766 "Popup the menu in the MIME Viewer buffer"
768 (select-window (event-window event))
769 (set-buffer (event-buffer event))
770 (popup-menu 'mime-view-xemacs-popup-menu))
771 (defvar mouse-button-2 'button2)
774 (defvar mouse-button-2 [mouse-2])
777 (defun mime-view-define-keymap (&optional default)
778 (let ((mime-view-mode-map (if (keymapp default)
779 (copy-keymap default)
782 (define-key mime-view-mode-map
783 "u" (function mime-preview-move-to-upper))
784 (define-key mime-view-mode-map
785 "p" (function mime-preview-move-to-previous))
786 (define-key mime-view-mode-map
787 "n" (function mime-preview-move-to-next))
788 (define-key mime-view-mode-map
789 "\e\t" (function mime-preview-move-to-previous))
790 (define-key mime-view-mode-map
791 "\t" (function mime-preview-move-to-next))
792 (define-key mime-view-mode-map
793 " " (function mime-preview-scroll-up-entity))
794 (define-key mime-view-mode-map
795 "\M- " (function mime-preview-scroll-down-entity))
796 (define-key mime-view-mode-map
797 "\177" (function mime-preview-scroll-down-entity))
798 (define-key mime-view-mode-map
799 "\C-m" (function mime-preview-next-line-entity))
800 (define-key mime-view-mode-map
801 "\C-\M-m" (function mime-preview-previous-line-entity))
802 (define-key mime-view-mode-map
803 "v" (function mime-preview-play-current-entity))
804 (define-key mime-view-mode-map
805 "e" (function mime-preview-extract-current-entity))
806 (define-key mime-view-mode-map
807 "\C-c\C-p" (function mime-preview-print-current-entity))
808 (define-key mime-view-mode-map
809 "a" (function mime-preview-follow-current-entity))
810 (define-key mime-view-mode-map
811 "q" (function mime-preview-quit))
812 (define-key mime-view-mode-map
813 "\C-c\C-x" (function mime-preview-kill-buffer))
814 ;; (define-key mime-view-mode-map
815 ;; "<" (function beginning-of-buffer))
816 ;; (define-key mime-view-mode-map
817 ;; ">" (function end-of-buffer))
818 (define-key mime-view-mode-map
819 "?" (function describe-mode))
820 (define-key mime-view-mode-map
821 [tab] (function mime-preview-move-to-next))
822 (define-key mime-view-mode-map
823 [delete] (function mime-preview-scroll-down-entity))
824 (define-key mime-view-mode-map
825 [backspace] (function mime-preview-scroll-down-entity))
826 (if (functionp default)
827 (cond (running-xemacs
828 (set-keymap-default-binding mime-view-mode-map default)
831 (setq mime-view-mode-map
832 (append mime-view-mode-map (list (cons t default))))
835 (define-key mime-view-mode-map
836 mouse-button-2 (function mime-button-dispatcher))
838 (cond (running-xemacs
839 (define-key mime-view-mode-map
840 mouse-button-3 (function mime-view-xemacs-popup-menu))
842 ((>= emacs-major-version 19)
843 (define-key mime-view-mode-map [menu-bar mime-view]
844 (cons mime-view-menu-title
845 (make-sparse-keymap mime-view-menu-title)))
848 (define-key mime-view-mode-map
849 (vector 'menu-bar 'mime-view (car item))
850 (cons (nth 1 item)(nth 2 item))
853 (reverse mime-view-menu-list)
856 (use-local-map mime-view-mode-map)
857 (run-hooks 'mime-view-define-keymap-hook)
860 (defsubst mime-maybe-hide-echo-buffer ()
861 "Clear mime-echo buffer and delete window for it."
862 (let ((buf (get-buffer mime-echo-buffer-name)))
867 (let ((win (get-buffer-window buf)))
874 (defvar mime-view-redisplay nil)
876 (defun mime-display-message (message &optional preview-buffer
877 mother default-keymap-or-function)
878 (mime-maybe-hide-echo-buffer)
879 (let ((win-conf (current-window-configuration))
880 (raw-buffer (mime-entity-buffer message)))
883 (concat "*Preview-" (buffer-name raw-buffer) "*")))
884 (set-buffer raw-buffer)
885 (setq mime-preview-buffer preview-buffer)
886 (let ((inhibit-read-only t))
887 (set-buffer (get-buffer-create preview-buffer))
890 (setq mime-raw-buffer raw-buffer)
892 (setq mime-mother-buffer mother)
894 (setq mime-preview-original-window-configuration win-conf)
895 (setq major-mode 'mime-view-mode)
896 (setq mode-name "MIME-View")
897 (mime-display-entity message nil
898 '((entity-button . invisible)
901 (mime-view-define-keymap default-keymap-or-function)
903 (next-single-property-change (point-min) 'mime-view-entity)))
906 (goto-char (point-min))
907 (search-forward "\n\n" nil t)
909 (run-hooks 'mime-view-mode-hook)
910 (set-buffer-modified-p nil)
911 (setq buffer-read-only t)
912 (or (get-buffer-window preview-buffer)
913 (let ((r-win (get-buffer-window raw-buffer)))
915 (set-window-buffer r-win preview-buffer)
916 (switch-to-buffer preview-buffer)
920 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
921 default-keymap-or-function)
923 (mime-display-message
924 (mime-parse-buffer raw-buffer)
925 preview-buffer mother default-keymap-or-function))
927 (defun mime-view-mode (&optional mother ctl encoding
928 raw-buffer preview-buffer
929 default-keymap-or-function)
930 "Major mode for viewing MIME message.
932 Here is a list of the standard keys for mime-view-mode.
937 u Move to upper content
938 p or M-TAB Move to previous content
939 n or TAB Move to next content
940 SPC Scroll up or move to next content
941 M-SPC or DEL Scroll down or move to previous content
942 RET Move to next line
943 M-RET Move to previous line
944 v Decode current content as `play mode'
945 e Decode current content as `extract mode'
946 C-c C-p Decode current content as `print mode'
947 a Followup to current content.
949 button-2 Move to point under the mouse cursor
950 and decode current content as `play mode'
955 (if raw-buffer (set-buffer raw-buffer))
956 (or mime-view-redisplay
957 (setq mime-message-structure (mime-parse-message ctl)))
959 (or (mime-entity-encoding message)
960 (mime-entity-set-encoding-internal message encoding))
961 (mime-display-message message preview-buffer
962 mother default-keymap-or-function)
969 (autoload 'mime-preview-play-current-entity "mime-play"
970 "Play current entity." t)
972 (defun mime-preview-extract-current-entity ()
973 "Extract current entity into file (maybe).
974 It decodes current entity to call internal or external method as
975 \"extract\" mode. The method is selected from variable
976 `mime-acting-condition'."
978 (mime-preview-play-current-entity "extract")
981 (defun mime-preview-print-current-entity ()
982 "Print current entity (maybe).
983 It decodes current entity to call internal or external method as
984 \"print\" mode. The method is selected from variable
985 `mime-acting-condition'."
987 (mime-preview-play-current-entity "print")
994 (defun mime-preview-follow-current-entity ()
995 "Write follow message to current entity.
996 It calls following-method selected from variable
997 `mime-preview-following-method-alist'."
1000 (while (null (setq entity
1001 (get-text-property (point) 'mime-view-entity)))
1005 (previous-single-property-change (point) 'mime-view-entity))
1007 (entity-node-id (mime-entity-node-id entity))
1008 (len (length entity-node-id))
1012 (if (eq (next-single-property-change (point-min)
1018 ((eq (next-single-property-change p-beg 'mime-view-entity)
1020 (setq p-beg (point))
1022 (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1024 (setq p-end (point-max))
1026 ((null entity-node-id)
1027 (setq p-end (point-max))
1035 (next-single-property-change
1036 (point) 'mime-view-entity))
1038 (let ((rc (mime-entity-node-id
1039 (get-text-property (point)
1040 'mime-view-entity))))
1041 (or (equal entity-node-id
1042 (nthcdr (- (length rc) len) rc))
1047 (setq p-end (point-max))
1050 (let* ((mode (mime-preview-original-major-mode 'recursive))
1052 (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1054 (the-buf (current-buffer))
1055 (a-buf mime-raw-buffer)
1058 (set-buffer (setq new-buf (get-buffer-create new-name)))
1060 (insert-buffer-substring the-buf p-beg p-end)
1061 (goto-char (point-min))
1062 (let ((entity-node-id (mime-entity-node-id entity)) ci str)
1069 (mime-find-entity-from-node-id entity-node-id))
1072 (mime-entity-point-min ci)
1073 (mime-entity-point-max ci)
1075 (std11-header-string-except
1077 (apply (function regexp-or) fields)
1080 (eq (mime-entity-media-type ci) 'message)
1081 (eq (mime-entity-media-subtype ci) 'rfc822))
1087 (setq fields (std11-collect-field-names)
1088 entity-node-id (cdr entity-node-id))
1091 (let ((rest mime-view-following-required-fields-list))
1093 (let ((field-name (car rest)))
1094 (or (std11-field-body field-name)
1100 (set-buffer the-buf)
1101 (set-buffer mime-mother-buffer)
1102 (set-buffer mime-raw-buffer)
1103 (std11-field-body field-name)
1107 (setq rest (cdr rest))
1109 (eword-decode-header)
1111 (let ((f (cdr (assq mode mime-preview-following-method-alist))))
1116 "Sorry, following method for %s is not implemented yet."
1125 (defun mime-preview-move-to-upper ()
1126 "Move to upper entity.
1127 If there is no upper entity, call function `mime-preview-quit'."
1130 (while (null (setq cinfo
1131 (get-text-property (point) 'mime-view-entity)))
1134 (let ((r (mime-entity-parent cinfo))
1137 (while (setq point (previous-single-property-change
1138 (point) 'mime-view-entity))
1140 (if (eq r (get-text-property (point) 'mime-view-entity))
1147 (defun mime-preview-move-to-previous ()
1148 "Move to previous entity.
1149 If there is no previous entity, it calls function registered in
1150 variable `mime-preview-over-to-previous-method-alist'."
1152 (while (null (get-text-property (point) 'mime-view-entity))
1155 (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1157 (if (get-text-property (1- point) 'mime-view-entity)
1159 (goto-char (1- point))
1160 (mime-preview-move-to-previous)
1162 (let ((f (assq (mime-preview-original-major-mode)
1163 mime-preview-over-to-previous-method-alist)))
1169 (defun mime-preview-move-to-next ()
1170 "Move to next entity.
1171 If there is no previous entity, it calls function registered in
1172 variable `mime-preview-over-to-next-method-alist'."
1174 (while (null (get-text-property (point) 'mime-view-entity))
1177 (let ((point (next-single-property-change (point) 'mime-view-entity)))
1181 (if (null (get-text-property point 'mime-view-entity))
1182 (mime-preview-move-to-next)
1184 (let ((f (assq (mime-preview-original-major-mode)
1185 mime-preview-over-to-next-method-alist)))
1191 (defun mime-preview-scroll-up-entity (&optional h)
1192 "Scroll up current entity.
1193 If reached to (point-max), it calls function registered in variable
1194 `mime-preview-over-to-next-method-alist'."
1197 (setq h (1- (window-height)))
1199 (if (= (point) (point-max))
1200 (let ((f (assq (mime-preview-original-major-mode)
1201 mime-preview-over-to-next-method-alist)))
1206 (or (next-single-property-change (point) 'mime-view-entity)
1209 (if (> (point) point)
1214 (defun mime-preview-scroll-down-entity (&optional h)
1215 "Scroll down current entity.
1216 If reached to (point-min), it calls function registered in variable
1217 `mime-preview-over-to-previous-method-alist'."
1220 (setq h (1- (window-height)))
1222 (if (= (point) (point-min))
1223 (let ((f (assq (mime-preview-original-major-mode)
1224 mime-preview-over-to-previous-method-alist)))
1229 (or (previous-single-property-change (point) 'mime-view-entity)
1231 (forward-line (- h))
1232 (if (< (point) point)
1236 (defun mime-preview-next-line-entity ()
1238 (mime-preview-scroll-up-entity 1)
1241 (defun mime-preview-previous-line-entity ()
1243 (mime-preview-scroll-down-entity 1)
1250 (defun mime-preview-quit ()
1251 "Quit from MIME-preview buffer.
1252 It calls function registered in variable
1253 `mime-preview-quitting-method-alist'."
1255 (let ((r (assq (mime-preview-original-major-mode)
1256 mime-preview-quitting-method-alist)))
1261 (defun mime-preview-kill-buffer ()
1263 (kill-buffer (current-buffer))
1270 (provide 'mime-view)
1272 (run-hooks 'mime-view-load-hook)
1274 ;;; mime-view.el ends here