1
;; mu4e-view.el -- part of mu4e, the mu mail user agent
3
;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
5
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
6
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
8
;; This file is not part of GNU Emacs.
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.
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.
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/>.
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:')
32
(eval-when-compile (require 'cl))
34
;; we prefer the improved fill-region
35
(require 'filladapt nil 'noerror)
38
(defconst mu4e-view-buffer-name "*mu4e-view*"
39
"*internal* Name for the message view buffer")
41
(defconst mu4e-raw-view-buffer-name "*mu4e-raw-view*"
42
"*internal* Name for the raw message view buffer")
44
;; some buffer-local variables
45
(defvar mu4e-hdrs-buffer nil
46
"*internal* Headers buffer connected to this view.")
48
(defvar mu4e-current-msg nil
49
"*internal* The plist describing the current message.")
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))
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.
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
72
(let ((fieldname (cdr (assoc field mu4e-header-names)))
73
(fieldval (plist-get msg field)))
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) "")))
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))
87
;; if we (`user-mail-address' are the From, show To, otherwise,
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))))
99
(when fieldval (format-time-string mu4e-view-date-format
101
(if datestr (mu4e-view-header fieldname datestr) "")))
103
(:size (mu4e-view-size msg)
104
(let ((sizestr (when size (format "%d bytes"))))
105
(if sizestr (mu4e-view-header fieldname sizestr))))
107
(:attachments (mu4e-view-attachments msg))
108
(t (error "Unsupported field: %S" field)))))
111
(mu4e-body-text msg))
113
;; initialize view-mode
115
(setq ;; these are buffer-local
118
mu4e-hdrs-buffer hdrsbuf
119
mu4e-link-map (make-hash-table :size 32 :rehash-size 2 :weakness nil))
121
(switch-to-buffer buf)
122
(goto-char (point-min))
126
(mu4e-make-urls-clickable)
129
(mu4e-view-mark-as-read-maybe)))))
131
(defun mu4e-view-header (key val &optional dont-propertize-val)
132
"Show header FIELD for MSG with KEY. ie. <KEY>: value-of-FIELD."
135
(propertize key 'face 'mu4e-view-header-key-face) ": "
136
(if dont-propertize-val
138
(propertize val 'face 'mu4e-view-header-value-face))
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)))
151
(let ((name (car c)) (email (cdr c)))
153
(format "%s <%s>" name email)
154
(format "%s" email)))) lst ", "))))
156
(mu4e-view-header fieldname contacts)
159
(defvar mu4e-attach-map nil
160
"*internal* Hash which maps a number to a (part-id name mime-type).")
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))
170
(mu4e-view-open-attachment num)
171
(mu4e-view-extract-attachment num)))))
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)))
179
(setq mu4e-attach-map
180
(make-hash-table :size 32 :rehash-size 2 :weakness nil))
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)))
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))
200
(propertize (format "[%d]" id)
201
'face 'mu4e-view-attach-number-face)
203
'face 'mu4e-view-link-face
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)))))))
211
(mu4e-view-header (format "Attachments(%d)" id) vals t)))))
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)
221
(define-key map "s" 'mu4e-search)
223
(define-key map "b" 'mu4e-search-bookmark)
224
(define-key map "j" 'mu4e-jump-to-maildir)
226
(define-key map "g" 'mu4e-view-go-to-url)
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)
233
(define-key map "." 'mu4e-raw-view)
234
(define-key map "|" 'mu4e-view-pipe)
235
;; (define-key map "I" 'mu4e-inspect-message)
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)))
249
;; navigation between messages
250
(define-key map "n" 'mu4e-view-next-header)
251
(define-key map "p" 'mu4e-view-prev-header)
254
(define-key map "e" 'mu4e-view-extract-attachment)
255
(define-key map "o" 'mu4e-view-open-attachment)
258
(define-key map (kbd "<backspace>") 'mu4e-mark-for-trash)
259
(define-key map "d" 'mu4e-view-mark-for-trash)
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)
266
(define-key map "w" 'mu4e-view-toggle-wrap-lines)
267
(define-key map "h" 'mu4e-view-toggle-hide-cited)
269
(define-key map "r" 'mu4e-view-refresh)
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)
276
(define-key map "H" 'mu4e-display-manual)
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))
283
(define-key menumap [quit-buffer] '("Quit view" . mu4e-view-quit-buffer))
284
(define-key menumap [display-help] '("Help" . mu4e-display-manual))
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))
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))
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))
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] '("--"))
320
(define-key menumap [search] '("Search" . mu4e-search))
321
(define-key menumap [jump] '("Jump to maildir" . mu4e-jump-to-maildir))
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)))
328
(fset 'mu4e-view-mode-map mu4e-view-mode-map)
331
(defvar mu4e-wrap-lines nil
332
"*internal* Whether to wrap lines or not (variable controlled by
333
`mu4e-view-toggle-wrap-lines').")
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').")
340
(defun mu4e-view-mode ()
341
"Major mode for viewing an e-mail message in mu4e.
343
\\{mu4e-view-mode-map}."
345
(kill-all-local-variables)
346
(use-local-map mu4e-view-mode-map)
348
(make-local-variable 'mu4e-hdrs-buffer)
349
(make-local-variable 'mu4e-current-msg)
350
(make-local-variable 'mu4e-link-map)
352
(make-local-variable 'mu4e-wrap-lines)
353
(make-local-variable 'mu4e-hide-cited)
356
major-mode 'mu4e-view-mode
357
mode-name "mu4e-view"
361
(put 'mu4e-view-mode 'mode-class 'special)
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.
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")))))
378
(defun mu4e-color-cited ()
379
"Colorize message content based on the citation level."
381
(let ((more-lines t))
382
(goto-char (point-min))
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))
389
(how-many ">" (line-beginning-position 1) text)) 0))
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)
399
(add-text-properties (line-beginning-position 1)
400
(line-end-position 1) `(face ,face))))
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)))))))))
408
(defun mu4e-mark-footer ()
409
"Give the message footers a distinctive color."
410
(let ((inhibit-read-only t))
412
;; give the footer a different color...
413
(goto-char (point-min))
414
(let ((p (search-forward "\n-- \n" nil t)))
416
(add-text-properties p (point-max) '(face mu4e-view-footer-face)))))))
418
(defvar mu4e-link-map nil
419
"*internal* A map of some number->url so we can jump to url by number.")
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.")
426
(defun mu4e-browse-url-func (url)
427
"Return a function that executes `browse-url' with URL."
428
(lexical-let ((url url))
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'."
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
452
(replace-match (concat url
453
(propertize (format "[%d]" num)
454
'face 'mu4e-view-url-number-face))))))))
457
;; raw mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
459
;; some buffer-local variables
460
(defvar mu4e-view-buffer nil
461
"*internal* View buffer connected to this raw view.")
463
(defvar mu4e-raw-view-mode-map nil
464
"Keymap for \"*mu4e-raw-view*\" buffers.")
466
(unless mu4e-raw-view-mode-map
467
(setq mu4e-raw-view-mode-map
468
(let ((map (make-sparse-keymap)))
470
(define-key map "q" 'mu4e-raw-view-quit-buffer)
471
(define-key map "." 'mu4e-raw-view-quit-buffer)
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)))
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))
492
(fset 'mu4e-raw-view-mode-map mu4e-raw-view-mode-map)
494
(defun mu4e-raw-view-mode ()
495
"Major mode for viewing of raw e-mail message in mu4e.
497
\\{mu4e-raw-view-mode-map}."
499
(kill-all-local-variables)
500
(use-local-map mu4e-raw-view-mode-map)
502
(make-local-variable 'mu4e-view-buffer)
505
major-mode 'mu4e-raw-view-mode
506
mode-name "mu4e-raw-view"
507
truncate-lines t buffer-read-only t))
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
520
;; initialize view-mode
522
(setq mu4e-view-buffer view-buffer)
523
(switch-to-buffer buf)
524
(goto-char (point-min)))))
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
536
(process-file-shell-command cmd file buf)
538
(setq mu4e-view-buffer view-buffer)
539
(switch-to-buffer buf)
540
(goto-char (point-min)))))
543
(defun mu4e-raw-view-quit-buffer ()
544
"Quit the raw view and return to the message."
546
(if (buffer-live-p mu4e-view-buffer)
547
(switch-to-buffer mu4e-view-buffer)
549
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
551
;; functions for org-contacts
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.
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)
562
:EMAIL: %(mu4e-view-snarf-from 'email)
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
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))))
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
586
;; Interactive functions
588
(defun mu4e-view-toggle-wrap-lines ()
589
"Toggle line wrap in the message body."
593
(setq mu4e-wrap-lines nil)
594
(mu4e-view-refresh)) ;; back to normal
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)))))))
602
(defun mu4e-view-toggle-hide-cited ()
603
"Toggle hiding of cited lines in the message body."
607
(setq mu4e-hide-cited nil)
610
(let ((inhibit-read-only t))
611
(goto-char (point-min))
612
(flush-lines "^[:blank:]*>")
613
(setq mu4e-hide-cited t)))))
616
(defun mu4e-view-refresh ()
617
"Redisplay the current message."
619
(mu4e-view mu4e-current-msg mu4e-hdrs-buffer t))
622
(defun mu4e-view-quit-buffer ()
623
"Quit the message view and return to the headers."
625
(if (buffer-live-p mu4e-hdrs-buffer)
626
(switch-to-buffer mu4e-hdrs-buffer)
629
(defun mu4e-view-next-header ()
630
"View the next header."
632
(when (mu4e-next-header)
633
(mu4e-view-message)))
635
(defun mu4e-view-prev-header ()
636
"View the previous header."
638
(when (mu4e-prev-header)
639
(mu4e-view-message)))
641
(defun mu4e-view-mark-for-move ()
642
"Mark the current message for moving."
644
(when (mu4e-mark-for-move)
645
(mu4e-view-message)))
647
(defun mu4e-view-mark-for-trash ()
648
"Mark the current message for moving to the trash folder."
650
(when (mu4e-mark-for-trash)
651
(mu4e-view-message)))
653
(defun mu4e-view-mark-for-delete ()
654
"Mark the current message for deletion."
656
(when (mu4e-mark-for-delete)
657
(mu4e-view-message)))
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)))
670
(unless att (error "Not a valid attachment number"))
672
(setq path (expand-file-name (read-string "Save as " path)))
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)))
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)))
688
(defun mu4e-view-unmark ()
689
"Warn user that unmarking only works in the header list."
691
(message "Unmarking needs to be done in the header list view"))
694
(defun mu4e-view-marked-execute ()
695
"Warn user that execution can only take place in n the header
698
(message "Execution needs to be done in the header list view"))
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"))
707
(defun mu4e-raw-view ()
708
"Show the the raw text of the current message."
710
(unless mu4e-current-msg
711
(error "No current message"))
712
(mu4e-raw-view-message mu4e-current-msg (current-buffer)))
714
(defun mu4e-view-pipe (cmd)
715
"Pipe the message through shell command CMD, and display the
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))