Synch with Oort Gnus.
[elisp/gnus.git-] / lisp / mm-decode.el
index 8978c07..9b2312a 100644 (file)
 
 ;;; Commentary:
 
+;; Jaap-Henk Hoepman (jhh@xs4all.nl): 
+;;
+;; Added support for delayed destroy of external MIME viewers. All external
+;; viewers for mime types in mm-keep-viewer-alive-types will remain active
+;; after switching articles or groups, and will only be removed when exiting
+;; gnus.
+;; 
+
 ;;; Code:
 
 (require 'mail-parse)
@@ -34,6 +42,8 @@
   (autoload 'mm-inline-external-body "mm-extern")
   (autoload 'mm-insert-inline "mm-view"))
 
+(add-hook 'gnus-exit-gnus-hook 'mm-destroy-postponed-undisplay-list)
+
 (defgroup mime-display ()
   "Display of MIME in mail and news articles."
   :link '(custom-manual "(emacs-mime)Customization")
     ("application/pkcs7-signature" ignore identity)
     ("multipart/alternative" ignore identity)
     ("multipart/mixed" ignore identity)
-    ("multipart/related" ignore identity))
+    ("multipart/related" ignore identity)
+    ;; Default to displaying as text
+    (".*" mm-inline-text identity))
   "Alist of media types/tests saying whether types can be displayed inline."
   :type '(repeat (list (string :tag "MIME type")
                       (function :tag "Display function")
     "message/partial" "message/external-body" "application/emacs-lisp"
     "application/pgp-signature" "application/x-pkcs7-signature"
     "application/pkcs7-signature")
-  "List of media types that are to be displayed inline."
+  "List of media types that are to be displayed inline.
+See also `mm-inline-media-tests', which says how to display a media
+type inline."
+  :type '(repeat string)
+  :group 'mime-display)
+
+(defcustom mm-keep-viewer-alive-types
+  '("application/postscript" "application/msword" "application/vnd.ms-excel"
+    "application/pdf" "application/x-dvi")
+  "List of media types for which the external viewer will not be killed
+when selecting a different article."
   :type '(repeat string)
   :group 'mime-display)
  
@@ -235,6 +257,21 @@ to:
   :type 'boolean
   :group 'mime-display)
 
+(defvar mm-file-name-rewrite-functions nil
+  "*List of functions used for rewriting file names of MIME parts.
+Each function takes a file name as input and returns a file name.
+
+Ready-made functions include
+`mm-file-name-delete-whitespace',
+`mm-file-name-trim-whitespace',
+`mm-file-name-collapse-whitespace',
+`mm-file-name-replace-whitespace',
+`capitalize', `downcase', `upcase', and
+`upcase-initials'.")
+
+(defvar mm-file-name-replace-whitespace nil
+  "String used for replacing whitespace characters; default is `\"_\"'.")
+
 (defcustom mm-default-directory nil
   "The default directory where mm will save files.
 If not set, `default-directory' will be used."
@@ -246,6 +283,7 @@ If not set, `default-directory' will be used."
 (defvar mm-dissection-list nil)
 (defvar mm-last-shell-command "")
 (defvar mm-content-id-alist nil)
+(defvar mm-postponed-undisplay-list nil)
 
 ;; According to RFC2046, in particular, in a digest, the default
 ;; Content-Type value for a body part is changed from "text/plain" to
@@ -332,6 +370,30 @@ The original alist is not modified.  See also `destructive-alist-to-plist'."
       (setq alist (cdr alist)))
     (nreverse plist)))
 
+(defun mm-keep-viewer-alive-p (handle)
+  "Say whether external viewer for HANDLE should stay alive."
+  (let ((types mm-keep-viewer-alive-types)
+       (type (mm-handle-media-type handle))
+       ty)
+    (catch 'found
+      (while (setq ty (pop types))
+       (when (string-match ty type)
+         (throw 'found t))))))
+
+(defun mm-handle-set-external-undisplayer (handle function)
+ "Set the undisplayer for this handle; postpone undisplaying of viewers
+for types in mm-keep-viewer-alive-types."
+ (if (mm-keep-viewer-alive-p handle)
+     (let ((new-handle (copy-sequence handle)))
+       (mm-handle-set-undisplayer new-handle function)
+       (mm-handle-set-undisplayer handle nil)
+       (push new-handle mm-postponed-undisplay-list))
+   (mm-handle-set-undisplayer handle function)))
+
+(defun mm-destroy-postponed-undisplay-list ()
+  (message "Destroying external MIME viewers")
+  (mm-destroy-parts mm-postponed-undisplay-list))
+
 (defun mm-dissect-buffer (&optional no-strict-mime)
   "Dissect the current buffer and return a list of MIME handles."
   (save-excursion
@@ -484,7 +546,8 @@ external if displayed external."
        (mm-remove-part handle)
       (let* ((type (mm-handle-media-type handle))
             (method (mailcap-mime-info type)))
-       (if (mm-inlined-p handle)
+       (if (and (mm-inlinable-p handle)
+                (mm-inlined-p handle))
            (progn
              (forward-line 1)
              (mm-display-inline handle)
@@ -560,7 +623,7 @@ external if displayed external."
                                    shell-command-switch
                                    (mm-mailcap-command
                                     method file (mm-handle-type handle)))
-                  (mm-handle-set-undisplayer handle (cons file buffer)))
+                  (mm-handle-set-external-undisplayer handle (cons file buffer)))
                 (message "Displaying %s..." (format method file))
                 'external)
                (copiousoutput
@@ -596,7 +659,7 @@ external if displayed external."
                                    shell-command-switch
                                    (mm-mailcap-command
                                     method file (mm-handle-type handle)))
-                  (mm-handle-set-undisplayer handle (cons file buffer)))
+                  (mm-handle-set-external-undisplayer handle (cons file buffer)))
                 (message "Displaying %s..." (format method file))
                 'external)))))))
 
@@ -695,6 +758,18 @@ external if displayed external."
     (when (string-match (car elem) type)
       (return elem))))
 
+(defun mm-automatic-display-p (handle)
+  "Say whether the user wants HANDLE to be displayed automatically."
+  (let ((methods mm-automatic-display)
+       (type (mm-handle-media-type handle))
+       method result)
+    (while (setq method (pop methods))
+      (when (and (not (mm-inline-override-p handle))
+                (string-match method type))
+       (setq result t
+             methods nil)))
+    result))
+
 (defun mm-inlinable-p (handle)
   "Say whether HANDLE can be displayed inline."
   (let ((alist mm-inline-media-tests)
@@ -708,28 +783,14 @@ external if displayed external."
       (pop alist))
     test))
 
-(defun mm-automatic-display-p (handle)
-  "Say whether the user wants HANDLE to be displayed automatically."
-  (let ((methods mm-automatic-display)
-       (type (mm-handle-media-type handle))
-       method result)
-    (while (setq method (pop methods))
-      (when (and (not (mm-inline-override-p handle))
-                (string-match method type)
-                (mm-inlinable-p handle))
-       (setq result t
-             methods nil)))
-    result))
-
 (defun mm-inlined-p (handle)
-  "Say whether the user wants HANDLE to be displayed automatically."
+  "Say whether the user wants HANDLE to be displayed inline."
   (let ((methods mm-inlined-types)
        (type (mm-handle-media-type handle))
        method result)
     (while (setq method (pop methods))
       (when (and (not (mm-inline-override-p handle))
-                (string-match method type)
-                (mm-inlinable-p handle))
+                (string-match method type))
        (setq result t
              methods nil)))
     result))
@@ -742,7 +803,7 @@ external if displayed external."
     (catch 'found
       (while (setq ty (pop types))
        (when (and (string-match ty type)
-                  (mm-inlinable-p handle))
+                  (mm-inlinable-p ty))
          (throw 'found t))))))
 
 (defun mm-inline-override-p (handle)
@@ -813,6 +874,35 @@ external if displayed external."
            (set-buffer cur)
            (insert-buffer-substring temp)))))))
 
+(defun mm-file-name-delete-whitespace (file-name)
+  "Remove all whitespace characters from FILE-NAME."
+  (while (string-match "\\s-+" file-name)
+    (setq file-name (replace-match "" t t file-name)))
+  file-name)
+
+(defun mm-file-name-trim-whitespace (file-name)
+  "Remove leading and trailing whitespace characters from FILE-NAME."
+  (when (string-match "\\`\\s-+" file-name)
+    (setq file-name (substring file-name (match-end 0))))
+  (when (string-match "\\s-+\\'" file-name)
+    (setq file-name (substring file-name 0 (match-beginning 0))))
+  file-name)
+
+(defun mm-file-name-collapse-whitespace (file-name)
+  "Collapse multiple whitespace characters in FILE-NAME."
+  (while (string-match "\\s-\\s-+" file-name)
+    (setq file-name (replace-match " " t t file-name)))
+  file-name)
+
+(defun mm-file-name-replace-whitespace (file-name)
+  "Replace whitespace characters in FILE-NAME with underscores.
+Set `mm-file-name-replace-whitespace' to any other string if you do not
+like underscores."
+  (let ((s (or mm-file-name-replace-whitespace "_")))
+    (while (string-match "\\s-" file-name)
+      (setq file-name (replace-match s t t file-name))))
+  file-name)
+
 (defun mm-save-part (handle)
   "Write HANDLE to a file."
   (let* ((name (mail-content-type-get (mm-handle-type handle) 'name))
@@ -820,7 +910,8 @@ external if displayed external."
                    (mm-handle-disposition handle) 'filename))
         file)
     (when filename
-      (setq filename (file-name-nondirectory filename)))
+      (setq filename (gnus-map-function mm-file-name-rewrite-functions
+                                       (file-name-nondirectory filename))))
     (setq file
          (read-file-name "Save MIME part to: "
                          (expand-file-name
@@ -853,7 +944,8 @@ external if displayed external."
          (read-string "Shell command on MIME part: " mm-last-shell-command)))
     (mm-with-unibyte-buffer
       (mm-insert-part handle)
-      (shell-command-on-region (point-min) (point-max) command nil))))
+      (let ((coding-system-for-write 'binary))
+       (shell-command-on-region (point-min) (point-max) command nil)))))
 
 (defun mm-interactively-view-part (handle)
   "Display HANDLE using METHOD."