diff --git a/emacs/mu4e-main.el b/emacs/mu4e-main.el index 8b982bdd..a3f7145f 100644 --- a/emacs/mu4e-main.el +++ b/emacs/mu4e-main.el @@ -37,7 +37,7 @@ (define-key map "b" 'mu4e-search-bookmark) (define-key map "B" 'mu4e-search-bookmark-edit-first) - + (define-key map "s" 'mu4e-search) (define-key map "q" 'mu4e-quit) (define-key map "j" 'mu4e-jump-to-maildir) @@ -53,24 +53,14 @@ "Keymap for the *mu4e-main* buffer.") (fset 'mu4e-main-mode-map mu4e-main-mode-map) -(defun mu4e-main-mode () +(define-derived-mode mu4e-main-mode special-mode "mu4e:main" "Major mode for the mu4e main screen. - \\{mu4e-main-mode-map}." - (interactive) - - (kill-all-local-variables) (use-local-map mu4e-main-mode-map) - (setq - major-mode 'mu4e-main-mode - mode-name "mu4e-main" truncate-lines t - buffer-read-only t overwrite-mode 'overwrite-mode-binary)) -(put 'mu4e-main-mode 'mode-class 'special) - (defun mu4e-action-str (str &optional func-or-shortcut) "Highlight the first occurence of [..] in STR. If @@ -181,7 +171,7 @@ split-window." (error "`smtp-queue-dir' does not exist")) (setq smtpmail-queue-mail (not smtpmail-queue-mail)) (message - (concat "Outgoing mail will now be " + (concat "Outgoing mail will now be " (if smtpmail-queue-mail "queued" "sent directly"))) (mu4e-main-view)) diff --git a/emacs/mu4e-send.el b/emacs/mu4e-send.el index f95a208d..44ef06a7 100644 --- a/emacs/mu4e-send.el +++ b/emacs/mu4e-send.el @@ -282,15 +282,9 @@ use the new docid. Returns the full path to the new message." (new (mu4e-send-create-new)) (t (error "unsupported compose-type %S" compose-type))))) (when str - (with-temp-file draft - (insert str) - (write-file draft))) - - ;; save our file immediately, add add it to the db; thus, we can retrieve - ;; the new docid from `mu4e-path-docid-map'. - (mu4e-proc-add draft mu4e-drafts-folder) - draft)) - + (with-current-buffer (find-file-noselect draft) + (insert str))) + draft)) ;; return the draft buffer file (defun mu4e-send-compose-handler (compose-type &optional original-msg includes) "Create a new draft message, or open an existing one. @@ -330,9 +324,6 @@ using Gnus' `message-mode'." (plist-get original-msg :path) (error "unsupported compose-type %S" compose-type))))) - (unless (file-readable-p draft) - (error "Cannot read %s" draft)) - (find-file draft) (message-mode) @@ -344,15 +335,21 @@ using Gnus' `message-mode'." (mml-attach-file (plist-get att :file-name) (plist-get att :mime-type)))) - (make-local-variable 'write-file-functions) + (make-local-variable 'after-save-hook) ;; update the db when the file is saved...] - (add-to-list 'write-file-functions + (add-hook 'after-save-hook (lambda() (mu4e-proc-add (buffer-file-name) mu4e-drafts-folder))) - ;; hook our functions up with sending of the message - (add-hook 'message-sent-hook 'mu4e-send-save-copy-maybe nil t) - (add-hook 'message-sent-hook 'mu4e-send-set-parent-flag nil t) + ;; notify the backend that a message has been sent. The backend will respond + ;; with (:sent ...) sexp, which is handled in . + (add-hook 'message-sent-hook + (lambda () + (mu4e-proc-sent (buffer-file-name) mu4e-drafts-folder))) + + ;; register the function; this function will be called when the '(:sent...)' + ;; message is received (see mu4e-proc.el) with parameters docid and path + (setq mu4e-proc-sent-func 'mu4e-sent-handler) (let ((message-hidden-headers `("^References:" "^Face:" "^X-Face:" "^X-Draft-From:" @@ -365,18 +362,27 @@ using Gnus' `message-mode'." -(defun mu4e-send-save-copy-maybe () - "If `mu4e-save-sent-messages-behavior' is a symbol 'delete, move - the message in this buffer to the sent folder. Otherwise, delete - the draft message. This is meant to be called from message mode's - `message-sent-hook'." - (let ((docid (gethash (buffer-file-name) mu4e-path-docid-map))) - (unless docid (error "unknown message (%S)" (buffer-file-name))) +(defun mu4e-sent-handler (docid path) + "Handler function, called with DOCID and PATH for the just-sent +message." + ;; for Forward ('Passed') and Replied messages, try to set the appropriate + ;; flag at the message forwarded or replied-to + (mu4e-send-set-parent-flag docid path) + ;; handle the draft -- should it be moved to the send folder, or elsewhere? + (mu4e-send-save-copy-maybe docid path)) + + +(defun mu4e-send-save-copy-maybe (docid path) + "Handler function, called with DOCID and PATH for the just-sent +message." + ;; first, what to do with the draft message in PATH? + (with-current-buffer (find-file-noselect path) (if (eq mu4e-sent-messages-behavior 'delete) (progn (save-buffer) (mu4e-proc-remove-msg docid)) ;; remove it - (progn ;; try to save the message the sent folder + ;; otherwise, + (progn ;; prepare the message for saving (save-excursion (goto-char (point-min)) ;; remove the --text follows this line-- separator @@ -390,10 +396,9 @@ using Gnus' `message-mode'." (mu4e-proc-move-msg docid mu4e-sent-folder "-T-D+S"))))))) - -(defun mu4e-send-set-parent-flag () - "Set the 'replied' flag on messages we replied to, and the -'passed' flag on message we have forwarded. +(defun mu4e-send-set-parent-flag (docid path) + "Set the 'replied' \"R\" flag on messages we replied to, and the +'passed' \"F\" flag on message we have forwarded. If a message has a 'in-reply-to' header, it is considered a reply to the message with the corresponding message id. If it does not @@ -404,27 +409,25 @@ corresponding with the /last/ message-id in the references header. Now, if the message has been determined to be either a forwarded message or a reply, we instruct the server to update that message with resp. the 'P' (passed) flag for a forwarded message, or the -'R' flag for a replied message. - -This is meant to be called from message mode's -`message-sent-hook'." - (let ((in-reply-to (message-fetch-field "in-reply-to")) - (forwarded-from) - (references (message-fetch-field "references"))) - (unless in-reply-to - (when references - (with-temp-buffer ;; inspired by `message-shorten-references'. - (insert references) - (goto-char (point-min)) - (let ((refs)) - (while (re-search-forward "<[^ <]+@[^ <]+>" nil t) - (push (match-string 0) refs)) - ;; the last will the first - (setq forwarded-from (first refs)))))) - ;; remove the <> - (when (and in-reply-to (string-match "<\\(.*\\)>" in-reply-to)) - (mu4e-proc-flag (match-string 1 in-reply-to) "+R")) - (when (and forwarded-from (string-match "<\\(.*\\)>" forwarded-from)) - (mu4e-proc-flag (match-string 1 forwarded-from) "+P")))) +'R' flag for a replied message." + (with-current-buffer (find-file-noselect path) + (let ((in-reply-to (message-fetch-field "in-reply-to")) + (forwarded-from) + (references (message-fetch-field "references"))) + (unless in-reply-to + (when references + (with-temp-buffer ;; inspired by `message-shorten-references'. + (insert references) + (goto-char (point-min)) + (let ((refs)) + (while (re-search-forward "<[^ <]+@[^ <]+>" nil t) + (push (match-string 0) refs)) + ;; the last will the first + (setq forwarded-from (first refs)))))) + ;; remove the <> + (when (and in-reply-to (string-match "<\\(.*\\)>" in-reply-to)) + (mu4e-proc-flag (match-string 1 in-reply-to) "+R")) + (when (and forwarded-from (string-match "<\\(.*\\)>" forwarded-from)) + (mu4e-proc-flag (match-string 1 forwarded-from) "+P"))))) (provide 'mu4e-send) diff --git a/man/mu-server.1 b/man/mu-server.1 index 5d2c5b1c..43b91c94 100644 --- a/man/mu-server.1 +++ b/man/mu-server.1 @@ -189,6 +189,22 @@ Using the \fBadd\fR command, we can add a message to the database. <- (:info add :path :docid ) .fi + +.TP +.B sent + +With the \fBsent\fR command, we tell the backend that a message will be sent. + +.nf +-> sent +<- (:sent :path :docid ) +.fi + +In the frontend, this message can then be used to trigger moving the message +from the draft folder to the sent folder, setting the Replied/Passed flags on +the parent messages. + + .TP .B mkdir diff --git a/src/mu-cmd-server.c b/src/mu-cmd-server.c index 2715d25a..eb31d886 100644 --- a/src/mu-cmd-server.c +++ b/src/mu-cmd-server.c @@ -170,6 +170,7 @@ enum _Cmd { CMD_QUIT, CMD_REMOVE, CMD_SAVE, + CMD_SENT, CMD_PING, CMD_VIEW, @@ -197,6 +198,7 @@ cmd_from_string (const char *str) { CMD_QUIT, "quit"}, { CMD_REMOVE, "remove" }, { CMD_SAVE, "save"}, + { CMD_SENT, "sent"}, { CMD_PING, "ping"}, { CMD_VIEW, "view"} }; @@ -856,7 +858,7 @@ index_msg_cb (MuIndexStats *stats, void *user_data) if (MU_CAUGHT_SIGNAL) return MU_STOP; - if (stats->_processed % 500) + if (stats->_processed % 1000) return MU_OK; send_expr ("(:info index :status running " @@ -867,14 +869,16 @@ index_msg_cb (MuIndexStats *stats, void *user_data) } static MuError -cmd_add (MuStore *store, GSList *args, GError **err) +cmd_add_or_sent (MuStore *store, Cmd add_or_sent, GSList *args, GError **err) { unsigned docid; const char *path, *maildir; gchar *escpath; + g_return_val_if_fail (add_or_sent == CMD_ADD || add_or_sent == CMD_SENT, + MU_ERROR_INTERNAL); return_if_fail_param_num (args, 2, 2, - "usage: add "); + "usage: add|sent "); path = (const char*)args->data; maildir = (const char*)g_slist_nth (args, 1)->data; @@ -885,15 +889,20 @@ cmd_add (MuStore *store, GSList *args, GError **err) "failed to add path '%s'", path); escpath = mu_str_escape_c_literal (path, TRUE); - send_expr ("(:info add :path %s :docid %u)", escpath, docid); + + if (add_or_sent == CMD_ADD) + send_expr ("(:info add :path %s :docid %u)", + escpath, docid); + else + send_expr ("(:sent t :path %s :docid %u)", + escpath, docid); + g_free (escpath); return MU_OK; } - - static MuError cmd_index (MuStore *store, GSList *args, GError **err) { @@ -953,7 +962,8 @@ handle_command (Cmd cmd, MuStore *store, MuQuery *query, GSList *args, switch (cmd) { - case CMD_ADD: rv = cmd_add (store, args, err); break; + case CMD_ADD: rv = cmd_add_or_sent (store, CMD_ADD, + args, err); break; case CMD_COMPOSE: rv = cmd_compose (store, args, err); break; case CMD_FIND: rv = cmd_find (store, query, args, err); break; case CMD_FLAG: rv = cmd_flag (store, query, args, err); break; @@ -964,6 +974,8 @@ handle_command (Cmd cmd, MuStore *store, MuQuery *query, GSList *args, case CMD_QUIT: rv = cmd_quit (args, err); break; case CMD_REMOVE: rv = cmd_remove (store, args, err); break; case CMD_SAVE: rv = cmd_save (store, args, err); break; + case CMD_SENT: rv = cmd_add_or_sent (store, CMD_SENT, + args, err); break; case CMD_PING: rv = cmd_ping (store, args, err); break; case CMD_VIEW: rv = cmd_view (store, query, args, err); break;