2012-04-09 10:50:23 +02:00
|
|
|
|
;;; mu4e-utils.el -- part of mu4e, the mu mail user agent
|
|
|
|
|
;;
|
|
|
|
|
;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
|
2013-03-10 20:58:05 +01:00
|
|
|
|
;; Copyright (C) 2013 Tibor Simko
|
2012-04-09 10:50:23 +02:00
|
|
|
|
|
|
|
|
|
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
2012-04-10 23:33:23 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
;; 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:
|
|
|
|
|
|
|
|
|
|
;; Utility functions used in the mu4e
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
2012-06-10 15:14:21 +02:00
|
|
|
|
(eval-when-compile (byte-compile-disable-warning 'cl-functions))
|
2012-04-09 15:34:52 +02:00
|
|
|
|
(require 'cl)
|
2012-06-10 15:14:21 +02:00
|
|
|
|
|
2012-10-16 09:59:11 +02:00
|
|
|
|
(eval-when-compile (require 'org nil 'noerror))
|
2012-10-16 09:58:27 +02:00
|
|
|
|
|
2012-04-11 21:31:52 +02:00
|
|
|
|
(require 'mu4e-vars)
|
2012-06-15 10:00:21 +02:00
|
|
|
|
(require 'mu4e-about)
|
2012-12-18 22:29:17 +01:00
|
|
|
|
(require 'mu4e-lists)
|
2012-04-22 10:35:11 +02:00
|
|
|
|
(require 'doc-view)
|
2013-06-29 12:07:32 +02:00
|
|
|
|
|
|
|
|
|
;; keep the byte-compiler happy
|
|
|
|
|
(declare-function mu4e~proc-mkdir "mu4e-proc")
|
|
|
|
|
(declare-function mu4e~proc-ping "mu4e-proc")
|
|
|
|
|
(declare-function mu4e~proc-contacts "mu4e-proc")
|
|
|
|
|
(declare-function mu4e~proc-kill "mu4e-proc")
|
|
|
|
|
(declare-function mu4e~proc-index "mu4e-proc")
|
|
|
|
|
(declare-function mu4e~proc-add "mu4e-proc")
|
|
|
|
|
(declare-function mu4e~proc-mkdir "mu4e-proc")
|
|
|
|
|
(declare-function mu4e~proc-running-p "mu4e-proc")
|
|
|
|
|
|
|
|
|
|
(declare-function show-all "org")
|
|
|
|
|
|
|
|
|
|
|
2012-09-03 10:15:27 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
;; the following is taken from org.el; we copy it here since we don't want to
|
|
|
|
|
;; depend on org-mode directly (it causes byte-compilation errors) TODO: a
|
|
|
|
|
;; cleaner solution....
|
|
|
|
|
(defconst mu4e~ts-regexp0
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(concat
|
|
|
|
|
"\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)"
|
2013-08-11 13:12:09 +02:00
|
|
|
|
"\\( +[^]+0-9>\r\n -]+\\)?\\( +\\([0-9]\\{1,2\\}\\):"
|
|
|
|
|
"\\([0-9]\\{2\\}\\)\\)?\\)")
|
2012-09-03 10:15:27 +02:00
|
|
|
|
"Regular expression matching time strings for analysis.
|
|
|
|
|
This one does not require the space after the date, so it can be
|
|
|
|
|
used on a string that terminates immediately after the date.")
|
|
|
|
|
|
|
|
|
|
(defun mu4e-parse-time-string (s &optional nodefault)
|
|
|
|
|
"Parse the standard Org-mode time string.
|
|
|
|
|
This should be a lot faster than the normal `parse-time-string'.
|
2012-11-10 14:01:17 +01:00
|
|
|
|
If time is not given, defaults to 0:00. However, with optional
|
|
|
|
|
NODEFAULT, hour and minute fields will be nil if not given."
|
2012-09-03 10:15:27 +02:00
|
|
|
|
(if (string-match mu4e~ts-regexp0 s)
|
|
|
|
|
(list 0
|
|
|
|
|
(if (or (match-beginning 8) (not nodefault))
|
|
|
|
|
(string-to-number (or (match-string 8 s) "0")))
|
|
|
|
|
(if (or (match-beginning 7) (not nodefault))
|
|
|
|
|
(string-to-number (or (match-string 7 s) "0")))
|
|
|
|
|
(string-to-number (match-string 4 s))
|
|
|
|
|
(string-to-number (match-string 3 s))
|
|
|
|
|
(string-to-number (match-string 2 s))
|
|
|
|
|
nil nil nil)
|
|
|
|
|
(mu4e-error "Not a standard mu4e time string: %s" s)))
|
2012-10-19 15:01:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defun mu4e-user-mail-address-p (addr)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"If ADDR is one of user's e-mail addresses return t, nil otherwise.
|
|
|
|
|
User's addresses are set in `mu4e-user-mail-address-list')."
|
2012-10-19 15:01:55 +02:00
|
|
|
|
(when (and addr mu4e-user-mail-address-list
|
|
|
|
|
(find addr mu4e-user-mail-address-list :test 'string=))
|
|
|
|
|
t))
|
2012-09-03 10:15:27 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
2012-09-27 09:03:58 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
;; the standard folders can be functions too
|
|
|
|
|
(defun mu4e~get-folder (foldervar msg)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Get message folder FOLDER.
|
|
|
|
|
If FOLDER is a string, return it, if it is a function, evaluate
|
|
|
|
|
this function with MSG as parameter (which may be `nil'), and
|
|
|
|
|
return the result."
|
2012-09-27 09:03:58 +02:00
|
|
|
|
(unless (member foldervar '(mu4e-sent-folder mu4e-drafts-folder
|
2012-09-27 11:53:16 +02:00
|
|
|
|
mu4e-trash-folder mu4e-refile-folder))
|
2012-09-27 09:03:58 +02:00
|
|
|
|
(mu4e-error "Folder must be either mu4e-sent-folder,
|
|
|
|
|
mu4e-drafts-folder or mu4e-trash-folder (not %S)" foldervar))
|
|
|
|
|
(let* ((folder (symbol-value foldervar))
|
|
|
|
|
(val
|
|
|
|
|
(cond
|
|
|
|
|
((stringp folder) folder)
|
|
|
|
|
((functionp folder) (funcall folder msg))
|
2012-10-03 11:53:16 +02:00
|
|
|
|
(t (mu4e-error "unsupported type for %S" folder)))))
|
2012-09-27 11:01:26 +02:00
|
|
|
|
(or val (mu4e-error "%S evaluates to nil" foldervar))))
|
2012-09-27 09:03:58 +02:00
|
|
|
|
|
2012-10-03 11:53:16 +02:00
|
|
|
|
(defun mu4e-get-drafts-folder (&optional msg)
|
2012-09-27 09:03:58 +02:00
|
|
|
|
"Get the sent folder. See `mu4e-drafts-folder'."
|
|
|
|
|
(mu4e~get-folder 'mu4e-drafts-folder msg))
|
|
|
|
|
|
2012-10-03 11:53:16 +02:00
|
|
|
|
(defun mu4e-get-refile-folder (&optional msg)
|
2012-09-27 11:53:16 +02:00
|
|
|
|
"Get the folder for refiling. See `mu4e-refile-folder'."
|
|
|
|
|
(mu4e~get-folder 'mu4e-refile-folder msg))
|
|
|
|
|
|
2012-10-03 11:53:16 +02:00
|
|
|
|
(defun mu4e-get-sent-folder (&optional msg)
|
2012-09-27 11:53:16 +02:00
|
|
|
|
"Get the sent folder. See `mu4e-sent-folder'."
|
|
|
|
|
(mu4e~get-folder 'mu4e-sent-folder msg))
|
|
|
|
|
|
2012-10-03 11:53:16 +02:00
|
|
|
|
(defun mu4e-get-trash-folder (&optional msg)
|
2012-09-27 09:03:58 +02:00
|
|
|
|
"Get the sent folder. See `mu4e-trash-folder'."
|
|
|
|
|
(mu4e~get-folder 'mu4e-trash-folder msg))
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
2012-09-28 16:10:34 +02:00
|
|
|
|
|
2012-10-03 11:53:16 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
;; mu4e-attachment-dir is either a string or a function that takes a filename
|
|
|
|
|
;; and the mime-type as argument, either (or both) which can be nil
|
|
|
|
|
(defun mu4e~get-attachment-dir (&optional fname mimetype)
|
|
|
|
|
"Get the directory for saving attachments from
|
|
|
|
|
`mu4e-attachment-dir' (which can be either a string or a function,
|
|
|
|
|
see its docstring)."
|
|
|
|
|
(let
|
|
|
|
|
((dir
|
|
|
|
|
(cond
|
|
|
|
|
((stringp mu4e-attachment-dir)
|
|
|
|
|
mu4e-attachment-dir)
|
|
|
|
|
((functionp mu4e-attachment-dir)
|
|
|
|
|
(funcall mu4e-attachment-dir fname mimetype))
|
|
|
|
|
(t
|
|
|
|
|
(mu4e-error "unsupported type for mu4e-attachment-dir" )))))
|
|
|
|
|
(if dir
|
|
|
|
|
(expand-file-name dir)
|
|
|
|
|
(mu4e-error (mu4e-error "mu4e-attachment-dir evaluates to nil")))))
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
2012-09-27 09:03:58 +02:00
|
|
|
|
|
|
|
|
|
|
2013-05-21 19:11:11 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2013-05-21 07:05:06 +02:00
|
|
|
|
(defun mu4e~guess-maildir (path)
|
|
|
|
|
"Guess the maildir for some path, or nil if cannot find it."
|
|
|
|
|
(when (zerop (string-match mu4e-maildir path))
|
|
|
|
|
(replace-regexp-in-string
|
|
|
|
|
mu4e-maildir
|
|
|
|
|
""
|
|
|
|
|
(expand-file-name
|
|
|
|
|
(concat path "/../..")))))
|
2013-05-21 19:11:11 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2013-05-21 07:05:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-09-27 09:03:58 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(defun mu4e-create-maildir-maybe (dir)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Offer to create maildir DIR if it does not exist yet.
|
2013-03-05 05:58:41 +01:00
|
|
|
|
Return t if the dir already existed, or an attempt has been made to
|
|
|
|
|
create it -- we cannot be sure creation succeeded here, since this
|
|
|
|
|
is done asynchronously. Otherwise, return nil. NOte, DIR has to be
|
|
|
|
|
an absolute path."
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(if (and (file-exists-p dir) (not (file-directory-p dir)))
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "%s exists, but is not a directory." dir))
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(cond
|
|
|
|
|
((file-directory-p dir) t)
|
2013-05-03 18:39:05 +02:00
|
|
|
|
((yes-or-no-p (mu4e-format "%s does not exist yet. Create now?" dir))
|
2013-03-05 15:35:29 +01:00
|
|
|
|
(mu4e~proc-mkdir dir) t)
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(t nil)))
|
2012-07-19 10:42:38 +02:00
|
|
|
|
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(defun mu4e-format (frm &rest args)
|
|
|
|
|
"Create [mu4e]-prefixed string based on format FRM and ARGS."
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(concat
|
|
|
|
|
"[" (propertize "mu4e" 'face 'mu4e-title-face) "] "
|
|
|
|
|
(apply 'format frm args)))
|
2012-06-26 21:49:01 +02:00
|
|
|
|
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(defun mu4e-message (frm &rest args)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Like `message', but prefixed with mu4e.
|
2013-05-30 15:16:05 +02:00
|
|
|
|
If we're waiting for user-input or if there's some message in the
|
|
|
|
|
echo area, don't show anything."
|
2013-06-03 22:35:01 +02:00
|
|
|
|
(unless (or (active-minibuffer-window))
|
2013-05-30 15:16:05 +02:00
|
|
|
|
(message "%s" (apply 'mu4e-format frm args))))
|
2012-06-26 21:49:01 +02:00
|
|
|
|
|
2013-08-11 13:37:19 +02:00
|
|
|
|
(defun mu4e-index-message (frm &rest args)
|
|
|
|
|
"Like `mu4e-message', but specifically for
|
|
|
|
|
index-messages. Doesn't display anything if
|
|
|
|
|
`mu4e-hide-index-messages' is non-nil. "
|
|
|
|
|
(unless mu4e-hide-index-messages
|
|
|
|
|
(apply 'mu4e-message frm args)))
|
|
|
|
|
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(defun mu4e-error (frm &rest args)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Create [mu4e]-prefixed error based on format FRM and ARGS.
|
|
|
|
|
Does a local-exit and does not return, and raises a
|
2012-09-26 16:28:30 +02:00
|
|
|
|
debuggable (backtrace) error."
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-log 'error (apply 'mu4e-format frm args))
|
2012-07-20 14:50:03 +02:00
|
|
|
|
(error "%s" (apply 'mu4e-format frm args)))
|
2012-07-19 10:42:38 +02:00
|
|
|
|
|
2012-12-09 14:07:21 +01:00
|
|
|
|
;; the user-error function is only available in emacs-trunk
|
|
|
|
|
(unless (fboundp 'user-error)
|
|
|
|
|
(defalias 'user-error 'error))
|
|
|
|
|
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(defun mu4e-warn (frm &rest args)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Create [mu4e]-prefixed warning based on format FRM and ARGS.
|
|
|
|
|
Does a local-exit and does not return. In emacs versions below
|
|
|
|
|
24.2, the functions is the same as `mu4e-error'."
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(mu4e-log 'error (apply 'mu4e-format frm args))
|
2012-12-09 14:07:21 +01:00
|
|
|
|
(user-error "%s" (apply 'mu4e-format frm args)))
|
2012-09-06 09:33:53 +02:00
|
|
|
|
|
2012-05-07 16:21:31 +02:00
|
|
|
|
(defun mu4e~read-char-choice (prompt choices)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Compatiblity wrapper for `read-char-choice'.
|
|
|
|
|
That function is available which emacs-24 only."
|
2012-05-07 20:59:06 +02:00
|
|
|
|
(let ((choice) (ok) (inhibit-quit nil))
|
|
|
|
|
(while (not ok)
|
|
|
|
|
(message nil);; this seems needed...
|
|
|
|
|
(setq choice (read-char-exclusive prompt))
|
|
|
|
|
(setq ok (member choice choices)))
|
|
|
|
|
choice))
|
2012-04-26 16:59:34 +02:00
|
|
|
|
|
2012-04-11 17:28:55 +02:00
|
|
|
|
(defun mu4e-read-option (prompt options)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Ask user for an option from a list on the input area.
|
|
|
|
|
PROMPT describes a multiple-choice question to the user.
|
|
|
|
|
OPTIONS describe the options, and is a list of cells describing
|
|
|
|
|
particular options. Cells have the following structure:
|
2012-04-26 16:59:34 +02:00
|
|
|
|
|
2012-06-11 15:40:23 +02:00
|
|
|
|
(OPTIONSTRING . RESULT)
|
2012-04-26 16:59:34 +02:00
|
|
|
|
|
2012-11-10 14:01:17 +01:00
|
|
|
|
where OPTIONSTRING is a non-empty string describing the
|
|
|
|
|
option. The first character of OPTIONSTRING is used as the
|
|
|
|
|
shortcut, and obviously all shortcuts must be different, so you
|
|
|
|
|
can prefix the string with an uniquifying character.
|
2012-06-11 23:15:06 +02:00
|
|
|
|
|
2012-04-11 17:28:55 +02:00
|
|
|
|
The options are provided as a list for the user to choose from;
|
2012-04-26 16:59:34 +02:00
|
|
|
|
user can then choose by typing CHAR. Example:
|
2012-04-11 17:28:55 +02:00
|
|
|
|
(mu4e-read-option \"Choose an animal: \"
|
2012-06-15 10:46:56 +02:00
|
|
|
|
'((\"Monkey\" . monkey) (\"Gnu\" . gnu) (\"xMoose\" . moose)))
|
|
|
|
|
|
|
|
|
|
User now will be presented with a list: \"Choose an animal:
|
|
|
|
|
[M]onkey, [G]nu, [x]Moose\".
|
|
|
|
|
|
|
|
|
|
Function will return the cdr of the list element."
|
2012-06-11 15:40:23 +02:00
|
|
|
|
(let* ((prompt (mu4e-format "%s" prompt))
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(chosen)
|
2012-04-11 17:28:55 +02:00
|
|
|
|
(optionsstr
|
|
|
|
|
(mapconcat
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(lambda (option)
|
2012-06-19 13:14:25 +02:00
|
|
|
|
;; try to detect old-style options, and warn
|
|
|
|
|
(when (characterp (car-safe (cdr-safe option)))
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error (concat "Please use the new format for options/actions; "
|
2012-12-09 14:07:21 +01:00
|
|
|
|
"see the manual")))
|
2012-06-11 15:40:23 +02:00
|
|
|
|
(let* ((kar (substring (car option) 0 1))
|
|
|
|
|
(val (cdr option)))
|
|
|
|
|
(concat
|
|
|
|
|
"[" (propertize kar 'face 'mu4e-highlight-face) "]"
|
|
|
|
|
(substring (car option) 1))))
|
|
|
|
|
options ", "))
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(response
|
2012-05-07 16:21:31 +02:00
|
|
|
|
(mu4e~read-char-choice
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(concat prompt optionsstr
|
2012-10-18 23:51:24 +02:00
|
|
|
|
" [" (propertize "C-g" 'face 'mu4e-highlight-face) " to cancel]")
|
2012-06-11 15:40:23 +02:00
|
|
|
|
;; the allowable chars
|
2012-06-11 23:15:06 +02:00
|
|
|
|
(map 'list (lambda(elm) (string-to-char (car elm))) options)))
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(chosen
|
2012-06-11 15:40:23 +02:00
|
|
|
|
(find-if
|
|
|
|
|
(lambda (option) (eq response (string-to-char (car option))))
|
|
|
|
|
options)))
|
2012-08-28 11:11:34 +02:00
|
|
|
|
(if chosen
|
|
|
|
|
(cdr chosen)
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(mu4e-warn "Unknown shortcut '%c'" response))))
|
2012-09-15 17:04:39 +02:00
|
|
|
|
|
2012-08-26 21:07:09 +02:00
|
|
|
|
(defun mu4e~get-maildirs-1 (path mdir)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Get maildirs under path, recursively, as a list of relative paths."
|
2012-08-26 21:07:09 +02:00
|
|
|
|
(let ((dirs)
|
|
|
|
|
(dentries
|
|
|
|
|
(ignore-errors
|
|
|
|
|
(directory-files-and-attributes
|
|
|
|
|
(concat path mdir) nil
|
|
|
|
|
"^[^.]\\|\\.[^.][^.]" t))))
|
|
|
|
|
(dolist (dentry dentries)
|
|
|
|
|
(when (and (booleanp (cadr dentry)) (cadr dentry))
|
|
|
|
|
(if (file-accessible-directory-p
|
2012-08-27 17:05:31 +02:00
|
|
|
|
(concat mu4e-maildir "/" mdir "/" (car dentry) "/cur"))
|
2013-03-08 21:05:09 +01:00
|
|
|
|
(setq dirs (cons (concat mdir (car dentry)) dirs)))
|
2013-05-27 20:47:42 +02:00
|
|
|
|
(unless (member (car dentry) '("cur" "new" "tmp"))
|
2013-04-06 09:17:03 +02:00
|
|
|
|
(setq dirs (append dirs (mu4e~get-maildirs-1 path
|
2012-08-26 21:07:09 +02:00
|
|
|
|
(concat mdir (car dentry) "/")))))))
|
|
|
|
|
dirs))
|
2012-04-17 19:35:32 +02:00
|
|
|
|
|
2012-05-29 10:12:32 +02:00
|
|
|
|
(defvar mu4e~maildir-list nil "Cached list of maildirs.")
|
2012-04-17 19:35:32 +02:00
|
|
|
|
|
2012-08-25 11:37:59 +02:00
|
|
|
|
(defun mu4e-get-maildirs ()
|
|
|
|
|
"Get maildirs under `mu4e-maildir', recursively, as a list of
|
|
|
|
|
relative paths (ie., /archive, /sent etc.). Most of the work is
|
|
|
|
|
done in `mu4e-get-maildirs-1'. Note, these results are /cached/, so
|
|
|
|
|
the list of maildirs will not change until you restart mu4e."
|
|
|
|
|
(unless mu4e-maildir (mu4e-error "`mu4e-maildir' is not defined"))
|
2012-05-29 10:12:32 +02:00
|
|
|
|
(unless mu4e~maildir-list
|
2012-08-27 17:05:31 +02:00
|
|
|
|
(setq mu4e~maildir-list
|
|
|
|
|
(sort
|
2012-08-26 21:07:09 +02:00
|
|
|
|
(append
|
|
|
|
|
(when (file-accessible-directory-p (concat mu4e-maildir "/cur")) '("/"))
|
|
|
|
|
(mu4e~get-maildirs-1 mu4e-maildir "/"))
|
|
|
|
|
(lambda (s1 s2) (string< (downcase s1) (downcase s2))))))
|
2012-05-29 10:12:32 +02:00
|
|
|
|
mu4e~maildir-list)
|
2012-04-17 19:35:32 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(defun mu4e-ask-maildir (prompt)
|
|
|
|
|
"Ask the user for a shortcut (using PROMPT) as defined in
|
|
|
|
|
`mu4e-maildir-shortcuts', then return the corresponding folder
|
|
|
|
|
name. If the special shortcut 'o' (for _o_ther) is used, or if
|
2012-12-12 20:04:23 +01:00
|
|
|
|
`mu4e-maildir-shortcuts' is not defined, let user choose from all
|
|
|
|
|
maildirs under `mu4e-maildir'."
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(let ((prompt (mu4e-format "%s" prompt)))
|
|
|
|
|
(if (not mu4e-maildir-shortcuts)
|
2013-09-12 23:42:45 +02:00
|
|
|
|
(funcall mu4e-completing-read-function prompt (mu4e-get-maildirs))
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(let* ((mlist (append mu4e-maildir-shortcuts '(("ther" . ?o))))
|
|
|
|
|
(fnames
|
|
|
|
|
(mapconcat
|
|
|
|
|
(lambda (item)
|
|
|
|
|
(concat
|
|
|
|
|
"["
|
|
|
|
|
(propertize (make-string 1 (cdr item))
|
|
|
|
|
'face 'mu4e-highlight-face)
|
|
|
|
|
"]"
|
|
|
|
|
(car item)))
|
|
|
|
|
mlist ", "))
|
|
|
|
|
(kar (read-char (concat prompt fnames))))
|
2012-09-09 12:17:09 +02:00
|
|
|
|
(if (member kar '(?/ ?o)) ;; user chose 'other'?
|
2013-09-12 23:42:45 +02:00
|
|
|
|
(funcall mu4e-completing-read-function prompt (mu4e-get-maildirs) nil nil "/")
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(or (car-safe
|
|
|
|
|
(find-if (lambda (item) (= kar (cdr item))) mu4e-maildir-shortcuts))
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(mu4e-warn "Unknown shortcut '%c'" kar)))))))
|
2012-04-24 18:17:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defun mu4e-ask-maildir-check-exists (prompt)
|
|
|
|
|
"Like `mu4e-ask-maildir', but check for existence of the maildir,
|
|
|
|
|
and offer to create it if it does not exist yet."
|
|
|
|
|
(let* ((mdir (mu4e-ask-maildir prompt))
|
|
|
|
|
(fullpath (concat mu4e-maildir mdir)))
|
|
|
|
|
(unless (file-directory-p fullpath)
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(and (yes-or-no-p
|
|
|
|
|
(mu4e-format "%s does not exist. Create now?" fullpath))
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(mu4e~proc-mkdir fullpath)))
|
|
|
|
|
mdir))
|
2012-05-10 13:36:45 +02:00
|
|
|
|
|
2012-04-24 18:17:42 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(defun mu4e-ask-bookmark (prompt &optional kar)
|
|
|
|
|
"Ask the user for a bookmark (using PROMPT) as defined in
|
|
|
|
|
`mu4e-bookmarks', then return the corresponding query."
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(unless mu4e-bookmarks (mu4e-error "`mu4e-bookmarks' is not defined"))
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(let* ((prompt (mu4e-format "%s" prompt))
|
|
|
|
|
(bmarks
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(mapconcat
|
|
|
|
|
(lambda (bm)
|
|
|
|
|
(let ((query (nth 0 bm)) (title (nth 1 bm)) (key (nth 2 bm)))
|
|
|
|
|
(concat
|
|
|
|
|
"[" (propertize (make-string 1 key)
|
2012-04-25 07:40:06 +02:00
|
|
|
|
'face 'mu4e-highlight-face)
|
2012-04-09 10:50:23 +02:00
|
|
|
|
"]"
|
|
|
|
|
title))) mu4e-bookmarks ", "))
|
|
|
|
|
(kar (read-char (concat prompt bmarks))))
|
|
|
|
|
(mu4e-get-bookmark-query kar)))
|
|
|
|
|
|
2012-08-23 09:59:03 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(defun mu4e-get-bookmark-query (kar)
|
|
|
|
|
"Get the corresponding bookmarked query for shortcut character
|
|
|
|
|
KAR, or raise an error if none is found."
|
|
|
|
|
(let ((chosen-bm
|
|
|
|
|
(find-if
|
|
|
|
|
(lambda (bm)
|
|
|
|
|
(= kar (nth 2 bm)))
|
|
|
|
|
mu4e-bookmarks)))
|
|
|
|
|
(if chosen-bm
|
|
|
|
|
(nth 0 chosen-bm)
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(mu4e-warn "Unknown shortcut '%c'" kar))))
|
2012-05-10 13:36:45 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
|
|
|
|
|
;;; converting flags->string and vice-versa ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 08:08:15 +02:00
|
|
|
|
(defun mu4e~flags-to-string-raw (flags)
|
2012-04-09 10:50:23 +02:00
|
|
|
|
"Convert a list of flags into a string as seen in Maildir
|
|
|
|
|
message files; flags are symbols draft, flagged, new, passed,
|
|
|
|
|
replied, seen, trashed and the string is the concatenation of the
|
|
|
|
|
uppercased first letters of these flags, as per [1]. Other flags
|
|
|
|
|
than the ones listed here are ignored.
|
|
|
|
|
Also see `mu4e-flags-to-string'.
|
|
|
|
|
\[1\]: http://cr.yp.to/proto/maildir.html"
|
|
|
|
|
(when flags
|
|
|
|
|
(let ((kar (case (car flags)
|
|
|
|
|
('draft ?D)
|
|
|
|
|
('flagged ?F)
|
|
|
|
|
('new ?N)
|
|
|
|
|
('passed ?P)
|
|
|
|
|
('replied ?R)
|
|
|
|
|
('seen ?S)
|
|
|
|
|
('trashed ?T)
|
|
|
|
|
('attach ?a)
|
|
|
|
|
('encrypted ?x)
|
|
|
|
|
('signed ?s)
|
|
|
|
|
('unread ?u))))
|
|
|
|
|
(concat (and kar (string kar))
|
2012-04-24 08:08:15 +02:00
|
|
|
|
(mu4e~flags-to-string-raw (cdr flags))))))
|
2012-04-09 10:50:23 +02:00
|
|
|
|
|
2012-04-24 08:08:15 +02:00
|
|
|
|
(defun mu4e-flags-to-string (flags)
|
|
|
|
|
"Remove duplicates and sort the output of `mu4e~flags-to-string-raw'."
|
|
|
|
|
(concat
|
|
|
|
|
(sort (remove-duplicates
|
|
|
|
|
(append (mu4e~flags-to-string-raw flags) nil)) '>)))
|
2012-04-09 10:50:23 +02:00
|
|
|
|
|
2012-04-24 08:08:15 +02:00
|
|
|
|
(defun mu4e~string-to-flags-1 (str)
|
2012-04-09 10:50:23 +02:00
|
|
|
|
"Convert a string with message flags as seen in Maildir
|
|
|
|
|
messages into a list of flags in; flags are symbols draft,
|
|
|
|
|
flagged, new, passed, replied, seen, trashed and the string is
|
|
|
|
|
the concatenation of the uppercased first letters of these flags,
|
|
|
|
|
as per [1]. Other letters than the ones listed here are ignored.
|
2012-04-24 08:08:15 +02:00
|
|
|
|
Also see `mu4e-flags-to-string'.
|
|
|
|
|
\[1\]: http://cr.yp.to/proto/maildir.html."
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(when (/= 0 (length str))
|
|
|
|
|
(let ((flag
|
|
|
|
|
(case (string-to-char str)
|
|
|
|
|
(?D 'draft)
|
|
|
|
|
(?F 'flagged)
|
|
|
|
|
(?P 'passed)
|
|
|
|
|
(?R 'replied)
|
|
|
|
|
(?S 'seen)
|
|
|
|
|
(?T 'trashed))))
|
|
|
|
|
(append (when flag (list flag))
|
2012-04-24 08:08:15 +02:00
|
|
|
|
(mu4e~string-to-flags-1 (substring str 1))))))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-string-to-flags (str)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Convert a string with message flags as seen in Maildir messages
|
2012-04-24 08:08:15 +02:00
|
|
|
|
into a list of flags in; flags are symbols draft, flagged, new,
|
|
|
|
|
passed, replied, seen, trashed and the string is the concatenation
|
|
|
|
|
of the uppercased first letters of these flags, as per [1]. Other
|
|
|
|
|
letters than the ones listed here are ignored. Also see
|
|
|
|
|
`mu4e-flags-to-string'. \[1\]:
|
|
|
|
|
http://cr.yp.to/proto/maildir.html "
|
|
|
|
|
;; "Remove duplicates from the output of `mu4e~string-to-flags-1'"
|
|
|
|
|
(remove-duplicates (mu4e~string-to-flags-1 str)))
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(defun mu4e-display-size (size)
|
|
|
|
|
"Get a string representation of SIZE (in bytes)."
|
|
|
|
|
(cond
|
|
|
|
|
((>= size 1000000) (format "%2.1fM" (/ size 1000000.0)))
|
|
|
|
|
((and (>= size 1000) (< size 1000000))
|
|
|
|
|
(format "%2.1fK" (/ size 1000.0)))
|
|
|
|
|
((< size 1000) (format "%d" size))
|
|
|
|
|
(t (propertize "?" 'face 'mu4e-system-face))))
|
2012-09-28 16:10:34 +02:00
|
|
|
|
|
2012-04-20 07:46:05 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(defun mu4e-display-manual ()
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Display the mu4e manual page for the current mode.
|
|
|
|
|
Or go to the top level if there is none."
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(interactive)
|
|
|
|
|
(info (case major-mode
|
2012-04-15 13:23:44 +02:00
|
|
|
|
('mu4e-main-mode "(mu4e)Main view")
|
2012-05-01 21:45:54 +02:00
|
|
|
|
('mu4e-headers-mode "(mu4e)Headers view")
|
2012-04-15 13:23:44 +02:00
|
|
|
|
('mu4e-view-mode "(mu4e)Message view")
|
|
|
|
|
(t "mu4e"))))
|
2012-04-09 10:50:23 +02:00
|
|
|
|
|
2012-04-21 11:46:37 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-29 21:03:27 +02:00
|
|
|
|
(defun mu4e-last-query ()
|
|
|
|
|
"Get the most recent query or nil if there is none."
|
2012-05-01 21:45:54 +02:00
|
|
|
|
(when (buffer-live-p mu4e~headers-buffer)
|
2012-09-13 20:12:12 +02:00
|
|
|
|
(with-current-buffer mu4e~headers-buffer
|
2012-06-10 10:19:51 +02:00
|
|
|
|
mu4e~headers-last-query)))
|
2012-04-29 21:03:27 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(defun mu4e-select-other-view ()
|
|
|
|
|
"When the headers view is selected, select the message view (if
|
|
|
|
|
that has a live window), and vice versa."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let* ((other-buf
|
|
|
|
|
(cond
|
2012-05-01 21:45:54 +02:00
|
|
|
|
((eq major-mode 'mu4e-headers-mode)
|
2012-04-29 21:03:27 +02:00
|
|
|
|
mu4e~view-buffer)
|
2012-04-09 10:50:23 +02:00
|
|
|
|
((eq major-mode 'mu4e-view-mode)
|
2012-05-01 21:45:54 +02:00
|
|
|
|
mu4e~headers-buffer)))
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(other-win (and other-buf (get-buffer-window other-buf))))
|
|
|
|
|
(if (window-live-p other-win)
|
|
|
|
|
(select-window other-win)
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(mu4e-message "No window to switch to"))))
|
2012-04-09 10:50:23 +02:00
|
|
|
|
|
2012-04-11 21:31:52 +02:00
|
|
|
|
|
2012-05-10 13:36:45 +02:00
|
|
|
|
(defconst mu4e-output-buffer-name "*mu4e-output*"
|
2012-04-18 18:10:48 +02:00
|
|
|
|
"*internal* Name of the mu4e output buffer.")
|
|
|
|
|
|
|
|
|
|
(defun mu4e-process-file-through-pipe (path pipecmd)
|
|
|
|
|
"Process file at PATH through a pipe with PIPECMD."
|
|
|
|
|
(let ((buf (get-buffer-create mu4e-output-buffer-name)))
|
|
|
|
|
(with-current-buffer buf
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(call-process-shell-command pipecmd path t t)
|
|
|
|
|
(view-mode)))
|
|
|
|
|
(switch-to-buffer buf)))
|
2012-04-11 21:31:52 +02:00
|
|
|
|
|
2012-12-18 22:29:17 +01:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
(defvar mu4e~lists-hash nil
|
2012-12-22 10:48:02 +01:00
|
|
|
|
"Hashtable of mailing-list-id => shortname, based on
|
|
|
|
|
`mu4e~mailing-lists' and `mu4e-user-mailing-lists'.")
|
2012-12-18 22:29:17 +01:00
|
|
|
|
|
|
|
|
|
(defun mu4e-get-mailing-list-shortname (list-id)
|
2013-08-11 13:12:09 +02:00
|
|
|
|
"Get the shortname for a mailing-list with list-id LIST-ID. based
|
|
|
|
|
on `mu4e~mailing-lists' and `mu4e-user-mailing-lists'."
|
2012-12-18 22:29:17 +01:00
|
|
|
|
(unless mu4e~lists-hash
|
|
|
|
|
(setq mu4e~lists-hash (make-hash-table :test 'equal))
|
2013-08-11 13:12:09 +02:00
|
|
|
|
(dolist (cell mu4e~mailing-lists)
|
|
|
|
|
(puthash (car cell) (cdr cell) mu4e~lists-hash))
|
|
|
|
|
(dolist (cell mu4e-user-mailing-lists)
|
|
|
|
|
(puthash (car cell) (cdr cell) mu4e~lists-hash)))
|
2012-12-22 10:48:02 +01:00
|
|
|
|
(or
|
|
|
|
|
(gethash list-id mu4e~lists-hash)
|
2012-12-26 12:01:36 +01:00
|
|
|
|
;; if it's not in the db, take the part until the first dot if there is one;
|
|
|
|
|
;; otherwise just return the whole thing
|
|
|
|
|
(if (string-match "\\([^.]*\\)\\." list-id)
|
|
|
|
|
(match-string 1 list-id)
|
|
|
|
|
list-id)))
|
2012-12-22 10:48:02 +01:00
|
|
|
|
|
2012-04-11 21:31:52 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-09-11 11:50:59 +02:00
|
|
|
|
|
2012-09-11 10:58:00 +02:00
|
|
|
|
(defvar mu4e-index-updated-hook nil
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Hook run when the indexing process had one or more updated messages.
|
|
|
|
|
This can be used as a simple way to invoke some action when new
|
|
|
|
|
messages appear, but note that an update in the index does not
|
|
|
|
|
necessarily mean a new message.")
|
2012-09-11 10:58:00 +02:00
|
|
|
|
|
2012-09-11 11:50:59 +02:00
|
|
|
|
;; some handler functions for server messages
|
|
|
|
|
;;
|
2012-04-11 21:31:52 +02:00
|
|
|
|
(defun mu4e-info-handler (info)
|
|
|
|
|
"Handler function for (:info ...) sexps received from the server
|
|
|
|
|
process."
|
|
|
|
|
(let ((type (plist-get info :info)))
|
|
|
|
|
(cond
|
2012-05-10 13:36:45 +02:00
|
|
|
|
((eq type 'add) t) ;; do nothing
|
2012-04-11 21:31:52 +02:00
|
|
|
|
((eq type 'index)
|
|
|
|
|
(if (eq (plist-get info :status) 'running)
|
2013-08-11 13:37:19 +02:00
|
|
|
|
(mu4e-index-message "Indexing... processed %d, updated %d"
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(plist-get info :processed) (plist-get info :updated))
|
2012-09-11 10:58:00 +02:00
|
|
|
|
(progn
|
2013-08-11 13:37:19 +02:00
|
|
|
|
(mu4e-index-message
|
2012-09-11 10:58:00 +02:00
|
|
|
|
"Indexing completed; processed %d, updated %d, cleaned-up %d"
|
|
|
|
|
(plist-get info :processed) (plist-get info :updated)
|
|
|
|
|
(plist-get info :cleaned-up))
|
|
|
|
|
(unless (zerop (plist-get info :updated))
|
|
|
|
|
(run-hooks 'mu4e-index-updated-hook)))))
|
2012-05-02 16:26:13 +02:00
|
|
|
|
((plist-get info :message)
|
2013-08-11 13:37:19 +02:00
|
|
|
|
(mu4e-index-message "%s" (plist-get info :message))))))
|
2012-04-11 21:31:52 +02:00
|
|
|
|
|
2012-04-15 13:23:44 +02:00
|
|
|
|
(defun mu4e-error-handler (errcode errmsg)
|
2012-04-11 21:31:52 +02:00
|
|
|
|
"Handler function for showing an error."
|
2013-08-11 13:37:19 +02:00
|
|
|
|
;; don't use mu4e-error here; it's running in the process filter context
|
2012-04-15 13:23:44 +02:00
|
|
|
|
(case errcode
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(4 (user-error "No matches for this search query."))
|
2012-07-24 00:33:52 +02:00
|
|
|
|
(t (error "Error %d: %s" errcode errmsg))))
|
2012-04-11 21:31:52 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
2013-08-11 13:12:09 +02:00
|
|
|
|
|
2013-05-27 20:47:42 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-11 21:31:52 +02:00
|
|
|
|
|
2013-08-11 13:12:09 +02:00
|
|
|
|
;;; RFC2822 handling of phrases in mail-addresses
|
|
|
|
|
;;; The optional display-name contains a phrase, it sits before the angle-addr
|
|
|
|
|
;;; as specified in RFC2822 for email-addresses in header fields.
|
2013-05-27 20:47:42 +02:00
|
|
|
|
;;; contributed by jhelberg
|
|
|
|
|
|
|
|
|
|
(defun mu4e~rfc822-phrase-type (ph)
|
|
|
|
|
"Return either atom, quoted-string, a corner-case or nil. This
|
2013-06-16 22:05:52 +02:00
|
|
|
|
checks for empty string first. Then quotes around the phrase
|
2013-05-27 20:47:42 +02:00
|
|
|
|
(returning 'rfc822-quoted-string). Then whether there is a quote
|
|
|
|
|
inside the phrase (returning 'rfc822-containing-quote).
|
|
|
|
|
The reverse of the RFC atext definition is then tested.
|
|
|
|
|
If it matches, nil is returned, if not, it is an 'rfc822-atom, which
|
|
|
|
|
is returned."
|
2013-08-11 13:12:09 +02:00
|
|
|
|
(cond
|
|
|
|
|
((= (length ph) 0) 'rfc822-empty)
|
|
|
|
|
((= (aref ph 0) ?\")
|
|
|
|
|
(if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph)
|
|
|
|
|
'rfc822-quoted-string
|
|
|
|
|
'rfc822-containing-quote)) ; starts with quote, but doesn't end with one
|
|
|
|
|
((string-match-p "[\"]" ph) 'rfc822-containing-quote)
|
|
|
|
|
((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil)
|
|
|
|
|
(t 'rfc822-atom)))
|
2013-05-27 20:47:42 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e~rfc822-quoteit (ph)
|
|
|
|
|
"Quote RFC822 phrase only if necessary.
|
|
|
|
|
Atoms and quoted strings don't need quotes. The rest do. In
|
|
|
|
|
case a phrase contains a quote, it will be escaped."
|
|
|
|
|
(let ((type (mu4e~rfc822-phrase-type ph)))
|
|
|
|
|
(cond
|
|
|
|
|
((eq type 'rfc822-atom) ph)
|
|
|
|
|
((eq type 'rfc822-quoted-string) ph)
|
2013-08-11 13:12:09 +02:00
|
|
|
|
((eq type 'rfc822-containing-quote)
|
|
|
|
|
(format "\"%s\""
|
|
|
|
|
(replace-regexp-in-string "\"" "\\\\\"" ph)))
|
2013-05-27 20:47:42 +02:00
|
|
|
|
(t (format "\"%s\"" ph)))))
|
|
|
|
|
|
2012-04-26 16:59:34 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 08:08:15 +02:00
|
|
|
|
;; start and stopping
|
2012-06-18 17:03:24 +02:00
|
|
|
|
(defun mu4e~fill-contacts (contacts)
|
|
|
|
|
"We receive a list of contacts, which each contact of the form
|
2013-06-16 22:05:52 +02:00
|
|
|
|
(:name NAME :mail EMAIL :tstamp TIMESTAMP :freq FREQUENCY)
|
2012-06-18 17:03:24 +02:00
|
|
|
|
and fill the list `mu4e~contacts-for-completion' with it, with
|
|
|
|
|
each element looking like
|
2012-11-10 14:01:17 +01:00
|
|
|
|
name <email>
|
2012-06-18 17:03:24 +02:00
|
|
|
|
This is used by the completion function in mu4e-compose."
|
2013-07-06 10:51:33 +02:00
|
|
|
|
(setq mu4e~contact-list contacts)
|
2013-06-16 22:05:52 +02:00
|
|
|
|
(let ((lst)
|
|
|
|
|
;; sort by the frequency (descending), then timestamp (descending)
|
|
|
|
|
;; FIXME: sadly, the emacs completion subsystem re-sorts the list
|
|
|
|
|
;; before showing candidates, so this doesn't do anything useful yet.
|
|
|
|
|
(contacts (sort contacts
|
|
|
|
|
(lambda (c1 c2)
|
|
|
|
|
(let ((freq1 (plist-get c1 :freq))
|
|
|
|
|
(tstamp1 (plist-get c1 :tstamp))
|
|
|
|
|
(freq2 (plist-get c2 :freq))
|
|
|
|
|
(tstamp2 (plist-get c2 :tstamp)))
|
|
|
|
|
(if (equal freq1 freq2)
|
|
|
|
|
(< tstamp1 tstamp2)
|
|
|
|
|
(< freq1 freq2)))))))
|
2012-06-18 17:03:24 +02:00
|
|
|
|
(dolist (contact contacts)
|
|
|
|
|
(let ((name (plist-get contact :name))
|
|
|
|
|
(mail (plist-get contact :mail)))
|
|
|
|
|
(when mail
|
2012-06-20 10:29:19 +02:00
|
|
|
|
(unless ;; ignore some address ('noreply' etc.)
|
|
|
|
|
(and mu4e-compose-complete-ignore-address-regexp
|
|
|
|
|
(string-match mu4e-compose-complete-ignore-address-regexp mail))
|
2012-06-18 17:03:24 +02:00
|
|
|
|
(add-to-list 'lst
|
2013-05-27 20:47:42 +02:00
|
|
|
|
(if name (format "%s <%s>" (mu4e~rfc822-quoteit name) mail) mail))))))
|
2012-06-18 17:03:24 +02:00
|
|
|
|
(setq mu4e~contacts-for-completion lst)
|
2013-08-11 13:37:19 +02:00
|
|
|
|
(mu4e-index-message "Contacts received: %d"
|
2012-06-24 09:38:09 +02:00
|
|
|
|
(length mu4e~contacts-for-completion))))
|
2012-06-18 17:03:24 +02:00
|
|
|
|
|
2012-04-26 16:59:34 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e~check-requirements ()
|
2012-04-24 08:08:15 +02:00
|
|
|
|
"Check for the settings required for running mu4e."
|
2012-09-15 17:04:39 +02:00
|
|
|
|
(unless (>= emacs-major-version 23)
|
|
|
|
|
(mu4e-error "Emacs >= 23.x is required for mu4e"))
|
|
|
|
|
(when mu4e~server-props
|
|
|
|
|
(let ((version (plist-get mu4e~server-props :version)))
|
|
|
|
|
(unless (string= version mu4e-mu-version)
|
|
|
|
|
(mu4e-error "mu server has version %s, but we need %s"
|
2012-09-28 16:10:34 +02:00
|
|
|
|
version mu4e-mu-version))))
|
2012-04-24 08:08:15 +02:00
|
|
|
|
(unless (and mu4e-mu-binary (file-executable-p mu4e-mu-binary))
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "Please set `mu4e-mu-binary' to the full path to the mu
|
2012-04-24 08:08:15 +02:00
|
|
|
|
binary."))
|
|
|
|
|
(unless mu4e-maildir
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "Please set `mu4e-maildir' to the full path to your
|
2012-04-24 08:08:15 +02:00
|
|
|
|
Maildir directory."))
|
|
|
|
|
;; expand mu4e-maildir, mu4e-attachment-dir
|
2012-10-03 11:53:16 +02:00
|
|
|
|
(setq mu4e-maildir (expand-file-name mu4e-maildir))
|
2012-04-24 08:08:15 +02:00
|
|
|
|
(unless (mu4e-create-maildir-maybe mu4e-maildir)
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "%s is not a valid maildir directory" mu4e-maildir))
|
2012-10-03 11:53:16 +02:00
|
|
|
|
(dolist (var '(mu4e-sent-folder mu4e-drafts-folder
|
2012-04-24 08:08:15 +02:00
|
|
|
|
mu4e-trash-folder))
|
|
|
|
|
(unless (and (boundp var) (symbol-value var))
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "Please set %S" var))
|
2012-09-27 21:26:16 +02:00
|
|
|
|
(unless (functionp (symbol-value var)) ;; functions are okay, too
|
|
|
|
|
(let* ((dir (symbol-value var))
|
|
|
|
|
(path (concat mu4e-maildir dir)))
|
|
|
|
|
(unless (string= (substring dir 0 1) "/")
|
|
|
|
|
(mu4e-error "%S must start with a '/'" dir))
|
|
|
|
|
(unless (mu4e-create-maildir-maybe path)
|
|
|
|
|
(mu4e-error "%s (%S) does not exist" path var))))))
|
2012-04-09 15:34:52 +02:00
|
|
|
|
|
2012-09-15 17:04:39 +02:00
|
|
|
|
|
2012-10-06 21:22:11 +02:00
|
|
|
|
(defun mu4e-running-p ()
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Whether mu4e is running.
|
|
|
|
|
Checks whether the server process is live."
|
2012-10-06 21:22:11 +02:00
|
|
|
|
(mu4e~proc-running-p))
|
|
|
|
|
|
2013-06-03 20:09:20 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
;; starting / getting mail / updating the index
|
|
|
|
|
;;
|
|
|
|
|
;;
|
|
|
|
|
(defvar mu4e~update-timer nil
|
|
|
|
|
"The mu4e update timer.")
|
|
|
|
|
(defconst mu4e~update-name "*mu4e-update*"
|
|
|
|
|
"Name of the process and buffer to update mail.")
|
|
|
|
|
(defconst mu4e~update-buffer-height 8
|
|
|
|
|
"Height of the mu4e message retrieval/update buffer.")
|
|
|
|
|
|
|
|
|
|
(defvar mu4e~get-mail-ask-password "mu4e get-mail: Enter password: "
|
|
|
|
|
"Query string for `mu4e-get-mail-command' password.")
|
|
|
|
|
(defvar mu4e~get-mail-password-regexp "^Remote: Enter password: $"
|
|
|
|
|
"Regexp to match a password query in the `mu4e-get-mail-command' output.")
|
|
|
|
|
|
2013-06-29 16:06:04 +02:00
|
|
|
|
(defun mu4e~request-contacts ()
|
|
|
|
|
"If `mu4e-compose-complete-addresses' is non-nil, get/update the
|
|
|
|
|
list of contacts we use for autocompletion; otherwise, do nothing."
|
|
|
|
|
(when mu4e-compose-complete-addresses
|
|
|
|
|
(setq mu4e-contacts-func 'mu4e~fill-contacts)
|
|
|
|
|
(mu4e~proc-contacts
|
|
|
|
|
mu4e-compose-complete-only-personal
|
|
|
|
|
(when mu4e-compose-complete-only-after
|
|
|
|
|
(float-time
|
|
|
|
|
(apply 'encode-time
|
|
|
|
|
(mu4e-parse-time-string mu4e-compose-complete-only-after)))))))
|
2013-06-03 20:09:20 +02:00
|
|
|
|
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(defun mu4e~start (&optional func)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"If mu4e is already running, execute function FUNC (if non-nil).
|
|
|
|
|
Otherwise, check various requirements, then start mu4e. When
|
|
|
|
|
successful, call FUNC (if non-nil) afterwards."
|
2012-04-19 07:30:42 +02:00
|
|
|
|
;; if we're already running, simply go to the main view
|
2012-10-06 21:22:11 +02:00
|
|
|
|
(if (mu4e-running-p) ;; already running?
|
2012-09-15 17:04:39 +02:00
|
|
|
|
(when func ;; yes! run func if defined
|
2012-09-28 16:10:34 +02:00
|
|
|
|
(funcall func))
|
2012-09-15 17:04:39 +02:00
|
|
|
|
(progn
|
|
|
|
|
;; no! do some checks, set up pong handler and ping the server
|
2012-09-28 16:10:34 +02:00
|
|
|
|
(lexical-let ((func func))
|
2012-09-15 17:04:39 +02:00
|
|
|
|
(mu4e~check-requirements)
|
|
|
|
|
;; set up the 'pong' handler func
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(setq mu4e-pong-func
|
2012-08-01 16:07:11 +02:00
|
|
|
|
(lambda (props)
|
2012-09-15 17:04:39 +02:00
|
|
|
|
(setq mu4e~server-props props) ;; save the props we got from the server
|
2012-08-01 16:07:11 +02:00
|
|
|
|
(let ((version (plist-get props :version))
|
|
|
|
|
(doccount (plist-get props :doccount)))
|
2012-09-15 17:04:39 +02:00
|
|
|
|
(mu4e~check-requirements)
|
2012-08-01 16:07:11 +02:00
|
|
|
|
(when func (funcall func))
|
|
|
|
|
(when (and mu4e-update-interval (null mu4e~update-timer))
|
|
|
|
|
(setq mu4e~update-timer
|
|
|
|
|
(run-at-time
|
2012-10-25 10:59:50 +02:00
|
|
|
|
0 mu4e-update-interval
|
|
|
|
|
(lambda () (mu4e-update-mail-and-index t)))))
|
2012-08-01 16:07:11 +02:00
|
|
|
|
(mu4e-message "Started mu4e with %d message%s in store"
|
2012-09-15 17:04:39 +02:00
|
|
|
|
doccount (if (= doccount 1) "" "s"))))))
|
2013-06-29 16:06:04 +02:00
|
|
|
|
;; wake up server
|
2012-06-18 17:03:24 +02:00
|
|
|
|
(mu4e~proc-ping)
|
2013-06-29 16:06:04 +02:00
|
|
|
|
;; maybe request the list of contacts, automatically refresh after
|
|
|
|
|
;; reindexing
|
|
|
|
|
(mu4e~request-contacts)
|
|
|
|
|
(add-hook 'mu4e-index-updated-hook 'mu4e~request-contacts))))
|
2012-09-15 17:04:39 +02:00
|
|
|
|
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(defun mu4e~stop ()
|
2012-05-10 13:36:45 +02:00
|
|
|
|
"Stop the mu4e session."
|
2012-07-11 08:15:26 +02:00
|
|
|
|
(when mu4e~update-timer
|
|
|
|
|
(cancel-timer mu4e~update-timer)
|
2012-05-29 10:12:32 +02:00
|
|
|
|
(setq
|
2012-07-11 08:15:26 +02:00
|
|
|
|
mu4e~update-timer nil
|
2012-07-10 10:51:54 +02:00
|
|
|
|
mu4e~maildir-list nil
|
|
|
|
|
mu4e~contacts-for-completion nil))
|
2012-05-10 13:36:45 +02:00
|
|
|
|
(mu4e~proc-kill)
|
2012-06-11 11:03:37 +02:00
|
|
|
|
;; kill all main/view/headers buffer
|
|
|
|
|
(mapcar
|
|
|
|
|
(lambda (buf)
|
|
|
|
|
(with-current-buffer buf
|
2012-10-06 21:22:11 +02:00
|
|
|
|
(when (member major-mode
|
|
|
|
|
'(mu4e-headers-mode mu4e-view-mode mu4e-main-mode))
|
2012-06-11 11:03:37 +02:00
|
|
|
|
(kill-buffer))))
|
|
|
|
|
(buffer-list)))
|
2012-06-11 23:15:06 +02:00
|
|
|
|
|
2012-10-25 10:59:50 +02:00
|
|
|
|
|
2012-04-26 16:59:34 +02:00
|
|
|
|
|
2012-08-24 16:03:18 +02:00
|
|
|
|
|
2012-10-25 10:59:50 +02:00
|
|
|
|
(defun mu4e~get-mail-process-filter (proc msg)
|
2012-08-24 16:03:18 +02:00
|
|
|
|
"Filter the output of `mu4e-get-mail-command'.
|
2012-10-25 10:59:50 +02:00
|
|
|
|
Currently the filter only checks if the command asks for a password
|
|
|
|
|
by matching the output against `mu4e~get-mail-password-regexp'.
|
|
|
|
|
The messages are inserted into the process buffer."
|
2013-02-17 16:32:12 +01:00
|
|
|
|
(when (string-match mu4e~get-mail-password-regexp msg)
|
|
|
|
|
(if (process-get proc 'x-interactive)
|
|
|
|
|
(process-send-string proc
|
2013-08-11 13:12:09 +02:00
|
|
|
|
(concat (read-passwd mu4e~get-mail-ask-password)
|
|
|
|
|
"\n"))
|
2013-02-17 16:32:12 +01:00
|
|
|
|
;; TODO kill process?
|
|
|
|
|
(mu4e-error "Unrecognized password request")))
|
|
|
|
|
(when (process-buffer proc)
|
|
|
|
|
(let ((inhibit-read-only t)
|
2013-03-05 15:35:29 +01:00
|
|
|
|
(procwin (get-buffer-window (process-buffer proc))))
|
2013-02-17 16:32:12 +01:00
|
|
|
|
;; Insert at end of buffer. Leave point alone.
|
|
|
|
|
(with-current-buffer (process-buffer proc)
|
|
|
|
|
(goto-char (point-max))
|
|
|
|
|
(insert msg))
|
|
|
|
|
;; Auto-scroll unless user is interacting with the window.
|
2013-03-05 15:35:29 +01:00
|
|
|
|
(when (and (window-live-p procwin)
|
|
|
|
|
(not (eq (selected-window) procwin)))
|
|
|
|
|
(with-selected-window procwin
|
2013-02-17 16:32:12 +01:00
|
|
|
|
(goto-char (point-max)))))))
|
2012-08-24 16:03:18 +02:00
|
|
|
|
|
2012-10-25 10:59:50 +02:00
|
|
|
|
(defun mu4e-update-index ()
|
|
|
|
|
"Update the mu4e index."
|
|
|
|
|
(interactive)
|
|
|
|
|
(unless mu4e-maildir
|
|
|
|
|
(mu4e-error "`mu4e-maildir' is not defined"))
|
|
|
|
|
(mu4e~proc-index mu4e-maildir mu4e-user-mail-address-list))
|
|
|
|
|
|
|
|
|
|
;; complicated function, as it:
|
|
|
|
|
;; - needs to check for errors
|
|
|
|
|
;; - (optionally) pop-up a window
|
|
|
|
|
;; - (optionally) check password requests
|
2013-10-08 14:42:30 +02:00
|
|
|
|
(defvar mu4e--update-buffer-name nil)
|
2012-10-25 10:59:50 +02:00
|
|
|
|
(defun mu4e-update-mail-and-index (run-in-background)
|
|
|
|
|
"Get a new mail by running `mu4e-get-mail-command'. If
|
2013-08-11 13:37:19 +02:00
|
|
|
|
run-in-background is non-nil (or called with prefix-argument), run
|
|
|
|
|
in the background; otherwise, pop up a window."
|
2012-10-25 10:59:50 +02:00
|
|
|
|
(interactive "P")
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(unless mu4e-get-mail-command
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "`mu4e-get-mail-command' is not defined"))
|
2013-10-08 14:42:30 +02:00
|
|
|
|
(let* ((process-connection-type t)
|
|
|
|
|
(proc (start-process-shell-command
|
|
|
|
|
"mu4e-update" mu4e~update-name
|
|
|
|
|
mu4e-get-mail-command))
|
|
|
|
|
(buf (process-buffer proc)))
|
|
|
|
|
(setq mu4e--update-buffer-name (buffer-name buf))
|
|
|
|
|
(when (not run-in-background)
|
|
|
|
|
(pop-to-buffer buf)
|
|
|
|
|
(special-mode)
|
|
|
|
|
(set-window-dedicated-p (selected-window) t))
|
2013-08-11 13:37:19 +02:00
|
|
|
|
(mu4e-index-message "Retrieving mail...")
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(set-process-sentinel proc
|
|
|
|
|
(lambda (proc msg)
|
|
|
|
|
(let* ((status (process-status proc))
|
|
|
|
|
(code (process-exit-status proc))
|
|
|
|
|
;; sadly, fetchmail returns '1' when there is no mail; this is
|
|
|
|
|
;; not really an error of course, but it's hard to distinguish
|
|
|
|
|
;; from a genuine error
|
2012-06-21 20:16:34 +02:00
|
|
|
|
(maybe-error (or (not (eq status 'exit)) (/= code 0)))
|
|
|
|
|
(buf (process-buffer proc)))
|
2012-04-26 16:59:34 +02:00
|
|
|
|
(message nil)
|
|
|
|
|
;; there may be an error, give the user up to 5 seconds to check
|
2012-10-25 10:59:50 +02:00
|
|
|
|
(when maybe-error (sit-for 5))
|
|
|
|
|
(mu4e-update-index)
|
2013-10-08 14:42:30 +02:00
|
|
|
|
(when (buffer-live-p buf)
|
|
|
|
|
(with-selected-window (get-buffer-window buf) (quit-window t))))))
|
2012-10-25 10:59:50 +02:00
|
|
|
|
;; if we're running in the foreground, handle password requests
|
|
|
|
|
(unless run-in-background
|
|
|
|
|
(process-put proc 'x-interactive (not run-in-background))
|
|
|
|
|
(set-process-filter proc 'mu4e~get-mail-process-filter))))
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-12-09 14:07:21 +01:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
|
2012-04-16 17:31:48 +02:00
|
|
|
|
|
2012-04-24 08:08:15 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-16 17:31:48 +02:00
|
|
|
|
;; logging / debugging
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(defvar mu4e~log-max-lines 1200
|
|
|
|
|
"*internal* Last <n> number of lines to keep around in the buffer.")
|
|
|
|
|
(defconst mu4e~log-buffer-name "*mu4e-log*"
|
|
|
|
|
"*internal* Name of the logging buffer.")
|
|
|
|
|
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(defun mu4e-log (type frm &rest args)
|
|
|
|
|
"Write a message of TYPE with format-string FRM and ARGS in
|
|
|
|
|
*mu4e-log* buffer, if the variable mu4e-debug is non-nil. Type is
|
|
|
|
|
either 'to-server, 'from-server or 'misc. This function is meant for debugging."
|
|
|
|
|
(when mu4e-debug
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(with-current-buffer (get-buffer-create mu4e~log-buffer-name)
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(view-mode)
|
|
|
|
|
(setq buffer-undo-list t)
|
|
|
|
|
(let* ((inhibit-read-only t)
|
2013-08-11 13:12:09 +02:00
|
|
|
|
(tstamp (propertize (format-time-string "%Y-%m-%d %T"
|
|
|
|
|
(current-time))
|
2012-04-16 17:31:48 +02:00
|
|
|
|
'face 'font-lock-string-face))
|
|
|
|
|
(msg-face
|
|
|
|
|
(case type
|
|
|
|
|
(from-server 'font-lock-type-face)
|
|
|
|
|
(to-server 'font-lock-function-name-face)
|
|
|
|
|
(misc 'font-lock-variable-name-face)
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(error 'font-lock-warning-face)
|
|
|
|
|
(otherwise (mu4e-error "Unsupported log type"))))
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(msg (propertize (apply 'format frm args) 'face msg-face)))
|
|
|
|
|
(goto-char (point-max))
|
2012-04-20 07:46:05 +02:00
|
|
|
|
(insert tstamp
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(case type
|
|
|
|
|
(from-server " <- ")
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(to-server " -> ")
|
|
|
|
|
(error " !! ")
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(otherwise " "))
|
|
|
|
|
msg "\n")
|
2012-04-20 07:46:05 +02:00
|
|
|
|
|
2012-04-16 17:31:48 +02:00
|
|
|
|
;; if `mu4e-log-max-lines is specified and exceeded, clearest the oldest
|
|
|
|
|
;; lines
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(when (numberp mu4e~log-max-lines)
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(let ((lines (count-lines (point-min) (point-max))))
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(when (> lines mu4e~log-max-lines)
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(goto-char (point-max))
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(forward-line (- mu4e~log-max-lines lines))
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(delete-region (point-min) (point)))))))))
|
2012-04-20 07:46:05 +02:00
|
|
|
|
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(defun mu4e-toggle-logging ()
|
|
|
|
|
"Toggle between enabling/disabling debug-mode (in debug-mode,
|
|
|
|
|
mu4e logs some of its internal workings to a log-buffer. See
|
|
|
|
|
`mu4e-visit-log'."
|
|
|
|
|
(interactive)
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(mu4e-log 'misc "logging disabled")
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(setq mu4e-debug (not mu4e-debug))
|
2012-05-02 16:26:13 +02:00
|
|
|
|
(mu4e-message "debug logging has been %s"
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(if mu4e-debug "enabled" "disabled"))
|
|
|
|
|
(mu4e-log 'misc "logging enabled"))
|
2012-04-16 17:31:48 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e-show-log ()
|
|
|
|
|
"Visit the mu4e debug log."
|
|
|
|
|
(interactive)
|
2012-04-24 18:17:42 +02:00
|
|
|
|
(let ((buf (get-buffer mu4e~log-buffer-name)))
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(unless (buffer-live-p buf)
|
2012-09-07 09:46:49 +02:00
|
|
|
|
(mu4e-warn "No debug log available"))
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(switch-to-buffer buf)))
|
2012-06-07 15:24:58 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defun mu4e-split-ranges-to-numbers (str n)
|
|
|
|
|
"Convert STR containing attachment numbers into a list of numbers.
|
|
|
|
|
STR is a string; N is the highest possible number in the list.
|
|
|
|
|
This includes expanding e.g. 3-5 into 3,4,5. If the letter
|
|
|
|
|
\"a\" ('all')) is given, that is expanded to a list with numbers [1..n]."
|
|
|
|
|
(let ((str-split (split-string str))
|
|
|
|
|
beg end list)
|
|
|
|
|
(dolist (elem str-split list)
|
|
|
|
|
;; special number "a" converts into all attachments 1-N.
|
|
|
|
|
(when (equal elem "a")
|
|
|
|
|
(setq elem (concat "1-" (int-to-string n))))
|
|
|
|
|
(if (string-match "\\([0-9]+\\)-\\([0-9]+\\)" elem)
|
|
|
|
|
;; we have found a range A-B, which needs converting
|
|
|
|
|
;; into the numbers A, A+1, A+2, ... B.
|
|
|
|
|
(progn
|
2012-06-10 10:33:31 +02:00
|
|
|
|
(setq beg (string-to-number (match-string 1 elem))
|
|
|
|
|
end (string-to-number (match-string 2 elem)))
|
2012-06-07 15:24:58 +02:00
|
|
|
|
(while (<= beg end)
|
|
|
|
|
(add-to-list 'list beg 'append)
|
|
|
|
|
(setq beg (1+ beg))))
|
|
|
|
|
;; else just a number
|
2012-06-10 10:33:31 +02:00
|
|
|
|
(add-to-list 'list (string-to-number elem) 'append)))
|
2012-06-07 15:24:58 +02:00
|
|
|
|
;; Check that all numbers are valid.
|
|
|
|
|
(mapc
|
2012-06-10 10:33:31 +02:00
|
|
|
|
#'(lambda (x)
|
|
|
|
|
(cond
|
|
|
|
|
((> x n)
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(mu4e-warn "Attachment %d bigger than maximum (%d)" x n))
|
2012-06-10 10:33:31 +02:00
|
|
|
|
((< x 1)
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(mu4e-warn "Attachment number must be greater than 0 (%d)" x))))
|
2012-06-07 15:24:58 +02:00
|
|
|
|
list)))
|
|
|
|
|
|
|
|
|
|
|
2012-04-24 08:08:15 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-16 17:31:48 +02:00
|
|
|
|
|
2012-05-16 19:47:13 +02:00
|
|
|
|
(defvar mu4e-imagemagick-identify "identify"
|
|
|
|
|
"Name/path of the Imagemagick 'identify' program.")
|
|
|
|
|
|
2013-07-06 10:51:33 +02:00
|
|
|
|
(defun mu4e-display-image (imgpath &optional maxwidth maxheight)
|
|
|
|
|
"Display image IMG at point; optionally specify MAXWIDTH and
|
|
|
|
|
MAXHEIGHT. Function tries to use imagemagick if available (ie.,
|
2012-06-04 11:29:07 +02:00
|
|
|
|
emacs was compiled with inmagemagick support); otherwise MAXWIDTH
|
2013-07-06 10:51:33 +02:00
|
|
|
|
and MAXHEIGHT are ignored."
|
2012-06-06 10:08:05 +02:00
|
|
|
|
(let* ((have-im (and (fboundp 'imagemagick-types)
|
|
|
|
|
(imagemagick-types))) ;; hmm, should check for specific type
|
|
|
|
|
(identify (and have-im maxwidth
|
|
|
|
|
(executable-find mu4e-imagemagick-identify)))
|
2012-06-04 11:29:07 +02:00
|
|
|
|
(props (and identify (shell-command-to-string
|
|
|
|
|
(format "%s -format '%%w' %s"
|
|
|
|
|
identify (shell-quote-argument imgpath)))))
|
|
|
|
|
(width (and props (string-to-number props)))
|
|
|
|
|
(img (if have-im
|
|
|
|
|
(if (> (or width 0) (or maxwidth 0))
|
|
|
|
|
(create-image imgpath 'imagemagick nil :width maxwidth)
|
|
|
|
|
(create-image imgpath 'imagemagick))
|
|
|
|
|
(create-image imgpath))))
|
|
|
|
|
;;(message "DISPLAY: %S %S" imgpath img)
|
2013-06-16 22:05:52 +02:00
|
|
|
|
(when img
|
2013-07-06 10:51:33 +02:00
|
|
|
|
(insert "\n")
|
|
|
|
|
(let ((size (image-size img))) ;; inspired by gnus..
|
2013-08-11 13:12:09 +02:00
|
|
|
|
(insert-char ?\n
|
|
|
|
|
(max 0 (round (- (window-height) (or maxheight (cdr size)) 1) 2)))
|
2013-08-11 14:28:30 +02:00
|
|
|
|
(insert-char ?\
|
2013-08-11 13:12:09 +02:00
|
|
|
|
(max 0 (round (- (window-width) (or maxwidth (car size))) 2)))
|
2013-07-06 10:51:33 +02:00
|
|
|
|
(insert-image img)))))
|
|
|
|
|
|
2012-05-16 19:47:13 +02:00
|
|
|
|
|
2012-06-11 11:03:37 +02:00
|
|
|
|
(defun mu4e-hide-other-mu4e-buffers ()
|
|
|
|
|
"Bury mu4e-buffers (main, headers, view) (and delete all windows
|
|
|
|
|
displaying it). Do _not_ bury the current buffer, though."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let ((curbuf (current-buffer)))
|
2012-06-23 17:14:49 +02:00
|
|
|
|
;; note: 'walk-windows' does not seem to work correctly when modifying
|
|
|
|
|
;; windows; therefore, the doloops here
|
|
|
|
|
(dolist (frame (frame-list))
|
|
|
|
|
(dolist (win (window-list frame nil))
|
2012-06-11 11:03:37 +02:00
|
|
|
|
(with-current-buffer (window-buffer win)
|
|
|
|
|
(unless (eq curbuf (current-buffer))
|
2012-06-18 17:03:24 +02:00
|
|
|
|
(when (member major-mode '(mu4e-headers-mode mu4e-view-mode))
|
2012-06-11 11:03:37 +02:00
|
|
|
|
(unless (one-window-p t)
|
2012-06-26 21:49:01 +02:00
|
|
|
|
(delete-window win))))))) nil t))
|
2012-06-11 23:15:06 +02:00
|
|
|
|
|
2012-06-11 11:03:37 +02:00
|
|
|
|
|
2012-06-14 20:54:24 +02:00
|
|
|
|
(defun mu4e-get-time-date (prompt)
|
|
|
|
|
"Determine the emacs time value for the time/date entered by user
|
|
|
|
|
after PROMPT. Formats are all that are accepted by
|
|
|
|
|
`parse-time-string'."
|
|
|
|
|
(let ((timestr (read-string (mu4e-format "%s" prompt))))
|
2012-09-03 10:15:27 +02:00
|
|
|
|
(apply 'encode-time (mu4e-parse-time-string timestr))))
|
2012-06-15 10:00:21 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
(defconst mu4e~main-about-buffer-name "*mu4e-about*"
|
|
|
|
|
"Name for the mu4e-about buffer.")
|
|
|
|
|
|
2013-03-10 20:58:05 +01:00
|
|
|
|
(define-derived-mode mu4e-about-mode org-mode "mu4e:about"
|
|
|
|
|
"Major mode for the mu4e About page, derived from `org-mode'.")
|
|
|
|
|
(define-key mu4e-about-mode-map (kbd "q") 'bury-buffer)
|
|
|
|
|
|
2012-06-15 10:00:21 +02:00
|
|
|
|
(defun mu4e-about ()
|
|
|
|
|
"Show a buffer with the mu4e-about text."
|
|
|
|
|
(interactive)
|
|
|
|
|
(with-current-buffer
|
|
|
|
|
(get-buffer-create mu4e~main-about-buffer-name)
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(insert mu4e-about)
|
2013-03-10 20:58:05 +01:00
|
|
|
|
(mu4e-about-mode)
|
2012-07-11 10:46:03 +02:00
|
|
|
|
(show-all)))
|
2012-06-15 10:00:21 +02:00
|
|
|
|
(switch-to-buffer mu4e~main-about-buffer-name)
|
|
|
|
|
(setq buffer-read-only t)
|
|
|
|
|
(goto-char (point-min)))
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
2012-12-09 14:07:21 +01:00
|
|
|
|
(defun mu4e-refresh-message (path maildir)
|
|
|
|
|
"Re-parse message at PATH and MAILDIR; if this works, we will
|
|
|
|
|
receive (:info add :path <path> :docid <docid>) as well as (:update
|
|
|
|
|
<msg-sexp>)."
|
|
|
|
|
(mu4e~proc-add path maildir))
|
2012-06-15 10:00:21 +02:00
|
|
|
|
|
2012-04-09 10:50:23 +02:00
|
|
|
|
(provide 'mu4e-utils)
|
|
|
|
|
;;; End of mu4e-utils.el
|