diff --git a/mu4e/mu4e-headers.el b/mu4e/mu4e-headers.el index 99a98164..3d37e6ce 100644 --- a/mu4e/mu4e-headers.el +++ b/mu4e/mu4e-headers.el @@ -149,16 +149,6 @@ match. * PARAM-FUNC is function that is evaluated once, and its value is then passed to PREDICATE-FUNC as PARAM. This is useful for getting user-input.") -(defvar mu4e-headers-sortfield :date - "Field to sort the headers by. -Field must be a symbol, one of: :date, :subject, :size, :prio, -:from, :to.") - -(defvar mu4e-headers-sort-revert t - "Whether to revert the sort-order. -i.e. Z>A instead of A>Z. When sorting by date, it's useful to go -from biggest to smallest, so newest messages come first.") - (defvar mu4e-headers-show-threads t "Whether to show threads in the headers list.") @@ -181,7 +171,7 @@ followed by the docid, followed by `mu4e~headers-docid-post'.") (defvar mu4e~headers-view-win nil "The view window connected to this headers view.") -(defvar mu4e~headers-sortfield-choices +(defvar mu4e~headers-sort-field-choices '( ("date" . :date) ("from" . :from) ("prio" . :prio) @@ -353,6 +343,8 @@ date. The formats used for date and time are (format-time-string mu4e-headers-time-format date) (format-time-string mu4e-headers-date-format date)))) + + ;; note: this function is very performance-sensitive (defun mu4e~headers-header-handler (msg &optional point) "Create a one line description of MSG in this buffer, at POINT, @@ -599,9 +591,9 @@ after the end of the search results." (define-key menumap [previous] '("Previous" . mu4e-headers-prev)) (define-key menumap [sepa4] '("--"))) map))) - (fset 'mu4e-headers-mode-map mu4e-headers-mode-map) + (defun mu4e~header-line-format () "Get the format for the header line." (cons @@ -617,8 +609,8 @@ after the end of the search results." (downarrow (if mu4e-use-fancy-chars " ▼" " V")) ;; triangle to mark the sorted-by column (arrow - (when (and sortable (eq (car item) mu4e-headers-sortfield)) - (if mu4e-headers-sort-revert downarrow uparrow))) + (when (and sortable (eq (car item) mu4e~headers-sort-field)) + (if (eq mu4e~headers-sort-direction 'descending) downarrow uparrow))) (name (concat (plist-get info :shortname) arrow)) (map (make-sparse-keymap))) (when sortable @@ -629,10 +621,9 @@ after the end of the search results." (let* ((obj (posn-object (event-start e))) (field (and obj (get-text-property 0 'field (car obj))))) - (if (eq field mu4e-headers-sortfield) - (setq mu4e-headers-sort-revert (not mu4e-headers-sort-revert)) - (setq mu4e-headers-sortfield field))) - (mu4e-headers-rerun-search)))) + ;; "t": if we're already sorted by field, the sort-order is + ;; changed + (mu4e-headers-change-sorting field t))))) (concat (propertize (if width @@ -812,6 +803,10 @@ docid is not found." (delete-region (line-beginning-position) (line-beginning-position 2))) (unless ignore-missing (mu4e-error "Cannot find message with docid %S" docid))))) + + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e~headers-search-execute (expr ignore-history) "Search in the mu database for EXPR, and switch to the output @@ -838,8 +833,8 @@ the query history stack." (mu4e~proc-find expr mu4e-headers-show-threads - mu4e-headers-sortfield - mu4e-headers-sort-revert + mu4e~headers-sort-field + mu4e~headers-sort-direction (unless mu4e-headers-full-search mu4e-search-results-limit)))) (defun mu4e~headers-redraw-get-view-window () @@ -1088,26 +1083,50 @@ the last search expression." (format "(%s) AND %s" mu4e~headers-last-query filter))) -(defun mu4e-headers-change-sorting (&optional dont-refresh) - "Interactively change the sorting/threading parameters. -With prefix-argument, do _not_ refresh the last search with the -new parameters." - (interactive "P") - (let* ((sortfield - (mu4e-read-option "Sortfield: " mu4e~headers-sortfield-choices)) - (revert - (mu4e-read-option "Direction: " - '(("ascending" . nil) ("descending" . t))))) +(defvar mu4e~headers-sort-field :date + "Field to sort the headers by. +Field must be a symbol, one of: :date, :subject, :size, :prio, +:from, :to.") + +(defvar mu4e~headers-sort-direction 'descending + "Direction to sort by; a symbol either `descending' (sorting + Z->A) or `ascending' (sorting A->Z).") + +(defun mu4e-headers-change-sorting (&optional field dir) + "Change the sorting/threading parameters. +FIELD is the field to sort by; DIR is a symbol: either 'ascending, +'descending, 't (meaning: if FIELD is the same as the current +sortfield, change the sort-order) or nil (ask the user)." + (interactive) + (let* ((field + (or field + (mu4e-read-option "Sortfield: " mu4e~headers-sort-field-choices))) + ;; note: 'sortable' is either a boolean (meaning: if non-nil, this is + ;; sortable field), _or_ another field (meaning: sort by this other field). + (sortable (plist-get (cdr (assoc field mu4e-header-info)) :sortable)) + ;; error check + (sortable + (if sortable + sortable + (mu4e-error "Not a sortable field"))) + (sortfield (if (booleanp sortable) field sortable)) + (dir + (case dir + ((ascending descending) dir) + ;; change the sort order if field = curfield + (t + (if (eq sortfield mu4e~headers-sort-field) + (if (eq mu4e~headers-sort-direction 'ascending) + 'descending 'ascending))) + (mu4e-read-option "Direction: " + '(("ascending" . 'ascending) ("descending" . 'descending)))))) (setq - mu4e-headers-sortfield sortfield - mu4e-headers-sort-revert revert) ;; "descending" means "revert" - (mu4e-message "Sorting by %s (%s)%s" + mu4e~headers-sort-field sortfield + mu4e~headers-sort-direction dir) + (mu4e-message "Sorting by %s (%s)" (symbol-name sortfield) - (if revert "descending" "ascending") - (if dont-refresh - " (press 'g' to refresh)" "")) - (unless dont-refresh - (mu4e-headers-rerun-search)))) + (symbol-name mu4e~headers-sort-direction)) + (mu4e-headers-rerun-search))) (defun mu4e-headers-toggle-threading (&optional dont-refresh) "Toggle threading on/off for the search results. diff --git a/mu4e/mu4e-proc.el b/mu4e/mu4e-proc.el index 06567556..48a569ce 100644 --- a/mu4e/mu4e-proc.el +++ b/mu4e/mu4e-proc.el @@ -317,23 +317,24 @@ In particular, backslashes and double-quotes." (let ((esc (replace-regexp-in-string "\\\\" "\\\\\\\\" query))) (replace-regexp-in-string "\"" "\\\\\"" esc))) -(defun mu4e~proc-find (query threads sortfield revert maxnum) +(defun mu4e~proc-find (query threads sortfield sortdir maxnum) "Start a database query for QUERY. If THREADS is non-nil, show results in threaded fasion, SORTFIELD is a symbol describing the field to sort by (or nil); see -`mu4e~headers-sortfield-choices'. If REVERT is non-nil, sort Z->A -instead of A->Z. MAXNUM determines the maximum number of results -to return, or nil for 'unlimited'. For each result found, a -function is called, depending on the kind of result. The -variables `mu4e-error-func' contain the function that will be -called for, resp., a message (header row) or an error." +`mu4e~headers-sortfield-choices'. If SORT is `descending', sort +Z->A, if it's `ascending', sort A->Z. MAXNUM determines the maximum +number of results to return, or nil for 'unlimited'. For each +result found, a function is called, depending on the kind of +result. The variables `mu4e-error-func' contain the function that +will be called for, resp., a message (header row) or an error." (mu4e~proc-send-command "find query:\"%s\" threads:%s sortfield:%s reverse:%s maxnum:%d" (mu4e~proc-escape-query query) (if threads "true" "false") ;; sortfield is e.g. ':subject'; this removes the ':' (if (null sortfield) "nil" (substring (symbol-name sortfield) 1)) - (if revert "true" "false") + ;; TODO: use ascending/descending in backend too (it's clearer than 'reverse' + (if (eq sortdir 'descending) "true" "false") (if maxnum maxnum -1))) (defun mu4e~proc-move (docid-or-msgid &optional maildir flags) diff --git a/mu4e/mu4e-vars.el b/mu4e/mu4e-vars.el index bbfadfc5..3f9ec7db 100644 --- a/mu4e/mu4e-vars.el +++ b/mu4e/mu4e-vars.el @@ -492,7 +492,7 @@ I.e. a message with the draft flag set." ( :name "Date" :shortname "Date" :help "Date/time when the message was written." - :sortable t)) + :sortable :date)) (:flags . ( :name "Flags" :shortname "Flgs" @@ -544,13 +544,19 @@ I.e. a message with the draft flag set." :help "Recipient of the message" :sortable t))) "An alist of all possible header fields and information about them. -This is used in the UI (the column headers in the header list, -and the fields the message view). +This is used in the user-interface (the column headers in the header list, and +the fields the message view). Most fields should be self-explanatory. A special one is `:from-or-to', which is equal to `:from' unless `:from' matches -`mu4e-user-mail-address-regexp', in which case it will be equal -to `:to'.") +`mu4e-user-mail-address-regexp', in which case it will be equal to +`:to'. + +Furthermore, the property `:sortable' determines whether we can +sort by this field. This can be either a boolean (nil or t), or a +symbol for /another/ field. For example, the `:human-date' field +uses `:date' for that. +") (defvar mu4e-custom-header-info nil "A list like `mu4e-custom-header-info', but for custom headers.