X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=lisp%2Fgnus-topic.el;h=528fb5a64f5bc2b228d453b598b97d85156c4716;hb=7d360ad9b65c2c51068117c290ebba043fe5924e;hp=249367daf636ae28ae5b53eb39d79c158fe9d6f4;hpb=aa1506499895e49a74c2ac8c22892333c1a01e48;p=elisp%2Fgnus.git- diff --git a/lisp/gnus-topic.el b/lisp/gnus-topic.el index 249367d..528fb5a 100644 --- a/lisp/gnus-topic.el +++ b/lisp/gnus-topic.el @@ -1,5 +1,6 @@ ;;; gnus-topic.el --- a folding minor mode for Gnus group buffers -;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc. +;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 +;; Free Software Foundation, Inc. ;; Author: Ilja Weis ;; Lars Magne Ingebrigtsen @@ -27,10 +28,12 @@ ;;; Code: (eval-when-compile (require 'cl)) +(eval-when-compile (require 'gnus-clfns)) (require 'gnus) (require 'gnus-group) (require 'gnus-start) +(require 'gnus-util) (defgroup gnus-topic nil "Group topics." @@ -73,6 +76,7 @@ with some simple extensions. (defvar gnus-topic-active-topology nil) (defvar gnus-topic-active-alist nil) +(defvar gnus-topic-unreads nil) (defvar gnus-topology-checked-p nil "Whether the topology has been checked in this session.") @@ -108,9 +112,7 @@ with some simple extensions. (defun gnus-topic-unread (topic) "Return the number of unread articles in TOPIC." - (or (save-excursion - (and (gnus-topic-goto-topic topic) - (gnus-group-topic-unread))) + (or (cdr (assoc topic gnus-topic-unreads)) 0)) (defun gnus-group-topic-p () @@ -149,11 +151,20 @@ with some simple extensions. (gnus-group-topic group)))) (defun gnus-topic-goto-topic (topic) - "Go to TOPIC." (when topic (gnus-goto-char (text-property-any (point-min) (point-max) 'gnus-topic (intern topic))))) +(defun gnus-topic-jump-to-topic (topic) + "Go to TOPIC." + (interactive + (list (completing-read "Go to topic: " + (mapcar 'list (gnus-topic-list)) + nil t))) + (dolist (topic (gnus-current-topics topic)) + (gnus-topic-fold t)) + (gnus-topic-goto-topic topic)) + (defun gnus-current-topic () "Return the name of the current topic." (let ((result @@ -166,9 +177,10 @@ with some simple extensions. (when result (symbol-name result)))) -(defun gnus-current-topics () - "Return a list of all current topics, lowest in hierarchy first." - (let ((topic (gnus-current-topic)) +(defun gnus-current-topics (&optional topic) + "Return a list of all current topics, lowest in hierarchy first. +If TOPIC, start with that topic." + (let ((topic (or topic (gnus-current-topic))) topics) (while topic (push topic topics) @@ -199,18 +211,20 @@ with some simple extensions. active (- (1+ (cdr active)) (car active)))) clevel (or (gnus-info-level info) - (if (member group gnus-zombie-list) gnus-level-zombie gnus-level-killed)))) + (if (member group gnus-zombie-list) + gnus-level-zombie gnus-level-killed)))) (and - unread ; nil means that the group is dead. + info ; nil means that the group is dead. (<= clevel level) (>= clevel lowest) ; Is inside the level we want. (or all - (if (eq unread t) + (if (or (eq unread t) + (eq unread nil)) gnus-group-list-inactive-groups (> unread 0)) (and gnus-list-groups-with-ticked-articles (cdr (assq 'tick (gnus-info-marks info)))) - ; Has right readedness. + ;; Has right readedness. ;; Check for permanent visibility. (and gnus-permanently-visible-groups (string-match gnus-permanently-visible-groups group)) @@ -324,27 +338,32 @@ with some simple extensions. (defun gnus-group-topic-parameters (group) "Compute the group parameters for GROUP taking into account inheritance from topics." - (let ((params-list (list (gnus-group-get-parameter group))) - topics params param out) + (let ((params-list (copy-sequence (gnus-group-get-parameter group)))) (save-excursion (gnus-group-goto-group group) - (setq topics (gnus-current-topics)) - (while topics - (push (gnus-topic-parameters (pop topics)) params-list)) - ;; We probably have lots of nil elements here, so - ;; we remove them. Probably faster than doing this "properly". - (setq params-list (delq nil params-list)) - ;; Now we have all the parameters, so we go through them - ;; and do inheritance in the obvious way. - (while (setq params (pop params-list)) - (while (setq param (pop params)) - (when (atom param) - (setq param (cons param t))) - ;; Override any old versions of this param. - (setq out (delq (assq (car param) out) out)) - (push param out))) - ;; Return the resulting parameter list. - out))) + (nconc params-list + (gnus-topic-hierarchical-parameters (gnus-current-topic)))))) + +(defun gnus-topic-hierarchical-parameters (topic) + "Return a topic list computed for TOPIC." + (let ((topics (gnus-current-topics topic)) + params-list param out params) + (while topics + (push (gnus-topic-parameters (pop topics)) params-list)) + ;; We probably have lots of nil elements here, so + ;; we remove them. Probably faster than doing this "properly". + (setq params-list (delq nil params-list)) + ;; Now we have all the parameters, so we go through them + ;; and do inheritance in the obvious way. + (while (setq params (pop params-list)) + (while (setq param (pop params)) + (when (atom param) + (setq param (cons param t))) + ;; Override any old versions of this param. + (gnus-pull (car param) out) + (push param out))) + ;; Return the resulting parameter list. + out)) ;;; General utility functions @@ -354,7 +373,8 @@ with some simple extensions. ;;; Generating group buffers -(defun gnus-group-prepare-topics (level &optional all lowest regexp list-topic topic-level) +(defun gnus-group-prepare-topics (level &optional all lowest + regexp list-topic topic-level) "List all newsgroups with unread articles of level LEVEL or lower. Use the `gnus-group-topics' to sort the groups. If ALL is non-nil, list groups that have no unread articles. @@ -406,7 +426,13 @@ If LOWEST is non-nil, list all newsgroups of level LOWEST or higher." If SILENT, don't insert anything. Return the number of unread articles in the topic and its subtopics." (let* ((type (pop topicl)) - (entries (gnus-topic-find-groups (car type) list-level all lowest)) + (entries (gnus-topic-find-groups + (car type) list-level + (or all + (cdr (assq 'visible + (gnus-topic-hierarchical-parameters + (car type))))) + lowest)) (visiblep (and (eq (nth 1 type) 'visible) (not silent))) (gnus-group-indentation (make-string (* gnus-topic-indent-level level) ? )) @@ -431,7 +457,8 @@ articles in the topic and its subtopics." (if (stringp entry) ;; Dead groups. (gnus-group-insert-group-line - entry (if (member entry gnus-zombie-list) gnus-level-zombie gnus-level-killed) + entry (if (member entry gnus-zombie-list) + gnus-level-zombie gnus-level-killed) nil (- (1+ (cdr (setq active (gnus-active entry)))) (car active)) nil) @@ -458,6 +485,7 @@ articles in the topic and its subtopics." (car type) visiblep (not (eq (nth 2 type) 'hidden)) level all-entries unread)) + (gnus-topic-update-unreads (car type) unread) (goto-char end) unread)) @@ -491,9 +519,9 @@ articles in the topic and its subtopics." (car gnus-group-list-mode) (cdr gnus-group-list-mode) nil nil topic level)) -(defun gnus-topic-fold (&optional insert) +(defun gnus-topic-fold (&optional insert topic) "Remove/insert the current topic." - (let ((topic (gnus-group-topic-name))) + (let ((topic (or topic (gnus-group-topic-name)))) (when topic (save-excursion (if (not (gnus-group-active-topic-p)) @@ -514,6 +542,7 @@ articles in the topic and its subtopics." (number-of-groups (length entries)) (active-topic (eq gnus-topic-alist gnus-topic-active-alist)) gnus-tmp-header) + (gnus-topic-update-unreads name unread) (beginning-of-line) ;; Insert the text. (gnus-add-text-properties @@ -526,6 +555,11 @@ articles in the topic and its subtopics." 'gnus-active active-topic 'gnus-topic-visible visiblep)))) +(defun gnus-topic-update-unreads (topic unreads) + (setq gnus-topic-unreads (delq (assoc topic gnus-topic-unreads) + gnus-topic-unreads)) + (push (cons topic unreads) gnus-topic-unreads)) + (defun gnus-topic-update-topics-containing-group (group) "Update all topics that have GROUP as a member." (when (and (eq major-mode 'gnus-group-mode) @@ -562,7 +596,8 @@ articles in the topic and its subtopics." (let* ((topic (gnus-group-topic group)) (groups (cdr (assoc topic gnus-topic-alist))) (g (cdr (member group groups))) - (unfound t)) + (unfound t) + entry) ;; Try to jump to a visible group. (while (and g (not (gnus-group-goto-group (car g) t))) (pop g)) @@ -576,8 +611,20 @@ articles in the topic and its subtopics." (when (and unfound topic (not (gnus-topic-goto-missing-topic topic))) - (gnus-topic-insert-topic-line - topic t t (car (gnus-topic-find-topology topic)) nil 0))))) + (let* ((top (gnus-topic-find-topology topic)) + (children (cddr top)) + (type (cadr top)) + (unread 0) + (entries (gnus-topic-find-groups + (car type) (car gnus-group-list-mode) + (cdr gnus-group-list-mode)))) + (while children + (incf unread (gnus-topic-unread (caar (pop children))))) + (while (setq entry (pop entries)) + (when (numberp (car entry)) + (incf unread (car entry)))) + (gnus-topic-insert-topic-line + topic t t (car (gnus-topic-find-topology topic)) nil unread)))))) (defun gnus-topic-goto-missing-topic (topic) (if (gnus-topic-goto-topic topic) @@ -586,15 +633,18 @@ articles in the topic and its subtopics." (let* ((top (gnus-topic-find-topology (gnus-topic-parent-topic topic))) (tp (reverse (cddr top)))) - (while (not (equal (caaar tp) topic)) - (setq tp (cdr tp))) - (pop tp) - (while (and tp - (not (gnus-topic-goto-topic (caaar tp)))) - (pop tp)) - (if tp - (gnus-topic-forward-topic 1) - (gnus-topic-goto-missing-topic (caadr top)))) + (if (not top) + (gnus-topic-insert-topic-line + topic t t (car (gnus-topic-find-topology topic)) nil 0) + (while (not (equal (caaar tp) topic)) + (setq tp (cdr tp))) + (pop tp) + (while (and tp + (not (gnus-topic-goto-topic (caaar tp)))) + (pop tp)) + (if tp + (gnus-topic-forward-topic 1) + (gnus-topic-goto-missing-topic (caadr top))))) nil)) (defun gnus-topic-update-topic-line (topic-name &optional reads) @@ -607,7 +657,7 @@ articles in the topic and its subtopics." (parent (gnus-topic-parent-topic topic-name)) (all-entries entries) (unread 0) - old-unread entry) + old-unread entry new-unread) (when (gnus-topic-goto-topic (car type)) ;; Tally all the groups that belong in this topic. (if reads @@ -623,11 +673,14 @@ articles in the topic and its subtopics." (car type) (gnus-topic-visible-p) (not (eq (nth 2 type) 'hidden)) (gnus-group-topic-level) all-entries unread) - (gnus-delete-line)) + (gnus-delete-line) + (forward-line -1) + (setq new-unread (gnus-group-topic-unread))) (when parent (forward-line -1) (gnus-topic-update-topic-line - parent (- (or old-unread 0) (or (gnus-group-topic-unread) 0)))) + parent + (- (or old-unread 0) (or new-unread 0)))) unread)) (defun gnus-topic-group-indentation () @@ -883,6 +936,7 @@ articles in the topic and its subtopics." "=" gnus-topic-select-group "\r" gnus-topic-select-group " " gnus-topic-read-group + "\C-c\C-x" gnus-topic-expire-articles "\C-k" gnus-topic-kill-group "\C-y" gnus-topic-yank-group "\M-g" gnus-topic-get-new-news-this-topic @@ -890,6 +944,10 @@ articles in the topic and its subtopics." "Gp" gnus-topic-edit-parameters "#" gnus-topic-mark-topic "\M-#" gnus-topic-unmark-topic + [tab] gnus-topic-indent + [(meta tab)] gnus-topic-unindent + "\C-i" gnus-topic-indent + "\M-\C-i" gnus-topic-unindent gnus-mouse-2 gnus-mouse-pick-topic) ;; Define a new submap. @@ -902,6 +960,7 @@ articles in the topic and its subtopics." "c" gnus-topic-copy-group "h" gnus-topic-hide-topic "s" gnus-topic-show-topic + "j" gnus-topic-jump-to-topic "M" gnus-topic-move-matching "C" gnus-topic-copy-matching "\C-i" gnus-topic-indent @@ -909,7 +968,7 @@ articles in the topic and its subtopics." "r" gnus-topic-rename "\177" gnus-topic-delete [delete] gnus-topic-delete - "h" gnus-topic-toggle-display-empty-topics) + "H" gnus-topic-toggle-display-empty-topics) (gnus-define-keys (gnus-topic-sort-map "S" gnus-group-topic-map) "s" gnus-topic-sort-groups @@ -933,6 +992,7 @@ articles in the topic and its subtopics." ["Copy matching" gnus-topic-copy-matching t] ["Move matching" gnus-topic-move-matching t]) ("Topics" + ["Goto" gnus-topic-jump-to-topic t] ["Show" gnus-topic-show-topic t] ["Hide" gnus-topic-hide-topic t] ["Delete" gnus-topic-delete t] @@ -940,6 +1000,7 @@ articles in the topic and its subtopics." ["Create" gnus-topic-create-topic t] ["Mark" gnus-topic-mark-topic t] ["Indent" gnus-topic-indent t] + ["Sort" gnus-topic-sort-topics t] ["Toggle hide empty" gnus-topic-toggle-display-empty-topics t] ["Edit parameters" gnus-topic-edit-parameters t]) ["List active" gnus-topic-list-active t])))) @@ -953,13 +1014,12 @@ articles in the topic and its subtopics." (if (null arg) (not gnus-topic-mode) (> (prefix-numeric-value arg) 0))) ;; Infest Gnus with topics. - (if (not gnus-topic-mode) + (if (not gnus-topic-mode) (setq gnus-goto-missing-group-function nil) (when (gnus-visual-p 'topic-menu 'menu) (gnus-topic-make-menu-bar)) (gnus-set-format 'topic t) (gnus-add-minor-mode 'gnus-topic-mode " Topic" gnus-topic-mode-map) - (add-hook 'gnus-summary-exit-hook 'gnus-topic-update-topic) (add-hook 'gnus-group-catchup-group-hook 'gnus-topic-update-topic) (set (make-local-variable 'gnus-group-prepare-function) 'gnus-group-prepare-topics) @@ -1013,6 +1073,19 @@ If performed over a topic line, toggle folding the topic." (mouse-set-point e) (gnus-topic-read-group nil)) +(defun gnus-topic-expire-articles (topic) + "Expire articles in this topic or group." + (interactive (list (gnus-group-topic-name))) + (if (not topic) + (call-interactively 'gnus-group-expire-articles) + (save-excursion + (gnus-message 5 "Expiring groups in %s..." topic) + (let ((gnus-group-marked + (mapcar (lambda (entry) (car (nth 2 entry))) + (gnus-topic-find-groups topic gnus-level-killed t)))) + (gnus-group-expire-articles nil)) + (gnus-message 5 "Expiring groups in %s...done" topic)))) + (defun gnus-topic-read-group (&optional all no-article group) "Read news in this newsgroup. If the prefix argument ALL is non-nil, already read articles become @@ -1066,23 +1139,25 @@ If COPYP, copy the groups instead." (completing-read "Move to topic: " gnus-topic-alist nil t))) (let ((groups (gnus-group-process-prefix n)) (topicl (assoc topic gnus-topic-alist)) - (start-group (progn (forward-line 1) (gnus-group-group-name))) (start-topic (gnus-group-topic-name)) + (start-group (progn (forward-line 1) (gnus-group-group-name))) entry) - (mapcar - (lambda (g) - (gnus-group-remove-mark g) - (when (and - (setq entry (assoc (gnus-current-topic) gnus-topic-alist)) - (not copyp)) - (setcdr entry (gnus-delete-first g (cdr entry)))) - (nconc topicl (list g))) - groups) - (gnus-topic-enter-dribble) - (if start-group - (gnus-group-goto-group start-group) - (gnus-topic-goto-topic start-topic)) - (gnus-group-list-groups))) + (if (and (not groups) (not copyp) start-topic) + (gnus-topic-move start-topic topic) + (mapcar + (lambda (g) + (gnus-group-remove-mark g) + (when (and + (setq entry (assoc (gnus-current-topic) gnus-topic-alist)) + (not copyp)) + (setcdr entry (gnus-delete-first g (cdr entry)))) + (nconc topicl (list g))) + groups) + (gnus-topic-enter-dribble) + (if start-group + (gnus-group-goto-group start-group) + (gnus-topic-goto-topic start-topic)) + (gnus-group-list-groups)))) (defun gnus-topic-remove-group (&optional arg) "Remove the current group from the topic." @@ -1253,7 +1328,11 @@ If COPYP, copy the groups instead." (read-string (format "Rename %s to: " topic))))) ;; Check whether the new name exists. (when (gnus-topic-find-topology new-name) - (error "Topic '%s' already exists")) + (error "Topic '%s' already exists" new-name)) + ;; "nil" is an invalid name, for reasons I'd rather not go + ;; into here. Trust me. + (when (equal new-name "nil") + (error "Invalid name: %s" nil)) ;; Do the renaming. (let ((top (gnus-topic-find-topology old-name)) (entry (assoc old-name gnus-topic-alist))) @@ -1263,7 +1342,8 @@ If COPYP, copy the groups instead." (setcar entry new-name)) (forward-line -1) (gnus-dribble-touch) - (gnus-group-list-groups))) + (gnus-group-list-groups) + (forward-line 1))) (defun gnus-topic-indent (&optional unindent) "Indent a topic -- make it a sub-topic of the previous topic. @@ -1417,6 +1497,68 @@ If REVERSE, sort in reverse order." (interactive "P") (gnus-topic-sort-groups 'gnus-group-sort-by-method reverse)) +(defun gnus-topic-sort-topics-1 (top reverse) + (if (cdr top) + (let ((subtop + (mapcar `(lambda (top) + (gnus-topic-sort-topics-1 top ,reverse)) + (sort (cdr top) + '(lambda (t1 t2) + (string-lessp (caar t1) (caar t2))))))) + (setcdr top (if reverse (reverse subtop) subtop)))) + top) + +(defun gnus-topic-sort-topics (&optional topic reverse) + "Sort topics in TOPIC alphabeticaly by topic name. +If REVERSE, reverse the sorting order." + (interactive + (list (completing-read "Sort topics in : " gnus-topic-alist nil t + (gnus-current-topic)) + current-prefix-arg)) + (let ((topic-topology (or (and topic (cdr (gnus-topic-find-topology topic))) + gnus-topic-topology))) + (gnus-topic-sort-topics-1 topic-topology reverse) + (gnus-topic-enter-dribble) + (gnus-group-list-groups) + (gnus-topic-goto-topic topic))) + +(defun gnus-topic-move (current to) + "Move the CURRENT topic to TO." + (interactive + (list + (gnus-group-topic-name) + (completing-read "Move to topic: " gnus-topic-alist nil t))) + (unless (and current to) + (error "Can't find topic")) + (let ((current-top (cdr (gnus-topic-find-topology current))) + (to-top (cdr (gnus-topic-find-topology to)))) + (unless current-top + (error "Can't find topic `%s'" current)) + (unless to-top + (error "Can't find topic `%s'" to)) + (if (gnus-topic-find-topology to current-top 0);; Don't care the level + (error "Can't move `%s' to its sub-level" current)) + (gnus-topic-find-topology current nil nil 'delete) + (while (cdr to-top) + (setq to-top (cdr to-top))) + (setcdr to-top (list current-top)) + (gnus-topic-enter-dribble) + (gnus-group-list-groups) + (gnus-topic-goto-topic current))) + +(defun gnus-subscribe-topics (newsgroup) + (catch 'end + (let (match gnus-group-change-level-function) + (dolist (topic (gnus-topic-list)) + (when (and (setq match (cdr (assq 'subscribe + (gnus-topic-parameters topic)))) + (string-match match newsgroup)) + ;; Just subscribe the group. + (gnus-subscribe-alphabetically newsgroup) + ;; Add the group to the topic. + (nconc (assoc topic gnus-topic-alist) (list newsgroup)) + (throw 'end t)))))) + (provide 'gnus-topic) ;;; gnus-topic.el ends here