* cleanup attachment handling, add 'actions' system (WIP)

- user can define arbitrary actions (functions) to apply on attachments
This commit is contained in:
djcb 2012-04-19 21:01:38 +03:00
parent 3d1c5bb1e9
commit bff93c9962
3 changed files with 94 additions and 87 deletions

View File

@ -392,6 +392,17 @@ there is no message at point."
point in eiter the headers buffer or the view buffer."
(plist-get (mu4e-message-at-point t) field))
(defun mu4e-offer-actions (prompt actions msg)
"Ask user with PROMPT to choose some action from ACTIONS. ACTIONS
is a list of actions like `mu4e-view-attachments-actions',
`mu4e-view-actions', `mu4e-header-actions'. Then, call the function
for this action, with the currrent message plist as the argument."
(let ((kar (mu4e-read-option prompt actions)))
(dolist (action actions)
(let ((shortcut (cadr action)) (func (nth 2 action)))
(when (eq kar shortcut)
(funcall func msg))))))
(defun mu4e-capture-message ()
"Capture the path of the message at point."
(interactive)

View File

@ -104,11 +104,12 @@ query, DESCRIPTION is a short description of the query (this will
show up in the UI), and KEY is a shortcut key for the query.")
(defvar mu4e-split-view 'horizontal
"How to show messages / headers; a symbol which is either:
* a symbol 'horizontal: split horizontally (headers on top)
* a symbol 'vertical: split vertically (headers on the left).
* anything else: don't split (show either headers or messages, not both)
Also see `mu4e-headers-visible-lines' and `mu4e-headers-visible-columns'.")
"How to show messages / headers; a symbol which is either: * a
symbol 'horizontal: split horizontally (headers on top) * a symbol
'vertical: split vertically (headers on the left). * anything
else: don't split (show either headers or messages, not both) Also
see `mu4e-headers-visible-lines' and
`mu4e-headers-visible-columns'.")
;; Folders
@ -262,6 +263,25 @@ display with `mu4e-view-toggle-hide-cited (default keybinding:
<w>)."
:group 'mu4e-view)
;; some *cough* forward declarations; real definitions are in mu4e-view
;; on which we don't want to circularly depend
(defun mu4e-dummy-func (&optional args) (error "dummy"))
(defalias 'mu4e-view-open-attachment-with 'mu4e-dummy-func)
(defalias 'mu4e-view-open-attachment-emacs 'mu4e-dummy-func)
(defalias 'mu4e-view-open-pipe-attachment 'mu4e-dummy-func)
(defvar mu4e-view-attachments-actions
'( ("open-with" ?w mu4e-view-open-attachment-with)
("in-emacs" ?e mu4e-view-open-attachment-emacs)
("pipe" ?| mu4e-view-pipe-attachment))
"List of actions to perform on message attachments. The actions
are of the form:
(NAME SHORTCUT FUNC)
where:
* NAME is the name of the action (e.g. \"Count lines\")
* SHORTCUT is a one-character shortcut to call this action
* FUNC is a function which takes as argument a message s-exp")
;; Composing / Sending messages
(defgroup mu4e-compose nil

View File

@ -106,7 +106,6 @@ marking if it still had that."
(inhibit-read-only t))
(with-current-buffer buf
(setq mu4e-view-buffer buf)
(setq mu4e-attach-map nil) ;; clear any old attachment stuff
(erase-buffer)
(insert (mu4e-view-message-text msg))
@ -179,8 +178,6 @@ DONT-PROPERTIZE-VAL is non-nil, do not add text-properties to VAL."
(mu4e-view-header fieldname contacts)
"")))
(defvar mu4e-attach-map nil
"*internal* Hash which maps a number to a (part-id name mime-type).")
(defun mu4e-open-save-attach-func (num is-open)
"Return a function that offers to save attachment NUM. If IS-OPEN
@ -192,45 +189,36 @@ is nil, and otherwise open it."
(mu4e-view-open-attachment num)
(mu4e-view-save-attachment num)))))
;; note -- attachments have an index which is needed for the backend, which does
;; not necessarily follow 1,2,3,4 etc.
(defun mu4e-view-attachments (msg)
"Display attachment information; the field looks like something like:
:attachments ((:index 4 :name \"test123.doc\"
:mime-type \"application/msword\" :size 1234))."
(let ((atts (plist-get msg :attachments)))
(when atts
(setq mu4e-attach-map
(make-hash-table :size 32 :rehash-size 2 :weakness nil))
(let* ((id 0)
(vals
(mapconcat
(lambda (att)
(let ( (index (plist-get att :index))
(name (plist-get att :name))
(mime-type (plist-get att :mime-type))
(size (plist-get att :size))
(map (make-sparse-keymap)))
(incf id)
(puthash id att mu4e-attach-map)
;; mouse-2, RET offers to save the attachment,
;; S-mouse-2, S-Ret opens it.
(define-key map [mouse-2] (mu4e-open-save-attach-func id nil))
(define-key map [?\r] (mu4e-open-save-attach-func id nil))
(define-key map [S-mouse-2](mu4e-open-save-attach-func id t))
(define-key map (kbd "<S-return>")
(mu4e-open-save-attach-func id t))
(concat
(propertize (format "[%d]" id)
'face 'mu4e-view-attach-number-face)
(propertize name
'face 'mu4e-view-link-face
'keymap map
'mouse-face 'highlight)
(when (and size (> size 0))
(concat (format "(%s)"
(propertize (mu4e-display-size size)
'face 'mu4e-view-header-key-face)))))))
atts ", ")))
(mu4e-view-header (format "Attachments(%d)" id) vals t)))))
(let* ((id 0)
(attstr
(mapconcat
(lambda (att)
(let ( (index (plist-get att :index))
(name (plist-get att :name))
(size (plist-get att :size))
(map (make-sparse-keymap)))
(incf id)
(define-key map [mouse-2] (mu4e-open-save-attach-func id nil))
(define-key map [?\r] (mu4e-open-save-attach-func id nil))
(define-key map [S-mouse-2](mu4e-open-save-attach-func id t))
(define-key map (kbd "<S-return>")
(mu4e-open-save-attach-func id t))
(concat
(propertize (format "[%d]" id) 'face 'mu4e-view-attach-number-face)
(propertize name 'face 'mu4e-view-link-face
'keymap map 'mouse-face 'highlight)
(when (and size (> size 0))
(concat (format "(%s)"
(propertize (mu4e-display-size size)
'face 'mu4e-view-header-key-face)))))))
(plist-get msg :attachments) ", ")))
(mu4e-view-header (format "Attachments(%d)" id) attstr t)))
(defvar mu4e-view-mode-map nil
@ -603,18 +591,13 @@ citations."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; attachment handling
(defun mu4e--get-attach-count ()
"Get the number of attachments for this message."
(if (null mu4e-attach-map)
0
(hash-table-count mu4e-attach-map)))
(defun mu4e--get-valid-attach (prompt &optional attachnum)
"Find an attachment sexp for attachment by asking user with
PROMPT. Return the sexp with this is present in the message, nil
otherwise. If (optional) INDEX is provided, don't ask user but just
return the corresponding attachment sexp."
(let ((count (mu4e--get-attach-count)))
(defun mu4e--get-valid-attach (prompt msg &optional attachnum)
"Find an attachment sexp in MSG by asking tne user with
PROMPT. Return the sexp when it is present in the message, nil
otherwise. If (optional) ATTACHNUM is provided, don't ask user but
just return the corresponding attachment sexp or mil."
(let* ((attlist (plist-get msg :attachments))
(count (length attlist)))
(when (zerop count)
(error "No attachments for this message"))
(let* ((attnum
@ -622,17 +605,19 @@ return the corresponding attachment sexp."
(if (= count 1)
(read-number (format "%s (1): " prompt) 1)
(read-number (format "%s (1-%d): " prompt count)))))
(att (gethash attnum mu4e-attach-map)))
(att (nth (- attnum 1) attlist)))
(unless att
(error "Not a valid attachment number"))
att)))
(defun mu4e-view-save-attachment (&optional attachnum)
(defun mu4e-view-save-attachment (&optional msg attachnum)
"Save some attachment (ask user which)."
(interactive)
(unless mu4e-attachment-dir
(error "`mu4e-attachment-dir' is not set"))
(let* ((att (mu4e--get-valid-attach "Attachment to save" attachnum))
(let* ((msg (or msg (mu4e-message-at-point)))
(att (mu4e--get-valid-attach "Attachment to save"
msg attachnum))
(path (concat mu4e-attachment-dir
"/" (plist-get att :name)))
(index (plist-get att :index))
@ -643,15 +628,16 @@ return the corresponding attachment sexp."
(and (file-exists-p path)
(not (y-or-n-p (concat "Overwrite " path "?"))))))
(mu4e-proc-extract
'save (plist-get mu4e-current-msg :docid) index path)))
'save (plist-get msg :docid) index path)))
(defun mu4e-view-open-attachment (&optional attachnum)
(defun mu4e-view-open-attachment (&optional msg attachnum)
"Open some attachment (ask user which)."
(interactive)
(let* ((att (mu4e--get-valid-attach "Attachment to open" attachnum))
(let* ((msg (or msg (mu4e-message-at-point)))
(att (mu4e--get-valid-attach "Attachment to open"
msg attachnum))
(index (plist-get att :index)))
(mu4e-proc-extract 'open
(plist-get mu4e-current-msg :docid) index)))
(mu4e-proc-extract 'open (plist-get msg :docid) index)))
(defun mu4e--temp-action (docid index what &optional param)
"Open attachment INDEX for message with DOCID, and invoke
@ -659,47 +645,39 @@ ACTION."
(interactive)
(mu4e-proc-extract 'temp docid index nil what param))
(defun mu4e-view-open-attachment-with ()
(defun mu4e-view-open-attachment-with (msg)
"Open some attachment with some program (ask user which)."
(interactive)
(let* ((att (mu4e--get-valid-attach "Attachment to open"))
(let* ((att (mu4e--get-valid-attach "Attachment to open" msg))
(cmd (read-string "Shell command to open it with: "))
(index (plist-get att :index)))
(mu4e--temp-action (plist-get mu4e-current-msg :docid) index
(mu4e--temp-action (plist-get msg :docid) index
"open-with" cmd)))
(defun mu4e-view-pipe-attachment ()
(defun mu4e-view-pipe-attachment (msg)
"Open some attachment with some program (ask user which)."
(interactive)
(let* ((att (mu4e--get-valid-attach "Attachment to process"))
(let* ((att (mu4e--get-valid-attach "Attachment to process" msg))
(cmd (read-string "Pipe: "))
(index (plist-get att :index)))
(mu4e--temp-action (plist-get mu4e-current-msg :docid) index
(mu4e--temp-action (plist-get msg :docid) index
"pipe" cmd)))
(defun mu4e-view-open-attachment-emacs ()
(defun mu4e-view-open-attachment-emacs (msg)
"Open some attachment in the current emacs instance."
(interactive)
(let* ((att (mu4e--get-valid-attach "Attachment to open in current emacs"))
(let* ((att (mu4e--get-valid-attach
"Attachment to open in current emacs" msg))
(index (plist-get att :index)))
(mu4e--temp-action (plist-get mu4e-current-msg :docid) index
(mu4e--temp-action (plist-get msg :docid) index
"emacs")))
(defun mu4e-view-handle-attachment ()
(defun mu4e-view-handle-attachment (&optional msg)
"Ask user what to do with attachments, then do it."
(interactive)
(let ((choice
(mu4e-read-option
"Handle attachments: "
'(("open-with" ?w)
("in emacs" ?e)
("pipe" ?|)))))
(case choice
(?w (call-interactively 'mu4e-view-open-attachment-with))
(?| (call-interactively 'mu4e-view-pipe-attachment))
(?e (call-interactively 'mu4e-view-open-attachment-emacs))
(otherwise (message "Not yet implemented")))))
(mu4e-offer-actions "Attachment action: "
mu4e-view-attachments-actions
(or msg (mu4e-message-at-point t))))
;; handler-function to handle the response we get from the server when we
;; want to do something with one of the attachments.
@ -772,7 +750,6 @@ that execution can only take place in n the header list."
(goto-char (point-min))))
(switch-to-buffer buf)))
(defun mu4e-view-pipe (cmd)
"Pipe the message through shell command CMD, and display the
results."
@ -780,5 +757,4 @@ results."
(let ((path (mu4e-field-at-point :path)))
(mu4e-process-file-through-pipe path cmd)))
(provide 'mu4e-view)