;;; gnus-group.el --- group mode commands for Gnus
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
;; Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
(require 'time-date)
(require 'gnus-ems)
-(eval-when-compile (require 'mm-url))
+(eval-when-compile
+ (require 'mm-url)
+ (let ((features (cons 'gnus-group features)))
+ (require 'gnus-sum))
+ (unless (boundp 'gnus-cache-active-hashtb)
+ (defvar gnus-cache-active-hashtb nil)))
+
+(autoload 'gnus-agent-total-fetched-for "gnus-agent")
+(autoload 'gnus-cache-total-fetched-for "gnus-cache")
(defcustom gnus-group-archive-directory
- "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
+ "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
"*The address of the (ding) archives."
:group 'gnus-group-foreign
:type 'directory)
(defcustom gnus-group-recent-archive-directory
- "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
+ "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
"*The address of the most recent (ding) articles."
:group 'gnus-group-foreign
:type 'directory)
-(defcustom gnus-no-groups-message "No gnus is bad news"
+(defcustom gnus-no-groups-message "No Gnus is good news"
"*Message displayed by Gnus when no groups are available."
:group 'gnus-start
:type 'string)
(function-item gnus-group-sort-by-rank)
(function :tag "other" nil))))
-(defcustom gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%g%)%l %O\n"
+(defcustom gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%g%)%O\n"
"*Format of group lines.
It works along the same lines as a normal formatting string,
with some simple extensions.
%P Topic indentation (string)
%m Whether there is new(ish) mail in the group (char, \"%\")
%w Number of new(ish) mails in the group (integer)
-%l Whether there are GroupLens predictions for this group (string)
%n Select from where (string)
%z A string that look like `<%s:%n>' if a foreign select method is used
%d The date the group was last entered.
%E Icon as defined by `gnus-group-icon-list'.
+%F The disk space used by the articles fetched by both the cache and agent.
%u User defined specifier. The next character in the format string should
be a letter. Gnus will call the function gnus-user-format-function-X,
where X is the letter following %u. The function will be passed a
groups.
If you use %o or %O, reading the active file will be slower and quite
-a bit of extra memory will be used. %D will also worsen performance.
-Also note that if you change the format specification to include any
-of these specs, you must probably re-start Gnus to see them go into
-effect.
+a bit of extra memory will be used. %D and %F will also worsen
+performance. Also note that if you change the format specification to
+include any of these specs, you must probably re-start Gnus to see
+them go into effect.
General format specifiers can also be used.
See Info node `(gnus)Formatting Variables'."
(defcustom gnus-group-jump-to-group-prompt nil
"Default prompt for `gnus-group-jump-to-group'.
-If non-nil, the value should be a string, e.g. \"nnml:\",
-in which case `gnus-group-jump-to-group' offers \"Group: nnml:\"
-in the minibuffer prompt."
+
+If non-nil, the value should be a string or an alist. If it is a string,
+e.g. \"nnml:\", in which case `gnus-group-jump-to-group' offers \"Group:
+nnml:\" in the minibuffer prompt.
+
+If it is an alist, it must consist of \(NUMBER . PROMPT\) pairs, for example:
+\((1 . \"\") (2 . \"nnfolder+archive:\")). The element with number 0 is
+used when no prefix argument is given to `gnus-group-jump-to-group'."
:group 'gnus-group-various
:type '(choice (string :tag "Prompt string")
- (const :tag "Empty" nil)))
+ (const :tag "Empty" nil)
+ (repeat (cons (integer :tag "Argument")
+ (string :tag "Prompt string")))))
(defvar gnus-group-listing-limit 1000
"*A limit of the number of groups when listing.
(?P gnus-group-indentation ?s)
(?E gnus-tmp-group-icon ?s)
(?B gnus-tmp-summary-live ?c)
- (?l gnus-tmp-grouplens ?s)
(?z gnus-tmp-news-method-string ?s)
(?m (gnus-group-new-mail gnus-tmp-group) ?c)
(?w (if (gnus-news-group-p gnus-tmp-group)
)))
?s)
(?d (gnus-group-timestamp-string gnus-tmp-group) ?s)
- (?u gnus-tmp-user-defined ?s)))
+ (?u gnus-tmp-user-defined ?s)
+ (?F (gnus-total-fetched-for gnus-tmp-group) ?s)
+ ))
(defvar gnus-group-mode-line-format-alist
`((?S gnus-tmp-news-server ?s)
"?" gnus-group-list-plus)
(gnus-define-keys (gnus-group-score-map "W" gnus-group-mode-map)
- "f" gnus-score-flush-cache)
+ "f" gnus-score-flush-cache
+ "e" gnus-score-edit-all-score)
(gnus-define-keys (gnus-group-help-map "H" gnus-group-mode-map)
"c" gnus-group-fetch-charter
(use-local-map gnus-group-mode-map)
(buffer-disable-undo)
(setq truncate-lines t)
- (setq buffer-read-only t)
+ (setq buffer-read-only t
+ show-trailing-whitespace nil)
(gnus-set-default-directory)
(gnus-update-format-specifications nil 'group 'group-mode)
(gnus-update-group-mark-positions)
(gnus-group-setup-buffer)
(gnus-update-format-specifications nil 'group 'group-mode)
(let ((case-fold-search nil)
- (props (text-properties-at (gnus-point-at-bol)))
+ (props (text-properties-at (point-at-bol)))
(empty (= (point-min) (point-max)))
(group (gnus-group-group-name))
number)
(point-min) (point-max)
'gnus-group (gnus-intern-safe
group gnus-active-hashtb))))
- (let ((newsrc (cdddr (gnus-gethash group gnus-newsrc-hashtb))))
+ (let ((newsrc (cdddr (gnus-group-entry group))))
(while (and newsrc
(not (gnus-goto-char
(text-property-any
group (gnus-info-group info)
params (gnus-info-params info)
newsrc (cdr newsrc)
- unread (car (gnus-gethash group gnus-newsrc-hashtb)))
+ unread (gnus-group-unread group))
(when not-in-list
(setq not-in-list (delete group not-in-list)))
(when (gnus-group-prepare-logic
"Update the current line in the group buffer."
(let* ((buffer-read-only nil)
(group (gnus-group-group-name))
- (entry (and group (gnus-gethash group gnus-newsrc-hashtb)))
+ (entry (and group (gnus-group-entry group)))
gnus-group-indentation)
(when group
(and entry
(defun gnus-group-insert-group-line-info (group)
"Insert GROUP on the current line."
- (let ((entry (gnus-gethash group gnus-newsrc-hashtb))
+ (let ((entry (gnus-group-entry group))
(gnus-group-indentation (gnus-group-group-indentation))
active info)
(if entry
(gnus-tmp-process-marked
(if (member gnus-tmp-group gnus-group-marked)
gnus-process-mark ? ))
- (gnus-tmp-grouplens
- (or (and gnus-use-grouplens
- (bbb-grouplens-group-p gnus-tmp-group))
- ""))
(buffer-read-only nil)
header gnus-tmp-header) ; passed as parameter to user-funcs.
(beginning-of-line)
"Highlight the current line according to `gnus-group-highlight'."
(let* ((list gnus-group-highlight)
(p (point))
- (end (gnus-point-at-eol))
+ (end (point-at-eol))
;; now find out where the line starts and leave point there.
(beg (progn (beginning-of-line) (point)))
(group (gnus-group-group-name))
(loc (point-min))
found buffer-read-only)
;; Enter the current status into the dribble buffer.
- (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
+ (let ((entry (gnus-group-entry group)))
(when (and entry
(not (gnus-ephemeral-group-p group)))
(gnus-dribble-enter
;; go, and insert it there (or at the end of the buffer).
(if gnus-goto-missing-group-function
(funcall gnus-goto-missing-group-function group)
- (let ((entry (cddr (gnus-gethash group gnus-newsrc-hashtb))))
+ (let ((entry (cddr (gnus-group-entry group))))
(while (and entry (car entry)
(not
(gnus-goto-char
(defun gnus-group-group-name ()
"Get the name of the newsgroup on the current line."
- (let ((group (get-text-property (gnus-point-at-bol) 'gnus-group)))
+ (let ((group (get-text-property (point-at-bol) 'gnus-group)))
(when group
(symbol-name group))))
(defun gnus-group-group-level ()
"Get the level of the newsgroup on the current line."
- (get-text-property (gnus-point-at-bol) 'gnus-level))
+ (get-text-property (point-at-bol) 'gnus-level))
(defun gnus-group-group-indentation ()
"Get the indentation of the newsgroup on the current line."
- (or (get-text-property (gnus-point-at-bol) 'gnus-indentation)
+ (or (get-text-property (point-at-bol) 'gnus-indentation)
(and gnus-group-indentation-function
(funcall gnus-group-indentation-function))
""))
(defun gnus-group-group-unread ()
"Get the number of unread articles of the newsgroup on the current line."
- (get-text-property (gnus-point-at-bol) 'gnus-unread))
+ (get-text-property (point-at-bol) 'gnus-unread))
(defun gnus-group-new-mail (group)
(if (nnmail-new-mail-p (gnus-group-real-name group))
(goto-char (or pos beg))
(and pos t))))
+(defun gnus-total-fetched-for (group)
+ (let* ((size-in-cache (or (gnus-cache-total-fetched-for group) 0))
+ (size-in-agent (or (gnus-agent-total-fetched-for group) 0))
+ (size (+ size-in-cache size-in-agent))
+ (suffix '("B" "K" "M" "G"))
+ (scale 1024.0)
+ (cutoff (* 10 scale)))
+ (while (> size cutoff)
+ (setq size (/ size scale)
+ suffix (cdr suffix)))
+ (format "%5.1f%s" size (car suffix))))
+
;;; Gnus group mode commands
;; Group marking.
;; Go to the mark position.
(beginning-of-line)
(forward-char (or (cdr (assq 'process gnus-group-mark-positions)) 2))
- (subst-char-in-region
- (point) (1+ (point)) (char-after)
- (if unmark
- (progn
- (setq gnus-group-marked (delete group gnus-group-marked))
- ? )
+ (delete-char 1)
+ (if unmark
+ (progn
+ (setq gnus-group-marked (delete group gnus-group-marked))
+ (insert-char ? 1 t))
(setq gnus-group-marked
(cons group (delete group gnus-group-marked)))
- gnus-process-mark)))
+ (insert-char gnus-process-mark 1 t)))
(unless no-advance
(gnus-group-next-group 1))
(decf n))
(unless group
(error "No group on current line"))
(setq marked (gnus-info-marks
- (nth 2 (setq entry (gnus-gethash
- group gnus-newsrc-hashtb)))))
+ (nth 2 (setq entry (gnus-group-entry group)))))
;; This group might be a dead group. In that case we have to get
;; the number of unread articles from `gnus-active-hashtb'.
(setq number
No article is selected automatically.
If the group is opened, just switch the summary buffer.
If ALL is non-nil, already read articles become readable.
-If ALL is a number, fetch this number of articles."
+If ALL is a positive number, fetch this number of the latest
+articles in the group.
+If ALL is a negative number, fetch this number of the earliest
+articles in the group."
(interactive "P")
(when (and (eobp) (not (gnus-group-group-name)))
(forward-line -1))
(message "Quit reading the ephemeral group")
nil)))))
-(defun gnus-group-jump-to-group (group)
- "Jump to newsgroup GROUP."
+(defun gnus-group-jump-to-group (group &optional prompt)
+ "Jump to newsgroup GROUP.
+
+If PROMPT (the prefix) is a number, use the prompt specified in
+`gnus-group-jump-to-group-prompt'."
(interactive
- (list (gnus-group-completing-read-group-name
- "Group: " gnus-active-hashtb nil
- (gnus-read-active-file-p)
- gnus-group-jump-to-group-prompt
- 'gnus-group-history)))
+ (list (mm-string-make-unibyte
+ (completing-read
+ "Group: " gnus-active-hashtb nil
+ (gnus-read-active-file-p)
+ (if current-prefix-arg
+ (cdr (assq current-prefix-arg gnus-group-jump-to-group-prompt))
+ (or (and (stringp gnus-group-jump-to-group-prompt)
+ gnus-group-jump-to-group-prompt)
+ (let ((p (cdr (assq 0 gnus-group-jump-to-group-prompt))))
+ (and (stringp p) p))))
+ 'gnus-group-history))))
(when (equal group "")
(error "Empty group name"))
;; Either go to the line in the group buffer...
(unless (gnus-group-goto-group group)
;; ... or insert the line.
- (gnus-group-update-group group)
+ (if (or (gnus-active group)
+ (gnus-y-or-n-p
+ (format "Group %s is not active. Continue? " group)))
+ (gnus-group-update-group group)
+ (error "No such group: %s." group))
(gnus-group-goto-group group)))
;; Adjust cursor point.
(gnus-group-position-point))
method))))
(nname (if method (gnus-group-prefixed-name name meth) name))
backend info)
- (when (gnus-gethash nname gnus-newsrc-hashtb)
+ (when (gnus-group-entry nname)
(error "Group %s already exists" nname))
;; Subscribe to the new group.
(gnus-group-change-level
(setq info (list t nname gnus-level-default-subscribed nil nil meth))
gnus-level-default-subscribed gnus-level-killed
(and (gnus-group-group-name)
- (gnus-gethash (gnus-group-group-name)
- gnus-newsrc-hashtb))
+ (gnus-group-entry (gnus-group-group-name)))
t)
;; Make it active.
(gnus-set-active nname (cons 1 0))
(lambda (group)
(gnus-group-delete-group group nil t))))))
-(defvar gnus-cache-active-altered)
-
(defun gnus-group-delete-group (group &optional force no-prompt)
"Delete the current group. Only meaningful with editable groups.
If FORCE (the prefix) is non-nil, all the articles in the group will
(gnus-message 6 "Deleting group %s...done" group)
(gnus-group-goto-group group)
(gnus-group-kill-group 1 t)
- (gnus-sethash group nil gnus-active-hashtb)
- (if (boundp 'gnus-cache-active-hashtb)
- (when gnus-cache-active-hashtb
- (gnus-sethash group nil gnus-cache-active-hashtb)
- (setq gnus-cache-active-altered t)))
+ (gnus-set-active group nil)
t))
(gnus-group-position-point)))
(interactive)
(let ((name (gnus-group-prefixed-name "gnus-help" '(nndoc "gnus-help")))
(file (nnheader-find-etc-directory "gnus-tut.txt" t)))
- (if (gnus-gethash name gnus-newsrc-hashtb)
+ (if (gnus-group-entry name)
(cond ((eq noerror nil)
(error "Documentation group already exists"))
((eq noerror t)
(interactive "P")
(let ((group (gnus-group-prefixed-name
(if all "ding.archives" "ding.recent") '(nndir ""))))
- (when (gnus-gethash group gnus-newsrc-hashtb)
+ (when (gnus-group-entry group)
(error "Archive group already exists"))
(gnus-group-make-group
(gnus-group-real-name group)
(let ((ext "")
(i 0)
group)
- (while (or (not group) (gnus-gethash group gnus-newsrc-hashtb))
+ (while (or (not group) (gnus-group-entry group))
(setq group
(gnus-group-prefixed-name
(expand-file-name ext dir)
(list
(read-string "nnkiboze group name: ")
(read-string "Source groups (regexp): ")
- (let ((headers (mapcar (lambda (group) (list group))
+ (let ((headers (mapcar 'list
'("subject" "from" "number" "date" "message-id"
"references" "chars" "lines" "xref"
"followup" "all" "body" "head")))
(let* ((method (list 'nnvirtual "^$"))
(pgroup (gnus-group-prefixed-name group method)))
;; Check whether it exists already.
- (when (gnus-gethash pgroup gnus-newsrc-hashtb)
+ (when (gnus-group-entry pgroup)
(error "Group %s already exists" pgroup))
;; Subscribe the new group after the group on the current line.
(gnus-subscribe-group pgroup (gnus-group-group-name) method)
(let (entries infos)
;; First find all the group entries for these groups.
(while groups
- (push (nthcdr 2 (gnus-gethash (pop groups) gnus-newsrc-hashtb))
+ (push (nthcdr 2 (gnus-group-entry (pop groups)))
entries))
;; Then sort the infos.
(setq infos
(defun gnus-group-sort-by-unread (info1 info2)
"Sort by number of unread articles."
- (let ((n1 (car (gnus-gethash (gnus-info-group info1) gnus-newsrc-hashtb)))
- (n2 (car (gnus-gethash (gnus-info-group info2) gnus-newsrc-hashtb))))
+ (let ((n1 (gnus-group-unread (gnus-info-group info1)))
+ (n2 (gnus-group-unread (gnus-info-group info1))))
(< (or (and (numberp n1) n1) 0)
(or (and (numberp n2) n2) 0))))
If ALL is non-nil, all articles are marked as read.
The return value is the number of articles that were marked as read,
or nil if no action could be taken."
- (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
+ (let* ((entry (gnus-group-entry group))
(num (car entry))
- (marks (nth 3 (nth 2 entry)))
- (unread (gnus-list-of-unread-articles group)))
+ (marks (gnus-info-marks (nth 2 entry)))
+ (unread (gnus-sequence-of-unread-articles group)))
;; Remove entries for this group.
(nnmail-purge-split-history (gnus-group-real-name group))
;; Do the updating only if the newsgroup isn't killed.
'del '(tick))
(list (cdr (assq 'dormant marks))
'del '(dormant))))
- (setq unread (gnus-uncompress-range
- (gnus-range-add (gnus-range-add
- unread (cdr (assq 'dormant marks)))
- (cdr (assq 'tick marks)))))
+ (setq unread (gnus-range-add (gnus-range-add
+ unread (cdr (assq 'dormant marks)))
+ (cdr (assq 'tick marks))))
(gnus-add-marked-articles group 'tick nil nil 'force)
(gnus-add-marked-articles group 'dormant nil nil 'force))
;; Do auto-expirable marks if that's required.
(when (gnus-group-auto-expirable-p group)
- (gnus-add-marked-articles group 'expire unread)
- (gnus-request-set-mark group (list (list unread 'add '(expire)))))
+ (gnus-range-map (lambda (article)
+ (gnus-add-marked-articles group 'expire (list article))
+ (gnus-request-set-mark group (list (list (list article) 'add '(expire)))))
+ unread))
(let ((gnus-newsgroup-name group))
(gnus-run-hooks 'gnus-group-catchup-group-hook))
num)))
(gnus-read-active-file-p)
nil
'gnus-group-history)))
- (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
+ (let ((newsrc (gnus-group-entry group)))
(cond
((string-match "^[ \t]*$" group)
(error "Empty group name"))
gnus-level-zombie)
gnus-level-killed)
(when (gnus-group-group-name)
- (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)))
+ (gnus-group-entry (gnus-group-group-name))))
(unless silent
(gnus-group-update-group group)))
(t (error "No such newsgroup: %s" group)))
(count-lines
(progn
(goto-char begin)
- (beginning-of-line)
- (point))
+ (point-at-bol))
(progn
(goto-char end)
- (beginning-of-line)
- (point))))))
+ (point-at-bol))))))
(goto-char begin)
(beginning-of-line) ;Important when LINES < 1
(gnus-group-kill-group lines)))
(setq level (gnus-group-group-level))
(gnus-delete-line)
(when (and (not discard)
- (setq entry (gnus-gethash group gnus-newsrc-hashtb)))
+ (setq entry (gnus-group-entry group)))
(gnus-undo-register
`(progn
(gnus-group-goto-group ,(gnus-group-group-name))
(funcall gnus-group-change-level-function
group gnus-level-killed 3))
(cond
- ((setq entry (gnus-gethash group gnus-newsrc-hashtb))
+ ((setq entry (gnus-group-entry group))
(push (cons (car entry) (nth 2 entry))
gnus-list-of-killed-groups)
(setcdr (cdr entry) (cdddr entry)))
(setq prev (gnus-group-group-name))
(gnus-group-change-level
info (gnus-info-level (cdr info)) gnus-level-killed
- (and prev (gnus-gethash prev gnus-newsrc-hashtb))
+ (and prev (gnus-group-entry prev))
t)
(gnus-group-insert-group-line-info group)
(gnus-undo-register
;; First we make sure that we have really read the active file.
(unless (gnus-read-active-file-p)
(let ((gnus-read-active-file t)
- (gnus-agent nil)) ; Trick the agent into ignoring the active file.
+ (gnus-agent gnus-plugged)); If we're actually plugged, store the active file in the agent.
(gnus-read-active-file)))
;; Find all groups and sort them.
(let ((groups
(let ((group-buf (get-buffer gnus-group-buffer)))
(mapcar (lambda (buf)
(unless (or (member buf (list group-buf gnus-dribble-buffer))
- (progn
- (save-excursion
- (set-buffer buf)
- (eq major-mode 'message-mode))))
+ (with-current-buffer buf
+ (eq major-mode 'message-mode)))
(gnus-kill-buffer buf)))
(gnus-buffers))
(setq gnus-backlog-articles nil)
;; Suggested by mapjph@bath.ac.uk.
(completing-read
"Address: "
- (mapcar (lambda (server) (list server))
- gnus-secondary-servers)))
+ (mapcar 'list gnus-secondary-servers)))
;; We got a server name.
how))))
(gnus-browse-foreign-server method))
(defun gnus-group-set-info (info &optional method-only-group part)
(when (or info part)
- (let* ((entry (gnus-gethash
- (or method-only-group (gnus-info-group info))
- gnus-newsrc-hashtb))
+ (let* ((entry (gnus-group-entry
+ (or method-only-group (gnus-info-group info))))
(part-info info)
(info (if method-only-group (nth 2 entry) info))
method)
(gnus-group-make-group (gnus-info-group info))))
(gnus-message 6 "Note: New group created")
(setq entry
- (gnus-gethash (gnus-group-prefixed-name
- (gnus-group-real-name (gnus-info-group info))
- (or (gnus-info-method info) gnus-select-method))
- gnus-newsrc-hashtb))))
+ (gnus-group-entry (gnus-group-prefixed-name
+ (gnus-group-real-name (gnus-info-group info))
+ (or (gnus-info-method info) gnus-select-method))))))
;; Whether it was a new group or not, we now have the entry, so we
;; can do the update.
(if entry