mu4e: Added new mu4e-split-view mode: single-window

Single-window mode is meant to minimize mu4e window operations (opening,
killing, resizing, etc) and buffer changes, while still retaining the
view and headers buffers. In addition, it replaces mu4e main view with a
minibuffer prompt containing the same information.
This commit is contained in:
Vladimir Sedach 2017-05-17 18:42:52 -07:00 committed by djcb
parent 34d5c61930
commit 9420d088eb
10 changed files with 281 additions and 236 deletions

View File

@ -105,7 +105,7 @@
Klaus Holst, Lukas Fürmetz, Magnus Therning, Maximilian Matthe,
Nicolas Richard, Piotr Trojanek, Prashant Sachdeva, Remco van 't
Veer, Stephen Eglen, Stig Brautaset, Thierry Volpiatto, Thomas
Moulia, Titus von der Malsburg, Yuri D'Elia
Moulia, Titus von der Malsburg, Yuri D'Elia, Vladimir Sedach
* Old news
:PROPERTIES:

View File

@ -621,6 +621,16 @@ tempfile)."
(push 'delete-frame message-exit-actions)
(push 'delete-frame message-postpone-actions)))
(defun mu4e~switch-back-to-mu4e-buffer ()
"Try to go back to some previous buffer, in the order view->headers->main."
(unless (eq mu4e-split-view 'single-window)
(if (buffer-live-p (mu4e-get-view-buffer))
(switch-to-buffer (mu4e-get-view-buffer))
(if (buffer-live-p (mu4e-get-headers-buffer))
(switch-to-buffer (mu4e-get-headers-buffer))
;; if all else fails, back to the main view
(when (fboundp 'mu4e) (mu4e))))))
(defun mu4e-sent-handler (docid path)
"Handler function, called with DOCID and PATH for the just-sent
message. For Forwarded ('Passed') and Replied messages, try to set
@ -634,15 +644,8 @@ the appropriate flag at the message forwarded or replied-to."
(when (and (buffer-file-name buf)
(string= (buffer-file-name buf) path))
(if message-kill-buffer-on-exit
(kill-buffer buf))))
;; now, try to go back to some previous buffer, in the order
;; view->headers->main
(if (buffer-live-p mu4e~view-buffer)
(switch-to-buffer mu4e~view-buffer)
(if (buffer-live-p mu4e~headers-buffer)
(switch-to-buffer mu4e~headers-buffer)
;; if all else fails, back to the main view
(when (fboundp 'mu4e) (mu4e))))
(kill-buffer buf))))
(mu4e~switch-back-to-mu4e-buffer)
(mu4e-message "Message sent"))
(defun mu4e-message-kill-buffer ()
@ -655,13 +658,8 @@ It restores mu4e window layout after killing the compose-buffer."
(when (not (equal current-buffer (current-buffer)))
;; Restore mu4e
(if mu4e-compose-in-new-frame
(delete-frame)
(if (buffer-live-p mu4e~view-buffer)
(switch-to-buffer mu4e~view-buffer)
(if (buffer-live-p mu4e~headers-buffer)
(switch-to-buffer mu4e~headers-buffer)
;; if all else fails, back to the main view
(when (fboundp 'mu4e) (mu4e))))))))
(delete-frame)
(mu4e~switch-back-to-mu4e-buffer)))))
(defun mu4e~compose-set-parent-flag (path)
"Set the 'replied' \"R\" flag on messages we replied to, and the
@ -735,9 +733,10 @@ is a symbol, one of `reply', `forward', `edit', `resend'
;; composing a new message, so that one will be replaced by the compose
;; window. The 10-or-so line headers buffer is not a good place to write
;; it...
(let ((viewwin (get-buffer-window mu4e~view-buffer)))
(when (window-live-p viewwin)
(select-window viewwin)))
(unless (eq mu4e-split-view 'single-window)
(let ((viewwin (get-buffer-window (mu4e-get-view-buffer))))
(when (window-live-p viewwin)
(select-window viewwin))))
;; talk to the backend
(mu4e~proc-compose compose-type decrypt docid)))))

View File

@ -115,7 +115,8 @@ non-nil."
(set (car cell) (cdr cell)))
(mu4e-context-vars context)))
(setq mu4e~context-current context)
(mu4e~main-view-real nil nil)
(unless (eq mu4e-split-view 'single-window)
(mu4e~main-view-real nil nil))
(mu4e-message "Switched context to %s" (mu4e-context-name context)))
context))

View File

@ -302,10 +302,9 @@ In the format needed for `mu4e-read-option'.")
(defun mu4e~headers-clear ()
"Clear the header buffer and related data structures."
(when (buffer-live-p mu4e~headers-buffer)
(when (buffer-live-p (mu4e-get-headers-buffer))
(let ((inhibit-read-only t))
(with-current-buffer mu4e~headers-buffer
(setq mu4e~view-msg nil)
(with-current-buffer (mu4e-get-headers-buffer)
(mu4e~mark-clear)
(erase-buffer)))))
@ -317,21 +316,20 @@ In the format needed for `mu4e-read-option'.")
(defun mu4e~headers-view-handler (msg)
"Handler function for displaying a message."
(mu4e-view msg mu4e~headers-buffer))
(mu4e-view msg))
(defun mu4e~headers-view-this-message-p (docid)
"Is DOCID currently being viewed?"
(let ((viewbuf (get-buffer mu4e~view-buffer-name)))
(when (and viewbuf (buffer-live-p viewbuf))
(with-current-buffer viewbuf
(eq docid (plist-get mu4e~view-msg :docid))))))
(when (buffer-live-p (mu4e-get-view-buffer))
(with-current-buffer (mu4e-get-view-buffer)
(eq docid (plist-get mu4e~view-msg :docid)))))
(defun mu4e~headers-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."
(when (buffer-live-p mu4e~headers-buffer)
(with-current-buffer mu4e~headers-buffer
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(let* ((docid (mu4e-message-field msg :docid))
(initial-message-at-point (mu4e~headers-docid-at-point))
(initial-column (current-column))
@ -353,13 +351,13 @@ headers."
;; first, remove the old one (otherwise, we'd have two headers with
;; the same docid...
(mu4e~headers-remove-handler docid t)
(mu4e~headers-remove-header docid t)
;; if we're actually viewing this message (in mu4e-view mode), we
;; update it; that way, the flags can be updated, as well as the path
;; (which is useful for viewing the raw message)
(when (mu4e~headers-view-this-message-p docid)
(mu4e-view msg mu4e~headers-buffer))
(mu4e-view msg))
;; 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
@ -376,26 +374,23 @@ headers."
(mu4e~headers-highlight docid))
(run-hooks 'mu4e-message-changed-hook))))))
(defun mu4e~headers-remove-handler (docid &optional skip-hook)
(defun mu4e~headers-remove-handler (docid)
"Remove handler, will be called when a message with DOCID has
been removed from the database. This function will hide the removed
message from the current list of headers. If the message is not
present, don't do anything.
If SKIP-HOOK is not nil, `mu4e-message-changed-hook' will be invoked."
(when (buffer-live-p mu4e~headers-buffer)
(with-current-buffer mu4e~headers-buffer
(mu4e~headers-remove-header docid t)
;; if we were viewing this message, close it now.
(when (and (mu4e~headers-view-this-message-p docid)
(buffer-live-p mu4e~view-buffer))
(with-current-buffer mu4e~view-buffer
;; XXX it seems this sometimes fails; investigate;
;; for now, just ignore the error
(ignore-errors
(kill-buffer-and-window))))
(unless skip-hook
(run-hooks 'mu4e-message-changed-hook)))))
(when (buffer-live-p (mu4e-get-headers-buffer))
(mu4e~headers-remove-header docid t))
;; if we were viewing this message, close it now.
(when (and (mu4e~headers-view-this-message-p docid)
(buffer-live-p (mu4e-get-view-buffer)))
(unless (eq mu4e-split-view 'single-window)
(mapc #'delete-window (get-buffer-window-list
(mu4e-get-view-buffer))))
(kill-buffer (mu4e-get-view-buffer)))
(run-hooks 'mu4e-message-changed-hook))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -596,16 +591,22 @@ found."
(defun mu4e~headers-header-handler (msg &optional point)
"Create a one line description of MSG in this buffer, at POINT,
if provided, or at the end of the buffer otherwise."
(when (buffer-live-p mu4e~headers-buffer)
(with-current-buffer mu4e~headers-buffer
(unless (and mu4e-headers-hide-predicate
(funcall mu4e-headers-hide-predicate msg))
(let ((docid (mu4e-message-field msg :docid))
(line (mapconcat
(lambda (f-w) (mu4e~headers-field-handler f-w msg))
mu4e-headers-fields " ")))
(setq line (mu4e~headers-line-handler msg line))
(mu4e~headers-add-header line docid point msg))))))
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(let ((line (mu4e~message-header-description msg)))
(when line
(mu4e~headers-add-header line (mu4e-message-field msg :docid)
point msg))))))
(defun mu4e~message-header-description (msg)
"Return a propertized description of MSG suitable for
displaying in the header view."
(unless (and mu4e-headers-hide-predicate
(funcall mu4e-headers-hide-predicate msg))
(let ((line (mapconcat
(lambda (f-w) (mu4e~headers-field-handler f-w msg))
mu4e-headers-fields " ")))
(mu4e~headers-line-handler msg line))))
(defconst mu4e~no-matches "No matching messages found")
(defconst mu4e~end-of-results "End of search results")
@ -613,8 +614,8 @@ if provided, or at the end of the buffer otherwise."
(defun mu4e~headers-found-handler (count)
"Create a one line description of the number of headers found
after the end of the search results."
(when (buffer-live-p mu4e~headers-buffer)
(with-current-buffer mu4e~headers-buffer
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(save-excursion
(goto-char (point-max))
(let ((inhibit-read-only t)
@ -907,8 +908,8 @@ no user-interaction ongoing."
(not (active-minibuffer-window))) ;; no user input only
;; rerun search if there's a live window with search results;
;; otherwise we'd trigger a headers view from out of nowhere.
(when (and (buffer-live-p mu4e~headers-buffer)
(window-live-p (get-buffer-window mu4e~headers-buffer)))
(when (and (buffer-live-p (mu4e-get-headers-buffer))
(window-live-p (get-buffer-window (mu4e-get-headers-buffer))))
(mu4e-headers-rerun-search))))
(define-derived-mode mu4e-headers-mode special-mode
@ -942,7 +943,7 @@ no user-interaction ongoing."
(defun mu4e~headers-highlight (docid)
"Highlight the header with DOCID, or do nothing if it's not found.
Also, unhighlight any previously highlighted headers."
(with-current-buffer mu4e~headers-buffer
(with-current-buffer (mu4e-get-headers-buffer)
(save-excursion
;; first, unhighlight the previously highlighted docid, if any
(when (and docid mu4e~highlighted-docid
@ -959,7 +960,7 @@ Also, unhighlight any previously highlighted headers."
"When there is a visible window for the headers buffer, make sure
to select it. This is needed when adding new headers, otherwise
adding a lot of new headers looks really choppy."
(let ((win (get-buffer-window mu4e~headers-buffer)))
(let ((win (get-buffer-window (mu4e-get-headers-buffer))))
(when win (select-window win))))
;;;; headers in the buffer are prefixed by an invisible string with the docid
@ -1028,7 +1029,7 @@ message plist, or nil if not found."
;;;; markers mark headers for
(defun mu4e~headers-mark (docid mark)
"(Visually) mark the header for DOCID with character MARK."
(with-current-buffer mu4e~headers-buffer
(with-current-buffer (mu4e-get-headers-buffer)
(let ((inhibit-read-only t) (oldpoint (point)))
(unless (mu4e~headers-goto-docid docid)
(mu4e-error "Cannot find message with docid %S" docid))
@ -1053,8 +1054,8 @@ message plist, or nil if not found."
"Add header STR with DOCID to the buffer at POINT if non-nil, or
at (point-max) otherwise. If MSG is not nil, add it as the
text-property `msg'."
(when (buffer-live-p mu4e~headers-buffer)
(with-current-buffer mu4e~headers-buffer
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(let ((inhibit-read-only t)
(is-first-header (= (point-min) (point-max))))
(save-excursion
@ -1071,7 +1072,7 @@ text-property `msg'."
"Remove header with DOCID at point.
When IGNORE-MISSING is non-nill, don't raise an error when the
docid is not found."
(with-current-buffer mu4e~headers-buffer
(with-current-buffer (mu4e-get-headers-buffer)
(if (mu4e~headers-goto-docid docid)
(let ((inhibit-read-only t))
(delete-region (line-beginning-position) (line-beginning-position 2)))
@ -1096,7 +1097,6 @@ the query history stack."
(when mu4e~headers-last-query
(mu4e~headers-push-query mu4e~headers-last-query 'past)))
(setq
mu4e~headers-buffer buf
mode-name "mu4e-headers"
mu4e~headers-last-query expr)
(add-to-list 'global-mode-string
@ -1124,26 +1124,29 @@ the query history stack."
(defun mu4e~headers-redraw-get-view-window ()
"Close all windows, redraw the headers buffer based on the value
of `mu4e-split-view', and return a window for the message view."
(mu4e-hide-other-mu4e-buffers)
(unless (buffer-live-p mu4e~headers-buffer)
(mu4e-error "No headers buffer available"))
(switch-to-buffer mu4e~headers-buffer)
;; kill the existing view win
(when (buffer-live-p mu4e~view-buffer)
(kill-buffer mu4e~view-buffer))
;; get a new view window
(setq mu4e~headers-view-win
(let* ((new-win-func
(cond
((eq mu4e-split-view 'horizontal) ;; split horizontally
'(split-window-vertically mu4e-headers-visible-lines))
((eq mu4e-split-view 'vertical) ;; split vertically
'(split-window-horizontally mu4e-headers-visible-columns)))))
(cond ((with-demoted-errors "Unable to split window: %S"
(eval new-win-func)))
(t ;; no splitting; just use the currently selected one
(selected-window)))))
mu4e~headers-view-win)
(if (eq mu4e-split-view 'single-window)
(or (and (buffer-live-p (mu4e-get-view-buffer))
(get-buffer-window (mu4e-get-view-buffer)))
(selected-window))
(mu4e-hide-other-mu4e-buffers)
(unless (buffer-live-p (mu4e-get-headers-buffer))
(mu4e-error "No headers buffer available"))
(switch-to-buffer (mu4e-get-headers-buffer))
;; kill the existing view buffer
(when (buffer-live-p (mu4e-get-view-buffer))
(kill-buffer (mu4e-get-view-buffer)))
;; get a new view window
(setq mu4e~headers-view-win
(let* ((new-win-func
(cond
((eq mu4e-split-view 'horizontal) ;; split horizontally
'(split-window-vertically mu4e-headers-visible-lines))
((eq mu4e-split-view 'vertical) ;; split vertically
'(split-window-horizontally mu4e-headers-visible-columns)))))
(cond ((with-demoted-errors "Unable to split window: %S"
(eval new-win-func)))
(t ;; no splitting; just use the currently selected one
(selected-window)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; search-based marking
@ -1518,7 +1521,9 @@ _not_ refresh the last search with the new setting for threading."
(with-current-buffer mu4e~headers-loading-buf
(let ((inhibit-read-only t))
(erase-buffer)
(local-set-key (kbd "q") 'kill-buffer-and-window)
(local-set-key (kbd "q") (if (eq mu4e-split-view 'single-window)
'kill-buffer
'kill-buffer-and-window))
(insert (propertize "Waiting for message..."
'face 'mu4e-system-face 'intangible t))))
mu4e~headers-loading-buf)
@ -1599,16 +1604,18 @@ docid. Otherwise, return nil."
(when docid
;; update all windows showing the headers buffer
(walk-windows
(lambda (win)
(when (eq (window-buffer win) mu4e~headers-buffer)
(set-window-point win (point))))
nil t)
;;(set-window-point (get-buffer-window mu4e~headers-buffer t) (point))
(lambda (win)
(when (eq (window-buffer win) (mu4e-get-headers-buffer))
(set-window-point win (point))))
nil t)
(if (eq mu4e-split-view 'single-window)
(when (eq (window-buffer) (mu4e-get-view-buffer))
(mu4e-headers-view-message))
;; update message view if it was already showing
(when (and mu4e-split-view (window-live-p mu4e~headers-view-win))
(mu4e-headers-view-message)))
;; attempt to highlight the new line, display the message
(mu4e~headers-highlight docid)
;; update message view if it was already showing
(when (and mu4e-split-view (window-live-p mu4e~headers-view-win))
(mu4e-headers-view-message))
docid)))
(defun mu4e-headers-next (&optional n)
@ -1670,8 +1677,8 @@ If N is negative shrink the headers window. When not in split-view
do nothing."
(interactive "P")
(let ((n (or n 1))
(hwin (get-buffer-window mu4e~headers-buffer)))
(when (and (buffer-live-p mu4e~view-buffer) (window-live-p hwin))
(hwin (get-buffer-window (mu4e-get-headers-buffer))))
(when (and (buffer-live-p (mu4e-get-view-buffer)) (window-live-p hwin))
(let ((n (or n 1)))
(case mu4e-split-view
;; emacs has weird ideas about what horizontal, vertical means...
@ -1713,29 +1720,33 @@ region if there is a region, then move to the next message."
This is a rather complex function, to ensure we don't disturb
other windows."
(interactive)
(unless (eq major-mode 'mu4e-headers-mode)
(mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode))
(mu4e-mark-handle-when-leaving)
(let ((curbuf (current-buffer)) (curwin (selected-window))
(headers-visible))
(walk-windows
(lambda (win)
(with-selected-window win
;; if we the view window connected to this one, kill it
(when (and (not (one-window-p win)) (eq mu4e~headers-view-win win))
(delete-window win)
(setq mu4e~headers-view-win nil)))
;; and kill any _other_ (non-selected) window that shows the current
;; buffer
(when (and
(eq curbuf (window-buffer win)) ;; does win show curbuf?
(not (eq curwin win)) ;; it's not the curwin?
(not (one-window-p))) ;; and not the last one?
(delete-window win)))) ;; delete it!
;; now, all *other* windows should be gone. kill ourselves, and return
;; to the main view
(kill-buffer)
(mu4e~main-view)))
(if (eq mu4e-split-view 'single-window)
(progn (mu4e-mark-handle-when-leaving)
(mu4e-quit))
(unless (eq major-mode 'mu4e-headers-mode)
(mu4e-error "Must be in mu4e-headers-mode (%S)" major-mode))
(mu4e-mark-handle-when-leaving)
(let ((curbuf (current-buffer))
(curwin (selected-window))
(headers-visible))
(walk-windows
(lambda (win)
(with-selected-window win
;; if we the view window connected to this one, kill it
(when (and (not (one-window-p win)) (eq mu4e~headers-view-win win))
(delete-window win)
(setq mu4e~headers-view-win nil)))
;; and kill any _other_ (non-selected) window that shows the current
;; buffer
(when (and
(eq curbuf (window-buffer win)) ;; does win show curbuf?
(not (eq curwin win)) ;; it's not the curwin?
(not (one-window-p))) ;; and not the last one?
(delete-window win)))) ;; delete it!
;; now, all *other* windows should be gone. kill ourselves, and return
;; to the main view
(kill-buffer)
(mu4e~main-view))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(provide 'mu4e-headers)

View File

@ -197,9 +197,13 @@ clicked."
(defun mu4e~main-view ()
"Create the mu4e main-view, and switch to it."
(mu4e~main-view-real nil nil)
(switch-to-buffer mu4e~main-buffer-name)
(goto-char (point-min))
(if (eq mu4e-split-view 'single-window)
(if (buffer-live-p (mu4e-get-headers-buffer))
(switch-to-buffer (mu4e-get-headers-buffer))
(mu4e~main-menu))
(mu4e~main-view-real nil nil)
(switch-to-buffer mu4e~main-buffer-name)
(goto-char (point-min)))
(add-to-list 'global-mode-string '(:eval (mu4e-context-label))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -209,16 +213,37 @@ clicked."
(defun mu4e~main-toggle-mail-sending-mode ()
"Toggle sending mail mode, either queued or direct."
(interactive)
(let ((curpos (point)))
(unless (file-directory-p smtpmail-queue-dir)
(mu4e-error "`smtpmail-queue-dir' does not exist"))
(setq smtpmail-queue-mail (not smtpmail-queue-mail))
(message
(concat "Outgoing mail will now be "
(if smtpmail-queue-mail "queued" "sent directly")))
(mu4e~main-view-real nil nil)
(goto-char curpos)))
(unless (file-directory-p smtpmail-queue-dir)
(mu4e-error "`smtpmail-queue-dir' does not exist"))
(setq smtpmail-queue-mail (not smtpmail-queue-mail))
(message (concat "Outgoing mail will now be "
(if smtpmail-queue-mail "queued" "sent directly")))
(unless (eq mu4e-split-view 'single-window)
(let ((curpos (point)))
(mu4e~main-view-real nil nil)
(goto-char curpos))))
(defun mu4e~main-menu ()
"mu4e main view in the minibuffer."
(interactive)
(let ((mu4e-hide-index-messages t))
(call-interactively
(lookup-key
mu4e-main-mode-map
(string
(read-key
(mu4e-format
"%s"
(concat
(mu4e~main-action-str "[j]ump " 'mu4e-jump-to-maildir)
(mu4e~main-action-str "[s]earch " 'mu4e-search)
(mu4e~main-action-str "[C]ompose " 'mu4e-compose-new)
(mu4e~main-action-str "[b]ookmarks " 'mu4e-headers-search-bookmark)
(mu4e~main-action-str "[;]Switch context " 'mu4e-context-switch)
(mu4e~main-action-str "[U]pdate " 'mu4e-update-mail-and-index)
(mu4e~main-action-str "[N]ews " 'mu4e-news)
(mu4e~main-action-str "[A]bout " 'mu4e-about)
(mu4e~main-action-str "[H]elp " 'mu4e-display-manual)))))))))
;; (progn
;; (define-key mu4e-compose-mode-map (kbd "C-c m") 'mu4e~main-toggle-mail-sending-mode)

View File

@ -108,10 +108,10 @@ is either a headers or view buffer."
`(cond
((eq major-mode 'mu4e-headers-mode) ,@body)
((eq major-mode 'mu4e-view-mode)
(when (buffer-live-p mu4e~view-headers-buffer)
(when (buffer-live-p (mu4e-get-headers-buffer))
(let* ((msg (mu4e-message-at-point))
(docid (mu4e-message-field msg :docid)))
(with-current-buffer mu4e~view-headers-buffer
(with-current-buffer (mu4e-get-headers-buffer)
(if (mu4e~headers-goto-docid docid)
,@body
(mu4e-error "cannot find message in headers buffer."))))))

View File

@ -563,10 +563,16 @@ Or go to the top level if there is none."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mu4e-last-query ()
"Get the most recent query or nil if there is none."
(when (buffer-live-p mu4e~headers-buffer)
(with-current-buffer mu4e~headers-buffer
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
mu4e~headers-last-query)))
(defun mu4e-get-view-buffer ()
(get-buffer mu4e~view-buffer-name))
(defun mu4e-get-headers-buffer ()
(get-buffer mu4e~headers-buffer-name))
(defun mu4e-select-other-view ()
"When the headers view is selected, select the message view (if
that has a live window), and vice versa."
@ -574,9 +580,9 @@ that has a live window), and vice versa."
(let* ((other-buf
(cond
((eq major-mode 'mu4e-headers-mode)
mu4e~view-buffer)
(mu4e-get-view-buffer))
((eq major-mode 'mu4e-view-mode)
mu4e~headers-buffer)))
(mu4e-get-headers-buffer))))
(other-win (and other-buf (get-buffer-window other-buf))))
(if (window-live-p other-win)
(select-window other-win)
@ -906,8 +912,8 @@ successful, call FUNC (if non-nil) afterwards."
(setq mu4e~update-timer nil))
(mu4e-clear-caches)
(mu4e~proc-kill)
;; kill all main/view/headers buffer
(mapcar
;; kill all mu4e buffers
(mapc
(lambda (buf)
(with-current-buffer buf
(when (member major-mode
@ -993,24 +999,21 @@ frame to display buffer BUF."
(when mu4e~progress-reporter
(progress-reporter-done mu4e~progress-reporter)
(setq mu4e~progress-reporter nil))
(let* ((status (process-status proc))
(code (process-exit-status proc))
(maybe-error (or (not (eq status 'exit)) (/= code 0)))
(buf (and (buffer-live-p mu4e~update-buffer) mu4e~update-buffer))
(win (and buf (get-buffer-window buf))))
(unless mu4e-hide-index-messages
(message nil))
(if maybe-error
(unless mu4e-hide-index-messages
(message nil))
(if (or (not (eq (process-status proc) 'exit))
(/= (process-exit-status proc) 0))
(progn
(when mu4e-index-update-error-warning
(mu4e-message "Update process returned with non-zero exit code")
(sit-for 5))
(when mu4e-index-update-error-continue
(mu4e-update-index)))
(mu4e-update-index))
(if (window-live-p win)
(with-selected-window win (kill-buffer-and-window))
(when (buffer-live-p buf) (kill-buffer buf)))))
(mu4e-update-index))
(when (buffer-live-p mu4e~update-buffer)
(unless (eq mu4e-split-view 'single-window)
(mapc #'delete-window (get-buffer-window-list mu4e~update-buffer)))
(kill-buffer mu4e~update-buffer)))
;; complicated function, as it:
;; - needs to check for errors
@ -1210,16 +1213,17 @@ and MAXHEIGHT are ignored."
"Bury mu4e-buffers (main, headers, view) (and delete all windows
displaying it). Do _not_ bury the current buffer, though."
(interactive)
(let ((curbuf (current-buffer)))
;; 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))
(with-current-buffer (window-buffer win)
(unless (eq curbuf (current-buffer))
(when (member major-mode '(mu4e-headers-mode mu4e-view-mode))
(when (eq t (window-deletable-p win))
(delete-window win))))))) t))
(unless (eq mu4e-split-view 'single-window)
(let ((curbuf (current-buffer)))
;; 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))
(with-current-buffer (window-buffer win)
(unless (eq curbuf (current-buffer))
(when (member major-mode '(mu4e-headers-mode mu4e-view-mode))
(when (eq t (window-deletable-p win))
(delete-window win))))))) t)))
(defun mu4e-get-time-date (prompt)

View File

@ -229,14 +229,17 @@ KEY) is still recognized as well, for backward-compatibility.")
(defcustom mu4e-split-view 'horizontal
"How to show messages / headers.
A symbol which is either:
* `horizontal': split horizontally (headers on top)
* `vertical': split vertically (headers on the left).
* anything else: don't split (show either headers or messages,
not both)
* `horizontal': split horizontally (headers on top)
* `vertical': split vertically (headers on the left).
* `single-window': view and headers in one window (mu4e will try not to
touch your window layout), main view in minibuffer
* anything else: don't split (show either headers or messages,
not both)
Also see `mu4e-headers-visible-lines'
and `mu4e-headers-visible-columns'."
:type '(choice (const :tag "Split horizontally" horizontal)
(const :tag "Split vertically" vertical)
(const :tag "Single window" single-window)
(const :tag "Don't split" nil))
:group 'mu4e-headers)
@ -848,7 +851,7 @@ argument, and returns a string. See the default value of
;; headers
(defconst mu4e~headers-buffer-name "*mu4e-headers*"
"Name of the buffer for message headers.")
(defvar mu4e~headers-buffer nil "Buffer for message headers.")
; view
(defconst mu4e~view-buffer-name "*mu4e-view*"
"Name for the message view buffer.")
@ -856,12 +859,8 @@ argument, and returns a string. See the default value of
(defconst mu4e~view-embedded-buffer-name " *mu4e-embedded-view*"
"Name for the embedded message view buffer.")
(defvar mu4e~view-buffer nil "The view buffer.")
(defvar mu4e~view-msg nil "The message being viewed in view mode.")
(defvar mu4e~view-headers-buffer nil
"The headers buffer connected to this view.")
(make-variable-buffer-local 'mu4e~view-msg)
(defvar mu4e~contacts nil
"Hash of that maps contacts (ie. 'name <e-mail>') to an integer

View File

@ -182,8 +182,11 @@ off, for example when using a read-only file-system."
(defvar mu4e~view-cited-hidden nil "Whether cited lines are hidden.")
(make-variable-buffer-local 'mu4e~view-cited-hidden)
(defvar mu4e~view-link-map nil
"A map of some number->url so we can jump to url by number.")
(make-variable-buffer-local 'mu4e~view-link-map)
(defvar mu4e~path-parent-docid-map (make-hash-table :test 'equal)
"A map of msg paths --> parent-docids.
@ -192,6 +195,7 @@ message extracted at some path.")
(defvar mu4e~view-attach-map nil
"A mapping of user-visible attachment number to the actual part index.")
(make-variable-buffer-local 'mu4e~view-attach-map)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mu4e-view-message-with-message-id (msgid)
@ -290,7 +294,7 @@ found."
(remove-overlays)))
(defun mu4e-view (msg headersbuf)
(defun mu4e-view (msg)
"Display the message MSG in a new buffer, and keep in sync with HDRSBUF.
'In sync' here means that moving to the next/previous message in
the the message view affects HDRSBUF, as does marking etc.
@ -304,11 +308,14 @@ marking if it still had that."
(if embedded
(mu4e~view-embedded-winbuf)
(get-buffer-create mu4e~view-buffer-name))))
;; note: mu4e~view-mark-as-read-maybe will pseudo-recursively call mu4e-view
;; again by triggering mu4e~view again as it marks the message as read
(with-current-buffer buf
(switch-to-buffer buf)
(unless (eq major-mode 'mu4e-view-mode)
(mu4e-view-mode))
(setq mu4e~view-msg msg)
(switch-to-buffer buf)
;; When MSG is unread, mu4e~view-mark-as-read-maybe will trigger
;; another call to mu4e-view (via mu4e~headers-update-handler as
;; the reply handler to mu4e~proc-move)
(when (or embedded (not (mu4e~view-mark-as-read-maybe msg)))
(let ((inhibit-read-only t))
(erase-buffer)
@ -319,11 +326,7 @@ marking if it still had that."
(mu4e~fontify-signature)
(mu4e~view-make-urls-clickable)
(mu4e~view-show-images-maybe msg)
(setq
mu4e~view-buffer buf
mu4e~view-headers-buffer headersbuf)
(when embedded (local-set-key "q" 'kill-buffer-and-window))
(mu4e-view-mode))))))
(when embedded (local-set-key "q" 'kill-buffer-and-window)))))))
(defun mu4e~view-get-property-from-event (prop)
"Get the property PROP at point, or the location of the mouse.
@ -803,16 +806,6 @@ FUNC should be a function taking two arguments:
\\{mu4e-view-mode-map}."
(use-local-map mu4e-view-mode-map)
(make-local-variable 'mu4e~view-headers-buffer)
(make-local-variable 'mu4e~view-msg)
(make-local-variable 'mu4e~view-link-map)
(make-local-variable 'mu4e~view-attach-map)
(make-local-variable 'mu4e~view-cited-hidden)
;; make permanent too, so they'll survive changing the mode
(put 'mu4e~view-link-map 'permanent-local t)
(put 'mu4e~view-attach-map 'permanent-local t)
;; show context in mode-string
(make-local-variable 'global-mode-string)
(add-to-list 'global-mode-string '(:eval (mu4e-context-label)))
@ -930,16 +923,16 @@ Also number them so they can be opened using `mu4e-view-go-to-url'."
"Evaluate BODY in the context of the headers buffer connected to
this view."
`(progn
(unless (buffer-live-p mu4e~view-headers-buffer)
(mu4e-error "no headers-buffer connected"))
(unless (buffer-live-p (mu4e-get-headers-buffer))
(mu4e-error "no headers buffer connected"))
(let* ((msg (mu4e-message-at-point))
(docid (mu4e-message-field msg :docid))
(curwin (selected-window)))
(docid (mu4e-message-field msg :docid)))
(unless docid
(mu4e-error "message without docid: action is not possible."))
(with-current-buffer mu4e~view-headers-buffer
(when (get-buffer-window)
(select-window (get-buffer-window)))
(with-current-buffer (mu4e-get-headers-buffer)
(unless (eq mu4e-split-view 'single-window)
(when (get-buffer-window)
(select-window (get-buffer-window))))
(if (mu4e~headers-goto-docid docid)
,@body
(mu4e-error "cannot find message in headers buffer."))))))
@ -969,8 +962,12 @@ message view. If this succeeds, return the new docid. Otherwise,
return nil."
(mu4e~view-in-headers-context
(mu4e~headers-prev-or-next-unread backwards))
(mu4e-select-other-view)
(mu4e-headers-view-message))
(if (eq mu4e-split-view 'single-window)
(when (eq (window-buffer) (mu4e-get-view-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(mu4e-headers-view-message)))
(mu4e-select-other-view)
(mu4e-headers-view-message)))
(defun mu4e-view-headers-prev-unread ()
"Move point to the previous unread message header in the headers
@ -1011,7 +1008,7 @@ or `html' or nil.")
(defun mu4e-view-refresh ()
"Redisplay the current message."
(interactive)
(mu4e-view mu4e~view-msg mu4e~view-headers-buffer)
(mu4e-view mu4e~view-msg)
(setq mu4e~view-cited-hidden nil))
(defun mu4e-view-action (&optional msg)
@ -1093,7 +1090,7 @@ Add this function to `mu4e-view-mode-hook' to enable this feature."
(defun mu4e-view-fill-long-lines ()
"Fill lines that are wider than the window width or `fill-column'."
(interactive)
(with-current-buffer mu4e~view-buffer
(with-current-buffer (mu4e-get-view-buffer)
(save-excursion
(let ((inhibit-read-only t)
(width (window-width (get-buffer-window (current-buffer)))))
@ -1582,38 +1579,42 @@ or message-at-point."
This is a rather complex function, to ensure we don't disturb
other windows."
(interactive)
(unless (eq major-mode 'mu4e-view-mode)
(mu4e-error "Must be in mu4e-view-mode (%S)" major-mode))
(let ((curbuf (current-buffer)) (curwin (selected-window))
(headers-win))
(walk-windows
(lambda (win)
;; check whether the headers buffer window is visible
(when (eq mu4e~view-headers-buffer (window-buffer win))
(setq headers-win win))
;; and kill any _other_ (non-selected) window that shows the current
;; buffer
(when
(and
(eq curbuf (window-buffer win)) ;; does win show curbuf?
(not (eq curwin win)) ;; but it's not the curwin?
(not (one-window-p))) ;; and not the last one on the frame?
(delete-window win)))) ;; delete it!
;; now, all *other* windows should be gone.
;; if the headers view is also visible, kill ourselves + window; otherwise
;; switch to the headers view
(if (window-live-p headers-win)
;; headers are visible
(progn
(kill-buffer-and-window) ;; kill the view win
(setq mu4e~headers-view-win nil)
(select-window headers-win)) ;; and switch to the headers win...
;; headers are not visible...
(progn
(kill-buffer)
(setq mu4e~headers-view-win nil)
(when (buffer-live-p mu4e~view-headers-buffer)
(switch-to-buffer mu4e~view-headers-buffer))))))
(if (eq mu4e-split-view 'single-window)
(when (buffer-live-p (mu4e-get-view-buffer))
(kill-buffer (mu4e-get-view-buffer)))
(unless (eq major-mode 'mu4e-view-mode)
(mu4e-error "Must be in mu4e-view-mode (%S)" major-mode))
(let ((curbuf (current-buffer))
(curwin (selected-window))
(headers-win))
(walk-windows
(lambda (win)
;; check whether the headers buffer window is visible
(when (eq (mu4e-get-headers-buffer) (window-buffer win))
(setq headers-win win))
;; and kill any _other_ (non-selected) window that shows the current
;; buffer
(when
(and
(eq curbuf (window-buffer win)) ;; does win show curbuf?
(not (eq curwin win)) ;; but it's not the curwin?
(not (one-window-p))) ;; and not the last one on the frame?
(delete-window win)))) ;; delete it!
;; now, all *other* windows should be gone.
;; if the headers view is also visible, kill ourselves + window; otherwise
;; switch to the headers view
(if (window-live-p headers-win)
;; headers are visible
(progn
(kill-buffer-and-window) ;; kill the view win
(setq mu4e~headers-view-win nil)
(select-window headers-win)) ;; and switch to the headers win...
;; headers are not visible...
(progn
(kill-buffer)
(setq mu4e~headers-view-win nil)
(when (buffer-live-p (mu4e-get-headers-buffer))
(switch-to-buffer (mu4e-get-headers-buffer))))))))
(provide 'mu4e-view)
;; end of mu4e-view

View File

@ -1060,6 +1060,11 @@ shown (default: 8).
@item @t{vertical}: display the message view on the
right side of the header view. Use @code{mu4e-headers-visible-columns} to set
the number of visible columns (default: 30).
@item @t{single-window}: single window mode. Single-window mode tries to
minimize mu4e window operations (opening, killing, resizing, etc) and buffer
changes, while still retaining the view and headers buffers. In addition, it
replaces mu4e main view with a minibuffer prompt containing the same
information.
@item anything else: don't do any splitting
@end itemize