mu/emacs/mu4e-hdrs.el

795 lines
28 KiB
EmacsLisp
Raw Normal View History

;; mu4e-hdrs.el -- part of mu4e, the mu mail user agent
2011-09-12 19:52:32 +02:00
;;
;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
2011-09-12 19:52:32 +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:
;; In this file are function related to creating the list of one-line
;; descriptions of emails, aka 'headers' (not to be confused with headers like
;; 'To:' or 'Subject:')
2011-09-18 22:57:46 +02:00
;; Code:
2011-09-12 19:52:32 +02:00
(eval-when-compile (require 'cl))
2011-12-13 08:07:38 +01:00
(require 'mu4e-proc)
2011-09-12 19:52:32 +02:00
;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-12-13 08:07:38 +01:00
(defvar mu4e-last-expr nil
2011-09-12 19:52:32 +02:00
"*internal* The most recent search expression.")
2011-12-13 08:07:38 +01:00
(defconst mu4e-hdrs-buffer-name "*mu4e-headers*"
2011-09-12 19:52:32 +02:00
"*internal* Name of the buffer for message headers.")
2011-12-13 08:07:38 +01:00
(defvar mu4e-hdrs-buffer nil
2011-09-12 19:52:32 +02:00
"*internal* Buffer for message headers")
2012-01-09 07:24:22 +01:00
(defconst mu4e-hdrs-fringe " "
"*internal* The space on the left of message headers to put marks.")
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-search (expr &optional full-search)
2011-09-12 19:52:32 +02:00
"Search in the mu database for EXPR, and switch to the output
buffer for the results. If FULL-SEARCH is non-nil return all
results, otherwise, limit number of results to
2011-12-13 08:07:38 +01:00
`mu4e-search-results-limit'."
(let ((buf (get-buffer-create mu4e-hdrs-buffer-name))
(inhibit-read-only t))
(with-current-buffer buf
(erase-buffer)
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-mode)
(setq
mode-name expr
2011-12-13 08:07:38 +01:00
mu4e-last-expr expr
mu4e-hdrs-buffer buf)))
(switch-to-buffer mu4e-hdrs-buffer)
(mu4e-proc-find expr ;; '-1' means 'unlimited search'
(if full-search -1 mu4e-search-results-limit)))
2011-09-12 19:52:32 +02:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; handler functions
;;
2011-12-13 08:07:38 +01:00
;; next are a bunch of handler functions; those will be called from mu4e-proc in
;; response to output from the server process
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-view-handler (msg)
"Handler function for displaying a message."
2011-12-13 08:07:38 +01:00
(mu4e-view msg mu4e-hdrs-buffer))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-error-handler (err)
"Handler function for showing an error."
(let ((errcode (plist-get err :error))
(errmsg (plist-get err :error-message)))
(case errcode
(4 (message "No matches for this search query."))
2011-12-23 18:09:03 +01:00
(t (message (format "Error %d: %s" errcode errmsg))))))
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-update-handler (msg is-move)
"Update handler, will be called when a message has been updated
in the database. This function will update the current list of
headers."
2011-12-13 08:07:38 +01:00
(when (buffer-live-p mu4e-hdrs-buffer)
(with-current-buffer mu4e-hdrs-buffer
(let* ((docid (plist-get msg :docid))
2011-12-13 08:07:38 +01:00
(marker (gethash docid mu4e-msg-map))
2011-09-22 20:01:35 +02:00
(point (when marker (marker-position marker))))
(when point ;; is the message present in this list?
2011-09-30 07:37:47 +02:00
;; if it's marked, unmark it now
2011-12-13 08:07:38 +01:00
(when (mu4e-hdrs-docid-is-marked docid) (mu4e-hdrs-mark 'unmark))
2011-09-30 07:37:47 +02:00
;; first, remove the old one (otherwise, we'd have to headers with
;; the same docid...
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-remove-handler docid)
2011-11-20 09:31:38 +01:00
2011-12-13 08:07:38 +01:00
;; if we we're actually viewing this message (in mu4e-view mode), we
;; update the `mu4e-current-msg' there as well; that way, the flags can
2011-11-20 09:31:38 +01:00
;; be updated, as well as the path (which is useful for viewing the
;; raw message)
2011-12-13 08:07:38 +01:00
(let ((viewbuf (get-buffer mu4e-view-buffer-name)))
2011-11-20 09:31:38 +01:00
(when (and viewbuf (buffer-live-p viewbuf))
(with-current-buffer viewbuf
2011-12-13 08:07:38 +01:00
(when (eq docid (plist-get mu4e-current-msg :docid))
(setq mu4e-current-msg msg)))))
2011-11-20 09:31:38 +01:00
2011-09-30 07:37:47 +02:00
;; now, if this update was about *moving* a message, we don't show it
;; anymore (of course, we cannot be sure if the message really no
;; longer matches the query, but this seem a good heuristic.
;; if it was only a flag-change, show the message with its updated flags.
(unless is-move
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-header-handler msg point)))))))
2011-09-30 07:37:47 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-remove-handler (docid)
"Remove handler, will be called when a message has been removed
from the database. This function will hide the remove message in
the current list of headers."
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(let* ((marker (gethash docid mu4e-msg-map))
2011-09-30 07:37:47 +02:00
(pos (and marker (marker-position marker)))
2011-12-13 08:07:38 +01:00
(docid-at-pos (and pos (mu4e-hdrs-get-docid pos))))
(unless marker (error "Message %d not found" docid))
2011-09-30 07:37:47 +02:00
(unless (eq docid docid-at-pos)
(error "At point %d, expected docid %d, but got %d"
pos docid docid-at-pos))
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-remove-header docid pos))))
2011-09-12 19:52:32 +02:00
2011-11-23 23:15:34 +01:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-09-20 22:59:20 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-contact-str (contacts)
2011-11-09 07:35:24 +01:00
"Turn the list of contacts CONTACTS (with elements (NAME . EMAIL)
into a string."
(mapconcat
(lambda (ct)
(let ((name (car ct)) (email (cdr ct)))
(or name email "?"))) contacts ", "))
2011-12-13 08:07:38 +01:00
(defun mu4e-thread-prefix (thread)
"Calculate the thread prefix based on thread info THREAD."
(if thread
(let ( (level (plist-get thread :level))
(first-child (plist-get thread :first-child))
(has-child (plist-get thread :has-child))
(duplicate (plist-get thread :duplicate))
(empty-parent (plist-get thread :empty-parent)))
(concat
(make-string (* (if empty-parent 0 2) level) ?\s)
(cond
(has-child "+ ")
(empty-parent "- ")
(first-child "\\ ")
(duplicate "= ")
(t "| "))))))
;; FIXME: when updating an header line, we don't know the thread
;; stuff
2011-11-09 07:35:24 +01:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-header-handler (msg &optional point)
2011-09-30 07:37:47 +02:00
"Create a one line description of MSG in this buffer, at POINT,
if provided, or at the end of the buffer otherwise."
(let* ( (docid (plist-get msg :docid))
(thread-info
2011-12-13 08:07:38 +01:00
(or (plist-get msg :thread) (gethash docid mu4e-thread-info-map)))
(line
2011-11-13 11:44:54 +01:00
(mapconcat
(lambda (f-w)
(let* ((field (car f-w)) (width (cdr f-w))
(val (plist-get msg field))
(str
(case field
2011-12-13 08:07:38 +01:00
(:subject (concat (mu4e-thread-prefix thread-info) val))
((:maildir :path) val)
2011-12-13 08:07:38 +01:00
((:to :from :cc :bcc) (mu4e-hdrs-contact-str val))
;; if we (ie. `user-mail-address' is the 'From', show
;; 'To', otherwise show From
2011-11-13 11:44:54 +01:00
(:from-or-to
(let* ((from-lst (plist-get msg :from))
(from (and from-lst (cdar from-lst))))
(if (and from (string-match
2011-12-13 08:07:38 +01:00
mu4e-user-mail-address-regexp from))
2011-11-13 11:44:54 +01:00
(concat "To "
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-contact-str (plist-get msg :to)))
(mu4e-hdrs-contact-str from-lst))))
(:date (format-time-string mu4e-headers-date-format val))
(:flags (mu4e-flags-to-string val))
(:size (mu4e-display-size val))
(t (error "Unsupported header field (%S)" field)))))
2011-11-13 11:44:54 +01:00
(when str
(if (not width)
str
(truncate-string-to-width str width 0 ?\s t)))))
2011-12-13 08:07:38 +01:00
mu4e-headers-fields " "))
2011-09-20 22:59:20 +02:00
(flags (plist-get msg :flags))
(line (cond
2011-10-23 23:20:32 +02:00
((member 'draft flags)
2011-12-13 08:07:38 +01:00
(propertize line 'face 'mu4e-draft-face 'draft t))
2011-10-23 23:20:32 +02:00
((member 'trashed flags)
2011-12-13 08:07:38 +01:00
(propertize line 'face 'mu4e-trashed-face))
2011-10-23 23:20:32 +02:00
((member 'unread flags)
2011-12-13 08:07:38 +01:00
(propertize line 'face 'mu4e-unread-face))
2011-10-23 23:20:32 +02:00
(t ;; else
2011-12-13 08:07:38 +01:00
(propertize line 'face 'mu4e-header-face)))))
2011-11-20 09:31:38 +01:00
;; store the thread info, so we can use it when updating the message
(when thread-info
2011-12-13 08:07:38 +01:00
(puthash docid thread-info mu4e-thread-info-map))
(mu4e-hdrs-add-header line (plist-get msg :docid)
2011-09-30 07:37:47 +02:00
(if point point (point-max)))))
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-found-handler (count)
"Create a one line description of the number of headers found
after the end of the search results."
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(save-excursion
(goto-char (point-max))
(let ((inhibit-read-only t))
(insert (propertize
(case count
(0 "No matching messages found")
2011-10-26 21:00:08 +02:00
;; note, don't show the number so we don't have to update it
;; when we delete messsages...
(otherwise "End of search results"))
;; (1 "Found 1 message")
;; (otherwise (format "Found %d messages" count)))
2011-12-13 08:07:38 +01:00
'face 'mu4e-system-face 'intangible t))))))
2011-09-12 19:52:32 +02:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; hdrs-mode and mode-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-12-13 08:07:38 +01:00
(setq mu4e-hdrs-mode-map nil)
(defvar mu4e-hdrs-mode-map nil
"Keymap for *mu4e-headers* buffers.")
(unless mu4e-hdrs-mode-map
(setq mu4e-hdrs-mode-map
(let ((map (make-sparse-keymap)))
2011-12-13 08:07:38 +01:00
(define-key map "s" 'mu4e-search)
2011-12-23 18:09:03 +01:00
2011-12-13 08:07:38 +01:00
(define-key map "b" 'mu4e-search-bookmark)
2011-12-13 08:07:38 +01:00
(define-key map "q" 'mu4e-quit-buffer)
2011-12-15 20:47:01 +01:00
(define-key map "r" 'mu4e-rerun-search)
(define-key map "g" 'mu4e-rerun-search) ;; for compatibility
;; navigation
2011-12-13 08:07:38 +01:00
(define-key map "n" 'mu4e-next-header)
(define-key map "p" 'mu4e-prev-header)
2011-11-05 11:29:07 +01:00
;; marking/unmarking/executing
2011-12-13 08:07:38 +01:00
(define-key map (kbd "<backspace>") 'mu4e-mark-for-trash)
(define-key map "d" 'mu4e-mark-for-trash)
2011-12-13 08:07:38 +01:00
(define-key map (kbd "<delete>") 'mu4e-mark-for-delete)
(define-key map "D" 'mu4e-mark-for-delete)
2011-12-13 08:07:38 +01:00
(define-key map "j" 'mu4e-jump-to-maildir)
(define-key map "m" 'mu4e-mark-for-move)
2011-12-13 08:07:38 +01:00
(define-key map "u" 'mu4e-unmark)
(define-key map "U" 'mu4e-unmark-all)
(define-key map "x" 'mu4e-execute-marks)
;; message composition
(define-key map "R" 'mu4e-compose-reply)
(define-key map "F" 'mu4e-compose-forward)
(define-key map "C" 'mu4e-compose-new)
(define-key map "E" 'mu4e-edit-draft)
2011-12-13 08:07:38 +01:00
(define-key map (kbd "RET") 'mu4e-view-message)
(define-key map [mouse-2] 'mu4e-view-message)
(define-key map "H" 'mu4e-display-manual)
2011-12-23 18:09:03 +01:00
;; menu
(define-key map [menu-bar] (make-sparse-keymap))
(let ((menumap (make-sparse-keymap "Headers")))
(define-key map [menu-bar headers] (cons "Headers" menumap))
2011-12-13 08:07:38 +01:00
(define-key menumap [quit-buffer] '("Quit view" . mu4e-quit-buffer))
(define-key menumap [display-help] '("Help" . mu4e-display-manual))
(define-key menumap [sepa0] '("--"))
2011-12-13 08:07:38 +01:00
(define-key menumap [execute-marks] '("Execute marks" . mu4e-execute-marks))
(define-key menumap [unmark-all] '("Unmark all" . mu4e-unmark-all))
(define-key menumap [unmark] '("Unmark" . mu4e-unmark))
(define-key menumap [mark-delete] '("Mark for deletion" . mu4e-mark-for-delete))
(define-key menumap [mark-trash] '("Mark for trash" . mu4e-mark-for-trash))
(define-key menumap [mark-move] '("Mark for move" . mu4e-mark-for-move))
(define-key menumap [sepa1] '("--"))
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))
(define-key menumap [sepa2] '("--"))
2011-12-13 08:07:38 +01:00
(define-key menumap [refresh] '("Refresh" . mu4e-rerun-search))
(define-key menumap [search] '("Search" . mu4e-search))
2011-12-13 08:07:38 +01:00
(define-key menumap [jump] '("Jump to maildir" . mu4e-jump-to-maildir))
(define-key menumap [sepa3] '("--"))
2011-12-13 08:07:38 +01:00
(define-key menumap [view] '("View" . mu4e-view-message))
(define-key menumap [next] '("Next" . mu4e-next-header))
(define-key menumap [previous] '("Previous" . mu4e-prev-header))
(define-key menumap [sepa4] '("--")))
2011-12-13 08:07:38 +01:00
;;(define-key menumap [draft] '("Edit draft" . mu4e-compose-new))
map)))
2011-12-13 08:07:38 +01:00
(fset 'mu4e-hdrs-mode-map mu4e-hdrs-mode-map)
2011-09-12 19:52:32 +02:00
;; we register our handler functions for the mu4e-proc (mu server) output
(setq mu4e-proc-error-func 'mu4e-hdrs-error-handler)
(setq mu4e-proc-update-func 'mu4e-hdrs-update-handler)
(setq mu4e-proc-header-func 'mu4e-hdrs-header-handler)
(setq mu4e-proc-found-func 'mu4e-hdrs-found-handler)
(setq mu4e-proc-view-func 'mu4e-hdrs-view-handler)
(setq mu4e-proc-remove-func 'mu4e-hdrs-remove-handler)
;; this last one is defined in mu4e-send.el
(setq mu4e-proc-compose-func 'mu4e-send-compose-handler)
2012-01-09 07:24:22 +01:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-mode ()
2011-09-12 19:52:32 +02:00
"Major mode for displaying mua search results."
(interactive)
(kill-all-local-variables)
2011-12-13 08:07:38 +01:00
(use-local-map mu4e-hdrs-mode-map)
(make-local-variable 'mu4e-last-expr)
(make-local-variable 'mu4e-hdrs-proc)
(make-local-variable 'mu4e-marks-map)
(make-local-variable 'mu4e-msg-map)
(make-local-variable 'mu4e-thread-info-map)
2011-09-12 19:52:32 +02:00
(setq
2011-12-13 08:07:38 +01:00
mu4e-marks-map (make-hash-table :size 16 :rehash-size 2)
mu4e-msg-map (make-hash-table :size 1024 :rehash-size 2 :weakness nil)
mu4e-thread-info-map (make-hash-table :size 512 :rehash-size 2)
major-mode 'mu4e-hdrs-mode
mode-name "mm: message headers"
2011-09-12 19:52:32 +02:00
truncate-lines t
2011-12-23 18:09:03 +01:00
buffer-undo-list t ;; don't record undo information
2011-09-12 19:52:32 +02:00
buffer-read-only t
2011-11-05 09:26:24 +01:00
overwrite-mode 'overwrite-mode-binary)
2011-11-05 09:26:24 +01:00
(setq header-line-format
(cons
2012-01-09 07:24:22 +01:00
(make-string
(+ (length mu4e-hdrs-fringe) (floor (fringe-columns 'left t))) ?\s)
2011-11-05 09:26:24 +01:00
(map 'list
(lambda (item)
2011-12-13 08:07:38 +01:00
(let ((field (cdr (assoc (car item) mu4e-header-names)))
2011-11-05 09:26:24 +01:00
(width (cdr item)))
(concat
(propertize
(if width
(truncate-string-to-width field width 0 ?\s t)
field)
2011-12-13 08:07:38 +01:00
'face 'mu4e-title-face) " ")))
mu4e-headers-fields))))
(put 'mu4e-hdrs-mode 'mode-class 'special)
2011-11-05 09:26:24 +01:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-12-13 08:07:38 +01:00
(defvar mu4e-msg-map nil
2011-09-12 19:52:32 +02:00
"*internal* A map (hashtable) which maps a database (Xapian)
docid (which uniquely identifies a message to a marker. where
marker points to the buffer position for the message.
Using this map, we can update message headers which are currently
on the screen, when we receive (:update ) notices from the mu
server.")
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-add-header (str docid point)
2011-10-23 23:20:32 +02:00
"Add header STR with DOCID to the buffer at POINT."
2011-09-20 22:59:20 +02:00
(unless docid (error "Invalid message"))
2011-12-13 08:07:38 +01:00
(when (buffer-live-p mu4e-hdrs-buffer)
(with-current-buffer mu4e-hdrs-buffer
2011-09-30 07:37:47 +02:00
(let ((inhibit-read-only t))
2011-09-20 22:59:20 +02:00
(save-excursion
(goto-char point)
2011-12-13 08:07:38 +01:00
;; Update `mu4e-msg-map' with MSG, and MARKER pointing to the buffer
2011-09-20 22:59:20 +02:00
;; position for the message header."
2012-01-09 07:24:22 +01:00
(insert (propertize (concat mu4e-hdrs-fringe str "\n") 'docid docid))
;; note: this maintaining the hash with the markers makes things slow
;; when there are many (say > 1000) headers. this seems to be mostly
;; in the use of markers. we use those to find messages when they need
;; to be updated.
2011-12-13 08:07:38 +01:00
(puthash docid (copy-marker point t) mu4e-msg-map))))))
2011-09-20 22:59:20 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-remove-header (docid point)
2011-09-22 20:01:35 +02:00
"Remove header with DOCID at POINT."
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
2011-09-30 07:37:47 +02:00
(goto-char point)
;; sanity check
2011-12-13 08:07:38 +01:00
(unless (eq docid (mu4e-hdrs-get-docid))
2011-09-30 07:37:47 +02:00
(error "%d: Expected %d, but got %d"
2011-12-13 08:07:38 +01:00
(line-number-at-pos) docid (mu4e-hdrs-get-docid)))
2011-09-30 07:37:47 +02:00
(let ((inhibit-read-only t))
;; (put-text-property (line-beginning-position line-beginning-positio 2)
;; 'invisible t))
(delete-region (line-beginning-position) (line-beginning-position 2)))
2011-12-13 08:07:38 +01:00
(remhash docid mu4e-msg-map)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-mark-header (docid mark)
2011-09-20 22:59:20 +02:00
"(Visually) mark the header for DOCID with character MARK."
2011-12-13 08:07:38 +01:00
(let ((marker (gethash docid mu4e-msg-map)))
2011-09-22 20:01:35 +02:00
;; (unless marker (error "Unregistered message"))
(when marker
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
2011-09-22 20:01:35 +02:00
(save-excursion
(let ((inhibit-read-only t) (pos (marker-position marker)))
(goto-char pos)
(delete-char 2)
2011-12-13 08:07:38 +01:00
(insert (propertize mark 'face 'mu4e-hdrs-marks-face) " ")
2011-09-22 20:01:35 +02:00
(put-text-property pos
2011-09-30 07:37:47 +02:00
(line-beginning-position 2) 'docid docid)
;; update the msg-map, ie., move it back to the start of the line
(puthash docid
(copy-marker (line-beginning-position) t)
2011-12-13 08:07:38 +01:00
mu4e-msg-map)))))))
2011-09-30 07:37:47 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-get-docid (&optional point)
2011-09-30 07:37:47 +02:00
"Get the docid for the message at POINT, if provided, or (point), otherwise."
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
2011-09-30 07:37:47 +02:00
(get-text-property (if point point (point)) 'docid)))
2011-12-13 08:07:38 +01:00
(defun mu4e-dump-msg-map ()
2011-09-30 07:37:47 +02:00
"*internal* dump the message map (for debugging)."
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(message "msg-map (%d)" (hash-table-count mu4e-msg-map))
2011-09-30 07:37:47 +02:00
(maphash
(lambda (k v)
(message "%s => %s" k v))
2011-12-13 08:07:38 +01:00
mu4e-msg-map)))
2011-09-12 19:52:32 +02:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; threadinfo-map ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-12-13 08:07:38 +01:00
(defvar mu4e-thread-info-map nil
"Map (hash) of docid->threadinfo; when filling the list of
messages, we fill a map of thread info, such that when a header
changes (e.g., it's read-flag gets set) through some (:update
...) message, we can restore the thread-info (this is needed
since :update messages do not include thread info).")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-09-12 19:52:32 +02:00
;;; marks ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-12-13 08:07:38 +01:00
(defvar mu4e-marks-map nil
2011-09-12 19:52:32 +02:00
"Map (hash) of docid->markinfo; when a message is marked, the
information is added here.
markinfo is a list consisting of the following:
\(marker mark target)
where
MARKER is an emacs-textmarker pointing to the beginning of the header line
MARK is the type of mark (move, trash, delete)
TARGET (optional) is the target directory (for 'move')")
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-mark-message (mark &optional target)
2011-09-30 07:37:47 +02:00
"Mark (or unmark) message at point. MARK specifies the
2011-09-12 19:52:32 +02:00
mark-type. For `move'-marks there is also the TARGET argument,
which specifies to which maildir the message is to be moved.
The following marks are available, and the corresponding props:
MARK TARGET description
----------------------------------------------------------
`move' y move the message to some folder
2011-12-13 08:07:38 +01:00
`trash' n move the message to `mu4e-trash-folder'
2011-09-12 19:52:32 +02:00
`delete' n remove the message
`unmark' n unmark this message"
2011-12-13 08:07:38 +01:00
(let* ((docid (mu4e-hdrs-get-docid))
2011-09-12 19:52:32 +02:00
(markkar
(case mark ;; the visual mark
('move "m")
('trash "d")
('delete "D")
2011-09-22 20:01:35 +02:00
('select "*")
2011-09-12 19:52:32 +02:00
('unmark " ")
(t (error "Invalid mark %S" mark)))))
(unless docid (error "No message on this line"))
(save-excursion
2011-12-13 08:07:38 +01:00
(when (mu4e-hdrs-mark-header docid markkar))
2011-09-22 20:01:35 +02:00
;; update the hash -- remove everything current, and if add the new stuff,
;; unless we're unmarking
2011-12-13 08:07:38 +01:00
(remhash docid mu4e-marks-map)
;; remove possible overlays
2011-12-12 07:25:14 +01:00
(remove-overlays (line-beginning-position) (line-end-position))
;; now, let's set a mark (unless we were unmarking)
2011-09-22 20:01:35 +02:00
(unless (eql mark 'unmark)
2011-12-13 08:07:38 +01:00
(puthash docid (list (point-marker) mark target) mu4e-marks-map)
;; when we have a target (ie., when moving), show the target folder in
;; an overlay
(when target
(let* ((targetstr (propertize (concat "-> " target " ")
2011-12-13 08:07:38 +01:00
'face 'mu4e-system-face))
2011-11-13 11:44:54 +01:00
(start (+ 2 (line-beginning-position))) ;; +2 for the marker fringe
(overlay (make-overlay start (+ start (length targetstr)))))
(overlay-put overlay 'display targetstr)))))))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-mark (mark &optional target)
2011-09-30 07:37:47 +02:00
"Mark the header at point, or, if
region is active, mark all headers in the region. Als see
2011-12-13 08:07:38 +01:00
`mu4e-hdrs-mark-message'."
(with-current-buffer mu4e-hdrs-buffer
2011-09-30 07:37:47 +02:00
(if (use-region-p)
;; mark all messages in the region.
(save-excursion
(let ((b (region-beginning)) (e (region-end)))
(goto-char b)
(while (<= (line-beginning-position) e)
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-mark-message mark target)
2011-09-30 07:37:47 +02:00
(forward-line 1))))
;; just a single message
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-mark-message mark target))))
2011-09-30 07:37:47 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-marks-execute ()
2011-09-12 19:52:32 +02:00
"Execute the actions for all marked messages in this
2011-09-20 22:59:20 +02:00
buffer. After the actions have been executed succesfully, the
affected messages are *hidden* from the current header list. Since
the headers are the result of a search, we cannot be certain that
the messages no longer matches the current one - to get that
certainty, we need to rerun the search, but we don't want to do
that automatically, as it may be too slow and/or break the users
2011-09-12 19:52:32 +02:00
flow. Therefore, we hide the message, which in practice seems to
work well."
2011-12-13 08:07:38 +01:00
(if (= 0 (hash-table-count mu4e-marks-map))
(message "Nothing is marked")
(maphash
(lambda (docid val)
2011-09-20 22:59:20 +02:00
(let ((marker (nth 0 val)) (mark (nth 1 val)) (target (nth 2 val)))
(case mark
(move
2011-12-13 08:07:38 +01:00
(mu4e-proc-move-msg docid target))
2011-09-20 22:59:20 +02:00
(trash
2011-12-13 08:07:38 +01:00
(unless mu4e-trash-folder
(error "`mu4e-trash-folder' not set"))
(mu4e-proc-move-msg docid mu4e-trash-folder "+T"))
2011-09-20 22:59:20 +02:00
(delete
2011-12-13 08:07:38 +01:00
(mu4e-proc-remove-msg docid)))))
mu4e-marks-map)
(mu4e-hdrs-unmark-all)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-unmark-all ()
2011-09-12 19:52:32 +02:00
"Unmark all marked messages."
2011-12-13 08:07:38 +01:00
(unless (/= 0 (hash-table-count mu4e-marks-map))
2011-09-12 19:52:32 +02:00
(error "Nothing is marked"))
(maphash
(lambda (docid val)
(save-excursion
(goto-char (marker-position (nth 0 val)))
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-mark 'unmark)))
mu4e-marks-map))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-view ()
"View message at point."
2011-12-13 08:07:38 +01:00
(let ((docid (mu4e-hdrs-get-docid)))
2011-09-12 19:52:32 +02:00
(unless docid (error "No message at point."))
2011-12-13 08:07:38 +01:00
(mu4e-proc-view-msg docid)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-compose (compose-type)
2011-09-30 07:37:47 +02:00
"Compose either a reply/forward based on the message at point. or
start editing it. COMPOSE-TYPE is either `reply', `forward' or
2011-10-02 20:35:03 +02:00
`edit'."
(if (eq compose-type 'new)
2011-12-13 08:07:38 +01:00
(mu4e-send-compose-handler 'new)
(let ((docid (mu4e-hdrs-get-docid))
2011-10-23 23:20:32 +02:00
;; note, the first two chars of the line (the mark margin) does *not*
;; have the 'draft property; thus, we check one char before the end of
;; the current line instead
(is-draft (get-text-property (- (line-end-position) 1) 'draft)))
2011-10-10 07:38:14 +02:00
(unless docid
2011-10-02 20:35:03 +02:00
(error "No message at point."))
(cond
((member compose-type '(reply forward))
2011-12-13 08:07:38 +01:00
(mu4e-proc-compose compose-type docid))
2011-10-02 20:35:03 +02:00
((eq compose-type 'edit)
2011-10-23 23:20:32 +02:00
(unless is-draft
(error "Cannot edit a non-draft message"))
2011-12-13 08:07:38 +01:00
(mu4e-proc-compose 'edit docid))
2011-10-02 20:35:03 +02:00
(t (error "invalid compose type %S" compose-type))))))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-hdrs-docid-is-marked (docid)
"Is the given docid marked?"
2011-12-13 08:07:38 +01:00
(when (gethash docid mu4e-marks-map) t))
2011-09-12 19:52:32 +02:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-12-13 08:07:38 +01:00
(defun mu4e-ignore-marks ()
"If there are still marks in the header list, warn the user."
(if mu4e-marks-map
(let* ((num (hash-table-count mu4e-marks-map))
(unmark (or (= 0 num)
(y-or-n-p
(format "Sure you want to unmark %d message(s)?" num)))))
(message nil)
unmark))
t)
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-search (expr)
"Start a new mu search. If prefix ARG is nil, limit the number of
results to `mu4e-search-results-limit', otherwise show all. In
other words, use the C-u prefix to get /all/ results, otherwise get
up to `mu4e-search-results-limit' much quicker."
(interactive "s[mu] search for: ")
2011-12-13 08:07:38 +01:00
(when (mu4e-ignore-marks)
(mu4e-hdrs-search expr current-prefix-arg)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-search-bookmark ()
"Search using some bookmarked query. With C-u prefix, show /all/ results, otherwise,
limit to up to `mu4e-search-results-limit'."
(interactive)
2011-12-13 08:07:38 +01:00
(let ((query (mu4e-ask-bookmark "Bookmark: ")))
(when query
(mu4e-hdrs-search query current-prefix-arg))))
2011-12-13 08:07:38 +01:00
(defun mu4e-quit-buffer ()
2011-09-12 19:52:32 +02:00
"Quit the current buffer."
(interactive)
2011-12-13 08:07:38 +01:00
(when (mu4e-ignore-marks)
(mu4e-kill-proc) ;; hmmm...
(kill-buffer)
2011-12-14 08:13:10 +01:00
(mu4e)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-rerun-search ()
2011-09-12 19:52:32 +02:00
"Rerun the search for the last search expression; if none exists,
do a new search."
(interactive)
2011-12-13 08:07:38 +01:00
(when (mu4e-ignore-marks)
(if mu4e-last-expr
(mu4e-hdrs-search mu4e-last-expr)
(mu4e-search))))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-view-message ()
2011-09-12 19:52:32 +02:00
"View the message at point."
(interactive)
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-view))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-next-header ()
"Move point to the next message header. If this succeeds, return
the new docid. Otherwise, return nil."
2011-09-12 19:52:32 +02:00
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
2011-09-30 07:37:47 +02:00
(when (= 0 (forward-line 1))
2011-12-13 08:07:38 +01:00
(or (mu4e-hdrs-get-docid) (mu4e-next-header)) ;; skip non-headers
2011-11-05 11:29:07 +01:00
;; trick to move point, even if this function is called when this window
;; is not visible
2011-12-13 08:07:38 +01:00
(set-window-point (get-buffer-window mu4e-hdrs-buffer) (point)))))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-prev-header ()
"Move point to the previous message header. If this succeeds,
return the new docid. Otherwise, return nil."
2011-09-12 19:52:32 +02:00
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
2011-09-30 07:37:47 +02:00
(when (= 0 (forward-line -1))
2011-12-13 08:07:38 +01:00
(or (mu4e-hdrs-get-docid) (mu4e-prev-header)) ;; skip non-headers
2011-11-05 11:29:07 +01:00
;; trick to move point, even if this function is called when this window
;; is not visible
2011-12-13 08:07:38 +01:00
(set-window-point (get-buffer-window mu4e-hdrs-buffer) (point)))))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-jump-to-maildir ()
2011-11-05 11:29:07 +01:00
"Show the messages in maildir TARGET. If TARGET is not provided,
ask user for it. With C-u prefix, show /all/ results, otherwise,
limit to up to `mu4e-search-results-limit'."
2011-09-12 19:52:32 +02:00
(interactive)
2011-12-13 08:07:38 +01:00
(let ((fld (mu4e-ask-maildir "Jump to maildir: ")))
(when fld
(mu4e-hdrs-search (concat "maildir:" fld) current-prefix-arg))))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-mark-for-move (&optional target)
2011-11-05 09:26:24 +01:00
"Mark message at point for moving to maildir TARGET. If target is
not provided, function asks for it."
2011-09-12 19:52:32 +02:00
(interactive)
(unless (mu4e-hdrs-get-docid)
(error "No message at point."))
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(let* ((target (or target (mu4e-ask-maildir "Move message to: ")))
(target (if (string= (substring target 0 1) "/")
target
(concat "/" target)))
2011-12-13 08:07:38 +01:00
(fulltarget (concat mu4e-maildir target)))
2011-09-20 22:59:20 +02:00
(when (or (file-directory-p fulltarget)
(and (yes-or-no-p
(format "%s does not exist. Create now?" fulltarget))
2011-12-13 08:07:38 +01:00
(mu4e-proc-mkdir fulltarget)))
(mu4e-hdrs-mark 'move target)
2011-12-13 08:07:38 +01:00
(mu4e-next-header)))))
2011-09-12 19:52:32 +02:00
2011-11-05 09:26:24 +01:00
2011-12-13 08:07:38 +01:00
(defun mu4e-mark-for-trash ()
2011-09-12 19:52:32 +02:00
"Mark message at point for moving to the trash
2011-12-13 08:07:38 +01:00
folder (`mu4e-trash-folder')."
2011-09-12 19:52:32 +02:00
(interactive)
2011-12-13 08:07:38 +01:00
(unless mu4e-trash-folder
(error "`mu4e-trash-folder' is not set"))
(with-current-buffer mu4e-hdrs-buffer
(mu4e-hdrs-mark 'trash)
(mu4e-next-header)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-mark-for-delete ()
2011-09-12 19:52:32 +02:00
"Mark message at point for direct deletion."
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(mu4e-hdrs-mark 'delete)
(mu4e-next-header)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-unmark ()
2011-09-12 19:52:32 +02:00
"Unmark message at point."
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(mu4e-hdrs-mark 'unmark)
(mu4e-next-header)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-unmark-all ()
2011-09-12 19:52:32 +02:00
"Unmark all messages."
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(if (= 0 (hash-table-count mu4e-marks-map))
2011-09-20 22:59:20 +02:00
(message "Nothing is marked")
2011-12-13 08:07:38 +01:00
(when (mu4e-ignore-marks)
(mu4e-hdrs-unmark-all)))))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-execute-marks ()
2011-09-12 19:52:32 +02:00
"Execute the actions for the marked messages."
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(if (= 0 (hash-table-count mu4e-marks-map))
2011-09-20 22:59:20 +02:00
(message "Nothing is marked")
(when (y-or-n-p (format "Sure you want to execute marks on %d message(s)?"
2011-12-13 08:07:38 +01:00
(hash-table-count mu4e-marks-map)))
(mu4e-hdrs-marks-execute)
2011-09-20 22:59:20 +02:00
(message nil)))))
2011-12-13 08:07:38 +01:00
(defun mu4e-compose-reply ()
"Start composing a reply to the current message."
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(mu4e-hdrs-compose 'reply)))
2011-12-13 08:07:38 +01:00
(defun mu4e-compose-forward ()
"Start composing a forward to the current message."
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(mu4e-hdrs-compose 'forward)))
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-compose-new ()
2011-10-02 20:35:03 +02:00
"Compose a new, empty message."
(interactive)
2011-12-13 08:07:38 +01:00
(mu4e-hdrs-compose 'new))
2011-10-02 20:35:03 +02:00
2011-12-13 08:07:38 +01:00
(defun mu4e-edit-draft ()
2011-09-30 07:37:47 +02:00
"Start editing the existing draft message at point."
(interactive)
2011-12-13 08:07:38 +01:00
(with-current-buffer mu4e-hdrs-buffer
(mu4e-hdrs-compose 'edit)))
2011-10-02 20:35:03 +02:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2011-09-12 19:52:32 +02:00
2011-12-13 08:07:38 +01:00
(provide 'mu4e-hdrs)