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