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