1 ;;; mime-view.el --- interactive MIME viewer for GNU Emacs
3 ;; Copyright (C) 1995,1996,1997,1998,1999 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 (Sample of Elastic 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.
41 (defconst mime-view-version
43 (concat (mime-product-name mime-user-interface-product) " MIME-View "
44 (mapconcat #'number-to-string
45 (mime-product-version mime-user-interface-product) ".")
46 " (" (mime-product-code-name mime-user-interface-product) ")")))
52 (defgroup mime-view nil
56 (defcustom mime-view-find-every-acting-situation t
57 "*Find every available acting-situation if non-nil."
61 (defcustom mime-acting-situation-examples-file "~/.mime-example"
62 "*File name of example about acting-situation demonstrated by user."
67 ;;; @ in raw-buffer (representation space)
70 (defvar mime-preview-buffer nil
71 "MIME-preview buffer corresponding with the (raw) buffer.")
72 (make-variable-buffer-local 'mime-preview-buffer)
75 (defvar mime-raw-representation-type-alist
76 '((mime-show-message-mode . binary)
77 (mime-temp-message-mode . binary)
80 "Alist of major-mode vs. representation-type of mime-raw-buffer.
81 Each element looks like (SYMBOL . REPRESENTATION-TYPE). SYMBOL is
82 major-mode or t. t means default. REPRESENTATION-TYPE must be
83 `binary' or `cooked'.")
86 (defun mime-raw-find-entity-from-point (point &optional message-info)
87 "Return entity from POINT in mime-raw-buffer.
88 If optional argument MESSAGE-INFO is not specified,
89 `mime-message-structure' is used."
91 (setq message-info mime-message-structure))
92 (if (and (<= (mime-entity-point-min message-info) point)
93 (<= point (mime-entity-point-max message-info)))
94 (let ((children (mime-entity-children message-info)))
98 (mime-raw-find-entity-from-point point (car children))))
102 (setq children (cdr children)))
106 ;;; @ in preview-buffer (presentation space)
109 (defvar mime-mother-buffer nil
110 "Mother buffer corresponding with the (MIME-preview) buffer.
111 If current MIME-preview buffer is generated by other buffer, such as
112 message/partial, it is called `mother-buffer'.")
113 (make-variable-buffer-local 'mime-mother-buffer)
115 (defvar mime-raw-buffer nil
116 "Raw buffer corresponding with the (MIME-preview) buffer.")
117 (make-variable-buffer-local 'mime-raw-buffer)
119 (defvar mime-preview-original-window-configuration nil
120 "Window-configuration before mime-view-mode is called.")
121 (make-variable-buffer-local 'mime-preview-original-window-configuration)
123 (defun mime-preview-original-major-mode (&optional recursive)
124 "Return major-mode of original buffer.
125 If optional argument RECURSIVE is non-nil and current buffer has
126 mime-mother-buffer, it returns original major-mode of the
128 (if (and recursive mime-mother-buffer)
130 (set-buffer mime-mother-buffer)
131 (mime-preview-original-major-mode recursive)
136 (get-text-property (point-min) 'mime-view-entity)))
140 ;;; @ entity information
143 (defun mime-entity-situation (entity)
144 "Return situation of ENTITY."
145 (append (or (mime-entity-content-type entity)
146 (make-mime-content-type 'text 'plain))
147 (let ((d (mime-entity-content-disposition entity)))
148 (cons (cons 'disposition-type
149 (mime-content-disposition-type d))
152 (let ((name (car param)))
153 (cons (cond ((string= name "filename")
155 ((string= name "creation-date")
157 ((string= name "modification-date")
159 ((string= name "read-date")
161 ((string= name "size")
163 (t (cons 'disposition (car param))))
165 (mime-content-disposition-parameters d))
167 (list (cons 'encoding (mime-entity-encoding entity))
170 (set-buffer (mime-entity-buffer entity))
175 (defun mime-view-entity-title (entity)
176 (or (mime-read-field 'Content-Description entity)
177 (mime-read-field 'Subject entity)
178 (mime-entity-filename entity)
182 (defsubst mime-raw-point-to-entity-node-id (point &optional message-info)
183 "Return entity-node-id from POINT in mime-raw-buffer.
184 If optional argument MESSAGE-INFO is not specified,
185 `mime-message-structure' is used."
186 (mime-entity-node-id (mime-raw-find-entity-from-point point message-info)))
188 (defsubst mime-raw-point-to-entity-number (point &optional message-info)
189 "Return entity-number from POINT in mime-raw-buffer.
190 If optional argument MESSAGE-INFO is not specified,
191 `mime-message-structure' is used."
192 (mime-entity-number (mime-raw-find-entity-from-point point message-info)))
194 (defun mime-raw-flatten-message-info (&optional message-info)
195 "Return list of entity in mime-raw-buffer.
196 If optional argument MESSAGE-INFO is not specified,
197 `mime-message-structure' is used."
199 (setq message-info mime-message-structure))
200 (let ((dest (list message-info))
201 (rcl (mime-entity-children message-info)))
203 (setq dest (nconc dest (mime-raw-flatten-message-info (car rcl))))
204 (setq rcl (cdr rcl)))
208 ;;; @ presentation of preview
214 ;;; @@@ predicate function
217 (defun mime-view-entity-button-visible-p (entity)
218 "Return non-nil if header of ENTITY is visible.
219 Please redefine this function if you want to change default setting."
220 (let ((media-type (mime-entity-media-type entity))
221 (media-subtype (mime-entity-media-subtype entity)))
222 (or (not (eq media-type 'application))
223 (and (not (eq media-subtype 'x-selection))
224 (or (not (eq media-subtype 'octet-stream))
225 (let ((mother-entity (mime-entity-parent entity)))
226 (or (not (eq (mime-entity-media-type mother-entity)
228 (not (eq (mime-entity-media-subtype mother-entity)
233 ;;; @@@ entity button generator
236 (defun mime-view-insert-entity-button (entity)
237 "Insert entity-button of ENTITY."
238 (let ((entity-node-id (mime-entity-node-id entity))
239 (params (mime-entity-parameters entity))
240 (subject (mime-view-entity-title entity)))
242 (let ((access-type (assoc "access-type" params))
243 (num (or (cdr (assoc "x-part-number" params))
244 (if (consp entity-node-id)
247 (format "%s" (1+ num))
249 (reverse entity-node-id) ".")
253 (let ((server (assoc "server" params)))
254 (setq access-type (cdr access-type))
256 (format "%s %s ([%s] %s)"
257 num subject access-type (cdr server))
258 (let ((site (cdr (assoc "site" params)))
259 (dir (cdr (assoc "directory" params)))
260 (url (cdr (assoc "url" params)))
263 (format "%s %s ([%s] %s)"
264 num subject access-type url)
265 (format "%s %s ([%s] %s:%s)"
266 num subject access-type site dir))
270 (let ((media-type (mime-entity-media-type entity))
271 (media-subtype (mime-entity-media-subtype entity))
272 (charset (cdr (assoc "charset" params)))
273 (encoding (mime-entity-encoding entity)))
277 (format " <%s/%s%s%s>"
278 media-type media-subtype
280 (concat "; " charset)
283 (concat " (" encoding ")")
285 (if (>= (+ (current-column)(length rest))(window-width))
289 (function mime-preview-play-current-entity))
296 (defvar mime-header-presentation-method-alist nil
297 "Alist of major mode vs. corresponding header-presentation-method functions.
298 Each element looks like (SYMBOL . FUNCTION).
299 SYMBOL must be major mode in raw-buffer or t. t means default.
300 Interface of FUNCTION must be (ENTITY SITUATION).")
302 (defvar mime-view-ignored-field-list
303 '(".*Received:" ".*Path:" ".*Id:" "^References:"
304 "^Replied:" "^Errors-To:"
305 "^Lines:" "^Sender:" ".*Host:" "^Xref:"
306 "^Content-Type:" "^Precedence:"
307 "^Status:" "^X-VM-.*:")
308 "All fields that match this list will be hidden in MIME preview buffer.
309 Each elements are regexp of field-name.")
311 (defvar mime-view-visible-field-list '("^Dnas.*:" "^Message-Id:")
312 "All fields that match this list will be displayed in MIME preview buffer.
313 Each elements are regexp of field-name.")
319 ;;; @@@ predicate function
322 (defun mime-calist::field-match-method-as-default-rule (calist
323 field-type field-value)
324 (let ((s-field (assq field-type calist)))
325 (cond ((null s-field)
326 (cons (cons field-type field-value) calist)
330 (define-calist-field-match-method
331 'header #'mime-calist::field-match-method-as-default-rule)
333 (define-calist-field-match-method
334 'body #'mime-calist::field-match-method-as-default-rule)
337 (defvar mime-preview-condition nil
338 "Condition-tree about how to display entity.")
340 (ctree-set-calist-strictly
341 'mime-preview-condition '((type . application)(subtype . octet-stream)
344 (ctree-set-calist-strictly
345 'mime-preview-condition '((type . application)(subtype . octet-stream)
348 (ctree-set-calist-strictly
349 'mime-preview-condition '((type . application)(subtype . octet-stream)
353 (ctree-set-calist-strictly
354 'mime-preview-condition '((type . application)(subtype . pgp)
357 (ctree-set-calist-strictly
358 'mime-preview-condition '((type . application)(subtype . x-latex)
361 (ctree-set-calist-strictly
362 'mime-preview-condition '((type . application)(subtype . x-selection)
365 (ctree-set-calist-strictly
366 'mime-preview-condition '((type . application)(subtype . x-comment)
369 (ctree-set-calist-strictly
370 'mime-preview-condition '((type . message)(subtype . delivery-status)
373 (ctree-set-calist-strictly
374 'mime-preview-condition
376 (body-presentation-method . mime-display-text/plain)))
378 (ctree-set-calist-strictly
379 'mime-preview-condition
382 (body-presentation-method . mime-display-text/plain)))
384 (ctree-set-calist-strictly
385 'mime-preview-condition
386 '((type . text)(subtype . enriched)
388 (body-presentation-method . mime-display-text/enriched)))
390 (ctree-set-calist-strictly
391 'mime-preview-condition
392 '((type . text)(subtype . richtext)
394 (body-presentation-method . mime-display-text/richtext)))
396 (ctree-set-calist-strictly
397 'mime-preview-condition
398 '((type . text)(subtype . x-vcard)
400 (body-presentation-method . mime-display-text/x-vcard)))
402 (ctree-set-calist-strictly
403 'mime-preview-condition
404 '((type . text)(subtype . t)
406 (body-presentation-method . mime-display-text/plain)))
408 (ctree-set-calist-strictly
409 'mime-preview-condition
410 '((type . multipart)(subtype . alternative)
412 (body-presentation-method . mime-display-multipart/alternative)))
414 (ctree-set-calist-strictly
415 'mime-preview-condition '((type . message)(subtype . partial)
416 (body-presentation-method
417 . mime-display-message/partial-button)))
419 (ctree-set-calist-strictly
420 'mime-preview-condition '((type . message)(subtype . rfc822)
421 (body-presentation-method . nil)
422 (childrens-situation (header . visible)
423 (entity-button . invisible))))
425 (ctree-set-calist-strictly
426 'mime-preview-condition '((type . message)(subtype . news)
427 (body-presentation-method . nil)
428 (childrens-situation (header . visible)
429 (entity-button . invisible))))
432 ;;; @@@ entity presentation
435 (defun mime-display-text/plain (entity situation)
437 (narrow-to-region (point-max)(point-max))
438 (mime-insert-text-content entity)
439 (run-hooks 'mime-text-decode-hook)
440 (goto-char (point-max))
441 (if (not (eq (char-after (1- (point))) ?\n))
444 (mime-add-url-buttons)
445 (run-hooks 'mime-display-text/plain-hook)
448 (defun mime-display-text/richtext (entity situation)
450 (narrow-to-region (point-max)(point-max))
451 (mime-insert-text-content entity)
452 (run-hooks 'mime-text-decode-hook)
453 (let ((beg (point-min)))
454 (remove-text-properties beg (point-max) '(face nil))
455 (richtext-decode beg (point-max))
458 (defun mime-display-text/enriched (entity situation)
460 (narrow-to-region (point-max)(point-max))
461 (mime-insert-text-content entity)
462 (run-hooks 'mime-text-decode-hook)
463 (let ((beg (point-min)))
464 (remove-text-properties beg (point-max) '(face nil))
465 (enriched-decode beg (point-max))
468 (defun mime-display-text/x-vcard (entity situation)
470 (narrow-to-region (point-max)(point-max))
471 (insert (string-as-multibyte (mime-entity-content entity)))
472 (goto-char (point-min))
473 (while (re-search-forward
474 "\\(;\\(encoding=\\)?quoted-printable:\\)\\(\\(=[0-9A-F][0-9A-F]\\|=\r\n\\|[^\r\n]\\)*\\)"
478 (buffer-substring (match-beginning 1) (match-end 1))
481 (decode-coding-string
482 (buffer-substring (match-beginning 3) (match-end 3)) 'raw-text-dos)
483 "quoted-printable")))
485 (decode-coding-region (point-min) (point-max) 'undecided)
486 (goto-char (point-max))
487 (if (not (eq (char-after (1- (point))) ?\n))
489 (mime-add-url-buttons)
490 (run-hooks 'mime-display-text/x-vcard-hook)
493 (defvar mime-view-announcement-for-message/partial
494 (if (and (>= emacs-major-version 19) window-system)
496 \[[ This is message/partial style split message. ]]
497 \[[ Please press `v' key in this buffer ]]
498 \[[ or click here by mouse button-2. ]]"
500 \[[ This is message/partial style split message. ]]
501 \[[ Please press `v' key in this buffer. ]]"
504 (defun mime-display-message/partial-button (&optional entity situation)
506 (goto-char (point-max))
507 (if (not (search-backward "\n\n" nil t))
510 (goto-char (point-max))
511 (narrow-to-region (point-max)(point-max))
512 (insert mime-view-announcement-for-message/partial)
513 (mime-add-button (point-min)(point-max)
514 #'mime-preview-play-current-entity)
517 (defun mime-display-multipart/mixed (entity situation)
518 (let ((children (mime-entity-children entity))
520 (cdr (assq 'childrens-situation situation))))
522 (mime-display-entity (car children) nil default-situation)
523 (setq children (cdr children))
526 (defcustom mime-view-type-subtype-score-alist
527 '(((text . enriched) . 3)
528 ((text . richtext) . 2)
531 "Alist MEDIA-TYPE vs corresponding score.
532 MEDIA-TYPE must be (TYPE . SUBTYPE), TYPE or t. t means default."
534 :type '(repeat (cons (choice :tag "Media-Type"
535 (cons :tag "Type/Subtype"
536 (symbol :tag "Primary-type")
537 (symbol :tag "Subtype"))
539 (const :tag "Default" t))
542 (defun mime-display-multipart/alternative (entity situation)
543 (let* ((children (mime-entity-children entity))
545 (cdr (assq 'childrens-situation situation)))
553 (or (ctree-match-calist
554 mime-preview-condition
555 (append (mime-entity-situation child)
558 (if (cdr (assq 'body-presentation-method situation))
563 (cdr (assq 'type situation))
564 (cdr (assq 'subtype situation)))
565 mime-view-type-subtype-score-alist)
567 (cdr (assq 'type situation))
568 mime-view-type-subtype-score-alist)
571 mime-view-type-subtype-score-alist)
573 (if (> score max-score)
583 (let ((child (car children))
584 (situation (car situations)))
585 (mime-display-entity child (if (= i p)
587 (del-alist 'body-presentation-method
588 (copy-alist situation))))
590 (setq children (cdr children)
591 situations (cdr situations)
596 ;;; @ acting-condition
599 (defvar mime-acting-condition nil
600 "Condition-tree about how to process entity.")
602 (if (file-readable-p mailcap-file)
603 (let ((entries (mailcap-parse-file)))
605 (let ((entry (car entries))
608 (let* ((field (car entry))
609 (field-type (car field)))
610 (cond ((eq field-type 'view) (setq view field))
611 ((eq field-type 'print) (setq print field))
612 ((memq field-type '(compose composetyped edit)))
613 (t (setq shared (cons field shared))))
615 (setq entry (cdr entry))
617 (setq shared (nreverse shared))
618 (ctree-set-calist-with-default
619 'mime-acting-condition
620 (append shared (list '(mode . "play")(cons 'method (cdr view)))))
622 (ctree-set-calist-with-default
623 'mime-acting-condition
625 (list '(mode . "print")(cons 'method (cdr view))))
628 (setq entries (cdr entries))
631 (ctree-set-calist-strictly
632 'mime-acting-condition
633 '((type . application)(subtype . octet-stream)
635 (method . mime-detect-content)
638 (ctree-set-calist-with-default
639 'mime-acting-condition
641 (method . mime-save-content)))
643 (ctree-set-calist-strictly
644 'mime-acting-condition
645 '((type . text)(subtype . x-rot13-47)(mode . "play")
646 (method . mime-view-caesar)
648 (ctree-set-calist-strictly
649 'mime-acting-condition
650 '((type . text)(subtype . x-rot13-47-48)(mode . "play")
651 (method . mime-view-caesar)
654 (ctree-set-calist-strictly
655 'mime-acting-condition
656 '((type . message)(subtype . rfc822)(mode . "play")
657 (method . mime-view-message/rfc822)
659 (ctree-set-calist-strictly
660 'mime-acting-condition
661 '((type . message)(subtype . partial)(mode . "play")
662 (method . mime-store-message/partial-piece)
665 (ctree-set-calist-strictly
666 'mime-acting-condition
667 '((type . message)(subtype . external-body)
668 ("access-type" . "anon-ftp")
669 (method . mime-view-message/external-anon-ftp)
672 (ctree-set-calist-strictly
673 'mime-acting-condition
674 '((type . message)(subtype . external-body)
675 ("access-type" . "url")
676 (method . mime-view-message/external-url)
679 (ctree-set-calist-strictly
680 'mime-acting-condition
681 '((type . application)(subtype . octet-stream)
682 (method . mime-save-content)
686 ;;; @ quitting method
689 (defvar mime-preview-quitting-method-alist
690 '((mime-show-message-mode
691 . mime-preview-quitting-method-for-mime-show-message-mode))
692 "Alist of major-mode vs. quitting-method of mime-view.")
694 (defvar mime-preview-over-to-previous-method-alist nil
695 "Alist of major-mode vs. over-to-previous-method of mime-view.")
697 (defvar mime-preview-over-to-next-method-alist nil
698 "Alist of major-mode vs. over-to-next-method of mime-view.")
701 ;;; @ following method
704 (defvar mime-preview-following-method-alist nil
705 "Alist of major-mode vs. following-method of mime-view.")
707 (defvar mime-view-following-required-fields-list
714 (defun mime-display-entity (entity &optional situation
715 default-situation preview-buffer)
717 (setq preview-buffer (current-buffer)))
718 (let* ((raw-buffer (mime-entity-buffer entity))
719 (start (mime-entity-point-min entity))
721 (set-buffer raw-buffer)
725 (or (ctree-match-calist mime-preview-condition
726 (append (mime-entity-situation entity)
729 (let ((button-is-invisible
730 (eq (cdr (assq 'entity-button situation)) 'invisible))
732 (eq (cdr (assq 'header situation)) 'visible))
733 (header-presentation-method
734 (or (cdr (assq 'header-presentation-method situation))
735 (cdr (assq major-mode mime-header-presentation-method-alist))))
736 (body-presentation-method
737 (cdr (assq 'body-presentation-method situation)))
738 (children (mime-entity-children entity)))
739 (set-buffer preview-buffer)
741 (narrow-to-region nb nb)
742 (or button-is-invisible
743 (if (mime-view-entity-button-visible-p entity)
744 (mime-view-insert-entity-button entity)
746 (when header-is-visible
747 (if header-presentation-method
748 (funcall header-presentation-method entity situation)
749 (mime-insert-header entity
750 mime-view-ignored-field-list
751 mime-view-visible-field-list))
752 (goto-char (point-max))
754 (run-hooks 'mime-display-header-hook)
757 ((functionp body-presentation-method)
758 (funcall body-presentation-method entity situation)
761 (when button-is-invisible
762 (goto-char (point-max))
763 (mime-view-insert-entity-button entity)
765 (or header-is-visible
767 (goto-char (point-max))
771 (setq ne (point-max))
773 (put-text-property nb ne 'mime-view-entity entity)
776 (if (functionp body-presentation-method)
777 (funcall body-presentation-method entity situation)
778 (mime-display-multipart/mixed entity situation)
783 ;;; @ MIME viewer mode
786 (defconst mime-view-menu-title "MIME-View")
787 (defconst mime-view-menu-list
788 '((up "Move to upper entity" mime-preview-move-to-upper)
789 (previous "Move to previous entity" mime-preview-move-to-previous)
790 (next "Move to next entity" mime-preview-move-to-next)
791 (scroll-down "Scroll-down" mime-preview-scroll-down-entity)
792 (scroll-up "Scroll-up" mime-preview-scroll-up-entity)
793 (play "Play current entity" mime-preview-play-current-entity)
794 (extract "Extract current entity" mime-preview-extract-current-entity)
795 (print "Print current entity" mime-preview-print-current-entity)
797 "Menu for MIME Viewer")
799 (cond ((featurep 'xemacs)
800 (defvar mime-view-xemacs-popup-menu
801 (cons mime-view-menu-title
804 (vector (nth 1 item)(nth 2 item) t)
806 mime-view-menu-list)))
807 (defun mime-view-xemacs-popup-menu (event)
808 "Popup the menu in the MIME Viewer buffer"
810 (select-window (event-window event))
811 (set-buffer (event-buffer event))
812 (popup-menu 'mime-view-xemacs-popup-menu))
813 (defvar mouse-button-2 'button2)
816 (defvar mouse-button-2 [mouse-2])
819 (defun mime-view-define-keymap (&optional default)
820 (let ((mime-view-mode-map (if (keymapp default)
821 (copy-keymap default)
824 (define-key mime-view-mode-map
825 "u" (function mime-preview-move-to-upper))
826 (define-key mime-view-mode-map
827 "p" (function mime-preview-move-to-previous))
828 (define-key mime-view-mode-map
829 "n" (function mime-preview-move-to-next))
830 (define-key mime-view-mode-map
831 "\e\t" (function mime-preview-move-to-previous))
832 (define-key mime-view-mode-map
833 "\t" (function mime-preview-move-to-next))
834 (define-key mime-view-mode-map
835 " " (function mime-preview-scroll-up-entity))
836 (define-key mime-view-mode-map
837 "\M- " (function mime-preview-scroll-down-entity))
838 (define-key mime-view-mode-map
839 "\177" (function mime-preview-scroll-down-entity))
840 (define-key mime-view-mode-map
841 "\C-m" (function mime-preview-next-line-entity))
842 (define-key mime-view-mode-map
843 "\C-\M-m" (function mime-preview-previous-line-entity))
844 (define-key mime-view-mode-map
845 "v" (function mime-preview-play-current-entity))
846 (define-key mime-view-mode-map
847 "e" (function mime-preview-extract-current-entity))
848 (define-key mime-view-mode-map
849 "\C-c\C-p" (function mime-preview-print-current-entity))
850 (define-key mime-view-mode-map
851 "a" (function mime-preview-follow-current-entity))
852 (define-key mime-view-mode-map
853 "q" (function mime-preview-quit))
854 (define-key mime-view-mode-map
855 "\C-c\C-x" (function mime-preview-kill-buffer))
856 ;; (define-key mime-view-mode-map
857 ;; "<" (function beginning-of-buffer))
858 ;; (define-key mime-view-mode-map
859 ;; ">" (function end-of-buffer))
860 (define-key mime-view-mode-map
861 "?" (function describe-mode))
862 (define-key mime-view-mode-map
863 [tab] (function mime-preview-move-to-next))
864 (define-key mime-view-mode-map
865 [delete] (function mime-preview-scroll-down-entity))
866 (define-key mime-view-mode-map
867 [backspace] (function mime-preview-scroll-down-entity))
868 (if (functionp default)
869 (cond ((featurep 'xemacs)
870 (set-keymap-default-binding mime-view-mode-map default)
873 (setq mime-view-mode-map
874 (append mime-view-mode-map (list (cons t default))))
877 (define-key mime-view-mode-map
878 mouse-button-2 (function mime-button-dispatcher))
880 (cond ((featurep 'xemacs)
881 (define-key mime-view-mode-map
882 mouse-button-3 (function mime-view-xemacs-popup-menu))
884 ((>= emacs-major-version 19)
885 (define-key mime-view-mode-map [menu-bar mime-view]
886 (cons mime-view-menu-title
887 (make-sparse-keymap mime-view-menu-title)))
890 (define-key mime-view-mode-map
891 (vector 'menu-bar 'mime-view (car item))
892 (cons (nth 1 item)(nth 2 item))
895 (reverse mime-view-menu-list)
898 (use-local-map mime-view-mode-map)
899 (run-hooks 'mime-view-define-keymap-hook)
902 (defsubst mime-maybe-hide-echo-buffer ()
903 "Clear mime-echo buffer and delete window for it."
904 (let ((buf (get-buffer mime-echo-buffer-name)))
909 (let ((win (get-buffer-window buf)))
916 (defvar mime-view-redisplay nil)
918 (defun mime-display-message (message &optional preview-buffer
919 mother default-keymap-or-function)
920 (mime-maybe-hide-echo-buffer)
921 (let ((win-conf (current-window-configuration))
922 (raw-buffer (mime-entity-buffer message)))
925 (concat "*Preview-" (buffer-name raw-buffer) "*")))
926 (set-buffer raw-buffer)
927 (setq mime-preview-buffer preview-buffer)
928 (let ((inhibit-read-only t))
929 (set-buffer (get-buffer-create preview-buffer))
932 (setq mime-raw-buffer raw-buffer)
934 (setq mime-mother-buffer mother)
936 (setq mime-preview-original-window-configuration win-conf)
937 (setq major-mode 'mime-view-mode)
938 (setq mode-name "MIME-View")
939 (mime-display-entity message nil
940 '((entity-button . invisible)
943 (mime-view-define-keymap default-keymap-or-function)
945 (next-single-property-change (point-min) 'mime-view-entity)))
948 (goto-char (point-min))
949 (search-forward "\n\n" nil t)
951 (run-hooks 'mime-view-mode-hook)
952 (set-buffer-modified-p nil)
953 (setq buffer-read-only t)
954 (or (get-buffer-window preview-buffer)
955 (let ((r-win (get-buffer-window raw-buffer)))
957 (set-window-buffer r-win preview-buffer)
958 (let ((m-win (and mother (get-buffer-window mother))))
960 (set-window-buffer m-win preview-buffer)
961 (switch-to-buffer preview-buffer)
965 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
966 default-keymap-or-function
968 "View RAW-BUFFER in MIME-View mode.
969 Optional argument PREVIEW-BUFFER is either nil or a name of preview
971 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
972 function. If it is a keymap, keymap of MIME-View mode will be added
973 to it. If it is a function, it will be bound as default binding of
974 keymap of MIME-View mode.
975 Optional argument REPRESENTATION-TYPE is representation-type of
976 message. It must be nil, `binary' or `cooked'. If it is nil,
977 `cooked' is used as default."
980 (setq raw-buffer (current-buffer)))
981 (or representation-type
982 (setq representation-type
984 (set-buffer raw-buffer)
985 (cdr (or (assq major-mode mime-raw-representation-type-alist)
986 (assq t mime-raw-representation-type-alist)))
988 (if (eq representation-type 'binary)
989 (setq representation-type 'buffer)
991 (mime-display-message
992 (mime-open-entity representation-type raw-buffer)
993 preview-buffer mother default-keymap-or-function))
995 (defun mime-view-mode (&optional mother ctl encoding
996 raw-buffer preview-buffer
997 default-keymap-or-function)
998 "Major mode for viewing MIME message.
1000 Here is a list of the standard keys for mime-view-mode.
1005 u Move to upper content
1006 p or M-TAB Move to previous content
1007 n or TAB Move to next content
1008 SPC Scroll up or move to next content
1009 M-SPC or DEL Scroll down or move to previous content
1010 RET Move to next line
1011 M-RET Move to previous line
1012 v Decode current content as `play mode'
1013 e Decode current content as `extract mode'
1014 C-c C-p Decode current content as `print mode'
1015 a Followup to current content.
1017 button-2 Move to point under the mouse cursor
1018 and decode current content as `play mode'
1021 (unless mime-view-redisplay
1023 (if raw-buffer (set-buffer raw-buffer))
1026 (or (assq major-mode mime-raw-representation-type-alist)
1027 (assq t mime-raw-representation-type-alist)))))
1028 (if (eq type 'binary)
1031 (setq mime-message-structure (mime-open-entity type raw-buffer))
1032 (or (mime-entity-content-type mime-message-structure)
1033 (mime-entity-set-content-type-internal
1034 mime-message-structure ctl))
1036 (or (mime-entity-encoding mime-message-structure)
1037 (mime-entity-set-encoding-internal mime-message-structure encoding))
1039 (mime-display-message mime-message-structure preview-buffer
1040 mother default-keymap-or-function)
1047 (autoload 'mime-preview-play-current-entity "mime-play"
1048 "Play current entity." t)
1050 (defun mime-preview-extract-current-entity (&optional ignore-examples)
1051 "Extract current entity into file (maybe).
1052 It decodes current entity to call internal or external method as
1053 \"extract\" mode. The method is selected from variable
1054 `mime-acting-condition'."
1056 (mime-preview-play-current-entity ignore-examples "extract")
1059 (defun mime-preview-print-current-entity (&optional ignore-examples)
1060 "Print current entity (maybe).
1061 It decodes current entity to call internal or external method as
1062 \"print\" mode. The method is selected from variable
1063 `mime-acting-condition'."
1065 (mime-preview-play-current-entity ignore-examples "print")
1072 (defun mime-preview-follow-current-entity ()
1073 "Write follow message to current entity.
1074 It calls following-method selected from variable
1075 `mime-preview-following-method-alist'."
1078 (while (null (setq entity
1079 (get-text-property (point) 'mime-view-entity)))
1083 (previous-single-property-change (point) 'mime-view-entity))
1085 (entity-node-id (mime-entity-node-id entity))
1086 (len (length entity-node-id))
1090 (if (eq (next-single-property-change (point-min)
1096 ((eq (next-single-property-change p-beg 'mime-view-entity)
1098 (setq p-beg (point))
1100 (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1102 (setq p-end (point-max))
1104 ((null entity-node-id)
1105 (setq p-end (point-max))
1113 (next-single-property-change
1114 (point) 'mime-view-entity))
1116 (let ((rc (mime-entity-node-id
1117 (get-text-property (point)
1118 'mime-view-entity))))
1119 (or (equal entity-node-id
1120 (nthcdr (- (length rc) len) rc))
1125 (setq p-end (point-max))
1128 (let* ((mode (mime-preview-original-major-mode 'recursive))
1130 (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1132 (the-buf (current-buffer))
1133 (a-buf mime-raw-buffer)
1136 (set-buffer (setq new-buf (get-buffer-create new-name)))
1138 (insert-buffer-substring the-buf p-beg p-end)
1139 (goto-char (point-min))
1140 (let ((entity-node-id (mime-entity-node-id entity)) ci str)
1147 (mime-find-entity-from-node-id entity-node-id))
1150 (mime-entity-point-min ci)
1151 (mime-entity-point-max ci)
1153 (std11-header-string-except
1155 (apply (function regexp-or) fields)
1158 (eq (mime-entity-media-type ci) 'message)
1159 (eq (mime-entity-media-subtype ci) 'rfc822))
1165 (setq fields (std11-collect-field-names)
1166 entity-node-id (cdr entity-node-id))
1169 (let ((rest mime-view-following-required-fields-list))
1171 (let ((field-name (car rest)))
1172 (or (std11-field-body field-name)
1178 (set-buffer the-buf)
1179 (set-buffer mime-mother-buffer)
1180 (set-buffer mime-raw-buffer)
1181 (std11-field-body field-name)
1185 (setq rest (cdr rest))
1187 (mime-decode-header-in-buffer)
1189 (let ((f (cdr (assq mode mime-preview-following-method-alist))))
1194 "Sorry, following method for %s is not implemented yet."
1203 (defun mime-preview-move-to-upper ()
1204 "Move to upper entity.
1205 If there is no upper entity, call function `mime-preview-quit'."
1208 (while (null (setq cinfo
1209 (get-text-property (point) 'mime-view-entity)))
1212 (let ((r (mime-entity-parent cinfo))
1215 (while (setq point (previous-single-property-change
1216 (point) 'mime-view-entity))
1218 (if (eq r (get-text-property (point) 'mime-view-entity))
1225 (defun mime-preview-move-to-previous ()
1226 "Move to previous entity.
1227 If there is no previous entity, it calls function registered in
1228 variable `mime-preview-over-to-previous-method-alist'."
1230 (while (null (get-text-property (point) 'mime-view-entity))
1233 (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1235 (if (get-text-property (1- point) 'mime-view-entity)
1237 (goto-char (1- point))
1238 (mime-preview-move-to-previous)
1240 (let ((f (assq (mime-preview-original-major-mode)
1241 mime-preview-over-to-previous-method-alist)))
1247 (defun mime-preview-move-to-next ()
1248 "Move to next entity.
1249 If there is no previous entity, it calls function registered in
1250 variable `mime-preview-over-to-next-method-alist'."
1252 (while (and (not (eobp))
1253 (null (get-text-property (point) 'mime-view-entity)))
1256 (let ((point (next-single-property-change (point) 'mime-view-entity)))
1260 (if (null (get-text-property point 'mime-view-entity))
1261 (mime-preview-move-to-next)
1263 (let ((f (assq (mime-preview-original-major-mode)
1264 mime-preview-over-to-next-method-alist)))
1270 (defun mime-preview-scroll-up-entity (&optional h)
1271 "Scroll up current entity.
1272 If reached to (point-max), it calls function registered in variable
1273 `mime-preview-over-to-next-method-alist'."
1276 (setq h (1- (window-height)))
1278 (if (= (point) (point-max))
1279 (let ((f (assq (mime-preview-original-major-mode)
1280 mime-preview-over-to-next-method-alist)))
1285 (or (next-single-property-change (point) 'mime-view-entity)
1288 (if (> (point) point)
1293 (defun mime-preview-scroll-down-entity (&optional h)
1294 "Scroll down current entity.
1295 If reached to (point-min), it calls function registered in variable
1296 `mime-preview-over-to-previous-method-alist'."
1299 (setq h (1- (window-height)))
1301 (if (= (point) (point-min))
1302 (let ((f (assq (mime-preview-original-major-mode)
1303 mime-preview-over-to-previous-method-alist)))
1308 (or (previous-single-property-change (point) 'mime-view-entity)
1310 (forward-line (- h))
1311 (if (< (point) point)
1315 (defun mime-preview-next-line-entity ()
1317 (mime-preview-scroll-up-entity 1)
1320 (defun mime-preview-previous-line-entity ()
1322 (mime-preview-scroll-down-entity 1)
1329 (defun mime-preview-quit ()
1330 "Quit from MIME-preview buffer.
1331 It calls function registered in variable
1332 `mime-preview-quitting-method-alist'."
1334 (let ((r (assq (mime-preview-original-major-mode)
1335 mime-preview-quitting-method-alist)))
1340 (defun mime-preview-kill-buffer ()
1342 (kill-buffer (current-buffer))
1349 (provide 'mime-view)
1351 (run-hooks 'mime-view-load-hook)
1353 ;;; mime-view.el ends here