From: Dmitry Kurochkin Date: Fri, 17 Feb 2012 07:48:02 +0000 (+0400) Subject: [PATCH v9 1/2] emacs: User-defined sections in notmuch-hello X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=ffc93d846658acd6e2a44d304f0fbce35ea83e28;p=notmuch-archives.git [PATCH v9 1/2] emacs: User-defined sections in notmuch-hello --- diff --git a/82/e206279a37c6b80ec80f44aa761cc753a10d61 b/82/e206279a37c6b80ec80f44aa761cc753a10d61 new file mode 100644 index 000000000..b1c84e503 --- /dev/null +++ b/82/e206279a37c6b80ec80f44aa761cc753a10d61 @@ -0,0 +1,771 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 08F66429E55 + for ; Thu, 16 Feb 2012 23:49:40 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: 1.061 +X-Spam-Level: * +X-Spam-Status: No, score=1.061 tagged_above=-999 required=5 + tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, + FREEMAIL_FROM=0.001, RCVD_IN_BL_SPAMCOP_NET=1.246, + RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_SORBS_WEB=0.614] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id MVpayFbz5TXH for ; + Thu, 16 Feb 2012 23:49:33 -0800 (PST) +Received: from mail-bk0-f53.google.com (mail-bk0-f53.google.com + [209.85.214.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) + (No client certificate requested) + by olra.theworths.org (Postfix) with ESMTPS id 244B8429E42 + for ; Thu, 16 Feb 2012 23:49:33 -0800 (PST) +Received: by mail-bk0-f53.google.com with SMTP id it16so3104161bkc.26 + for ; Thu, 16 Feb 2012 23:49:32 -0800 (PST) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; + h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; + bh=eRvB1Ye+iV0nwD87OiNW2PlYEyvsv55+Gc3/r8AYGCU=; + b=tFIAJIF7S6VxvwbtBEXZXUTcJy4Mq0MtKRSA8IaUFcKhVI7G3dTnPb9hXK/nv5h9Qn + FDsndmq0JoBwuYf4pJcWFAWjkoMwpmSXPcuPlRbI5dhDiF99J1c2iib+AU1iEHdkr8Rb + pf2LOO5a9LXAUvrz5TaRiq8IyC4Ic4Mu7ft6I= +Received: by 10.204.129.7 with SMTP id m7mr3682642bks.13.1329464972742; + Thu, 16 Feb 2012 23:49:32 -0800 (PST) +Received: from localhost ([217.26.0.107]) + by mx.google.com with ESMTPS id e13sm21132048bku.12.2012.02.16.23.49.31 + (version=TLSv1/SSLv3 cipher=OTHER); + Thu, 16 Feb 2012 23:49:32 -0800 (PST) +From: Dmitry Kurochkin +To: notmuch@notmuchmail.org +Subject: [PATCH v9 1/2] emacs: User-defined sections in notmuch-hello +Date: Fri, 17 Feb 2012 11:48:02 +0400 +Message-Id: <1329464884-1653-2-git-send-email-dmitry.kurochkin@gmail.com> +X-Mailer: git-send-email 1.7.9 +In-Reply-To: <1329464884-1653-1-git-send-email-dmitry.kurochkin@gmail.com> +References: <1310079227-19120-1-git-send-email-daniel.schoepe@googlemail.com> + <1329464884-1653-1-git-send-email-dmitry.kurochkin@gmail.com> +Cc: Daniel Schoepe +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Fri, 17 Feb 2012 07:49:40 -0000 + +From: Daniel Schoepe + +This patch makes the notmuch-hello screen fully customizable +by allowing the user to add and remove arbitrary sections. It +also provides some convenience functions for constructing sections, +e.g. showing the unread message count for each tag. + +This is done by specifying a list of functions that will be run +when notmuch-hello is invoked. +--- + emacs/notmuch-hello.el | 624 ++++++++++++++++++++++++++++++++---------------- + 1 files changed, 424 insertions(+), 200 deletions(-) + +diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el +index d17a30f..89c5637 100644 +--- a/emacs/notmuch-hello.el ++++ b/emacs/notmuch-hello.el +@@ -154,6 +154,108 @@ International Bureau of Weights and Measures." + (defvar notmuch-hello-url "http://notmuchmail.org" + "The `notmuch' web site.") + ++(defvar notmuch-hello-search-pos nil ++ "Position of search widget, if any. ++ ++This should only be set by `notmuch-hello-insert-search'.") ++ ++(defvar notmuch-hello-custom-section-options ++ '((:filter (string :tag "Filter for each tag")) ++ (:filter-count (string :tag "Different filter to generate message counts")) ++ (:initially-hidden (const :tag "Hide this section on startup" t)) ++ (:show-empty-searches (const :tag "Show queries with no matching messages" t)) ++ (:hide-if-empty (const :tag "Hide this section if all queries are empty ++\(and not shown by show-empty-searches)" t))) ++ "Various customization-options for notmuch-hello-tags/query-section.") ++ ++(define-widget 'notmuch-hello-tags-section 'lazy ++ "Customize-type for notmuch-hello tag-list sections." ++ :tag "Customized tag-list section (see docstring for details)" ++ :type ++ `(list :tag "" ++ (const :tag "" notmuch-hello-insert-tags-section) ++ (string :tag "Title for this section") ++ (plist ++ :inline t ++ :options ++ ,(append notmuch-hello-custom-section-options ++ '((:hide-tags (repeat :tag "Tags that will be hidden" ++ string))))))) ++ ++(define-widget 'notmuch-hello-query-section 'lazy ++ "Customize-type for custom saved-search-like sections" ++ :tag "Customized queries section (see docstring for details)" ++ :type ++ `(list :tag "" ++ (const :tag "" notmuch-hello-insert-query-list) ++ (string :tag "Title for this section") ++ (repeat :tag "Queries" ++ (cons (string :tag "Name") (string :tag "Query"))) ++ (plist :inline t :options ,notmuch-hello-custom-section-options))) ++ ++(defcustom notmuch-hello-sections ++ (list #'notmuch-hello-insert-header ++ #'notmuch-hello-insert-saved-searches ++ #'notmuch-hello-insert-search ++ #'notmuch-hello-insert-recent-searches ++ #'notmuch-hello-insert-alltags ++ #'notmuch-hello-insert-footer) ++ "Sections for notmuch-hello. ++ ++The list contains functions which are used to construct sections in ++notmuch-hello buffer. When notmuch-hello buffer is constructed, ++these functions are run in the order they appear in this list. Each ++function produces a section simply by adding content to the current ++buffer. A section should not end with an empty line, because a ++newline will be inserted after each section by `notmuch-hello'. ++ ++Each function should take no arguments. If the produced section ++includes `notmuch-hello-target' (i.e. cursor should be positioned ++inside this section), the function should return this element's ++position. ++Otherwise, it should return nil. ++ ++For convenience an element can also be a list of the form (FUNC ARG1 ++ARG2 .. ARGN) in which case FUNC will be applied to the rest of the ++list. ++ ++A \"Customized tag-list section\" item in the customize-interface ++displays a list of all tags, optionally hiding some of them. It ++is also possible to filter the list of messages matching each tag ++by an additional filter query. Similarly, the count of messages ++displayed next to the buttons can be generated by applying a ++different filter to the tag query. These filters are also ++supported for \"Customized queries section\" items." ++ :group 'notmuch ++ :type ++ '(repeat ++ (choice (function-item notmuch-hello-insert-header) ++ (function-item notmuch-hello-insert-saved-searches) ++ (function-item notmuch-hello-insert-search) ++ (function-item notmuch-hello-insert-recent-searches) ++ (function-item notmuch-hello-insert-alltags) ++ (function-item notmuch-hello-insert-footer) ++ (function-item notmuch-hello-insert-inbox) ++ notmuch-hello-tags-section ++ notmuch-hello-query-section ++ (function :tag "Custom section")))) ++ ++(defvar notmuch-hello-target nil ++ "Button text at position of point before rebuilding the notmuch-buffer. ++ ++This variable contains the text of the button, if any, the ++point was positioned at before the notmuch-hello buffer was ++rebuilt. This should never actually be global and is defined as a ++defvar only for documentation purposes and to avoid a compiler ++warning about it occurring as a free variable.") ++ ++(defvar notmuch-hello-hidden-sections nil ++ "List of sections titles whose contents are hidden") ++ ++(defvar notmuch-hello-first-run t ++ "True if `notmuch-hello' is run for the first time, set to nil ++afterwards.") ++ + (defun notmuch-hello-nice-number (n) + (let (result) + (while (> n 0) +@@ -201,8 +303,8 @@ International Bureau of Weights and Measures." + (message "Saved '%s' as '%s'." search name) + (notmuch-hello-update))) + +-(defun notmuch-hello-longest-label (tag-alist) +- (or (loop for elem in tag-alist ++(defun notmuch-hello-longest-label (searches-alist) ++ (or (loop for elem in searches-alist + maximize (length (car elem))) + 0)) + +@@ -266,12 +368,70 @@ should be. Returns a cons cell `(tags-per-line width)'." + (* tags-per-line (+ 9 1)))) + tags-per-line)))) + +-(defun notmuch-hello-insert-tags (tag-alist widest target) +- (let* ((tags-and-width (notmuch-hello-tags-per-line widest)) ++(defun notmuch-hello-filtered-query (query filter) ++ "Constructs a query to search all messages matching QUERY and FILTER. ++ ++If FILTER is a string, it is directly used in the returned query. ++ ++If FILTER is a function, it is called with QUERY as a parameter and ++the string it returns is used as the query. If nil is returned, ++the entry is hidden. ++ ++Otherwise, FILTER is ignored. ++" ++ (cond ++ ((functionp filter) (funcall filter query)) ++ ((stringp filter) ++ (concat "(" query ") and (" filter ")")) ++ (t query))) ++ ++(defun notmuch-hello-query-counts (query-alist &rest options) ++ "Compute list of counts of matched messages from QUERY-ALIST. ++ ++QUERY-ALIST must be a list containing elements of the form (NAME . QUERY) ++or (NAME QUERY COUNT-QUERY). If the latter form is used, ++COUNT-QUERY specifies an alternate query to be used to generate ++the count for the associated query. ++ ++The result is the list of elements of the form (NAME QUERY COUNT). ++ ++The values :show-empty-searches, :filter and :filter-count from ++options will be handled as specified for ++`notmuch-hello-insert-searches'." ++ (notmuch-remove-if-not ++ #'identity ++ (mapcar ++ (lambda (elem) ++ (let* ((name (car elem)) ++ (query-and-count (if (consp (cdr elem)) ++ ;; do we have a different query for the message count? ++ (cons (second elem) (third elem)) ++ (cons (cdr elem) (cdr elem)))) ++ (message-count ++ (string-to-number ++ (notmuch-saved-search-count ++ (notmuch-hello-filtered-query (cdr query-and-count) ++ (or (plist-get options :filter-count) ++ (plist-get options :filter))))))) ++ (and (or (plist-get options :show-empty-searches) (> message-count 0)) ++ (list name (notmuch-hello-filtered-query ++ (car query-and-count) (plist-get options :filter)) ++ message-count)))) ++ query-alist))) ++ ++(defun notmuch-hello-insert-buttons (searches) ++ "Insert buttons for SEARCHES. ++ ++SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where ++QUERY is the query to start when the button for the corresponding entry is ++activated. COUNT should be the number of messages matching the query. ++Such a list can be computed with `notmuch-hello-query-counts'." ++ (let* ((widest (notmuch-hello-longest-label searches)) ++ (tags-and-width (notmuch-hello-tags-per-line widest)) + (tags-per-line (car tags-and-width)) + (widest (cdr tags-and-width)) + (count 0) +- (reordered-list (notmuch-hello-reflect tag-alist tags-per-line)) ++ (reordered-list (notmuch-hello-reflect searches tags-per-line)) + ;; Hack the display of the buttons used. + (widget-push-button-prefix "") + (widget-push-button-suffix "") +@@ -281,13 +441,13 @@ should be. Returns a cons cell `(tags-per-line width)'." + (mapc (lambda (elem) + ;; (not elem) indicates an empty slot in the matrix. + (when elem +- (let* ((name (car elem)) +- (query (cdr elem)) ++ (let* ((name (first elem)) ++ (query (second elem)) ++ (msg-count (third elem)) + (formatted-name (format "%s " name))) + (widget-insert (format "%8s " +- (notmuch-hello-nice-number +- (string-to-number (notmuch-saved-search-count query))))) +- (if (string= formatted-name target) ++ (notmuch-hello-nice-number msg-count))) ++ (if (string= formatted-name notmuch-hello-target) + (setq found-target-pos (point-marker))) + (widget-create 'push-button + :notify #'notmuch-hello-widget-search +@@ -359,29 +519,240 @@ Complete list of currently available key bindings: + (kill-all-local-variables) + (use-local-map notmuch-hello-mode-map) + (setq major-mode 'notmuch-hello-mode +- mode-name "notmuch-hello") ++ mode-name "notmuch-hello") + (run-mode-hooks 'notmuch-hello-mode-hook) + ;;(setq buffer-read-only t) + ) + +-(defun notmuch-hello-generate-tag-alist () ++(defun notmuch-hello-generate-tag-alist (&optional hide-tags) + "Return an alist from tags to queries to display in the all-tags section." +- (notmuch-remove-if-not +- #'cdr +- (mapcar (lambda (tag) +- (cons tag +- (cond +- ((functionp notmuch-hello-tag-list-make-query) +- (concat "tag:" tag " and (" +- (funcall notmuch-hello-tag-list-make-query tag) ")")) +- ((stringp notmuch-hello-tag-list-make-query) +- (concat "tag:" tag " and (" +- notmuch-hello-tag-list-make-query ")")) +- (t (concat "tag:" tag))))) +- (notmuch-remove-if-not +- (lambda (tag) +- (not (member tag notmuch-hello-hide-tags))) +- (process-lines notmuch-command "search-tags"))))) ++ (mapcar (lambda (tag) ++ (cons tag (format "tag:%s" tag))) ++ (notmuch-remove-if-not ++ (lambda (tag) ++ (not (member tag hide-tags))) ++ (process-lines notmuch-command "search-tags")))) ++ ++(defun notmuch-hello-insert-header () ++ "Insert the default notmuch-hello header." ++ (when notmuch-show-logo ++ (let ((image notmuch-hello-logo)) ++ ;; The notmuch logo uses transparency. That can display poorly ++ ;; when inserting the image into an emacs buffer (black logo on ++ ;; a black background), so force the background colour of the ++ ;; image. We use a face to represent the colour so that ++ ;; `defface' can be used to declare the different possible ++ ;; colours, which depend on whether the frame has a light or ++ ;; dark background. ++ (setq image (cons 'image ++ (append (cdr image) ++ (list :background (face-background 'notmuch-hello-logo-background))))) ++ (insert-image image)) ++ (widget-insert " ")) ++ ++ (widget-insert "Welcome to ") ++ ;; Hack the display of the links used. ++ (let ((widget-link-prefix "") ++ (widget-link-suffix "")) ++ (widget-create 'link ++ :notify (lambda (&rest ignore) ++ (browse-url notmuch-hello-url)) ++ :help-echo "Visit the notmuch website." ++ "notmuch") ++ (widget-insert ". ") ++ (widget-insert "You have ") ++ (widget-create 'link ++ :notify (lambda (&rest ignore) ++ (notmuch-hello-update)) ++ :help-echo "Refresh" ++ (notmuch-hello-nice-number ++ (string-to-number (car (process-lines notmuch-command "count"))))) ++ (widget-insert " messages.\n"))) ++ ++ ++(defun notmuch-hello-insert-saved-searches () ++ "Insert the saved-searches section." ++ (let ((searches (notmuch-hello-query-counts ++ (if notmuch-saved-search-sort-function ++ (funcall notmuch-saved-search-sort-function ++ notmuch-saved-searches) ++ notmuch-saved-searches) ++ :show-empty-searches notmuch-show-empty-saved-searches)) ++ found-target-pos) ++ (when searches ++ (widget-insert "Saved searches: ") ++ (widget-create 'push-button ++ :notify (lambda (&rest ignore) ++ (customize-variable 'notmuch-saved-searches)) ++ "edit") ++ (widget-insert "\n\n") ++ (let ((start (point))) ++ (setq found-target-pos ++ (notmuch-hello-insert-buttons searches)) ++ (indent-rigidly start (point) notmuch-hello-indent) ++ found-target-pos)))) ++ ++(defun notmuch-hello-insert-search () ++ "Insert a search widget." ++ (widget-insert "Search: ") ++ (setq notmuch-hello-search-pos (point-marker)) ++ (widget-create 'editable-field ++ ;; Leave some space at the start and end of the ++ ;; search boxes. ++ :size (max 8 (- (window-width) notmuch-hello-indent ++ (length "Search: "))) ++ :action (lambda (widget &rest ignore) ++ (notmuch-hello-search (widget-value widget)))) ++ ;; Add an invisible dot to make `widget-end-of-line' ignore ++ ;; trailing spaces in the search widget field. A dot is used ++ ;; instead of a space to make `show-trailing-whitespace' ++ ;; happy, i.e. avoid it marking the whole line as trailing ++ ;; spaces. ++ (widget-insert ".") ++ (put-text-property (1- (point)) (point) 'invisible t) ++ (widget-insert "\n")) ++ ++(defun notmuch-hello-insert-recent-searches () ++ "Insert recent searches." ++ (when notmuch-search-history ++ (widget-insert "Recent searches: ") ++ (widget-create 'push-button ++ :notify (lambda (&rest ignore) ++ (setq notmuch-search-history nil) ++ (notmuch-hello-update)) ++ "clear") ++ (widget-insert "\n\n") ++ (let ((start (point))) ++ (loop for i from 1 to notmuch-hello-recent-searches-max ++ for search in notmuch-search-history do ++ (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i)))) ++ (set widget-symbol ++ (widget-create 'editable-field ++ ;; Don't let the search boxes be ++ ;; less than 8 characters wide. ++ :size (max 8 ++ (- (window-width) ++ ;; Leave some space ++ ;; at the start and ++ ;; end of the ++ ;; boxes. ++ (* 2 notmuch-hello-indent) ++ ;; 1 for the space ++ ;; before the ++ ;; `[save]' button. 6 ++ ;; for the `[save]' ++ ;; button. ++ 1 6)) ++ :action (lambda (widget &rest ignore) ++ (notmuch-hello-search (widget-value widget))) ++ search)) ++ (widget-insert " ") ++ (widget-create 'push-button ++ :notify (lambda (widget &rest ignore) ++ (notmuch-hello-add-saved-search widget)) ++ :notmuch-saved-search-widget widget-symbol ++ "save")) ++ (widget-insert "\n")) ++ (indent-rigidly start (point) notmuch-hello-indent)))) ++ ++(defun notmuch-hello-insert-searches (title query-alist &rest options) ++ "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST. ++ ++QUERY-ALIST must be a list containing elements of the form (NAME . QUERY) ++or (NAME QUERY COUNT-QUERY). If the latter form is used, ++COUNT-QUERY specifies an alternate query to be used to generate ++the count for the associated item. ++ ++Supports the following entries in OPTIONS as a plist: ++:initially-hidden - if non-nil, section will be hidden on startup ++:show-empty-searches - show buttons with no matching messages ++:hide-if-empty - hide if no buttons would be shown ++ (only makes sense without :show-empty-searches) ++:filter - This can be a function that takes the search query as its argument and ++ returns a filter to be used in conjuction with the query for that search or nil ++ to hide the element. This can also be a string that is used as a combined with ++ each query using \"and\". ++:filter-count - Separate filter to generate the count displayed each search. Accepts ++ the same values as :filter. If :filter and :filter-count are specified, this ++ will be used instead of :filter, not in conjunction with it." ++ (widget-insert title ": ") ++ (if (and notmuch-hello-first-run (plist-get options :initially-hidden)) ++ (add-to-list 'notmuch-hello-hidden-sections title)) ++ (let ((is-hidden (member title notmuch-hello-hidden-sections)) ++ (start (point))) ++ (if is-hidden ++ (widget-create 'push-button ++ :notify `(lambda (widget &rest ignore) ++ (setq notmuch-hello-hidden-sections ++ (delete ,title notmuch-hello-hidden-sections)) ++ (notmuch-hello-update)) ++ "show") ++ (widget-create 'push-button ++ :notify `(lambda (widget &rest ignore) ++ (add-to-list 'notmuch-hello-hidden-sections ++ ,title) ++ (notmuch-hello-update)) ++ "hide")) ++ (widget-insert "\n") ++ (let (target-pos ++ (searches (apply 'notmuch-hello-query-counts query-alist options))) ++ (when (and (not is-hidden) ++ (or (not (plist-get options :hide-if-empty)) ++ searches)) ++ (widget-insert "\n") ++ (setq target-pos ++ (notmuch-hello-insert-buttons searches)) ++ (indent-rigidly start (point) notmuch-hello-indent) ++ target-pos)))) ++ ++(defun notmuch-hello-insert-tags-section (&optional title &rest options) ++ "Insert a section displaying all tags with message counts. ++ ++TITLE defaults to \"All tags\". ++Allowed options are those accepted by `notmuch-hello-insert-searches' and the ++following: ++ ++:hide-tags - List of tags that should be excluded." ++ (apply 'notmuch-hello-insert-searches ++ (or title "All tags") ++ (notmuch-hello-generate-tag-alist (plist-get options :hide-tags)) ++ options)) ++ ++(defun notmuch-hello-insert-inbox () ++ "Show an entry for each saved search and inboxed messages for each tag" ++ (notmuch-hello-insert-searches "What's in your inbox" ++ (append ++ (notmuch-saved-searches) ++ (notmuch-hello-generate-tag-alist)) ++ :filter "tag:inbox")) ++ ++(defun notmuch-hello-insert-alltags () ++ "Insert a section displaying all tags and associated message counts" ++ (notmuch-hello-insert-tags-section ++ nil ++ :initially-hidden (not notmuch-show-all-tags-list) ++ :hide-tags notmuch-hello-hide-tags ++ :filter notmuch-hello-tag-list-make-query)) ++ ++(defun notmuch-hello-insert-footer () ++ "Insert the notmuch-hello footer." ++ (let ((start (point))) ++ (widget-insert "Type a search query and hit RET to view matching threads.\n") ++ (when notmuch-search-history ++ (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n") ++ (widget-insert "Save recent searches with the `save' button.\n")) ++ (when notmuch-saved-searches ++ (widget-insert "Edit saved searches with the `edit' button.\n")) ++ (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n") ++ (widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n") ++ (widget-create 'link ++ :notify (lambda (&rest ignore) ++ (customize-variable 'notmuch-hello-sections)) ++ :button-prefix "" :button-suffix "" ++ "Customize") ++ (widget-insert " this page.") ++ (let ((fill-column (- (window-width) notmuch-hello-indent))) ++ (center-region start (point))))) + + ;;;###autoload + (defun notmuch-hello (&optional no-display) +@@ -397,13 +768,13 @@ Complete list of currently available key bindings: + (set-buffer "*notmuch-hello*") + (switch-to-buffer "*notmuch-hello*")) + +- (let ((target (if (widget-at) +- (widget-value (widget-at)) +- (condition-case nil +- (progn +- (widget-forward 1) +- (widget-value (widget-at))) +- (error nil)))) ++ (let ((notmuch-hello-target (if (widget-at) ++ (widget-value (widget-at)) ++ (condition-case nil ++ (progn ++ (widget-forward 1) ++ (widget-value (widget-at))) ++ (error nil)))) + (inhibit-read-only t)) + + ;; Delete all editable widget fields. Editable widget fields are +@@ -422,168 +793,20 @@ Complete list of currently available key bindings: + (mapc 'delete-overlay (car all)) + (mapc 'delete-overlay (cdr all))) + +- (when notmuch-show-logo +- (let ((image notmuch-hello-logo)) +- ;; The notmuch logo uses transparency. That can display poorly +- ;; when inserting the image into an emacs buffer (black logo on +- ;; a black background), so force the background colour of the +- ;; image. We use a face to represent the colour so that +- ;; `defface' can be used to declare the different possible +- ;; colours, which depend on whether the frame has a light or +- ;; dark background. +- (setq image (cons 'image +- (append (cdr image) +- (list :background (face-background 'notmuch-hello-logo-background))))) +- (insert-image image)) +- (widget-insert " ")) +- +- (widget-insert "Welcome to ") +- ;; Hack the display of the links used. +- (let ((widget-link-prefix "") +- (widget-link-suffix "")) +- (widget-create 'link +- :notify (lambda (&rest ignore) +- (browse-url notmuch-hello-url)) +- :help-echo "Visit the notmuch website." +- "notmuch") +- (widget-insert ". ") +- (widget-insert "You have ") +- (widget-create 'link +- :notify (lambda (&rest ignore) +- (notmuch-hello-update)) +- :help-echo "Refresh" +- (notmuch-hello-nice-number +- (string-to-number (car (process-lines notmuch-command "count"))))) +- (widget-insert " messages.\n")) +- +- (let ((found-target-pos nil) +- (final-target-pos nil) +- (default-pos)) +- (let* ((saved-alist +- ;; Filter out empty saved searches if required. +- (if notmuch-show-empty-saved-searches +- notmuch-saved-searches +- (loop for elem in notmuch-saved-searches +- if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0) +- collect elem))) +- (saved-widest (notmuch-hello-longest-label saved-alist)) +- (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist))) +- (alltags-widest (notmuch-hello-longest-label alltags-alist)) +- (widest (max saved-widest alltags-widest))) +- +- (when saved-alist +- ;; Sort saved searches if required. +- (when notmuch-saved-search-sort-function +- (setq saved-alist +- (funcall notmuch-saved-search-sort-function saved-alist))) +- (widget-insert "\nSaved searches: ") +- (widget-create 'push-button +- :notify (lambda (&rest ignore) +- (customize-variable 'notmuch-saved-searches)) +- "edit") +- (widget-insert "\n\n") +- (setq final-target-pos (point-marker)) +- (let ((start (point))) +- (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target)) +- (if found-target-pos +- (setq final-target-pos found-target-pos)) +- (indent-rigidly start (point) notmuch-hello-indent))) +- +- (widget-insert "\nSearch: ") +- (setq default-pos (point-marker)) +- (widget-create 'editable-field +- ;; Leave some space at the start and end of the +- ;; search boxes. +- :size (max 8 (- (window-width) notmuch-hello-indent +- (length "Search: "))) +- :action (lambda (widget &rest ignore) +- (notmuch-hello-search (widget-value widget)))) +- ;; Add an invisible dot to make `widget-end-of-line' ignore +- ;; trailing spaces in the search widget field. A dot is used +- ;; instead of a space to make `show-trailing-whitespace' +- ;; happy, i.e. avoid it marking the whole line as trailing +- ;; spaces. +- (widget-insert ".") +- (put-text-property (1- (point)) (point) 'invisible t) +- (widget-insert "\n") +- +- (when notmuch-search-history +- (widget-insert "\nRecent searches: ") +- (widget-create 'push-button +- :notify (lambda (&rest ignore) +- (setq notmuch-search-history nil) +- (notmuch-hello-update)) +- "clear") +- (widget-insert "\n\n") +- (let ((start (point))) +- (loop for i from 1 to notmuch-hello-recent-searches-max +- for search in notmuch-search-history do +- (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i)))) +- (set widget-symbol +- (widget-create 'editable-field +- ;; Don't let the search boxes be +- ;; less than 8 characters wide. +- :size (max 8 +- (- (window-width) +- ;; Leave some space +- ;; at the start and +- ;; end of the +- ;; boxes. +- (* 2 notmuch-hello-indent) +- ;; 1 for the space +- ;; before the +- ;; `[save]' button. 6 +- ;; for the `[save]' +- ;; button. +- 1 6)) +- :action (lambda (widget &rest ignore) +- (notmuch-hello-search (widget-value widget))) +- search)) +- (widget-insert " ") +- (widget-create 'push-button +- :notify (lambda (widget &rest ignore) +- (notmuch-hello-add-saved-search widget)) +- :notmuch-saved-search-widget widget-symbol +- "save")) +- (widget-insert "\n")) +- (indent-rigidly start (point) notmuch-hello-indent))) +- +- (when alltags-alist +- (widget-insert "\nAll tags: ") +- (widget-create 'push-button +- :notify (lambda (widget &rest ignore) +- (setq notmuch-show-all-tags-list nil) +- (notmuch-hello-update)) +- "hide") +- (widget-insert "\n\n") +- (let ((start (point))) +- (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target)) +- (unless final-target-pos +- (setq final-target-pos found-target-pos)) +- (indent-rigidly start (point) notmuch-hello-indent))) +- +- (widget-insert "\n") +- +- (unless notmuch-show-all-tags-list +- (widget-create 'push-button +- :notify (lambda (widget &rest ignore) +- (setq notmuch-show-all-tags-list t) +- (notmuch-hello-update)) +- "Show all tags"))) +- +- (let ((start (point))) +- (widget-insert "\n\n") +- (widget-insert "Type a search query and hit RET to view matching threads.\n") +- (when notmuch-search-history +- (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n") +- (widget-insert "Save recent searches with the `save' button.\n")) +- (when notmuch-saved-searches +- (widget-insert "Edit saved searches with the `edit' button.\n")) +- (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n") +- (widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n") +- (let ((fill-column (- (window-width) notmuch-hello-indent))) +- (center-region start (point)))) +- ++ (let (final-target-pos) ++ (mapc ++ (lambda (section) ++ (let ((point-before (point)) ++ (result (if (functionp section) ++ (funcall section) ++ (apply (car section) (cdr section))))) ++ (if (and (not final-target-pos) (integer-or-marker-p result)) ++ (setq final-target-pos result)) ++ ;; don't insert a newline when the previous section didn't show ++ ;; anything. ++ (unless (eq (point) point-before) ++ (widget-insert "\n")))) ++ notmuch-hello-sections) + (widget-setup) + + (when final-target-pos +@@ -592,9 +815,10 @@ Complete list of currently available key bindings: + (widget-forward 1))) + + (unless (widget-at) +- (goto-char default-pos)))) +- +- (run-hooks 'notmuch-hello-refresh-hook)) ++ (when notmuch-hello-search-pos ++ (goto-char notmuch-hello-search-pos))))) ++ (run-hooks 'notmuch-hello-refresh-hook) ++ (setq notmuch-hello-first-run nil)) + + (defun notmuch-folder () + "Deprecated function for invoking notmuch---calling `notmuch' is preferred now." +-- +1.7.9 +