;;; Commentary:
-;; Jaap-Henk Hoepman (jhh@xs4all.nl):
+;; 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)
(require 'gnus-mailcap)
(require 'mm-bodies)
-(eval-when-compile (require 'cl))
+(eval-when-compile (require 'cl)
+ (require 'term))
(eval-and-compile
(autoload 'mm-inline-partial "mm-partial")
("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)
when selecting a different article."
:type '(repeat string)
:group 'mime-display)
-
+
(defcustom mm-automatic-display
'("text/plain" "text/enriched" "text/richtext" "text/html"
"text/x-vcard" "image/.*" "message/delivery-status" "multipart/.*"
: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."
:type 'directory
:group 'mime-display)
+(defcustom mm-external-terminal-program "xterm"
+ "The program to start an external terminal."
+ :type 'string
+ :group 'mime-display)
+
;;; Internal variables.
(defvar mm-dissection-list nil)
(throw 'found t))))))
(defun mm-handle-set-external-undisplayer (handle function)
- "Set the undisplayer for this handle; postpone undisplaying of viewers
+ "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)))
+ (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")
(let ((mm-dissect-default-type (if (equal subtype "digest")
"message/rfc822"
"text/plain")))
- (add-text-properties 0 (length (car ctl))
- (mm-alist-to-plist (cdr ctl)) (car ctl))
+ (add-text-properties 0 (length (car ctl))
+ (mm-alist-to-plist (cdr ctl)) (car ctl))
;; what really needs to be done here is a way to link a
;; MIME handle back to it's parent MIME handle (in a multilevel
;; MIME article). That would probably require changing
;; the mm-handle API so we simply store the multipart buffert
;; name as a text property of the "multipart/whatever" string.
- (add-text-properties 0 (length (car ctl))
+ (add-text-properties 0 (length (car ctl))
(list 'buffer (mm-copy-to-buffer))
- (car ctl))
- (add-text-properties 0 (length (car ctl))
+ (car ctl))
+ (add-text-properties 0 (length (car ctl))
(list 'from from)
- (car ctl))
+ (car ctl))
(cons (car ctl) (mm-dissect-multipart ctl))))
(t
(mm-dissect-singlepart
(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)
(message "Viewing with %s" method)
(cond (needsterm
(unwind-protect
- (start-process "*display*" nil
- "xterm"
- "-e" shell-file-name
- shell-command-switch
- (mm-mailcap-command
- method file (mm-handle-type handle)))
+ (if window-system
+ (start-process "*display*" nil
+ mm-external-terminal-program
+ "-e" shell-file-name
+ shell-command-switch
+ (mm-mailcap-command
+ method file (mm-handle-type handle)))
+ (require 'term)
+ (require 'gnus-win)
+ (set-buffer
+ (setq buffer
+ (make-term "display"
+ shell-file-name
+ nil
+ shell-command-switch
+ (mm-mailcap-command
+ method file
+ (mm-handle-type handle)))))
+ (term-mode)
+ (term-char-mode)
+ (set-process-sentinel
+ (get-buffer-process buffer)
+ `(lambda (process state)
+ (if (eq 'exit (process-status process))
+ (gnus-configure-windows
+ ',gnus-current-window-configuration))))
+ (gnus-configure-windows 'display-term))
(mm-handle-set-external-undisplayer handle (cons file buffer)))
(message "Displaying %s..." (format method file))
'external)
((consp object)
(ignore-errors (delete-file (car object)))
(ignore-errors (delete-directory (file-name-directory (car object))))
- (ignore-errors (kill-buffer (cdr object))))
+ (ignore-errors (and (cdr object) (kill-buffer (cdr object)))))
((bufferp object)
(when (buffer-live-p object)
(kill-buffer object)))))
(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)
(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))
(save-excursion
(if (member (mm-handle-media-supertype handle) '("text" "message"))
(with-temp-buffer
- (insert-buffer-substring (mm-handle-buffer handle))
+ (insert-buffer-substring (mm-handle-buffer handle))
(mm-decode-content-transfer-encoding
(mm-handle-encoding handle)
(mm-handle-media-type handle))
(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))
(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
(prog1
(setq spec
(ignore-errors
- ;; Avoid testing `make-glyph' since W3 may define
- ;; a bogus version of it.
+ ;; Avoid testing `make-glyph' since W3 may define
+ ;; a bogus version of it.
(if (fboundp 'create-image)
- (create-image (buffer-string) (intern type) 'data-p)
+ (or
+ (create-image (buffer-string) nil 'data-p)
+ (create-image (buffer-string) (intern type)
+ 'data-p))
(cond
((equal type "xbm")
;; xbm images require special handling, since
(write-region (point-min) (point-max) file)
(make-glyph (list (cons 'x file))))
(ignore-errors
- (delete-file file)))))
+ (delete-file file)))))
(t
(make-glyph
(vector (intern type) :data (buffer-string))))))))
parts))
(defun mm-multiple-handles (handles)
- (and (listp (car handles))
- (> (length handles) 1)))
+ (and (listp (car handles))
+ (> (length handles) 1)))
-(defun mm-merge-handles (handles1 handles2)
+(defun mm-merge-handles (handles1 handles2)
(append
- (if (listp (car handles1))
+ (if (listp (car handles1))
handles1
(list handles1))
(if (listp (car handles2))