mirror of https://github.com/djcb/mu.git
* cleanup attachment handling, add 'actions' system (WIP)
- user can define arbitrary actions (functions) to apply on attachments
This commit is contained in:
parent
3d1c5bb1e9
commit
bff93c9962
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue