]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch-show.el
Merge branch 'release'
[notmuch] / emacs / notmuch-show.el
index ddb9c65c6d05c43319df0e08752f0f44bbdcffc9..6d3149bf45a99d594833e62719fb19eac43fb9d4 100644 (file)
@@ -1,4 +1,4 @@
-;; notmuch-show.el --- displaying notmuch forests.
+;;; notmuch-show.el --- displaying notmuch forests.
 ;;
 ;; Copyright © Carl Worth
 ;; Copyright © David Edmondson
 ;; General Public License for more details.
 ;;
 ;; You should have received a copy of the GNU General Public License
-;; along with Notmuch.  If not, see <http://www.gnu.org/licenses/>.
+;; along with Notmuch.  If not, see <https://www.gnu.org/licenses/>.
 ;;
 ;; Authors: Carl Worth <cworth@cworth.org>
 ;;          David Edmondson <dme@dme.org>
 
+;;; Code:
+
 (eval-when-compile (require 'cl))
 (require 'mm-view)
 (require 'message)
@@ -180,9 +182,9 @@ each attachment handler is logged in buffers with names beginning
 
 (defcustom notmuch-show-stash-mlarchive-link-alist
   '(("Gmane" . "http://mid.gmane.org/")
-    ("MARC" . "http://marc.info/?i=")
-    ("Mail Archive, The" . "http://mid.mail-archive.com/")
-    ("LKML" . "http://lkml.kernel.org/r/")
+    ("MARC" . "https://marc.info/?i=")
+    ("Mail Archive, The" . "https://mid.mail-archive.com/")
+    ("LKML" . "https://lkml.kernel.org/r/")
     ;; FIXME: can these services be searched by `Message-Id' ?
     ;; ("MarkMail" . "http://markmail.org/")
     ;; ("Nabble" . "http://nabble.com/")
@@ -501,36 +503,37 @@ message at DEPTH in the current thread."
 
 (defun notmuch-show-toggle-part-invisibility (&optional button)
   (interactive)
-  (let* ((button (or button (button-at (point))))
-        (overlay (button-get button 'overlay))
-        (lazy-part (button-get button :notmuch-lazy-part)))
-    ;; We have a part to toggle if there is an overlay or if there is a lazy part.
-    ;; If neither is present we cannot toggle the part so we just return nil.
-    (when (or overlay lazy-part)
-      (let* ((show (button-get button :notmuch-part-hidden))
-            (new-start (button-start button))
-            (button-label (button-get button :base-label))
-            (old-point (point))
-            (properties (text-properties-at (button-start button)))
-            (inhibit-read-only t))
-       ;; Toggle the button itself.
-       (button-put button :notmuch-part-hidden (not show))
-       (goto-char new-start)
-       (insert "[ " button-label (if show " ]" " (hidden) ]"))
-       (set-text-properties new-start (point) properties)
-       (let ((old-end (button-end button)))
-         (move-overlay button new-start (point))
-         (delete-region (point) old-end))
-       (goto-char (min old-point (1- (button-end button))))
-       ;; Return nil if there is a lazy-part, it is empty, and we are
-       ;; trying to show it.  In all other cases return t.
-       (if lazy-part
-           (when show
-             (button-put button :notmuch-lazy-part nil)
-             (notmuch-show-lazy-part lazy-part button))
-         ;; else there must be an overlay.
-         (overlay-put overlay 'invisible (not show))
-         t)))))
+  (let ((button (or button (button-at (point)))))
+    (when button
+      (let ((overlay (button-get button 'overlay))
+           (lazy-part (button-get button :notmuch-lazy-part)))
+       ;; We have a part to toggle if there is an overlay or if there is a lazy part.
+       ;; If neither is present we cannot toggle the part so we just return nil.
+       (when (or overlay lazy-part)
+         (let* ((show (button-get button :notmuch-part-hidden))
+                (new-start (button-start button))
+                (button-label (button-get button :base-label))
+                (old-point (point))
+                (properties (text-properties-at (button-start button)))
+                (inhibit-read-only t))
+           ;; Toggle the button itself.
+           (button-put button :notmuch-part-hidden (not show))
+           (goto-char new-start)
+           (insert "[ " button-label (if show " ]" " (hidden) ]"))
+           (set-text-properties new-start (point) properties)
+           (let ((old-end (button-end button)))
+             (move-overlay button new-start (point))
+             (delete-region (point) old-end))
+           (goto-char (min old-point (1- (button-end button))))
+           ;; Return nil if there is a lazy-part, it is empty, and we are
+           ;; trying to show it.  In all other cases return t.
+           (if lazy-part
+               (when show
+                 (button-put button :notmuch-lazy-part nil)
+                 (notmuch-show-lazy-part lazy-part button))
+             ;; else there must be an overlay.
+             (overlay-put overlay 'invisible (not show))
+             t)))))))
 
 ;; Part content ID handling
 
@@ -606,7 +609,7 @@ will return nil if the CID is unknown or cannot be retrieved."
          (plist-get part :content)))
 
 (defun notmuch-show-insert-part-multipart/alternative (msg part content-type nth depth button)
-  (let ((chosen-type (car (notmuch-multipart/alternative-choose (notmuch-show-multipart/*-to-list part))))
+  (let ((chosen-type (car (notmuch-multipart/alternative-choose msg (notmuch-show-multipart/*-to-list part))))
        (inner-parts (plist-get part :content))
        (start (point)))
     ;; This inserts all parts of the chosen type rather than just one,
@@ -639,14 +642,12 @@ will return nil if the CID is unknown or cannot be retrieved."
   t)
 
 (defun notmuch-show-insert-part-multipart/signed (msg part content-type nth depth button)
-  (button-put button 'face 'notmuch-crypto-part-header)
-  ;; add signature status button if sigstatus provided
-  (if (plist-member part :sigstatus)
-      (let* ((from (notmuch-show-get-header :From msg))
-            (sigstatus (car (plist-get part :sigstatus))))
-       (notmuch-crypto-insert-sigstatus-button sigstatus from))
-    ;; if we're not adding sigstatus, tell the user how they can get it
-    (button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic MIME parts."))
+  (when button
+    (button-put button 'face 'notmuch-crypto-part-header))
+
+  ;; Insert a button detailing the signature status.
+  (notmuch-crypto-insert-sigstatus-button (car (plist-get part :sigstatus))
+                                         (notmuch-show-get-header :From msg))
 
   (let ((inner-parts (plist-get part :content))
        (start (point)))
@@ -660,18 +661,15 @@ will return nil if the CID is unknown or cannot be retrieved."
   t)
 
 (defun notmuch-show-insert-part-multipart/encrypted (msg part content-type nth depth button)
-  (button-put button 'face 'notmuch-crypto-part-header)
-  ;; add encryption status button if encstatus specified
-  (if (plist-member part :encstatus)
-      (let ((encstatus (car (plist-get part :encstatus))))
-       (notmuch-crypto-insert-encstatus-button encstatus)
-       ;; add signature status button if sigstatus specified
-       (if (plist-member part :sigstatus)
-           (let* ((from (notmuch-show-get-header :From msg))
-                  (sigstatus (car (plist-get part :sigstatus))))
-             (notmuch-crypto-insert-sigstatus-button sigstatus from))))
-    ;; if we're not adding encstatus, tell the user how they can get it
-    (button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic MIME parts."))
+  (when button
+    (button-put button 'face 'notmuch-crypto-part-header))
+
+  ;; Insert a button detailing the encryption status.
+  (notmuch-crypto-insert-encstatus-button (car (plist-get part :encstatus)))
+
+  ;; Insert a button detailing the signature status.
+  (notmuch-crypto-insert-sigstatus-button (car (plist-get part :sigstatus))
+                                         (notmuch-show-get-header :From msg))
 
   (let ((inner-parts (plist-get part :content))
        (start (point)))
@@ -840,21 +838,16 @@ will return nil if the CID is unknown or cannot be retrieved."
 ;; \f
 
 (defun notmuch-show-insert-bodypart-internal (msg part content-type nth depth button)
-  (let ((handlers (notmuch-show-handlers-for content-type)))
-    ;; Run the content handlers until one of them returns a non-nil
-    ;; value.
-    (while (and handlers
-               (not (condition-case err
-                        (funcall (car handlers) msg part content-type nth depth button)
-                      ;; Specifying `debug' here lets the debugger
-                      ;; run if `debug-on-error' is non-nil.
-                      ((debug error)
-                       (progn
-                               (insert "!!! Bodypart insert error: ")
-                               (insert (error-message-string err))
-                               (insert " !!!\n") nil)))))
-      (setq handlers (cdr handlers))))
-  t)
+  ;; Run the handlers until one of them succeeds.
+  (loop for handler in (notmuch-show-handlers-for content-type)
+       until (condition-case err
+                 (funcall handler msg part content-type nth depth button)
+               ;; Specifying `debug' here lets the debugger run if
+               ;; `debug-on-error' is non-nil.
+               ((debug error)
+                (insert "!!! Bodypart handler `" (prin1-to-string handler) "' threw an error:\n"
+                        "!!! " (error-message-string err) "\n")
+                nil))))
 
 (defun notmuch-show-create-part-overlays (button beg end)
   "Add an overlay to the part between BEG and END"
@@ -947,14 +940,22 @@ this part.")
 (defun notmuch-show-reply-insert-header-p-never (part hide)
   nil)
 
+(defun notmuch-show-reply-insert-header-p-trimmed (part hide)
+  (let ((mime-type (notmuch-show-mime-type part)))
+    (and (not (notmuch-match-content-type mime-type "multipart/*"))
+        (not hide))))
+
+(defun notmuch-show-reply-insert-header-p-minimal (part hide)
+  (let ((mime-type (notmuch-show-mime-type part)))
+    (and (notmuch-match-content-type mime-type "text/*")
+        (not hide))))
+
 (defun notmuch-show-insert-bodypart (msg part depth &optional hide)
   "Insert the body part PART at depth DEPTH in the current thread.
 
 HIDE determines whether to show or hide the part and the button
 as follows: If HIDE is nil, show the part and the button. If HIDE
-is t, hide the part initially and show the button. If HIDE is
-'no-buttons, show the part but do not add any buttons (this is
-useful for quoting in replies)."
+is t, hide the part initially and show the button."
 
   (let* ((content-type (downcase (plist-get part :content-type)))
         (mime-type (notmuch-show-mime-type part))
@@ -965,12 +966,10 @@ useful for quoting in replies)."
         (beg (point))
         ;; This default header-p function omits the part button for
         ;; the first (or only) part if this is text/plain.
-        (button (when (and (not (equal hide 'no-buttons))
-                    (funcall notmuch-show-insert-header-p-function part hide))
+        (button (when (funcall notmuch-show-insert-header-p-function part hide)
                   (notmuch-show-insert-part-header nth mime-type content-type (plist-get part :filename))))
         ;; Hide the part initially if HIDE is t, or if it is too long
-        ;; and we have a button to allow toggling (thus reply which
-        ;; uses 'no-buttons automatically includes long parts)
+        ;; and we have a button to allow toggling.
         (show-part (not (or (equal hide t)
                             (and long button))))
         (content-beg (point)))
@@ -980,8 +979,9 @@ useful for quoting in replies)."
 
     (if show-part
         (notmuch-show-insert-bodypart-internal msg part mime-type nth depth button)
-      (button-put button :notmuch-lazy-part
-                  (list msg part mime-type nth depth button)))
+      (when button
+       (button-put button :notmuch-lazy-part
+                   (list msg part mime-type nth depth button))))
 
     ;; Some of the body part handlers leave point somewhere up in the
     ;; part, so we make sure that we're down at the end.
@@ -1417,6 +1417,8 @@ reset based on the original query."
     (define-key map (kbd "<backtab>") 'notmuch-show-previous-button)
     (define-key map (kbd "TAB") 'notmuch-show-next-button)
     (define-key map "f" 'notmuch-show-forward-message)
+    (define-key map "F" 'notmuch-show-forward-open-messages)
+    (define-key map "b" 'notmuch-show-resend-message)
     (define-key map "l" 'notmuch-show-filter-thread)
     (define-key map "r" 'notmuch-show-reply-sender)
     (define-key map "R" 'notmuch-show-reply)
@@ -1699,12 +1701,23 @@ user decision and we should not override it."
        (notmuch-show-mark-read)
        (notmuch-show-set-prop :seen t)))
 
+(defvar notmuch-show--seen-has-errored nil)
+(make-variable-buffer-local 'notmuch-show--seen-has-errored)
+
 (defun notmuch-show-command-hook ()
   (when (eq major-mode 'notmuch-show-mode)
     ;; We need to redisplay to get window-start and window-end correct.
     (redisplay)
     (save-excursion
-      (funcall notmuch-show-mark-read-function (window-start) (window-end)))))
+      (condition-case err
+         (funcall notmuch-show-mark-read-function (window-start) (window-end))
+       ((debug error)
+        (unless notmuch-show--seen-has-errored
+          (setq notmuch-show--seen-has-errored 't)
+          (setq header-line-format
+                (concat header-line-format
+                        (propertize "  [some mark read tag changes may have failed]"
+                                    'face font-lock-warning-face)))))))))
 
 (defun notmuch-show-filter-thread (query)
   "Filter or LIMIT the current thread based on a new query string.
@@ -1841,8 +1854,26 @@ any effects from previous calls to
 (defun notmuch-show-forward-message (&optional prompt-for-sender)
   "Forward the current message."
   (interactive "P")
-  (with-current-notmuch-show-message
-   (notmuch-mua-new-forward-message prompt-for-sender)))
+  (notmuch-mua-new-forward-messages (list (notmuch-show-get-message-id))
+                                   prompt-for-sender))
+
+(put 'notmuch-show-forward-open-messages 'notmuch-prefix-doc
+     "... and prompt for sender")
+(defun notmuch-show-forward-open-messages (&optional prompt-for-sender)
+  "Forward the currently open messages."
+  (interactive "P")
+  (let ((open-messages (notmuch-show-get-message-ids-for-open-messages)))
+    (unless open-messages
+      (error "No open messages to forward."))
+    (notmuch-mua-new-forward-messages open-messages prompt-for-sender)))
+
+(defun notmuch-show-resend-message (addresses)
+  "Resend the current message."
+  (interactive (list (notmuch-address-from-minibuffer "Resend to: ")))
+  (when (y-or-n-p (concat "Confirm resend to " addresses " "))
+    (notmuch-show-view-raw-message)
+    (message-resend addresses)
+    (notmuch-bury-or-kill-this-buffer)))
 
 (defun notmuch-show-next-message (&optional pop-at-end)
   "Show the next message.
@@ -2370,3 +2401,5 @@ is destroyed when FN returns."
 
 
 (provide 'notmuch-show)
+
+;;; notmuch-show.el ends here