2012-04-08 19:28:49 +02:00
|
|
|
|
;;; mu4e-view.el -- part of mu4e, the mu mail user agent
|
2011-09-18 13:39:36 +02:00
|
|
|
|
;;
|
2012-01-06 11:31:28 +01:00
|
|
|
|
;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
|
|
|
|
|
;; This file is not part of GNU Emacs.
|
|
|
|
|
;;
|
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
2012-03-31 16:20:03 +02:00
|
|
|
|
;; In this file we define mu4e-view-mode (+ helper functions), which is used for
|
|
|
|
|
;; viewing e-mail messages
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
;;; Code:
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(require 'mu4e-utils) ;; utility functions
|
2012-04-09 15:34:52 +02:00
|
|
|
|
(require 'mu4e-vars)
|
|
|
|
|
|
2012-02-16 20:37:40 +01:00
|
|
|
|
;; we prefer the improved fill-region
|
|
|
|
|
(require 'filladapt nil 'noerror)
|
2011-11-27 15:23:14 +01:00
|
|
|
|
(require 'comint)
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
;; some buffer-local variables
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defvar mu4e-hdrs-buffer nil
|
2011-09-18 13:39:36 +02:00
|
|
|
|
"*internal* Headers buffer connected to this view.")
|
|
|
|
|
|
2012-04-18 18:10:48 +02:00
|
|
|
|
(defconst mu4e-view-raw-buffer-name "*mu4e-raw-view*"
|
|
|
|
|
"*internal* Name for the raw message view buffer")
|
|
|
|
|
|
2012-01-06 11:31:28 +01:00
|
|
|
|
(defun mu4e-view-message-with-msgid (msgid)
|
|
|
|
|
"View message with MSGID. This is meant for external programs
|
|
|
|
|
wanting to show specific messages - for example, `mu4e-org'."
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(mu4e-proc-view msgid))
|
2012-01-06 11:31:28 +01:00
|
|
|
|
|
2012-04-16 21:06:49 +02:00
|
|
|
|
(defun mu4e-view-message-text (msg)
|
|
|
|
|
"Return the message to display (as a string), based on the MSG
|
|
|
|
|
plist."
|
|
|
|
|
(concat
|
|
|
|
|
(mapconcat
|
|
|
|
|
(lambda (field)
|
|
|
|
|
(let ((fieldname (cdr (assoc field mu4e-header-names)))
|
|
|
|
|
(fieldval (plist-get msg field)))
|
|
|
|
|
(case field
|
|
|
|
|
(:subject (mu4e-view-header fieldname fieldval))
|
|
|
|
|
(:path (mu4e-view-header fieldname fieldval))
|
|
|
|
|
(:maildir (mu4e-view-header fieldname fieldval))
|
|
|
|
|
(:flags (mu4e-view-header fieldname
|
|
|
|
|
(if fieldval (format "%S" fieldval) "")))
|
|
|
|
|
;; contact fields
|
|
|
|
|
(:to (mu4e-view-contacts msg field))
|
|
|
|
|
(:from (mu4e-view-contacts msg field))
|
|
|
|
|
(:cc (mu4e-view-contacts msg field))
|
|
|
|
|
(:bcc (mu4e-view-contacts msg field))
|
|
|
|
|
|
|
|
|
|
;; if we (`user-mail-address' are the From, show To, otherwise,
|
|
|
|
|
;; show From
|
|
|
|
|
(:from-or-to
|
|
|
|
|
(let* ((from (plist-get msg :from))
|
|
|
|
|
(from (and from (cdar from))))
|
|
|
|
|
(if (and from (string-match mu4e-user-mail-address-regexp from))
|
|
|
|
|
(mu4e-view-contacts msg :to)
|
|
|
|
|
(mu4e-view-contacts msg :from))))
|
|
|
|
|
;; date
|
|
|
|
|
(:date
|
|
|
|
|
(let ((datestr
|
|
|
|
|
(when fieldval (format-time-string mu4e-view-date-format
|
|
|
|
|
fieldval))))
|
|
|
|
|
(if datestr (mu4e-view-header fieldname datestr) "")))
|
|
|
|
|
;; size
|
|
|
|
|
(:size
|
|
|
|
|
(let* (size (mu4e-view-size msg)
|
|
|
|
|
(sizestr (when size (format "%d bytes" size))))
|
|
|
|
|
(if sizestr (mu4e-view-header fieldname sizestr))))
|
|
|
|
|
;; attachments
|
|
|
|
|
(:attachments (mu4e-view-attachments msg))
|
|
|
|
|
(t (error "Unsupported field: %S" field)))))
|
|
|
|
|
mu4e-view-fields "")
|
|
|
|
|
"\n"
|
|
|
|
|
(mu4e-body-text msg)))
|
|
|
|
|
|
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view (msg hdrsbuf &optional update)
|
2011-09-18 13:39:36 +02:00
|
|
|
|
"Display the message MSG in a new buffer, and keep in sync with HDRSBUF.
|
|
|
|
|
'In sync' here means that moving to the next/previous message in
|
2011-09-22 20:01:35 +02:00
|
|
|
|
the the message view affects HDRSBUF, as does marking etc. If
|
2012-04-01 11:31:23 +02:00
|
|
|
|
UPDATE is nil, the current message may be (visually) 'massaged',
|
|
|
|
|
based on the settings of `mu4e-view-wrap-lines' and
|
|
|
|
|
`mu4e-view-hide-cited'.
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
As a side-effect, a message that is being viewed loses its 'unread'
|
|
|
|
|
marking if it still had that."
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(let ((buf (get-buffer-create mu4e-view-buffer-name))
|
2011-09-30 07:37:47 +02:00
|
|
|
|
(inhibit-read-only t))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(with-current-buffer buf
|
2012-04-08 11:43:37 +02:00
|
|
|
|
(setq mu4e-view-buffer buf)
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(setq mu4e-attach-map nil) ;; clear any old attachment stuff
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(erase-buffer)
|
2012-04-16 21:06:49 +02:00
|
|
|
|
(insert (mu4e-view-message-text msg))
|
|
|
|
|
|
2011-09-20 22:59:20 +02:00
|
|
|
|
;; initialize view-mode
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(mu4e-view-mode)
|
2011-09-20 22:59:20 +02:00
|
|
|
|
(setq ;; these are buffer-local
|
2011-12-26 11:16:51 +01:00
|
|
|
|
buffer-read-only t
|
2012-04-09 10:51:24 +02:00
|
|
|
|
mu4e-current-msg msg
|
2011-12-13 08:07:38 +01:00
|
|
|
|
mu4e-hdrs-buffer hdrsbuf
|
|
|
|
|
mu4e-link-map (make-hash-table :size 32 :rehash-size 2 :weakness nil))
|
2012-04-16 21:06:49 +02:00
|
|
|
|
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(switch-to-buffer buf)
|
2011-09-20 22:59:20 +02:00
|
|
|
|
(goto-char (point-min))
|
2011-09-22 20:01:35 +02:00
|
|
|
|
|
2012-02-08 21:18:34 +01:00
|
|
|
|
(mu4e-color-cited)
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(mu4e-mark-footer)
|
|
|
|
|
(mu4e-make-urls-clickable)
|
2012-01-06 11:31:28 +01:00
|
|
|
|
|
2012-04-01 11:31:23 +02:00
|
|
|
|
(unless update
|
|
|
|
|
;; if we're showing the message for the first time, use the values of
|
|
|
|
|
;; user-settable variables `mu4e-view-wrap-lines' and
|
|
|
|
|
;; `mu4e-view-hide-cited' to determine whether we should wrap/hide
|
|
|
|
|
(progn
|
|
|
|
|
(when mu4e-view-wrap-lines (mu4e-view-wrap-lines))
|
|
|
|
|
(when mu4e-view-hide-cited (mu4e-view-hide-cited))))
|
2012-04-16 21:06:49 +02:00
|
|
|
|
|
2012-04-01 11:31:23 +02:00
|
|
|
|
;; no use in trying to set flags again
|
2011-09-22 20:01:35 +02:00
|
|
|
|
(unless update
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(mu4e-view-mark-as-read-maybe)))))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2012-04-16 21:06:49 +02:00
|
|
|
|
|
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-header (key val &optional dont-propertize-val)
|
2011-11-05 09:26:24 +01:00
|
|
|
|
"Show header FIELD for MSG with KEY. ie. <KEY>: value-of-FIELD."
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(if val
|
|
|
|
|
(concat
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(propertize key 'face 'mu4e-view-header-key-face) ": "
|
2011-11-20 00:18:12 +01:00
|
|
|
|
(if dont-propertize-val
|
|
|
|
|
val
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(propertize val 'face 'mu4e-view-header-value-face))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
"\n")
|
2011-09-18 13:39:36 +02:00
|
|
|
|
""))
|
|
|
|
|
|
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-contacts (msg field)
|
2011-11-05 09:26:24 +01:00
|
|
|
|
"Add a header for a contact field (ie., :to, :from, :cc, :bcc)."
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(let* ((lst (plist-get msg field))
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(fieldname (cdr (assoc field mu4e-header-names)))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(contacts
|
2011-11-05 09:26:24 +01:00
|
|
|
|
(and lst
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(mapconcat
|
|
|
|
|
(lambda(c)
|
|
|
|
|
(let ((name (car c)) (email (cdr c)))
|
|
|
|
|
(if name
|
|
|
|
|
(format "%s <%s>" name email)
|
|
|
|
|
(format "%s" email)))) lst ", "))))
|
|
|
|
|
(if contacts
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(mu4e-view-header fieldname contacts)
|
2011-09-18 13:39:36 +02:00
|
|
|
|
"")))
|
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defvar mu4e-attach-map nil
|
2011-09-19 23:20:59 +02:00
|
|
|
|
"*internal* Hash which maps a number to a (part-id name mime-type).")
|
|
|
|
|
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(defun mu4e-open-save-attach-func (num is-open)
|
2012-04-15 13:22:43 +02:00
|
|
|
|
"Return a function that offers to save attachment NUM. If IS-OPEN
|
|
|
|
|
is nil, and otherwise open it."
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(lexical-let ((num num) (is-open is-open))
|
|
|
|
|
(lambda ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(if is-open
|
|
|
|
|
(mu4e-view-open-attachment num)
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(mu4e-view-save-attachment num)))))
|
2011-12-26 11:16:51 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-attachments (msg)
|
2011-09-19 23:20:59 +02:00
|
|
|
|
"Display attachment information; the field looks like something like:
|
2011-11-20 00:18:12 +01:00
|
|
|
|
:attachments ((:index 4 :name \"test123.doc\"
|
|
|
|
|
:mime-type \"application/msword\" :size 1234))."
|
2011-09-19 23:20:59 +02:00
|
|
|
|
(let ((atts (plist-get msg :attachments)))
|
|
|
|
|
(when atts
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(setq mu4e-attach-map
|
2011-09-19 23:20:59 +02:00
|
|
|
|
(make-hash-table :size 32 :rehash-size 2 :weakness nil))
|
|
|
|
|
(let* ((id 0)
|
|
|
|
|
(vals
|
|
|
|
|
(mapconcat
|
|
|
|
|
(lambda (att)
|
2011-11-20 00:18:12 +01:00
|
|
|
|
(let ( (index (plist-get att :index))
|
|
|
|
|
(name (plist-get att :name))
|
|
|
|
|
(mime-type (plist-get att :mime-type))
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(size (plist-get att :size))
|
|
|
|
|
(map (make-sparse-keymap)))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
(incf id)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(puthash id att mu4e-attach-map)
|
2011-12-26 11:16:51 +01:00
|
|
|
|
;; 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))
|
2012-02-16 20:37:40 +01:00
|
|
|
|
(define-key map (kbd "<S-return>")
|
|
|
|
|
(mu4e-open-save-attach-func id t))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
(concat
|
2011-12-21 07:49:13 +01:00
|
|
|
|
(propertize (format "[%d]" id)
|
|
|
|
|
'face 'mu4e-view-attach-number-face)
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(propertize name
|
|
|
|
|
'face 'mu4e-view-link-face
|
|
|
|
|
'keymap map
|
|
|
|
|
'mouse-face 'highlight)
|
2012-02-13 21:37:26 +01:00
|
|
|
|
(when (and size (> size 0))
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(concat (format "(%s)"
|
|
|
|
|
(propertize (mu4e-display-size size)
|
|
|
|
|
'face 'mu4e-view-header-key-face)))))))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
atts ", ")))
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(mu4e-view-header (format "Attachments(%d)" id) vals t)))))
|
2011-09-19 23:20:59 +02:00
|
|
|
|
|
2011-11-20 09:31:38 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defvar mu4e-view-mode-map nil
|
|
|
|
|
"Keymap for \"*mu4e-view*\" buffers.")
|
|
|
|
|
(unless mu4e-view-mode-map
|
|
|
|
|
(setq mu4e-view-mode-map
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(let ((map (make-sparse-keymap)))
|
2012-04-11 01:08:32 +02:00
|
|
|
|
|
2012-04-08 19:28:49 +02:00
|
|
|
|
(define-key map "q" 'mu4e-view-kill-buffer-and-window)
|
2011-10-18 11:39:49 +02:00
|
|
|
|
|
2012-04-08 19:28:49 +02:00
|
|
|
|
;; note, 'z' is by-default bound to 'bury-buffer'
|
|
|
|
|
;; but that's not very useful in this case
|
|
|
|
|
(define-key map "z" 'mu4e-view-kill-buffer-and-window)
|
2012-04-11 01:08:32 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "s" 'mu4e-search)
|
2011-12-07 07:50:03 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "b" 'mu4e-search-bookmark)
|
2012-03-25 19:33:17 +02:00
|
|
|
|
(define-key map "B" 'mu4e-search-bookmark-edit-first)
|
2012-03-27 20:17:00 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "j" 'mu4e-jump-to-maildir)
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "g" 'mu4e-view-go-to-url)
|
2011-12-19 08:06:47 +01:00
|
|
|
|
|
2011-12-15 07:51:39 +01:00
|
|
|
|
(define-key map "F" 'mu4e-compose-forward)
|
|
|
|
|
(define-key map "R" 'mu4e-compose-reply)
|
|
|
|
|
(define-key map "C" 'mu4e-compose-new)
|
2012-03-27 20:17:00 +02:00
|
|
|
|
(define-key map "E" 'mu4e-compose-edit)
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2012-04-18 18:10:48 +02:00
|
|
|
|
(define-key map "." 'mu4e-view-raw-message)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "|" 'mu4e-view-pipe)
|
|
|
|
|
;; (define-key map "I" 'mu4e-inspect-message)
|
2011-11-20 09:31:38 +01:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
;; intra-message navigation
|
|
|
|
|
(define-key map (kbd "SPC") 'scroll-up)
|
|
|
|
|
(define-key map (kbd "<home>")
|
|
|
|
|
'(lambda () (interactive) (goto-char (point-min))))
|
|
|
|
|
(define-key map (kbd "<end>")
|
|
|
|
|
'(lambda () (interactive) (goto-char (point-max))))
|
|
|
|
|
(define-key map (kbd "RET")
|
|
|
|
|
'(lambda () (interactive) (scroll-up 1)))
|
|
|
|
|
(define-key map (kbd "<backspace>")
|
|
|
|
|
'(lambda () (interactive) (scroll-up -1)))
|
|
|
|
|
|
|
|
|
|
;; navigation between messages
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "p" 'mu4e-view-prev-header)
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(define-key map "n" 'mu4e-view-next-header)
|
|
|
|
|
;; the same
|
|
|
|
|
(define-key map (kbd "<M-down>") 'mu4e-view-next-header)
|
|
|
|
|
(define-key map (kbd "<M-up>") 'mu4e-view-prev-header)
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2012-04-09 10:51:24 +02:00
|
|
|
|
;; switching to view mode (if it's visible)
|
|
|
|
|
(define-key map "y" 'mu4e-select-other-view)
|
2012-04-11 01:08:32 +02:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
;; attachments
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(define-key map "e" 'mu4e-view-save-attachment)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "o" 'mu4e-view-open-attachment)
|
2012-04-12 08:02:25 +02:00
|
|
|
|
(define-key map "a" 'mu4e-view-handle-attachment)
|
2012-04-15 13:22:43 +02:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
;; marking/unmarking
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map (kbd "<backspace>") 'mu4e-mark-for-trash)
|
|
|
|
|
(define-key map "d" 'mu4e-view-mark-for-trash)
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map (kbd "<delete>") 'mu4e-view-mark-for-delete)
|
2012-03-31 16:20:03 +02:00
|
|
|
|
(define-key map (kbd "<deletechar>") 'mu4e-mark-for-delete)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "D" 'mu4e-view-mark-for-delete)
|
|
|
|
|
(define-key map "m" 'mu4e-view-mark-for-move)
|
2011-11-05 11:29:07 +01:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
;; misc
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "w" 'mu4e-view-toggle-wrap-lines)
|
|
|
|
|
(define-key map "h" 'mu4e-view-toggle-hide-cited)
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2011-12-15 20:47:01 +01:00
|
|
|
|
(define-key map "r" 'mu4e-view-refresh)
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
;; next 3 only warn user when attempt in the message view
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "u" 'mu4e-view-unmark)
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(define-key map "U" 'mu4e-view-unmark-all)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key map "x" 'mu4e-view-marked-execute)
|
2011-10-18 11:39:49 +02:00
|
|
|
|
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(define-key map "$" 'mu4e-show-log)
|
2011-12-15 07:51:39 +01:00
|
|
|
|
(define-key map "H" 'mu4e-display-manual)
|
2011-12-19 08:06:47 +01:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
;; menu
|
|
|
|
|
(define-key map [menu-bar] (make-sparse-keymap))
|
|
|
|
|
(let ((menumap (make-sparse-keymap "View")))
|
|
|
|
|
(define-key map [menu-bar headers] (cons "View" menumap))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2012-04-08 19:28:49 +02:00
|
|
|
|
(define-key menumap [quit-buffer]
|
|
|
|
|
'("Quit view" . mu4e-view-kill-buffer-and-window))
|
2011-12-15 07:51:39 +01:00
|
|
|
|
(define-key menumap [display-help] '("Help" . mu4e-display-manual))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
|
|
|
|
|
(define-key menumap [sepa0] '("--"))
|
|
|
|
|
(define-key menumap [wrap-lines]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Toggle wrap lines" . mu4e-view-toggle-wrap-lines))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [hide-cited]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Toggle hide cited" . mu4e-view-toggle-hide-cited))
|
2012-01-14 10:05:23 +01:00
|
|
|
|
(define-key menumap [raw-view]
|
|
|
|
|
'("View raw message" . mu4e-raw-view))
|
2011-11-27 15:23:14 +01:00
|
|
|
|
(define-key menumap [pipe]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Pipe through shell" . mu4e-view-pipe))
|
2011-12-15 20:47:01 +01:00
|
|
|
|
;; (define-key menumap [inspect]
|
|
|
|
|
;; '("Inspect with guile" . mu4e-inspect-message))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
|
|
|
|
|
(define-key menumap [sepa8] '("--"))
|
|
|
|
|
(define-key menumap [open-att]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Open attachment" . mu4e-view-open-attachment))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [extract-att]
|
2012-04-15 13:22:43 +02:00
|
|
|
|
'("Extract attachment" . mu4e-view-save-attachment))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [goto-url]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Visit URL" . mu4e-view-go-to-url))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [sepa1] '("--"))
|
|
|
|
|
(define-key menumap [mark-delete]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Mark for deletion" . mu4e-view-mark-for-delete))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [mark-trash]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Mark for trash" . mu4e-view-mark-for-trash))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [mark-move]
|
2011-12-13 08:07:38 +01:00
|
|
|
|
'("Mark for move" . mu4e-view-mark-for-move))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [sepa2] '("--"))
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key menumap [compose-new] '("Compose new" . mu4e-compose-new))
|
|
|
|
|
(define-key menumap [forward] '("Forward" . mu4e-compose-forward))
|
|
|
|
|
(define-key menumap [reply] '("Reply" . mu4e-compose-reply))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
(define-key menumap [sepa3] '("--"))
|
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key menumap [search] '("Search" . mu4e-search))
|
|
|
|
|
(define-key menumap [jump] '("Jump to maildir" . mu4e-jump-to-maildir))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
|
|
|
|
|
(define-key menumap [sepa4] '("--"))
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(define-key menumap [next] '("Next" . mu4e-view-next-header))
|
|
|
|
|
(define-key menumap [previous] '("Previous" . mu4e-view-prev-header)))
|
2011-10-18 11:39:49 +02:00
|
|
|
|
map)))
|
2011-11-20 00:18:12 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(fset 'mu4e-view-mode-map mu4e-view-mode-map)
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
|
2012-04-01 11:31:23 +02:00
|
|
|
|
(defvar mu4e-lines-wrapped nil "*internal* Whether lines are wrapped.")
|
|
|
|
|
(defvar mu4e-cited-hidden nil "*internal* Whether cited lines are hidden.")
|
2011-09-30 07:37:47 +02:00
|
|
|
|
|
2012-03-27 20:17:00 +02:00
|
|
|
|
(define-derived-mode mu4e-view-mode special-mode "mu4e:view"
|
2012-01-14 10:05:23 +01:00
|
|
|
|
"Major mode for viewing an e-mail message in mu4e.
|
|
|
|
|
\\{mu4e-view-mode-map}."
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(use-local-map mu4e-view-mode-map)
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(make-local-variable 'mu4e-hdrs-buffer)
|
2012-04-11 01:08:32 +02:00
|
|
|
|
;;(make-local-variable 'mu4e-current-msg)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(make-local-variable 'mu4e-link-map)
|
2011-09-30 07:37:47 +02:00
|
|
|
|
|
2012-04-01 11:31:23 +02:00
|
|
|
|
(make-local-variable 'mu4e-lines-wrapped)
|
|
|
|
|
(make-local-variable 'mu4e-cited-hidden)
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(setq buffer-undo-list t) ;; don't record undo info
|
|
|
|
|
|
2012-04-01 11:31:23 +02:00
|
|
|
|
;; filladapt is much better than the built-in filling
|
|
|
|
|
;; esp. with '>' cited parts
|
|
|
|
|
(when (fboundp 'filladapt-mode)
|
|
|
|
|
(filladapt-mode))
|
|
|
|
|
|
|
|
|
|
(setq truncate-lines t))
|
2011-12-29 09:39:30 +01:00
|
|
|
|
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2012-04-15 13:22:43 +02:00
|
|
|
|
;; we mark messages are as read when we leave the message; i.e., when skipping
|
|
|
|
|
;; to the next/previous one, or leaving the view buffer altogether.
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-mark-as-read-maybe ()
|
2011-09-18 13:39:36 +02:00
|
|
|
|
"Clear the current message's New/Unread status and set it to
|
|
|
|
|
Seen; if the message is not New/Unread, do nothing."
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(when mu4e-current-msg
|
|
|
|
|
(let ((flags (plist-get mu4e-current-msg :flags))
|
|
|
|
|
(docid (plist-get mu4e-current-msg :docid)))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
;; is it a new message?
|
|
|
|
|
(when (or (member 'unread flags) (member 'new flags))
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(mu4e-proc-move docid nil "+S-u-N")))))
|
2011-09-20 22:59:20 +02:00
|
|
|
|
|
2012-02-08 21:18:34 +01:00
|
|
|
|
(defun mu4e-color-cited ()
|
2012-02-13 21:37:26 +01:00
|
|
|
|
"Colorize message content based on the citation level."
|
2012-02-08 21:18:34 +01:00
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((more-lines t))
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(while more-lines
|
2012-04-01 11:31:23 +02:00
|
|
|
|
;; Get the citation level at point -- i.e., the number of '>'
|
2012-02-08 21:18:34 +01:00
|
|
|
|
;; prefixes, starting with 0 for 'no citation'
|
|
|
|
|
(beginning-of-line 1)
|
|
|
|
|
(let* ((text (re-search-forward "[[:word:]]" (line-end-position 1) t 1))
|
|
|
|
|
(level (or (and text
|
|
|
|
|
(how-many ">" (line-beginning-position 1) text)) 0))
|
|
|
|
|
(face
|
|
|
|
|
(cond
|
|
|
|
|
((= 0 level) nil) ;; don't do anything
|
|
|
|
|
((= 1 level) 'mu4e-cited-1-face)
|
|
|
|
|
((= 2 level) 'mu4e-cited-2-face)
|
|
|
|
|
((= 3 level) 'mu4e-cited-3-face)
|
|
|
|
|
((= 4 level) 'mu4e-cited-4-face)
|
|
|
|
|
(t nil))))
|
|
|
|
|
(when face
|
|
|
|
|
(add-text-properties (line-beginning-position 1)
|
|
|
|
|
(line-end-position 1) `(face ,face))))
|
|
|
|
|
(setq more-lines
|
|
|
|
|
(and (= 0 (forward-line 1))
|
|
|
|
|
;; we need to add this weird check below; it seems in some cases
|
|
|
|
|
;; `forward-line' continues to return 0, even when at the end, which
|
|
|
|
|
;; would lead to an infinite loop
|
|
|
|
|
(not (= (point-max) (line-end-position)))))))))
|
|
|
|
|
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(defun mu4e-mark-footer ()
|
|
|
|
|
"Give the message footers a distinctive color."
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(save-excursion
|
|
|
|
|
;; give the footer a different color...
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(let ((p (search-forward "\n-- \n" nil t)))
|
|
|
|
|
(when p
|
2012-04-16 22:10:16 +02:00
|
|
|
|
(add-text-properties p (point-max) '(face mu4e-footer-face)))))))
|
2011-12-26 11:16:51 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defvar mu4e-link-map nil
|
2011-09-20 22:59:20 +02:00
|
|
|
|
"*internal* A map of some number->url so we can jump to url by number.")
|
|
|
|
|
|
2011-12-21 07:49:13 +01:00
|
|
|
|
(defconst mu4e-url-regexp
|
2012-02-16 06:29:17 +01:00
|
|
|
|
"\\(https?://[-+a-zA-Z0-9.?_$%/+&#@!~,:;=/()]+\\)"
|
2011-12-21 07:49:13 +01:00
|
|
|
|
"*internal* regexp that matches URLs; match-string 1 will contain
|
|
|
|
|
the matched URL, if any.")
|
|
|
|
|
|
2011-12-26 11:16:51 +01:00
|
|
|
|
(defun mu4e-browse-url-func (url)
|
|
|
|
|
"Return a function that executes `browse-url' with URL."
|
|
|
|
|
(lexical-let ((url url))
|
|
|
|
|
(lambda ()
|
|
|
|
|
(interactive)
|
2012-01-06 11:31:28 +01:00
|
|
|
|
(browse-url url))))
|
2011-12-26 11:16:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; this is fairly simplistic...
|
|
|
|
|
(defun mu4e-make-urls-clickable ()
|
|
|
|
|
"Turn things that look like URLs into clickable things, and
|
|
|
|
|
number them so they can be opened using `mu4e-view-go-to-url'."
|
|
|
|
|
(let ((num 0))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(while (re-search-forward mu4e-url-regexp nil t)
|
|
|
|
|
(let ((url (match-string 0))
|
|
|
|
|
(map (make-sparse-keymap)))
|
|
|
|
|
(define-key map [mouse-2] (mu4e-browse-url-func url))
|
|
|
|
|
(define-key map [?\r] (mu4e-browse-url-func url))
|
|
|
|
|
(puthash (incf num) url mu4e-link-map)
|
|
|
|
|
(add-text-properties 0 (length url)
|
|
|
|
|
`(face mu4e-view-link-face
|
|
|
|
|
mouse-face highlight
|
|
|
|
|
keymap ,map) url)
|
|
|
|
|
(replace-match (concat url
|
|
|
|
|
(propertize (format "[%d]" num)
|
|
|
|
|
'face 'mu4e-view-url-number-face))))))))
|
2012-01-06 11:31:28 +01:00
|
|
|
|
|
2011-12-01 20:21:29 +01:00
|
|
|
|
|
|
|
|
|
;; functions for org-contacts
|
2012-01-06 11:31:28 +01:00
|
|
|
|
(defun mu4e-view-snarf-from (name-or-email)
|
|
|
|
|
"Get the From:-data for the current message; NAME-OR-EMAIL should
|
|
|
|
|
be a symbol 'name or 'email to get the corresponding field. If the
|
|
|
|
|
field is not found, \"\" is returned.
|
2011-12-01 20:21:29 +01:00
|
|
|
|
|
2012-01-06 11:31:28 +01:00
|
|
|
|
You can use this with e.g. org-contact with a template like:
|
2011-12-01 20:21:29 +01:00
|
|
|
|
(\"c\" \"Contacts\" entry (file \"~/Org/contacts.org\")
|
2012-01-06 11:31:28 +01:00
|
|
|
|
\"* %(mu4e-view-snarf-from 'name)
|
2011-12-01 20:21:29 +01:00
|
|
|
|
:PROPERTIES:
|
2012-01-06 11:31:28 +01:00
|
|
|
|
:EMAIL: %(mu4e-view-snarf-from 'email)
|
2011-12-01 20:21:29 +01:00
|
|
|
|
:END:\")))
|
|
|
|
|
|
|
|
|
|
See the `org-contacts' documentation for more details."
|
2012-01-06 13:04:08 +01:00
|
|
|
|
;; FIXME: we need to explictly go to some (hopefully the right!) view buffer,
|
|
|
|
|
;; since when using this from org-capture, we'll be taken to the capture
|
|
|
|
|
;; buffer instead.
|
2012-01-14 10:05:23 +01:00
|
|
|
|
(with-current-buffer mu4e-view-buffer-name
|
2012-01-06 13:04:08 +01:00
|
|
|
|
(unless (eq major-mode 'mu4e-view-mode)
|
|
|
|
|
(error "Not in mu4e-view mode."))
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(unless mu4e-current-msg
|
2012-01-06 13:04:08 +01:00
|
|
|
|
(error "No current message."))
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(let ((from (car-safe (plist-get mu4e-current-msg :from))))
|
2012-01-06 13:04:08 +01:00
|
|
|
|
(cond
|
|
|
|
|
((not from) "") ;; nothing found
|
|
|
|
|
((eq name-or-email 'name)
|
|
|
|
|
(or (car-safe from) ""))
|
|
|
|
|
((eq name-or-email 'email)
|
|
|
|
|
(or (cdr-safe from) ""))
|
|
|
|
|
(t (error "Not supported: %S" name-or-email))))))
|
2011-11-20 09:31:38 +01:00
|
|
|
|
|
|
|
|
|
|
2012-04-01 11:31:23 +02:00
|
|
|
|
(defun mu4e-view-wrap-lines ()
|
|
|
|
|
"Wrap lines in the message body."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(when (search-forward "\n\n") ;; search for the message body
|
|
|
|
|
(fill-region (point) (point-max)))
|
|
|
|
|
(setq mu4e-lines-wrapped t))))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-view-hide-cited ()
|
|
|
|
|
"Toggle hiding of cited lines in the message body."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(flush-lines "^[:blank:]*>")
|
|
|
|
|
(setq mu4e-cited-hidden t))))
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2011-09-22 20:01:35 +02:00
|
|
|
|
|
2011-09-18 13:39:36 +02:00
|
|
|
|
;; Interactive functions
|
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-toggle-wrap-lines ()
|
2011-09-22 20:01:35 +02:00
|
|
|
|
"Toggle line wrap in the message body."
|
|
|
|
|
(interactive)
|
2012-04-01 11:31:23 +02:00
|
|
|
|
(if mu4e-lines-wrapped
|
|
|
|
|
(mu4e-view-refresh)
|
|
|
|
|
(mu4e-view-wrap-lines)))
|
2011-09-22 20:01:35 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-toggle-hide-cited ()
|
2011-09-30 07:37:47 +02:00
|
|
|
|
"Toggle hiding of cited lines in the message body."
|
|
|
|
|
(interactive)
|
2012-04-01 11:31:23 +02:00
|
|
|
|
(if mu4e-cited-hidden
|
|
|
|
|
(mu4e-view-refresh)
|
|
|
|
|
(mu4e-view-hide-cited)))
|
2011-09-30 07:37:47 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-refresh ()
|
2012-04-01 11:31:23 +02:00
|
|
|
|
"Redisplay the current message, without wrapped lines or hidden
|
|
|
|
|
citations."
|
2011-09-30 07:37:47 +02:00
|
|
|
|
(interactive)
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(mu4e-view mu4e-current-msg mu4e-hdrs-buffer t)
|
2012-04-01 11:31:23 +02:00
|
|
|
|
(setq
|
|
|
|
|
mu4e-lines-wrapped nil
|
|
|
|
|
mu4e-cited-hidden nil))
|
2011-09-22 20:01:35 +02:00
|
|
|
|
|
2012-04-08 19:28:49 +02:00
|
|
|
|
(defun mu4e-view-kill-buffer-and-window ()
|
2011-09-18 13:39:36 +02:00
|
|
|
|
"Quit the message view and return to the headers."
|
|
|
|
|
(interactive)
|
2012-04-11 17:27:26 +02:00
|
|
|
|
(when (buffer-live-p mu4e-view-buffer)
|
|
|
|
|
(with-current-buffer mu4e-view-buffer
|
2012-04-16 19:50:16 +02:00
|
|
|
|
(if (window-parent)
|
|
|
|
|
(kill-buffer-and-window)
|
|
|
|
|
(kill-buffer)))))
|
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-next-header ()
|
2011-09-20 22:59:20 +02:00
|
|
|
|
"View the next header."
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(interactive)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(when (mu4e-next-header)
|
|
|
|
|
(mu4e-view-message)))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-prev-header ()
|
2011-09-20 22:59:20 +02:00
|
|
|
|
"View the previous header."
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(interactive)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(when (mu4e-prev-header)
|
|
|
|
|
(mu4e-view-message)))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-mark-for-move ()
|
2011-09-20 22:59:20 +02:00
|
|
|
|
"Mark the current message for moving."
|
|
|
|
|
(interactive)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(when (mu4e-mark-for-move)
|
|
|
|
|
(mu4e-view-message)))
|
2011-09-20 22:59:20 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-mark-for-trash ()
|
2011-09-20 22:59:20 +02:00
|
|
|
|
"Mark the current message for moving to the trash folder."
|
|
|
|
|
(interactive)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(when (mu4e-mark-for-trash)
|
|
|
|
|
(mu4e-view-message)))
|
2011-09-20 22:59:20 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-mark-for-delete ()
|
2011-09-20 22:59:20 +02:00
|
|
|
|
"Mark the current message for deletion."
|
|
|
|
|
(interactive)
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(when (mu4e-mark-for-delete)
|
|
|
|
|
(mu4e-view-message)))
|
2011-09-19 23:20:59 +02:00
|
|
|
|
|
2012-04-12 08:02:25 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
;; attachment handling
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(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)))
|
|
|
|
|
(when (zerop count)
|
|
|
|
|
(error "No attachments for this message"))
|
|
|
|
|
(let* ((attnum
|
|
|
|
|
(or attachnum
|
|
|
|
|
(if (= count 1)
|
|
|
|
|
(read-number (format "%s (1): " prompt) 1)
|
|
|
|
|
(read-number (format "%s (1-%d): " prompt count)))))
|
|
|
|
|
(att (gethash attnum mu4e-attach-map)))
|
|
|
|
|
(unless att
|
|
|
|
|
(error "Not a valid attachment number"))
|
|
|
|
|
att)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-view-save-attachment (&optional 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))
|
|
|
|
|
(path (concat mu4e-attachment-dir
|
|
|
|
|
"/" (plist-get att :name)))
|
|
|
|
|
(index (plist-get att :index))
|
2011-09-19 23:20:59 +02:00
|
|
|
|
(retry t))
|
|
|
|
|
(while retry
|
|
|
|
|
(setq path (expand-file-name (read-string "Save as " path)))
|
|
|
|
|
(setq retry
|
|
|
|
|
(and (file-exists-p path)
|
|
|
|
|
(not (y-or-n-p (concat "Overwrite " path "?"))))))
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(mu4e-proc-extract
|
|
|
|
|
'save (plist-get mu4e-current-msg :docid) index path)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-view-open-attachment (&optional attachnum)
|
|
|
|
|
"Open some attachment (ask user which)."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let* ((att (mu4e--get-valid-attach "Attachment to open" attachnum))
|
|
|
|
|
(index (plist-get att :index)))
|
|
|
|
|
(mu4e-proc-extract 'open
|
|
|
|
|
(plist-get mu4e-current-msg :docid) index)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e--temp-action (docid index what &optional param)
|
|
|
|
|
"Open attachment INDEX for message with DOCID, and invoke
|
|
|
|
|
ACTION."
|
|
|
|
|
(interactive)
|
|
|
|
|
(mu4e-proc-extract 'temp docid index nil what param))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-view-open-attachment-with ()
|
|
|
|
|
"Open some attachment with some program (ask user which)."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let* ((att (mu4e--get-valid-attach "Attachment to open"))
|
|
|
|
|
(cmd (read-string "Shell command to open it with: "))
|
|
|
|
|
(index (plist-get att :index)))
|
|
|
|
|
(mu4e--temp-action (plist-get mu4e-current-msg :docid) index
|
|
|
|
|
"open-with" cmd)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-view-pipe-attachment ()
|
|
|
|
|
"Open some attachment with some program (ask user which)."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let* ((att (mu4e--get-valid-attach "Attachment to process"))
|
|
|
|
|
(cmd (read-string "Pipe: "))
|
|
|
|
|
(index (plist-get att :index)))
|
|
|
|
|
(mu4e--temp-action (plist-get mu4e-current-msg :docid) index
|
|
|
|
|
"pipe" cmd)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-view-open-attachment-emacs ()
|
|
|
|
|
"Open some attachment in the current emacs instance."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let* ((att (mu4e--get-valid-attach "Attachment to open in current emacs"))
|
|
|
|
|
(index (plist-get att :index)))
|
|
|
|
|
(mu4e--temp-action (plist-get mu4e-current-msg :docid) index
|
|
|
|
|
"emacs")))
|
2011-09-19 23:20:59 +02:00
|
|
|
|
|
2012-04-12 08:02:25 +02:00
|
|
|
|
(defun mu4e-view-handle-attachment ()
|
|
|
|
|
"Ask user what to do with attachments, then do it."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let ((choice
|
|
|
|
|
(mu4e-read-option
|
|
|
|
|
"Handle attachments: "
|
2012-04-15 13:22:43 +02:00
|
|
|
|
'(("open-with" ?w)
|
|
|
|
|
("in emacs" ?e)
|
|
|
|
|
("pipe" ?|)))))
|
2012-04-12 08:02:25 +02:00
|
|
|
|
(case choice
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(?w (call-interactively 'mu4e-view-open-attachment-with))
|
|
|
|
|
(?| (call-interactively 'mu4e-view-pipe-attachment))
|
|
|
|
|
(?e (call-interactively 'mu4e-view-open-attachment-emacs))
|
2012-04-12 08:02:25 +02:00
|
|
|
|
(otherwise (message "Not yet implemented")))))
|
|
|
|
|
|
2012-04-18 18:10:48 +02:00
|
|
|
|
|
2012-04-15 13:22:43 +02:00
|
|
|
|
;; handler-function to handle the response we get from the server when we
|
|
|
|
|
;; want to do something with one of the attachments.
|
|
|
|
|
(defun mu4e-view-temp-handler (path what param)
|
|
|
|
|
"Handler function for doing things with temp files (ie.,
|
|
|
|
|
attachments) in response to a (mu4e-proc-extract 'temp ... )."
|
|
|
|
|
(cond
|
|
|
|
|
((string= what "open-with")
|
|
|
|
|
;; 'param' will be the program to open-with
|
|
|
|
|
(start-file-process-shell-command "*mu4e-open-with*" nil
|
|
|
|
|
(concat param " " path)))
|
|
|
|
|
((string= what "pipe")
|
|
|
|
|
;; 'param' will be the pipe command, path the infile for this
|
2012-04-18 18:10:48 +02:00
|
|
|
|
(mu4e-process-file-through-pipe path param))
|
2012-04-15 13:22:43 +02:00
|
|
|
|
((string= what "emacs")
|
2012-04-18 06:41:37 +02:00
|
|
|
|
(find-file path)
|
|
|
|
|
;; make the buffer read-only since it usually does not make
|
|
|
|
|
;; sense to edit the temp buffer; use C-x C-q if you insist...
|
2012-04-18 18:10:48 +02:00
|
|
|
|
(setq buffer-read-only t))
|
2012-04-15 13:22:43 +02:00
|
|
|
|
(t (error "Unsupported action %S" what))))
|
2012-04-12 08:02:25 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-09 10:51:24 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e--in-split-view ()
|
|
|
|
|
"Return t if we're in split-view, nil otherwise."
|
|
|
|
|
(member mu4e-split-view '(horizontal vertical)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-view-unmark-all ()
|
|
|
|
|
"If we're in split-view, unmark all messages. Otherwise, warn
|
|
|
|
|
user that unmarking only works in the header list."
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(interactive)
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(if (mu4e--in-split-view)
|
|
|
|
|
(mu4e-unmark-all)
|
|
|
|
|
(message "Unmarking needs to be done in the header list view")))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(defun mu4e-view-unmark ()
|
|
|
|
|
"If we're in split-view, unmark message at point. Otherwise, warn
|
|
|
|
|
user that unmarking only works in the header list."
|
|
|
|
|
(interactive)
|
|
|
|
|
(if (mu4e--in-split-view)
|
|
|
|
|
(mu4e-unmark)
|
|
|
|
|
(message "Unmarking needs to be done in the header list view")))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-marked-execute ()
|
2012-04-09 10:51:24 +02:00
|
|
|
|
"If we're in split-view, execute the marks. Otherwise, warn user
|
|
|
|
|
that execution can only take place in n the header list."
|
2011-09-18 13:39:36 +02:00
|
|
|
|
(interactive)
|
2012-04-09 10:51:24 +02:00
|
|
|
|
(if (mu4e--in-split-view)
|
|
|
|
|
(mu4e-execute-marks)
|
|
|
|
|
(message "Execution needs to be done in the header list view")))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-go-to-url (num)
|
2011-09-20 22:59:20 +02:00
|
|
|
|
"Go to a numbered url."
|
|
|
|
|
(interactive "nGo to url with number: ")
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(let ((url (gethash num mu4e-link-map)))
|
2011-09-20 22:59:20 +02:00
|
|
|
|
(unless url (error "Invalid number for URL"))
|
|
|
|
|
(browse-url url)))
|
|
|
|
|
|
2012-04-18 18:10:48 +02:00
|
|
|
|
(defun mu4e-view-raw-message ()
|
|
|
|
|
"Display the raw contents of message at point in a new buffer."
|
2011-11-20 09:31:38 +01:00
|
|
|
|
(interactive)
|
2012-04-18 18:10:48 +02:00
|
|
|
|
(let ((path (mu4e-field-at-point :path))
|
|
|
|
|
(buf (get-buffer-create mu4e-view-raw-buffer-name)))
|
|
|
|
|
(unless (and path (file-readable-p path))
|
|
|
|
|
(error "Not a readable file: %S" path))
|
|
|
|
|
(with-current-buffer buf
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(insert-file-contents path)
|
|
|
|
|
(view-mode)
|
|
|
|
|
(goto-char (point-min))))
|
|
|
|
|
(switch-to-buffer buf)))
|
|
|
|
|
|
2011-11-20 09:31:38 +01:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(defun mu4e-view-pipe (cmd)
|
2011-11-27 15:23:14 +01:00
|
|
|
|
"Pipe the message through shell command CMD, and display the
|
|
|
|
|
results."
|
|
|
|
|
(interactive "sShell command: ")
|
2012-04-18 18:10:48 +02:00
|
|
|
|
(let ((path (mu4e-field-at-point :path)))
|
|
|
|
|
(mu4e-process-file-through-pipe path cmd)))
|
|
|
|
|
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2011-12-13 08:07:38 +01:00
|
|
|
|
(provide 'mu4e-view)
|