Sync up with SEMI 1.9.0.
[elisp/semi.git] / mime-view.el
1 ;;; mime-view.el --- interactive MIME viewer for GNU Emacs
2
3 ;; Copyright (C) 1995,1996,1997,1998 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
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 WEMI (Widget based Emacs MIME Interfaces).
12
13 ;; This program is free software; you can redistribute it and/or
14 ;; modify it under the terms of the GNU General Public License as
15 ;; published by the Free Software Foundation; either version 2, or (at
16 ;; your option) any later version.
17
18 ;; This program is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 ;; General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Code:
29
30 (require 'mime)
31 (require 'semi-def)
32 (require 'calist)
33 (require 'alist)
34 (require 'mailcap)
35
36
37 ;;; @ version
38 ;;;
39
40 (defconst mime-view-version
41   (eval-when-compile
42     (concat (mime-product-name mime-user-interface-product) " MIME-View "
43             (mapconcat #'number-to-string
44                        (mime-product-version mime-user-interface-product) ".")
45             " (" (mime-product-code-name mime-user-interface-product) ")")))
46
47
48 ;;; @ variables
49 ;;;
50
51 (defgroup mime-view nil
52   "MIME view mode"
53   :group 'mime)
54
55 (defcustom mime-view-find-every-acting-situation t
56   "*Find every available acting-situation if non-nil."
57   :group 'mime-view
58   :type 'boolean)
59
60 (defcustom mime-acting-situation-examples-file "~/.mime-example"
61   "*File name of example about acting-situation demonstrated by user."
62   :group 'mime-view
63   :type 'file)
64
65
66 ;;; @ in raw-buffer (representation space)
67 ;;;
68
69 (defvar mime-preview-buffer nil
70   "MIME-preview buffer corresponding with the (raw) buffer.")
71 (make-variable-buffer-local 'mime-preview-buffer)
72
73
74 (defvar mime-raw-representation-type-alist
75   '((mime-show-message-mode     . binary)
76     (mime-temp-message-mode     . binary)
77     (t                          . cooked)
78     )
79   "Alist of major-mode vs. representation-type of mime-raw-buffer.
80 Each element looks like (SYMBOL . REPRESENTATION-TYPE).  SYMBOL is
81 major-mode or t.  t means default.  REPRESENTATION-TYPE must be
82 `binary' or `cooked'.")
83
84
85 (defun mime-raw-find-entity-from-point (point &optional message-info)
86   "Return entity from POINT in mime-raw-buffer.
87 If optional argument MESSAGE-INFO is not specified,
88 `mime-message-structure' is used."
89   (or message-info
90       (setq message-info mime-message-structure))
91   (if (and (<= (mime-entity-point-min message-info) point)
92            (<= point (mime-entity-point-max message-info)))
93       (let ((children (mime-entity-children message-info)))
94         (catch 'tag
95           (while children
96             (let ((ret
97                    (mime-raw-find-entity-from-point point (car children))))
98               (if ret
99                   (throw 'tag ret)
100                 ))
101             (setq children (cdr children)))
102           message-info))))
103
104
105 ;;; @ in preview-buffer (presentation space)
106 ;;;
107
108 (defvar mime-mother-buffer nil
109   "Mother buffer corresponding with the (MIME-preview) buffer.
110 If current MIME-preview buffer is generated by other buffer, such as
111 message/partial, it is called `mother-buffer'.")
112 (make-variable-buffer-local 'mime-mother-buffer)
113
114 (defvar mime-raw-buffer nil
115   "Raw buffer corresponding with the (MIME-preview) buffer.")
116 (make-variable-buffer-local 'mime-raw-buffer)
117
118 (defvar mime-preview-original-window-configuration nil
119   "Window-configuration before mime-view-mode is called.")
120 (make-variable-buffer-local 'mime-preview-original-window-configuration)
121
122 (defun mime-preview-original-major-mode (&optional recursive)
123   "Return major-mode of original buffer.
124 If optional argument RECURSIVE is non-nil and current buffer has
125 mime-mother-buffer, it returns original major-mode of the
126 mother-buffer."
127   (if (and recursive mime-mother-buffer)
128       (save-excursion
129         (set-buffer mime-mother-buffer)
130         (mime-preview-original-major-mode recursive)
131         )
132     (save-excursion
133       (set-buffer
134        (mime-entity-buffer
135         (get-text-property (point-min) 'mime-view-entity)))
136       major-mode)))
137
138
139 ;;; @ entity information
140 ;;;
141
142 (defun mime-entity-situation (entity)
143   "Return situation of ENTITY."
144   (append (or (mime-entity-content-type entity)
145               (make-mime-content-type 'text 'plain))
146           (let ((d (mime-entity-content-disposition entity)))
147             (cons (cons 'disposition-type
148                         (mime-content-disposition-type d))
149                   (mapcar (function
150                            (lambda (param)
151                              (let ((name (car param)))
152                                (cons (cond ((string= name "filename")
153                                             'filename)
154                                            ((string= name "creation-date")
155                                             'creation-date)
156                                            ((string= name "modification-date")
157                                             'modification-date)
158                                            ((string= name "read-date")
159                                             'read-date)
160                                            ((string= name "size")
161                                             'size)
162                                            (t (cons 'disposition (car param))))
163                                      (cdr param)))))
164                           (mime-content-disposition-parameters d))
165                   ))
166           (list (cons 'encoding (mime-entity-encoding entity))
167                 (cons 'major-mode
168                       (save-excursion
169                         (set-buffer (mime-entity-buffer entity))
170                         major-mode)))
171           ))
172
173
174 (defun mime-view-entity-title (entity)
175   (or (mime-read-field 'Content-Description entity)
176       (mime-read-field 'Subject entity)
177       (mime-entity-filename entity)
178       ""))
179
180
181 (defsubst mime-raw-point-to-entity-node-id (point &optional message-info)
182   "Return entity-node-id from POINT in mime-raw-buffer.
183 If optional argument MESSAGE-INFO is not specified,
184 `mime-message-structure' is used."
185   (mime-entity-node-id (mime-raw-find-entity-from-point point message-info)))
186
187 (defsubst mime-raw-point-to-entity-number (point &optional message-info)
188   "Return entity-number from POINT in mime-raw-buffer.
189 If optional argument MESSAGE-INFO is not specified,
190 `mime-message-structure' is used."
191   (mime-entity-number (mime-raw-find-entity-from-point point message-info)))
192
193 (defun mime-raw-flatten-message-info (&optional message-info)
194   "Return list of entity in mime-raw-buffer.
195 If optional argument MESSAGE-INFO is not specified,
196 `mime-message-structure' is used."
197   (or message-info
198       (setq message-info mime-message-structure))
199   (let ((dest (list message-info))
200         (rcl (mime-entity-children message-info)))
201     (while rcl
202       (setq dest (nconc dest (mime-raw-flatten-message-info (car rcl))))
203       (setq rcl (cdr rcl)))
204     dest))
205
206
207 ;;; @ presentation of preview
208 ;;;
209
210 ;;; @@ entity-button
211 ;;;
212
213 ;;; @@@ predicate function
214 ;;;
215
216 (defun mime-view-entity-button-visible-p (entity)
217   "Return non-nil if header of ENTITY is visible.
218 Please redefine this function if you want to change default setting."
219   (let ((media-type (mime-entity-media-type entity))
220         (media-subtype (mime-entity-media-subtype entity)))
221     (or (not (eq media-type 'application))
222         (and (not (eq media-subtype 'x-selection))
223              (or (not (eq media-subtype 'octet-stream))
224                  (let ((mother-entity (mime-entity-parent entity)))
225                    (or (not (eq (mime-entity-media-type mother-entity)
226                                 'multipart))
227                        (not (eq (mime-entity-media-subtype mother-entity)
228                                 'encrypted)))
229                    )
230                  )))))
231
232 ;;; @@@ entity button generator
233 ;;;
234
235 (defun mime-view-insert-entity-button (entity)
236   "Insert entity-button of ENTITY."
237   (let ((entity-node-id (mime-entity-node-id entity))
238         (params (mime-entity-parameters entity))
239         (subject (mime-view-entity-title entity)))
240     (mime-insert-button
241      (let ((access-type (assoc "access-type" params))
242            (num (or (cdr (assoc "x-part-number" params))
243                     (if (consp entity-node-id)
244                         (mapconcat (function
245                                     (lambda (num)
246                                       (format "%s" (1+ num))
247                                       ))
248                                    (reverse entity-node-id) ".")
249                       "0"))
250                 ))
251        (cond (access-type
252               (let ((server (assoc "server" params)))
253                 (setq access-type (cdr access-type))
254                 (if server
255                     (format "%s %s ([%s] %s)"
256                             num subject access-type (cdr server))
257                 (let ((site (cdr (assoc "site" params)))
258                       (dir (cdr (assoc "directory" params)))
259                       )
260                   (format "%s %s ([%s] %s:%s)"
261                           num subject access-type site dir)
262                   )))
263             )
264            (t
265             (let ((media-type (mime-entity-media-type entity))
266                   (media-subtype (mime-entity-media-subtype entity))
267                   (charset (cdr (assoc "charset" params)))
268                   (encoding (mime-entity-encoding entity)))
269               (concat
270                num " " subject
271                (let ((rest
272                       (format " <%s/%s%s%s>"
273                               media-type media-subtype
274                               (if charset
275                                   (concat "; " charset)
276                                 "")
277                               (if encoding
278                                   (concat " (" encoding ")")
279                                 ""))))
280                  (if (>= (+ (current-column)(length rest))(window-width))
281                      "\n\t")
282                  rest)))
283             )))
284      (function mime-preview-play-current-entity))
285     ))
286
287
288 ;;; @@ entity-header
289 ;;;
290
291 (defvar mime-header-presentation-method-alist nil
292   "Alist of major mode vs. corresponding header-presentation-method functions.
293 Each element looks like (SYMBOL . FUNCTION).
294 SYMBOL must be major mode in raw-buffer or t.  t means default.
295 Interface of FUNCTION must be (ENTITY SITUATION).")
296
297 (defvar mime-view-ignored-field-list
298   '(".*Received" ".*Path" ".*Id" "References"
299     "Replied" "Errors-To"
300     "Lines" "Sender" ".*Host" "Xref"
301     "Content-Type" "Precedence"
302     "Status" "X-VM-.*")
303   "All fields that match this list will be hidden in MIME preview buffer.
304 Each elements are regexp of field-name.")
305
306 (defvar mime-view-visible-field-list '("Dnas.*" "Message-Id")
307   "All fields that match this list will be displayed in MIME preview buffer.
308 Each elements are regexp of field-name.")
309
310
311 ;;; @@ entity-body
312 ;;;
313
314 ;;; @@@ predicate function
315 ;;;
316
317 (defun mime-calist::field-match-method-as-default-rule (calist
318                                                         field-type field-value)
319   (let ((s-field (assq field-type calist)))
320     (cond ((null s-field)
321            (cons (cons field-type field-value) calist)
322            )
323           (t calist))))
324
325 (define-calist-field-match-method
326   'header #'mime-calist::field-match-method-as-default-rule)
327
328 (define-calist-field-match-method
329   'body #'mime-calist::field-match-method-as-default-rule)
330
331
332 (defvar mime-preview-condition nil
333   "Condition-tree about how to display entity.")
334
335 (ctree-set-calist-strictly
336  'mime-preview-condition '((type . application)(subtype . octet-stream)
337                            (encoding . nil)
338                            (body . visible)))
339 (ctree-set-calist-strictly
340  'mime-preview-condition '((type . application)(subtype . octet-stream)
341                            (encoding . "7bit")
342                            (body . visible)))
343 (ctree-set-calist-strictly
344  'mime-preview-condition '((type . application)(subtype . octet-stream)
345                            (encoding . "8bit")
346                            (body . visible)))
347
348 (ctree-set-calist-strictly
349  'mime-preview-condition '((type . application)(subtype . pgp)
350                            (body . visible)))
351
352 (ctree-set-calist-strictly
353  'mime-preview-condition '((type . application)(subtype . x-latex)
354                            (body . visible)))
355
356 (ctree-set-calist-strictly
357  'mime-preview-condition '((type . application)(subtype . x-selection)
358                            (body . visible)))
359
360 (ctree-set-calist-strictly
361  'mime-preview-condition '((type . application)(subtype . x-comment)
362                            (body . visible)))
363
364 (ctree-set-calist-strictly
365  'mime-preview-condition '((type . message)(subtype . delivery-status)
366                            (body . visible)))
367
368 (ctree-set-calist-strictly
369  'mime-preview-condition
370  '((body . visible)
371    (body-presentation-method . mime-display-text/plain)))
372
373 (ctree-set-calist-strictly
374  'mime-preview-condition
375  '((type . nil)
376    (body . visible)
377    (body-presentation-method . mime-display-text/plain)))
378
379 (ctree-set-calist-strictly
380  'mime-preview-condition
381  '((type . text)(subtype . enriched)
382    (body . visible)
383    (body-presentation-method . mime-display-text/enriched)))
384
385 (ctree-set-calist-strictly
386  'mime-preview-condition
387  '((type . text)(subtype . richtext)
388    (body . visible)
389    (body-presentation-method . mime-display-text/richtext)))
390
391 (ctree-set-calist-strictly
392  'mime-preview-condition
393  '((type . text)(subtype . t)
394    (body . visible)
395    (body-presentation-method . mime-display-text/plain)))
396
397 (ctree-set-calist-strictly
398  'mime-preview-condition
399  '((type . multipart)(subtype . alternative)
400    (body . visible)
401    (body-presentation-method . mime-display-multipart/alternative)))
402
403 (ctree-set-calist-strictly
404  'mime-preview-condition '((type . message)(subtype . partial)
405                            (body-presentation-method
406                             . mime-display-message/partial-button)))
407
408 (ctree-set-calist-strictly
409  'mime-preview-condition '((type . message)(subtype . rfc822)
410                            (body-presentation-method . nil)
411                            (childrens-situation (header . visible)
412                                                 (entity-button . invisible))))
413
414 (ctree-set-calist-strictly
415  'mime-preview-condition '((type . message)(subtype . news)
416                            (body-presentation-method . nil)
417                            (childrens-situation (header . visible)
418                                                 (entity-button . invisible))))
419
420
421 ;;; @@@ entity presentation
422 ;;;
423
424 (autoload 'mime-display-text/plain "mime-text")
425 (autoload 'mime-display-text/enriched "mime-text")
426 (autoload 'mime-display-text/richtext "mime-text")
427
428 (defvar mime-view-announcement-for-message/partial
429   (if (and (>= emacs-major-version 19) window-system)
430       "\
431 This is message/partial style split message.
432 Please press `v' key in this buffer or click here by mouse button-2."
433     "\
434 This is message/partial style split message.
435 Please press `v' key in this buffer."
436     ))
437
438 (defun mime-display-message/partial-button (&optional entity situation)
439   (save-restriction
440     (goto-char (point-max))
441     (if (not (search-backward "\n\n" nil t))
442         (insert "\n")
443       )
444     (goto-char (point-max))
445     ;;(narrow-to-region (point-max)(point-max))
446     ;;(insert mime-view-announcement-for-message/partial)
447     ;; (mime-add-button (point-min)(point-max)
448     ;;                  #'mime-preview-play-current-entity)
449     (mime-insert-button mime-view-announcement-for-message/partial
450                         #'mime-preview-play-current-entity)
451     ))
452
453 (defun mime-display-multipart/mixed (entity situation)
454   (let ((children (mime-entity-children entity))
455         (default-situation
456           (cdr (assq 'childrens-situation situation))))
457     (while children
458       (mime-display-entity (car children) nil default-situation)
459       (setq children (cdr children))
460       )))
461
462 (defcustom mime-view-type-subtype-score-alist
463   '(((text . enriched) . 3)
464     ((text . richtext) . 2)
465     ((text . plain)    . 1)
466     (t . 0))
467   "Alist MEDIA-TYPE vs corresponding score.
468 MEDIA-TYPE must be (TYPE . SUBTYPE), TYPE or t.  t means default."
469   :group 'mime-view
470   :type '(repeat (cons (choice :tag "Media-Type"
471                                (cons :tag "Type/Subtype"
472                                      (symbol :tag "Primary-type")
473                                      (symbol :tag "Subtype"))
474                                (symbol :tag "Type")
475                                (const :tag "Default" t))
476                        integer)))
477
478 (defun mime-display-multipart/alternative (entity situation)
479   (let* ((children (mime-entity-children entity))
480          (default-situation
481            (cdr (assq 'childrens-situation situation)))
482          (i 0)
483          (p 0)
484          (max-score 0)
485          (situations
486           (mapcar (function
487                    (lambda (child)
488                      (let ((situation
489                             (or (ctree-match-calist
490                                  mime-preview-condition
491                                  (append (mime-entity-situation child)
492                                          default-situation))
493                                 default-situation)))
494                        (if (cdr (assq 'body-presentation-method situation))
495                            (let ((score
496                                   (cdr
497                                    (or (assoc
498                                         (cons
499                                          (cdr (assq 'type situation))
500                                          (cdr (assq 'subtype situation)))
501                                         mime-view-type-subtype-score-alist)
502                                        (assq
503                                         (cdr (assq 'type situation))
504                                         mime-view-type-subtype-score-alist)
505                                        (assq
506                                         t
507                                         mime-view-type-subtype-score-alist)
508                                        ))))
509                              (if (> score max-score)
510                                  (setq p i
511                                        max-score score)
512                                )))
513                        (setq i (1+ i))
514                        situation)
515                      ))
516                   children)))
517     (setq i 0)
518     (while children
519       (let ((child (car children))
520             (situation (car situations)))
521         (mime-display-entity child (if (= i p)
522                                        situation
523                                      (del-alist 'body-presentation-method
524                                                 (copy-alist situation))))
525         )
526       (setq children (cdr children)
527             situations (cdr situations)
528             i (1+ i))
529       )))
530
531
532 ;;; @ acting-condition
533 ;;;
534
535 (defvar mime-acting-condition nil
536   "Condition-tree about how to process entity.")
537
538 (if (file-readable-p mailcap-file)
539     (let ((entries (mailcap-parse-file)))
540       (while entries
541         (let ((entry (car entries))
542               view print shared)
543           (while entry
544             (let* ((field (car entry))
545                    (field-type (car field)))
546               (cond ((eq field-type 'view)  (setq view field))
547                     ((eq field-type 'print) (setq print field))
548                     ((memq field-type '(compose composetyped edit)))
549                     (t (setq shared (cons field shared))))
550               )
551             (setq entry (cdr entry))
552             )
553           (setq shared (nreverse shared))
554           (ctree-set-calist-with-default
555            'mime-acting-condition
556            (append shared (list '(mode . "play")(cons 'method (cdr view)))))
557           (if print
558               (ctree-set-calist-with-default
559                'mime-acting-condition
560                (append shared
561                        (list '(mode . "print")(cons 'method (cdr view))))
562                ))
563           )
564         (setq entries (cdr entries))
565         )))
566
567 (ctree-set-calist-strictly
568  'mime-acting-condition
569  '((type . application)(subtype . octet-stream)
570    (mode . "play")
571    (method . mime-detect-content)
572    ))
573
574 (ctree-set-calist-with-default
575  'mime-acting-condition
576  '((mode . "extract")
577    (method . mime-save-content)))
578
579 (ctree-set-calist-strictly
580  'mime-acting-condition
581  '((type . text)(subtype . x-rot13-47)(mode . "play")
582    (method . mime-view-caesar)
583    ))
584 (ctree-set-calist-strictly
585  'mime-acting-condition
586  '((type . text)(subtype . x-rot13-47-48)(mode . "play")
587    (method . mime-view-caesar)
588    ))
589
590 (ctree-set-calist-strictly
591  'mime-acting-condition
592  '((type . message)(subtype . rfc822)(mode . "play")
593    (method . mime-view-message/rfc822)
594    ))
595 (ctree-set-calist-strictly
596  'mime-acting-condition
597  '((type . message)(subtype . partial)(mode . "play")
598    (method . mime-store-message/partial-piece)
599    ))
600
601 (ctree-set-calist-strictly
602  'mime-acting-condition
603  '((type . message)(subtype . external-body)
604    ("access-type" . "anon-ftp")
605    (method . mime-view-message/external-anon-ftp)
606    ))
607
608 (ctree-set-calist-strictly
609  'mime-acting-condition
610  '((type . message)(subtype . external-body)
611    ("access-type" . "url")
612    (method . mime-view-message/external-url)
613    ))
614
615 (ctree-set-calist-strictly
616  'mime-acting-condition
617  '((type . application)(subtype . octet-stream)
618    (method . mime-save-content)
619    ))
620
621
622 ;;; @ quitting method
623 ;;;
624
625 (defvar mime-preview-quitting-method-alist
626   '((mime-show-message-mode
627      . mime-preview-quitting-method-for-mime-show-message-mode))
628   "Alist of major-mode vs. quitting-method of mime-view.")
629
630 (defvar mime-preview-over-to-previous-method-alist nil
631   "Alist of major-mode vs. over-to-previous-method of mime-view.")
632
633 (defvar mime-preview-over-to-next-method-alist nil
634   "Alist of major-mode vs. over-to-next-method of mime-view.")
635
636
637 ;;; @ following method
638 ;;;
639
640 (defvar mime-preview-following-method-alist nil
641   "Alist of major-mode vs. following-method of mime-view.")
642
643 (defvar mime-view-following-required-fields-list
644   '("From"))
645
646
647 ;;; @ buffer setup
648 ;;;
649
650 (defun mime-display-entity (entity &optional situation
651                                    default-situation preview-buffer)
652   (or preview-buffer
653       (setq preview-buffer (current-buffer)))
654   (let* ((raw-buffer (mime-entity-buffer entity))
655          (start (mime-entity-point-min entity))
656          e nb ne)
657     (set-buffer raw-buffer)
658     (goto-char start)
659     (or situation
660         (setq situation
661               (or (ctree-match-calist mime-preview-condition
662                                       (append (mime-entity-situation entity)
663                                               default-situation))
664                   default-situation)))
665     (let ((button-is-invisible
666            (eq (cdr (assq 'entity-button situation)) 'invisible))
667           (header-is-visible
668            (eq (cdr (assq 'header situation)) 'visible))
669           (header-presentation-method
670            (or (cdr (assq 'header-presentation-method situation))
671                (cdr (assq major-mode mime-header-presentation-method-alist))))
672           (body-presentation-method
673            (cdr (assq 'body-presentation-method situation)))
674           (children (mime-entity-children entity)))
675       (set-buffer preview-buffer)
676       (setq nb (point))
677       (narrow-to-region nb nb)
678       (or button-is-invisible
679           (if (mime-view-entity-button-visible-p entity)
680               (mime-view-insert-entity-button entity)
681             ))
682       (when header-is-visible
683         (if header-presentation-method
684             (funcall header-presentation-method entity situation)
685           (mime-insert-decoded-header entity
686                                       mime-view-ignored-field-list
687                                       mime-view-visible-field-list))
688         (goto-char (point-max))
689         (insert "\n")
690         (run-hooks 'mime-display-header-hook)
691         )
692       (cond (children)
693             ((functionp body-presentation-method)
694              (funcall body-presentation-method entity situation)
695              )
696             (t
697              (when button-is-invisible
698                (goto-char (point-max))
699                (mime-view-insert-entity-button entity)
700                )
701              (or header-is-visible
702                  (progn
703                    (goto-char (point-max))
704                    (insert "\n")
705                    ))
706              ))
707       (setq ne (point-max))
708       (widen)
709       (put-text-property nb ne 'mime-view-entity entity)
710       (goto-char ne)
711       (if children
712           (if (functionp body-presentation-method)
713               (funcall body-presentation-method entity situation)
714             (mime-display-multipart/mixed entity situation)
715             ))
716       )))
717
718
719 ;;; @ MIME viewer mode
720 ;;;
721
722 (defconst mime-view-menu-title "MIME-View")
723 (defconst mime-view-menu-list
724   '((up          "Move to upper entity"    mime-preview-move-to-upper)
725     (previous    "Move to previous entity" mime-preview-move-to-previous)
726     (next        "Move to next entity"     mime-preview-move-to-next)
727     (scroll-down "Scroll-down"             mime-preview-scroll-down-entity)
728     (scroll-up   "Scroll-up"               mime-preview-scroll-up-entity)
729     (play        "Play current entity"     mime-preview-play-current-entity)
730     (extract     "Extract current entity"  mime-preview-extract-current-entity)
731     (print       "Print current entity"    mime-preview-print-current-entity)
732     )
733   "Menu for MIME Viewer")
734
735 (cond (running-xemacs
736        (defvar mime-view-xemacs-popup-menu
737          (cons mime-view-menu-title
738                (mapcar (function
739                         (lambda (item)
740                           (vector (nth 1 item)(nth 2 item) t)
741                           ))
742                        mime-view-menu-list)))
743        (defun mime-view-xemacs-popup-menu (event)
744          "Popup the menu in the MIME Viewer buffer"
745          (interactive "e")
746          (select-window (event-window event))
747          (set-buffer (event-buffer event))
748          (popup-menu 'mime-view-xemacs-popup-menu))
749        (defvar mouse-button-2 'button2)
750        )
751       (t
752        (defvar mouse-button-2 [mouse-2])
753        ))
754
755 (defun mime-view-define-keymap (&optional default)
756   (let ((mime-view-mode-map (if (keymapp default)
757                                 (copy-keymap default)
758                               (make-sparse-keymap)
759                               )))
760     (define-key mime-view-mode-map
761       "u"        (function mime-preview-move-to-upper))
762     (define-key mime-view-mode-map
763       "p"        (function mime-preview-move-to-previous))
764     (define-key mime-view-mode-map
765       "n"        (function mime-preview-move-to-next))
766     (define-key mime-view-mode-map
767       "\e\t"     (function mime-preview-move-to-previous))
768     (define-key mime-view-mode-map
769       "\t"       (function mime-preview-move-to-next))
770     (define-key mime-view-mode-map
771       " "        (function mime-preview-scroll-up-entity))
772     (define-key mime-view-mode-map
773       "\M- "     (function mime-preview-scroll-down-entity))
774     (define-key mime-view-mode-map
775       "\177"     (function mime-preview-scroll-down-entity))
776     (define-key mime-view-mode-map
777       "\C-m"     (function mime-preview-next-line-entity))
778     (define-key mime-view-mode-map
779       "\C-\M-m"  (function mime-preview-previous-line-entity))
780     (define-key mime-view-mode-map
781       "v"        (function mime-preview-play-current-entity))
782     (define-key mime-view-mode-map
783       "e"        (function mime-preview-extract-current-entity))
784     (define-key mime-view-mode-map
785       "\C-c\C-p" (function mime-preview-print-current-entity))
786     (define-key mime-view-mode-map
787       "a"        (function mime-preview-follow-current-entity))
788     (define-key mime-view-mode-map
789       "q"        (function mime-preview-quit))
790     (define-key mime-view-mode-map
791       "\C-c\C-x" (function mime-preview-kill-buffer))
792     ;; (define-key mime-view-mode-map
793     ;;   "<"        (function beginning-of-buffer))
794     ;; (define-key mime-view-mode-map
795     ;;   ">"        (function end-of-buffer))
796     (define-key mime-view-mode-map
797       "?"        (function describe-mode))
798     (define-key mime-view-mode-map
799       [tab] (function mime-preview-move-to-next))
800     (define-key mime-view-mode-map
801       [delete] (function mime-preview-scroll-down-entity))
802     (define-key mime-view-mode-map
803       [backspace] (function mime-preview-scroll-down-entity))
804     (if (functionp default)
805         (cond (running-xemacs
806                (set-keymap-default-binding mime-view-mode-map default)
807                )
808               (t
809                (setq mime-view-mode-map
810                      (append mime-view-mode-map (list (cons t default))))
811                )))
812     (if mouse-button-2
813         (define-key mime-view-mode-map
814           mouse-button-2 (function mime-button-dispatcher))
815       )
816     (cond (running-xemacs
817            (define-key mime-view-mode-map
818              mouse-button-3 (function mime-view-xemacs-popup-menu))
819            )
820           ((>= emacs-major-version 19)
821            (define-key mime-view-mode-map [menu-bar mime-view]
822              (cons mime-view-menu-title
823                    (make-sparse-keymap mime-view-menu-title)))
824            (mapcar (function
825                     (lambda (item)
826                       (define-key mime-view-mode-map
827                         (vector 'menu-bar 'mime-view (car item))
828                         (cons (nth 1 item)(nth 2 item))
829                         )
830                       ))
831                    (reverse mime-view-menu-list)
832                    )
833            ))
834     (use-local-map mime-view-mode-map)
835     (run-hooks 'mime-view-define-keymap-hook)
836     ))
837
838 (defsubst mime-maybe-hide-echo-buffer ()
839   "Clear mime-echo buffer and delete window for it."
840   (let ((buf (get-buffer mime-echo-buffer-name)))
841     (if buf
842         (save-excursion
843           (set-buffer buf)
844           (erase-buffer)
845           (let ((win (get-buffer-window buf)))
846             (if win
847                 (delete-window win)
848               ))
849           (bury-buffer buf)
850           ))))
851
852 (defvar mime-view-redisplay nil)
853
854 (defun mime-display-message (message &optional preview-buffer
855                                      mother default-keymap-or-function)
856   (mime-maybe-hide-echo-buffer)
857   (let ((win-conf (current-window-configuration))
858         (raw-buffer (mime-entity-buffer message)))
859     (or preview-buffer
860         (setq preview-buffer
861               (concat "*Preview-" (buffer-name raw-buffer) "*")))
862     (set-buffer raw-buffer)
863     (setq mime-preview-buffer preview-buffer)
864     (let ((inhibit-read-only t))
865       (set-buffer (get-buffer-create preview-buffer))
866       (widen)
867       (erase-buffer)
868       (setq mime-raw-buffer raw-buffer)
869       (if mother
870           (setq mime-mother-buffer mother)
871         )
872       (setq mime-preview-original-window-configuration win-conf)
873       (setq major-mode 'mime-view-mode)
874       (setq mode-name "MIME-View")
875       (mime-display-entity message nil
876                            '((entity-button . invisible)
877                              (header . visible))
878                            preview-buffer)
879       (mime-view-define-keymap default-keymap-or-function)
880       (let ((point
881              (next-single-property-change (point-min) 'mime-view-entity)))
882         (if point
883             (goto-char point)
884           (goto-char (point-min))
885           (search-forward "\n\n" nil t)
886           ))
887       (run-hooks 'mime-view-mode-hook)
888       (set-buffer-modified-p nil)
889       (setq buffer-read-only t)
890       (or (get-buffer-window preview-buffer)
891           (let ((r-win (get-buffer-window raw-buffer)))
892             (if r-win
893                 (set-window-buffer r-win preview-buffer)
894               (let ((m-win (and mother (get-buffer-window mother))))
895                 (if m-win
896                     (set-window-buffer m-win preview-buffer)
897                   (switch-to-buffer preview-buffer)
898                   )))))
899       )))
900
901 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
902                                    default-keymap-or-function
903                                    representation-type)
904   "View RAW-BUFFER in MIME-View mode.
905 Optional argument PREVIEW-BUFFER is either nil or a name of preview
906 buffer.
907 Optional argument DEFAULT-KEYMAP-OR-FUNCTION is nil, keymap or
908 function.  If it is a keymap, keymap of MIME-View mode will be added
909 to it.  If it is a function, it will be bound as default binding of
910 keymap of MIME-View mode.
911 Optional argument REPRESENTATION-TYPE is representation-type of
912 message.  It must be nil, `binary' or `cooked'.  If it is nil,
913 `binary' is used as default."
914   (interactive)
915   (or raw-buffer
916       (setq raw-buffer (current-buffer)))
917   (or representation-type
918       (setq representation-type
919             (save-excursion
920               (set-buffer raw-buffer)
921               (cdr (or (assq major-mode mime-raw-representation-type-alist)
922                        (assq t mime-raw-representation-type-alist)))
923               )))
924   (if (eq representation-type 'binary)
925       (setq representation-type 'buffer)
926     )
927   (mime-display-message
928    (mime-open-entity representation-type raw-buffer)
929    preview-buffer mother default-keymap-or-function))
930
931 (defun mime-view-mode (&optional mother ctl encoding
932                                  raw-buffer preview-buffer
933                                  default-keymap-or-function)
934   "Major mode for viewing MIME message.
935
936 Here is a list of the standard keys for mime-view-mode.
937
938 key             feature
939 ---             -------
940
941 u               Move to upper content
942 p or M-TAB      Move to previous content
943 n or TAB        Move to next content
944 SPC             Scroll up or move to next content
945 M-SPC or DEL    Scroll down or move to previous content
946 RET             Move to next line
947 M-RET           Move to previous line
948 v               Decode current content as `play mode'
949 e               Decode current content as `extract mode'
950 C-c C-p         Decode current content as `print mode'
951 a               Followup to current content.
952 q               Quit
953 button-2        Move to point under the mouse cursor
954                 and decode current content as `play mode'
955 "
956   (interactive)
957   (unless mime-view-redisplay
958     (save-excursion
959       (if raw-buffer (set-buffer raw-buffer))
960       (let ((type
961              (cdr
962               (or (assq major-mode mime-raw-representation-type-alist)
963                   (assq t mime-raw-representation-type-alist)))))
964         (if (eq type 'binary)
965             (setq type 'buffer)
966           )
967         (setq mime-message-structure (mime-open-entity type raw-buffer))
968         (or (mime-entity-content-type mime-message-structure)
969             (mime-entity-set-content-type-internal
970              mime-message-structure ctl))
971         )
972       (or (mime-entity-encoding mime-message-structure)
973           (mime-entity-set-encoding-internal mime-message-structure encoding))
974       ))
975   (mime-display-message mime-message-structure preview-buffer
976                         mother default-keymap-or-function)
977   )
978
979
980 ;;; @@ playing
981 ;;;
982
983 (autoload 'mime-preview-play-current-entity "mime-play"
984   "Play current entity." t)
985
986 (defun mime-preview-extract-current-entity (&optional ignore-examples)
987   "Extract current entity into file (maybe).
988 It decodes current entity to call internal or external method as
989 \"extract\" mode.  The method is selected from variable
990 `mime-acting-condition'."
991   (interactive "P")
992   (mime-preview-play-current-entity ignore-examples "extract")
993   )
994
995 (defun mime-preview-print-current-entity (&optional ignore-examples)
996   "Print current entity (maybe).
997 It decodes current entity to call internal or external method as
998 \"print\" mode.  The method is selected from variable
999 `mime-acting-condition'."
1000   (interactive "P")
1001   (mime-preview-play-current-entity ignore-examples "print")
1002   )
1003
1004
1005 ;;; @@ following
1006 ;;;
1007
1008 (defun mime-preview-follow-current-entity ()
1009   "Write follow message to current entity.
1010 It calls following-method selected from variable
1011 `mime-preview-following-method-alist'."
1012   (interactive)
1013   (let (entity)
1014     (while (null (setq entity
1015                        (get-text-property (point) 'mime-view-entity)))
1016       (backward-char)
1017       )
1018     (let* ((p-beg
1019             (previous-single-property-change (point) 'mime-view-entity))
1020            p-end
1021            (entity-node-id (mime-entity-node-id entity))
1022            (len (length entity-node-id))
1023            )
1024       (cond ((null p-beg)
1025              (setq p-beg
1026                    (if (eq (next-single-property-change (point-min)
1027                                                         'mime-view-entity)
1028                            (point))
1029                        (point)
1030                      (point-min)))
1031              )
1032             ((eq (next-single-property-change p-beg 'mime-view-entity)
1033                  (point))
1034              (setq p-beg (point))
1035              ))
1036       (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1037       (cond ((null p-end)
1038              (setq p-end (point-max))
1039              )
1040             ((null entity-node-id)
1041              (setq p-end (point-max))
1042              )
1043             (t
1044              (save-excursion
1045                (goto-char p-end)
1046                (catch 'tag
1047                  (let (e)
1048                    (while (setq e
1049                                 (next-single-property-change
1050                                  (point) 'mime-view-entity))
1051                      (goto-char e)
1052                      (let ((rc (mime-entity-node-id
1053                                 (get-text-property (point)
1054                                                    'mime-view-entity))))
1055                        (or (equal entity-node-id
1056                                   (nthcdr (- (length rc) len) rc))
1057                            (throw 'tag nil)
1058                            ))
1059                      (setq p-end e)
1060                      ))
1061                  (setq p-end (point-max))
1062                  ))
1063              ))
1064       (let* ((mode (mime-preview-original-major-mode 'recursive))
1065              (new-name
1066               (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1067              new-buf
1068              (the-buf (current-buffer))
1069              (a-buf mime-raw-buffer)
1070              fields)
1071         (save-excursion
1072           (set-buffer (setq new-buf (get-buffer-create new-name)))
1073           (erase-buffer)
1074           (insert-buffer-substring the-buf p-beg p-end)
1075           (goto-char (point-min))
1076           (let ((entity-node-id (mime-entity-node-id entity)) ci str)
1077             (while (progn
1078                      (setq
1079                       str
1080                       (save-excursion
1081                         (set-buffer a-buf)
1082                         (setq ci
1083                               (mime-find-entity-from-node-id entity-node-id))
1084                         (save-restriction
1085                           (narrow-to-region
1086                            (mime-entity-point-min ci)
1087                            (mime-entity-point-max ci)
1088                            )
1089                           (std11-header-string-except
1090                            (concat "^"
1091                                    (apply (function regexp-or) fields)
1092                                    ":") ""))))
1093                      (if (and
1094                           (eq (mime-entity-media-type ci) 'message)
1095                           (eq (mime-entity-media-subtype ci) 'rfc822))
1096                          nil
1097                        (if str
1098                            (insert str)
1099                          )
1100                        entity-node-id))
1101               (setq fields (std11-collect-field-names)
1102                     entity-node-id (cdr entity-node-id))
1103               )
1104             )
1105           (let ((rest mime-view-following-required-fields-list))
1106             (while rest
1107               (let ((field-name (car rest)))
1108                 (or (std11-field-body field-name)
1109                     (insert
1110                      (format
1111                       (concat field-name
1112                               ": "
1113                               (save-excursion
1114                                 (set-buffer the-buf)
1115                                 (set-buffer mime-mother-buffer)
1116                                 (set-buffer mime-raw-buffer)
1117                                 (std11-field-body field-name)
1118                                 )
1119                               "\n")))
1120                     ))
1121               (setq rest (cdr rest))
1122               ))
1123           (eword-decode-header)
1124           )
1125         (let ((f (cdr (assq mode mime-preview-following-method-alist))))
1126           (if (functionp f)
1127               (funcall f new-buf)
1128             (message
1129              (format
1130               "Sorry, following method for %s is not implemented yet."
1131               mode))
1132             ))
1133         ))))
1134
1135
1136 ;;; @@ moving
1137 ;;;
1138
1139 (defun mime-preview-move-to-upper ()
1140   "Move to upper entity.
1141 If there is no upper entity, call function `mime-preview-quit'."
1142   (interactive)
1143   (let (cinfo)
1144     (while (null (setq cinfo
1145                        (get-text-property (point) 'mime-view-entity)))
1146       (backward-char)
1147       )
1148     (let ((r (mime-entity-parent cinfo))
1149           point)
1150       (catch 'tag
1151         (while (setq point (previous-single-property-change
1152                             (point) 'mime-view-entity))
1153           (goto-char point)
1154           (if (eq r (get-text-property (point) 'mime-view-entity))
1155               (throw 'tag t)
1156             )
1157           )
1158         (mime-preview-quit)
1159         ))))
1160
1161 (defun mime-preview-move-to-previous ()
1162   "Move to previous entity.
1163 If there is no previous entity, it calls function registered in
1164 variable `mime-preview-over-to-previous-method-alist'."
1165   (interactive)
1166   (while (null (get-text-property (point) 'mime-view-entity))
1167     (backward-char)
1168     )
1169   (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1170     (if point
1171         (if (get-text-property (1- point) 'mime-view-entity)
1172             (goto-char point)
1173           (goto-char (1- point))
1174           (mime-preview-move-to-previous)
1175           )
1176       (let ((f (assq (mime-preview-original-major-mode)
1177                      mime-preview-over-to-previous-method-alist)))
1178         (if f
1179             (funcall (cdr f))
1180           ))
1181       )))
1182
1183 (defun mime-preview-move-to-next ()
1184   "Move to next entity.
1185 If there is no previous entity, it calls function registered in
1186 variable `mime-preview-over-to-next-method-alist'."
1187   (interactive)
1188   (while (null (get-text-property (point) 'mime-view-entity))
1189     (forward-char)
1190     )
1191   (let ((point (next-single-property-change (point) 'mime-view-entity)))
1192     (if point
1193         (progn
1194           (goto-char point)
1195           (if (null (get-text-property point 'mime-view-entity))
1196               (mime-preview-move-to-next)
1197             ))
1198       (let ((f (assq (mime-preview-original-major-mode)
1199                      mime-preview-over-to-next-method-alist)))
1200         (if f
1201             (funcall (cdr f))
1202           ))
1203       )))
1204
1205 (defun mime-preview-scroll-up-entity (&optional h)
1206   "Scroll up current entity.
1207 If reached to (point-max), it calls function registered in variable
1208 `mime-preview-over-to-next-method-alist'."
1209   (interactive)
1210   (or h
1211       (setq h (1- (window-height)))
1212       )
1213   (if (= (point) (point-max))
1214       (let ((f (assq (mime-preview-original-major-mode)
1215                      mime-preview-over-to-next-method-alist)))
1216         (if f
1217             (funcall (cdr f))
1218           ))
1219     (let ((point
1220            (or (next-single-property-change (point) 'mime-view-entity)
1221                (point-max))))
1222       (forward-line h)
1223       (if (> (point) point)
1224           (goto-char point)
1225         )
1226       )))
1227
1228 (defun mime-preview-scroll-down-entity (&optional h)
1229   "Scroll down current entity.
1230 If reached to (point-min), it calls function registered in variable
1231 `mime-preview-over-to-previous-method-alist'."
1232   (interactive)
1233   (or h
1234       (setq h (1- (window-height)))
1235       )
1236   (if (= (point) (point-min))
1237       (let ((f (assq (mime-preview-original-major-mode)
1238                      mime-preview-over-to-previous-method-alist)))
1239         (if f
1240             (funcall (cdr f))
1241           ))
1242     (let ((point
1243            (or (previous-single-property-change (point) 'mime-view-entity)
1244                (point-min))))
1245       (forward-line (- h))
1246       (if (< (point) point)
1247           (goto-char point)
1248         ))))
1249
1250 (defun mime-preview-next-line-entity ()
1251   (interactive)
1252   (mime-preview-scroll-up-entity 1)
1253   )
1254
1255 (defun mime-preview-previous-line-entity ()
1256   (interactive)
1257   (mime-preview-scroll-down-entity 1)
1258   )
1259
1260
1261 ;;; @@ quitting
1262 ;;;
1263
1264 (defun mime-preview-quit ()
1265   "Quit from MIME-preview buffer.
1266 It calls function registered in variable
1267 `mime-preview-quitting-method-alist'."
1268   (interactive)
1269   (let ((r (assq (mime-preview-original-major-mode)
1270                  mime-preview-quitting-method-alist)))
1271     (if r
1272         (funcall (cdr r))
1273       )))
1274
1275 (defun mime-preview-kill-buffer ()
1276   (interactive)
1277   (kill-buffer (current-buffer))
1278   )
1279
1280
1281 ;;; @ end
1282 ;;;
1283
1284 (provide 'mime-view)
1285
1286 (run-hooks 'mime-view-load-hook)
1287
1288 ;;; mime-view.el ends here