~ubuntu-branches/ubuntu/quantal/maildir-utils/quantal

« back to all changes in this revision

Viewing changes to emacs/mu4e-view.el

  • Committer: Package Import Robot
  • Author(s): Norbert Preining
  • Date: 2012-03-15 08:45:56 UTC
  • mfrom: (1.2.5)
  • Revision ID: package-import@ubuntu.com-20120315084556-vqdvw8sgodo24aji
Tags: 0.9.8.2-1
* several new upstream versions
* b-d on libgmime-2.6-dev (Closes: #664001, #664006)
* bump standards version to 3.9.3, no changes necessary
* switch to source format 3.0 (quilt): debian/control, debian/rules
* maildir-utils depends on dpkg/install-info (lintian warning)
* fix man page lintian warnings

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
;; mu4e-view.el -- part of mu4e, the mu mail user agent
 
2
;;
 
3
;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
 
4
 
 
5
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
 
6
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
 
7
 
 
8
;; This file is not part of GNU Emacs.
 
9
;;
 
10
;; GNU Emacs is free software: you can redistribute it and/or modify
 
11
;; it under the terms of the GNU General Public License as published by
 
12
;; the Free Software Foundation, either version 3 of the License, or
 
13
;; (at your option) any later version.
 
14
 
 
15
;; GNU Emacs is distributed in the hope that it will be useful,
 
16
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
18
;; GNU General Public License for more details.
 
19
 
 
20
;; You should have received a copy of the GNU General Public License
 
21
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
22
 
 
23
;;; Commentary:
 
24
 
 
25
;; In this file are function related to creating the list of one-line
 
26
;; descriptions of emails, aka 'headers' (not to be confused with headers like
 
27
;; 'To:' or 'Subject:')
 
28
 
 
29
;; mm
 
30
 
 
31
;;; Code:
 
32
(eval-when-compile (require 'cl))
 
33
(require 'html2text)
 
34
;; we prefer the improved fill-region
 
35
(require 'filladapt nil 'noerror)
 
36
(require 'comint)
 
37
 
 
38
(defconst mu4e-view-buffer-name "*mu4e-view*"
 
39
  "*internal* Name for the message view buffer")
 
40
 
 
41
(defconst mu4e-raw-view-buffer-name "*mu4e-raw-view*"
 
42
  "*internal* Name for the raw message view buffer")
 
43
 
 
44
;; some buffer-local variables
 
45
(defvar mu4e-hdrs-buffer nil
 
46
  "*internal* Headers buffer connected to this view.")
 
47
 
 
48
(defvar mu4e-current-msg nil
 
49
  "*internal* The plist describing the current message.")
 
50
 
 
51
(defun mu4e-view-message-with-msgid (msgid)
 
52
  "View message with MSGID. This is meant for external programs
 
53
wanting to show specific messages - for example, `mu4e-org'."
 
54
  (mu4e-proc-view-msg msgid))
 
55
 
 
56
 
 
57
(defun mu4e-view (msg hdrsbuf &optional update)
 
58
  "Display the message MSG in a new buffer, and keep in sync with HDRSBUF.
 
59
'In sync' here means that moving to the next/previous message in
 
60
the the message view affects HDRSBUF, as does marking etc. If
 
61
UPDATE is non-nil, the current message will be (visually) updated.
 
62
 
 
63
As a side-effect, a message that is being viewed loses its 'unread'
 
64
marking if it still had that."
 
65
  (let ((buf (get-buffer-create mu4e-view-buffer-name))
 
66
         (inhibit-read-only t))
 
67
    (with-current-buffer buf
 
68
      (erase-buffer)
 
69
      (insert
 
70
        (mapconcat
 
71
          (lambda (field)
 
72
            (let ((fieldname (cdr (assoc field mu4e-header-names)))
 
73
                   (fieldval (plist-get msg field)))
 
74
              (case field
 
75
 
 
76
                (:subject  (mu4e-view-header fieldname fieldval))
 
77
                (:path     (mu4e-view-header fieldname fieldval))
 
78
                (:maildir  (mu4e-view-header fieldname fieldval))
 
79
                (:flags    (mu4e-view-header fieldname
 
80
                             (if fieldval (format "%S" fieldval) "")))
 
81
                ;; contact fields
 
82
                (:to       (mu4e-view-contacts msg field))
 
83
                (:from     (mu4e-view-contacts msg field))
 
84
                (:cc       (mu4e-view-contacts msg field))
 
85
                (:bcc      (mu4e-view-contacts msg field))
 
86
 
 
87
                ;; if we (`user-mail-address' are the From, show To, otherwise,
 
88
                ;; show From
 
89
                (:from-or-to
 
90
                  (let* ((from (plist-get msg :from))
 
91
                          (from (and from (cdar from))))
 
92
                    (if (and from (string-match mu4e-user-mail-address-regexp from))
 
93
                      (mu4e-view-contacts msg :to)
 
94
                      (mu4e-view-contacts msg :from))))
 
95
 
 
96
                ;; date
 
97
                (:date
 
98
                  (let ((datestr
 
99
                          (when fieldval (format-time-string mu4e-view-date-format
 
100
                                           fieldval))))
 
101
                    (if datestr (mu4e-view-header fieldname datestr) "")))
 
102
                ;; size
 
103
                (:size  (mu4e-view-size  msg)
 
104
                  (let ((sizestr (when size (format "%d bytes"))))
 
105
                    (if sizestr (mu4e-view-header fieldname sizestr))))
 
106
                ;; attachments
 
107
                (:attachments (mu4e-view-attachments msg))
 
108
                (t               (error "Unsupported field: %S" field)))))
 
109
            mu4e-view-fields "")
 
110
        "\n"
 
111
        (mu4e-body-text msg))
 
112
 
 
113
      ;; initialize view-mode
 
114
      (mu4e-view-mode)
 
115
      (setq ;; these are buffer-local
 
116
        buffer-read-only t
 
117
        mu4e-current-msg msg
 
118
        mu4e-hdrs-buffer hdrsbuf
 
119
        mu4e-link-map (make-hash-table :size 32 :rehash-size 2 :weakness nil))
 
120
 
 
121
      (switch-to-buffer buf)
 
122
      (goto-char (point-min))
 
123
 
 
124
      (mu4e-color-cited)
 
125
      (mu4e-mark-footer)
 
126
      (mu4e-make-urls-clickable)
 
127
 
 
128
      (unless update
 
129
        (mu4e-view-mark-as-read-maybe)))))
 
130
 
 
131
(defun mu4e-view-header (key val &optional dont-propertize-val)
 
132
  "Show header FIELD for MSG with KEY. ie. <KEY>: value-of-FIELD."
 
133
  (if val
 
134
    (concat
 
135
      (propertize key 'face 'mu4e-view-header-key-face) ": "
 
136
      (if dont-propertize-val
 
137
        val
 
138
        (propertize val 'face 'mu4e-view-header-value-face))
 
139
      "\n")
 
140
    ""))
 
141
 
 
142
 
 
143
(defun mu4e-view-contacts (msg field)
 
144
  "Add a header for a contact field (ie., :to, :from, :cc, :bcc)."
 
145
  (let* ((lst (plist-get msg field))
 
146
          (fieldname (cdr (assoc field mu4e-header-names)))
 
147
          (contacts
 
148
            (and lst
 
149
              (mapconcat
 
150
                (lambda(c)
 
151
                  (let ((name (car c)) (email (cdr c)))
 
152
                    (if name
 
153
                      (format "%s <%s>" name email)
 
154
                      (format "%s" email)))) lst ", "))))
 
155
    (if contacts
 
156
      (mu4e-view-header fieldname contacts)
 
157
      "")))
 
158
 
 
159
(defvar mu4e-attach-map nil
 
160
  "*internal* Hash which maps a number to a (part-id name mime-type).")
 
161
 
 
162
 
 
163
(defun mu4e-open-save-attach-func (num is-open)
 
164
  "Return a function that offers to extracts (saves) attachment NUM
 
165
if IS-OPEN is nil, and otherwise open it."
 
166
  (lexical-let ((num num) (is-open is-open))
 
167
    (lambda ()
 
168
      (interactive)
 
169
      (if is-open
 
170
        (mu4e-view-open-attachment num)
 
171
        (mu4e-view-extract-attachment num)))))
 
172
 
 
173
(defun mu4e-view-attachments (msg)
 
174
  "Display attachment information; the field looks like something like:
 
175
        :attachments ((:index 4 :name \"test123.doc\"
 
176
                       :mime-type \"application/msword\" :size 1234))."
 
177
  (let ((atts (plist-get msg :attachments)))
 
178
    (when atts
 
179
      (setq mu4e-attach-map
 
180
        (make-hash-table :size 32 :rehash-size 2 :weakness nil))
 
181
      (let* ((id 0)
 
182
              (vals
 
183
                (mapconcat
 
184
                  (lambda (att)
 
185
                    (let ( (index (plist-get att :index))
 
186
                           (name (plist-get att :name))
 
187
                           (mime-type (plist-get att :mime-type))
 
188
                           (size (plist-get att :size))
 
189
                           (map (make-sparse-keymap)))
 
190
                      (incf id)
 
191
                      (puthash id att mu4e-attach-map)
 
192
                      ;; mouse-2, RET offers to save the attachment,
 
193
                      ;; S-mouse-2, S-Ret opens it.
 
194
                      (define-key map [mouse-2] (mu4e-open-save-attach-func id nil))
 
195
                      (define-key map [?\r]     (mu4e-open-save-attach-func id nil))
 
196
                      (define-key map [S-mouse-2](mu4e-open-save-attach-func id t))
 
197
                      (define-key map (kbd "<S-return>")
 
198
                        (mu4e-open-save-attach-func id t))
 
199
                      (concat
 
200
                        (propertize (format "[%d]" id)
 
201
                          'face 'mu4e-view-attach-number-face)
 
202
                        (propertize name
 
203
                          'face 'mu4e-view-link-face
 
204
                          'keymap map
 
205
                          'mouse-face 'highlight)
 
206
                        (when (and size (> size 0))
 
207
                          (concat (format "(%s)"
 
208
                             (propertize (mu4e-display-size size)
 
209
                                  'face 'mu4e-view-header-key-face)))))))
 
210
                    atts ", ")))
 
211
                (mu4e-view-header (format "Attachments(%d)" id) vals t)))))
 
212
 
 
213
 
 
214
(defvar mu4e-view-mode-map nil
 
215
  "Keymap for \"*mu4e-view*\" buffers.")
 
216
(unless mu4e-view-mode-map
 
217
  (setq mu4e-view-mode-map
 
218
    (let ((map (make-sparse-keymap)))
 
219
      (define-key map "q" 'mu4e-view-quit-buffer)
 
220
 
 
221
      (define-key map "s" 'mu4e-search)
 
222
 
 
223
      (define-key map "b" 'mu4e-search-bookmark)
 
224
      (define-key map "j" 'mu4e-jump-to-maildir)
 
225
 
 
226
      (define-key map "g" 'mu4e-view-go-to-url)
 
227
 
 
228
      (define-key map "F" 'mu4e-compose-forward)
 
229
      (define-key map "R" 'mu4e-compose-reply)
 
230
      (define-key map "C" 'mu4e-compose-new)
 
231
      (define-key map "E" 'mu4e-edit-draft)
 
232
 
 
233
      (define-key map "." 'mu4e-raw-view)
 
234
      (define-key map "|" 'mu4e-view-pipe)
 
235
      ;; (define-key map "I" 'mu4e-inspect-message)
 
236
 
 
237
      ;; intra-message navigation
 
238
      (define-key map (kbd "SPC") 'scroll-up)
 
239
      (define-key map (kbd "<home>")
 
240
        '(lambda () (interactive) (goto-char (point-min))))
 
241
      (define-key map (kbd "<end>")
 
242
        '(lambda () (interactive) (goto-char (point-max))))
 
243
      (define-key map (kbd "RET")
 
244
        '(lambda () (interactive) (scroll-up 1)))
 
245
      (define-key map (kbd "<backspace>")
 
246
        '(lambda () (interactive) (scroll-up -1)))
 
247
 
 
248
 
 
249
      ;; navigation between messages
 
250
      (define-key map "n" 'mu4e-view-next-header)
 
251
      (define-key map "p" 'mu4e-view-prev-header)
 
252
 
 
253
      ;; attachments
 
254
      (define-key map "e" 'mu4e-view-extract-attachment)
 
255
      (define-key map "o" 'mu4e-view-open-attachment)
 
256
 
 
257
      ;; marking/unmarking
 
258
      (define-key map (kbd "<backspace>") 'mu4e-mark-for-trash)
 
259
      (define-key map "d" 'mu4e-view-mark-for-trash)
 
260
 
 
261
      (define-key map (kbd "<delete>") 'mu4e-view-mark-for-delete)
 
262
      (define-key map "D" 'mu4e-view-mark-for-delete)
 
263
      (define-key map "m" 'mu4e-view-mark-for-move)
 
264
 
 
265
      ;; misc
 
266
      (define-key map "w" 'mu4e-view-toggle-wrap-lines)
 
267
      (define-key map "h" 'mu4e-view-toggle-hide-cited)
 
268
 
 
269
      (define-key map "r" 'mu4e-view-refresh)
 
270
 
 
271
      ;; next 3 only warn user when attempt in the message view
 
272
      (define-key map "u" 'mu4e-view-unmark)
 
273
      (define-key map "U" 'mu4e-view-unmark)
 
274
      (define-key map "x" 'mu4e-view-marked-execute)
 
275
 
 
276
      (define-key map "H" 'mu4e-display-manual)
 
277
 
 
278
      ;; menu
 
279
      (define-key map [menu-bar] (make-sparse-keymap))
 
280
      (let ((menumap (make-sparse-keymap "View")))
 
281
        (define-key map [menu-bar headers] (cons "View" menumap))
 
282
 
 
283
        (define-key menumap [quit-buffer] '("Quit view" . mu4e-view-quit-buffer))
 
284
        (define-key menumap [display-help] '("Help" . mu4e-display-manual))
 
285
 
 
286
        (define-key menumap [sepa0] '("--"))
 
287
        (define-key menumap [wrap-lines]
 
288
          '("Toggle wrap lines" . mu4e-view-toggle-wrap-lines))
 
289
        (define-key menumap [hide-cited]
 
290
          '("Toggle hide cited" . mu4e-view-toggle-hide-cited))
 
291
        (define-key menumap [raw-view]
 
292
          '("View raw message" . mu4e-raw-view))
 
293
        (define-key menumap [pipe]
 
294
          '("Pipe through shell" . mu4e-view-pipe))
 
295
        ;; (define-key menumap [inspect]
 
296
        ;;   '("Inspect with guile" . mu4e-inspect-message))
 
297
 
 
298
        (define-key menumap [sepa8] '("--"))
 
299
        (define-key menumap [open-att]
 
300
          '("Open attachment" . mu4e-view-open-attachment))
 
301
        (define-key menumap [extract-att]
 
302
          '("Extract attachment" . mu4e-view-extract-attachment))
 
303
        (define-key menumap [goto-url]
 
304
          '("Visit URL" . mu4e-view-go-to-url))
 
305
 
 
306
        (define-key menumap [sepa1] '("--"))
 
307
        (define-key menumap [mark-delete]
 
308
          '("Mark for deletion" . mu4e-view-mark-for-delete))
 
309
        (define-key menumap [mark-trash]
 
310
          '("Mark for trash" .  mu4e-view-mark-for-trash))
 
311
        (define-key menumap [mark-move]
 
312
          '("Mark for move" . mu4e-view-mark-for-move))
 
313
 
 
314
        (define-key menumap [sepa2] '("--"))
 
315
        (define-key menumap [compose-new]  '("Compose new" . mu4e-compose-new))
 
316
        (define-key menumap [forward]  '("Forward" . mu4e-compose-forward))
 
317
        (define-key menumap [reply]  '("Reply" . mu4e-compose-reply))
 
318
        (define-key menumap [sepa3] '("--"))
 
319
 
 
320
        (define-key menumap [search]  '("Search" . mu4e-search))
 
321
        (define-key menumap [jump]  '("Jump to maildir" . mu4e-jump-to-maildir))
 
322
 
 
323
        (define-key menumap [sepa4] '("--"))
 
324
        (define-key menumap [next]  '("Next" . mu4e-view-next-header))
 
325
        (define-key menumap [previous]  '("Previous" . mu4e-view-prev-header)))
 
326
      map)))
 
327
 
 
328
(fset 'mu4e-view-mode-map mu4e-view-mode-map)
 
329
 
 
330
 
 
331
(defvar mu4e-wrap-lines nil
 
332
  "*internal* Whether to wrap lines or not (variable controlled by
 
333
  `mu4e-view-toggle-wrap-lines').")
 
334
 
 
335
(defvar mu4e-hide-cited nil
 
336
  "*internal* Whether to hide cited lines or not (the variable can
 
337
  be changed with `mu4e-view-toggle-hide-cited').")
 
338
 
 
339
 
 
340
(defun mu4e-view-mode ()
 
341
  "Major mode for viewing an e-mail message in mu4e.
 
342
 
 
343
\\{mu4e-view-mode-map}."
 
344
  (interactive)
 
345
  (kill-all-local-variables)
 
346
  (use-local-map mu4e-view-mode-map)
 
347
 
 
348
  (make-local-variable 'mu4e-hdrs-buffer)
 
349
  (make-local-variable 'mu4e-current-msg)
 
350
  (make-local-variable 'mu4e-link-map)
 
351
 
 
352
  (make-local-variable 'mu4e-wrap-lines)
 
353
  (make-local-variable 'mu4e-hide-cited)
 
354
 
 
355
  (setq
 
356
    major-mode 'mu4e-view-mode
 
357
    mode-name "mu4e-view"
 
358
    truncate-lines t
 
359
    buffer-read-only t))
 
360
 
 
361
(put 'mu4e-view-mode 'mode-class 'special)
 
362
 
 
363
 
 
364
;; we mark messages are as read when we leave the message; ie., when skipping to
 
365
;; the next/previous one, or leaving the view buffer altogether.
 
366
 
 
367
(defun mu4e-view-mark-as-read-maybe ()
 
368
  "Clear the current message's New/Unread status and set it to
 
369
Seen; if the message is not New/Unread, do nothing."
 
370
  (when mu4e-current-msg
 
371
    (let ((flags (plist-get mu4e-current-msg :flags))
 
372
           (docid (plist-get mu4e-current-msg :docid)))
 
373
      ;; is it a new message?
 
374
      (when (or (member 'unread flags) (member 'new flags))
 
375
        (mu4e-proc-flag docid "+S-u-N")))))
 
376
 
 
377
 
 
378
(defun mu4e-color-cited ()
 
379
  "Colorize message content based on the citation level."
 
380
  (save-excursion
 
381
    (let ((more-lines t))
 
382
      (goto-char (point-min))
 
383
      (while more-lines
 
384
        ;; Get the citation level at point -- ie., the number of '>'
 
385
        ;; prefixes, starting with 0 for 'no citation'
 
386
        (beginning-of-line 1)
 
387
        (let* ((text (re-search-forward "[[:word:]]" (line-end-position 1) t 1))
 
388
                (level (or (and text
 
389
                             (how-many ">" (line-beginning-position 1) text)) 0))
 
390
                (face
 
391
                  (cond
 
392
                    ((= 0 level) nil) ;; don't do anything
 
393
                    ((= 1 level) 'mu4e-cited-1-face)
 
394
                    ((= 2 level) 'mu4e-cited-2-face)
 
395
                    ((= 3 level) 'mu4e-cited-3-face)
 
396
                    ((= 4 level) 'mu4e-cited-4-face)
 
397
                    (t           nil))))
 
398
          (when face
 
399
            (add-text-properties (line-beginning-position 1)
 
400
              (line-end-position 1) `(face ,face))))
 
401
        (setq more-lines
 
402
          (and (= 0 (forward-line 1))
 
403
            ;; we need to add this weird check below; it seems in some cases
 
404
            ;; `forward-line' continues to return 0, even when at the end, which
 
405
            ;; would lead to an infinite loop
 
406
            (not (= (point-max) (line-end-position)))))))))
 
407
 
 
408
(defun mu4e-mark-footer ()
 
409
  "Give the message footers a distinctive color."
 
410
  (let ((inhibit-read-only t))
 
411
    (save-excursion
 
412
      ;; give the footer a different color...
 
413
      (goto-char (point-min))
 
414
      (let ((p (search-forward "\n-- \n" nil t)))
 
415
        (when p
 
416
          (add-text-properties p (point-max) '(face mu4e-view-footer-face)))))))
 
417
 
 
418
(defvar mu4e-link-map nil
 
419
  "*internal* A map of some number->url so we can jump to url by number.")
 
420
 
 
421
(defconst mu4e-url-regexp
 
422
  "\\(https?://[-+a-zA-Z0-9.?_$%/+&#@!~,:;=/()]+\\)"
 
423
  "*internal* regexp that matches URLs; match-string 1 will contain
 
424
  the matched URL, if any.")
 
425
 
 
426
(defun mu4e-browse-url-func (url)
 
427
  "Return a function that executes `browse-url' with URL."
 
428
  (lexical-let ((url url))
 
429
    (lambda ()
 
430
      (interactive)
 
431
      (browse-url url))))
 
432
 
 
433
 
 
434
 
 
435
;; this is fairly simplistic...
 
436
(defun mu4e-make-urls-clickable ()
 
437
  "Turn things that look like URLs into clickable things, and
 
438
number them so they can be opened using `mu4e-view-go-to-url'."
 
439
  (let ((num 0))
 
440
    (save-excursion
 
441
      (goto-char (point-min))
 
442
      (while (re-search-forward mu4e-url-regexp nil t)
 
443
        (let ((url (match-string 0))
 
444
               (map (make-sparse-keymap)))
 
445
          (define-key map [mouse-2] (mu4e-browse-url-func url))
 
446
          (define-key map [?\r] (mu4e-browse-url-func url))
 
447
          (puthash (incf num) url mu4e-link-map)
 
448
          (add-text-properties 0 (length url)
 
449
            `(face mu4e-view-link-face
 
450
               mouse-face highlight
 
451
               keymap ,map) url)
 
452
          (replace-match (concat url
 
453
                           (propertize (format "[%d]" num)
 
454
                             'face 'mu4e-view-url-number-face))))))))
 
455
 
 
456
 
 
457
;; raw mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
458
 
 
459
;; some buffer-local variables
 
460
(defvar mu4e-view-buffer nil
 
461
  "*internal* View buffer connected to this raw view.")
 
462
 
 
463
(defvar mu4e-raw-view-mode-map nil
 
464
  "Keymap for \"*mu4e-raw-view*\" buffers.")
 
465
 
 
466
(unless mu4e-raw-view-mode-map
 
467
  (setq mu4e-raw-view-mode-map
 
468
    (let ((map (make-sparse-keymap)))
 
469
 
 
470
      (define-key map "q" 'mu4e-raw-view-quit-buffer)
 
471
      (define-key map "." 'mu4e-raw-view-quit-buffer)
 
472
 
 
473
      ;; intra-message navigation
 
474
      (define-key map (kbd "SPC") 'scroll-up)
 
475
      (define-key map (kbd "<home>")
 
476
        '(lambda () (interactive) (goto-char (point-min))))
 
477
      (define-key map (kbd "<end>")
 
478
        '(lambda () (interactive) (goto-char (point-max))))
 
479
      (define-key map (kbd "RET")
 
480
        '(lambda () (interactive) (scroll-up 1)))
 
481
      (define-key map (kbd "<backspace>")
 
482
        '(lambda () (interactive) (scroll-up -1)))
 
483
 
 
484
      ;; ;; menu
 
485
      ;; (define-key map [menu-bar] (make-sparse-keymap))
 
486
      ;; (let ((menumap (make-sparse-keymap "Raw view")))
 
487
      ;;        (define-key map [menu-bar headers] (cons "Raw view" menumap))
 
488
      ;;        (define-key menumap [quit-buffer] '("Quit" .
 
489
      ;;                                             mu4e-raw-view-quit-buffer))
 
490
      map)))
 
491
 
 
492
(fset 'mu4e-raw-view-mode-map mu4e-raw-view-mode-map)
 
493
 
 
494
(defun mu4e-raw-view-mode ()
 
495
  "Major mode for viewing of raw e-mail message in mu4e.
 
496
 
 
497
\\{mu4e-raw-view-mode-map}."
 
498
  (interactive)
 
499
  (kill-all-local-variables)
 
500
  (use-local-map mu4e-raw-view-mode-map)
 
501
 
 
502
  (make-local-variable 'mu4e-view-buffer)
 
503
 
 
504
  (setq
 
505
    major-mode 'mu4e-raw-view-mode
 
506
    mode-name "mu4e-raw-view"
 
507
    truncate-lines t buffer-read-only t))
 
508
 
 
509
 
 
510
(defun mu4e-raw-view-message (msg view-buffer)
 
511
  "Display the raw contents of message MSG in a new buffer."
 
512
  (let ((buf (get-buffer-create mu4e-raw-view-buffer-name))
 
513
         (inhibit-read-only t)
 
514
         (file (plist-get msg :path)))
 
515
    (unless (and file (file-readable-p file))
 
516
      (error "Not a readable file: %S" file))
 
517
    (with-current-buffer buf
 
518
      (erase-buffer)
 
519
      (insert-file file)
 
520
      ;; initialize view-mode
 
521
      (mu4e-raw-view-mode)
 
522
      (setq mu4e-view-buffer view-buffer)
 
523
      (switch-to-buffer buf)
 
524
      (goto-char (point-min)))))
 
525
 
 
526
 
 
527
(defun mu4e-view-shell-command-on-raw-message (msg view-buffer cmd)
 
528
  "Process the raw message with shell command CMD."
 
529
  (let ((buf (get-buffer-create mu4e-raw-view-buffer-name))
 
530
         (inhibit-read-only t)
 
531
         (file (plist-get msg :path)))
 
532
    (unless (and file (file-readable-p file))
 
533
      (error "Not a readable file: %S" file))
 
534
    (with-current-buffer buf
 
535
      (erase-buffer)
 
536
      (process-file-shell-command cmd file buf)
 
537
      (mu4e-raw-view-mode)
 
538
      (setq mu4e-view-buffer view-buffer)
 
539
      (switch-to-buffer buf)
 
540
      (goto-char (point-min)))))
 
541
 
 
542
 
 
543
(defun mu4e-raw-view-quit-buffer ()
 
544
  "Quit the raw view and return to the message."
 
545
  (interactive)
 
546
  (if (buffer-live-p mu4e-view-buffer)
 
547
    (switch-to-buffer mu4e-view-buffer)
 
548
    (kill-buffer)))
 
549
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
550
 
 
551
;; functions for org-contacts
 
552
 
 
553
(defun mu4e-view-snarf-from (name-or-email)
 
554
  "Get the From:-data for the current message; NAME-OR-EMAIL should
 
555
be a symbol 'name or 'email to get the corresponding field. If the
 
556
field is not found, \"\" is returned.
 
557
 
 
558
You can use this with e.g. org-contact with a template like:
 
559
  (\"c\" \"Contacts\" entry (file \"~/Org/contacts.org\")
 
560
          \"* %(mu4e-view-snarf-from 'name)
 
561
  :PROPERTIES:
 
562
  :EMAIL: %(mu4e-view-snarf-from 'email)
 
563
  :END:\")))
 
564
 
 
565
See the `org-contacts' documentation for more details."
 
566
  ;; FIXME: we need to explictly go to some (hopefully the right!) view buffer,
 
567
  ;; since when using this from org-capture, we'll be taken to the capture
 
568
  ;; buffer instead.
 
569
  (with-current-buffer mu4e-view-buffer-name
 
570
    (unless (eq major-mode 'mu4e-view-mode)
 
571
      (error "Not in mu4e-view mode."))
 
572
    (unless mu4e-current-msg
 
573
      (error "No current message."))
 
574
    (let ((from (car-safe (plist-get mu4e-current-msg :from))))
 
575
      (cond
 
576
        ((not from) "") ;; nothing found
 
577
        ((eq name-or-email 'name)
 
578
          (or (car-safe from) ""))
 
579
        ((eq name-or-email 'email)
 
580
          (or (cdr-safe from) ""))
 
581
        (t (error "Not supported: %S" name-or-email))))))
 
582
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
583
 
 
584
 
 
585
 
 
586
;; Interactive functions
 
587
 
 
588
(defun mu4e-view-toggle-wrap-lines ()
 
589
  "Toggle line wrap in the message body."
 
590
  (interactive)
 
591
  (if mu4e-wrap-lines
 
592
    (progn
 
593
      (setq mu4e-wrap-lines nil)
 
594
      (mu4e-view-refresh)) ;; back to normal
 
595
    (save-excursion
 
596
      (let ((inhibit-read-only t))
 
597
        (setq mu4e-wrap-lines t)
 
598
        (goto-char (point-min))
 
599
        (when (search-forward "\n\n") ;; search for the message body
 
600
          (fill-region (point) (point-max)))))))
 
601
 
 
602
(defun mu4e-view-toggle-hide-cited ()
 
603
  "Toggle hiding of cited lines in the message body."
 
604
  (interactive)
 
605
  (if mu4e-hide-cited
 
606
    (progn
 
607
      (setq mu4e-hide-cited nil)
 
608
      (mu4e-view-refresh))
 
609
    (save-excursion
 
610
      (let ((inhibit-read-only t))
 
611
        (goto-char (point-min))
 
612
        (flush-lines "^[:blank:]*>")
 
613
        (setq mu4e-hide-cited t)))))
 
614
 
 
615
 
 
616
(defun mu4e-view-refresh ()
 
617
  "Redisplay the current message."
 
618
  (interactive)
 
619
  (mu4e-view mu4e-current-msg mu4e-hdrs-buffer t))
 
620
 
 
621
 
 
622
(defun mu4e-view-quit-buffer ()
 
623
  "Quit the message view and return to the headers."
 
624
  (interactive)
 
625
  (if (buffer-live-p mu4e-hdrs-buffer)
 
626
    (switch-to-buffer mu4e-hdrs-buffer)
 
627
    (kill-buffer)))
 
628
 
 
629
(defun mu4e-view-next-header ()
 
630
  "View the next header."
 
631
  (interactive)
 
632
  (when (mu4e-next-header)
 
633
    (mu4e-view-message)))
 
634
 
 
635
(defun mu4e-view-prev-header ()
 
636
  "View the previous header."
 
637
  (interactive)
 
638
  (when (mu4e-prev-header)
 
639
    (mu4e-view-message)))
 
640
 
 
641
(defun mu4e-view-mark-for-move ()
 
642
  "Mark the current message for moving."
 
643
  (interactive)
 
644
  (when (mu4e-mark-for-move)
 
645
    (mu4e-view-message)))
 
646
 
 
647
(defun mu4e-view-mark-for-trash ()
 
648
  "Mark the current message for moving to the trash folder."
 
649
  (interactive)
 
650
  (when (mu4e-mark-for-trash)
 
651
    (mu4e-view-message)))
 
652
 
 
653
(defun mu4e-view-mark-for-delete ()
 
654
  "Mark the current message for deletion."
 
655
  (interactive)
 
656
  (when (mu4e-mark-for-delete)
 
657
    (mu4e-view-message)))
 
658
 
 
659
(defun mu4e-view-extract-attachment (attnum)
 
660
  "Extract the attachment with ATTNUM."
 
661
  (unless mu4e-attachment-dir (error "`mu4e-attachment-dir' is not set"))
 
662
  (when (or (null mu4e-attach-map) (zerop (hash-table-count mu4e-attach-map)))
 
663
    (error "No attachments for this message"))
 
664
  (interactive "nAttachment to extract:")
 
665
  (let* ((att  (gethash attnum mu4e-attach-map))
 
666
          (path (and att (concat mu4e-attachment-dir
 
667
                           "/"  (plist-get att :name))))
 
668
          (id (and att (plist-get att :index)))
 
669
          (retry t))
 
670
    (unless att (error "Not a valid attachment number"))
 
671
    (while retry
 
672
      (setq path (expand-file-name (read-string "Save as " path)))
 
673
      (setq retry
 
674
        (and (file-exists-p path)
 
675
          (not (y-or-n-p (concat "Overwrite " path "?"))))))
 
676
    (mu4e-proc-save (plist-get mu4e-current-msg :docid) id path)))
 
677
 
 
678
(defun mu4e-view-open-attachment (attnum)
 
679
  "Extract the attachment with ATTNUM"
 
680
  (unless mu4e-attach-map
 
681
    (error "No attachments for this message"))
 
682
  (interactive "nAttachment to open:")
 
683
  (let* ((att (gethash attnum mu4e-attach-map))
 
684
          (id (and att (plist-get att :index))))
 
685
    (unless id (error "Not a valid attachment number"))
 
686
    (mu4e-proc-open (plist-get mu4e-current-msg :docid) id)))
 
687
 
 
688
(defun mu4e-view-unmark ()
 
689
  "Warn user that unmarking only works in the header list."
 
690
  (interactive)
 
691
  (message "Unmarking needs to be done in the header list view"))
 
692
 
 
693
 
 
694
(defun mu4e-view-marked-execute ()
 
695
  "Warn user that execution can only take place in n the header
 
696
list."
 
697
  (interactive)
 
698
  (message "Execution needs to be done in the header list view"))
 
699
 
 
700
(defun mu4e-view-go-to-url (num)
 
701
  "Go to a numbered url."
 
702
  (interactive "nGo to url with number: ")
 
703
  (let ((url (gethash num mu4e-link-map)))
 
704
    (unless url (error "Invalid number for URL"))
 
705
    (browse-url url)))
 
706
 
 
707
(defun mu4e-raw-view ()
 
708
  "Show the the raw text of the current message."
 
709
  (interactive)
 
710
  (unless mu4e-current-msg
 
711
    (error "No current message"))
 
712
  (mu4e-raw-view-message mu4e-current-msg (current-buffer)))
 
713
 
 
714
(defun mu4e-view-pipe (cmd)
 
715
  "Pipe the message through shell command CMD, and display the
 
716
results."
 
717
  (interactive "sShell command: ")
 
718
  (unless mu4e-current-msg
 
719
    (error "No current message"))
 
720
  (mu4e-view-shell-command-on-raw-message mu4e-current-msg
 
721
    (current-buffer) cmd))
 
722
 
 
723
(provide 'mu4e-view)