+Sat Dec 16 14:26:27 1995 Lars Magne Ingebrigtsen <larsi@narfi.ifi.uio.no>
+
+ * gnus.el (gnus-summary-exit): Would nix out the group name of
+ parents to nndoc groups.
+
+Fri Dec 15 20:55:26 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-buffer-configuration): New default value.
+ (gnus-configure-windows): Use it.
+ (gnus-all-windows-visible-p): New implementation.
+
+Fri Dec 15 19:28:07 1995 Jason L. Tibbitts, III <tibbs@uh.edu>
+
+ * nnml.el (nnml-generate-nov-file): Directory names with/without
+ slashes.
+
+Fri Dec 15 18:55:28 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-cache.el (gnus-cache-generate-nov-databases): Called wrong
+ nnml function.
+
+ * gnus.el (gnus-summary-exit): Don't clear the group name until
+ the last hook has been run.
+
+Fri Dec 15 18:53:29 1995 Lance A. Brown <labrown@dg-rtp.dg.com>
+
+ * gnus.el (gnus-parse-simple-format): %4,4i would break function.
+
+Fri Dec 15 18:48:07 1995 Michael Sperber <sperber@informatik.uni-tuebingen.de>
+
+ * nnheader.el (nnheader-file-to-number): Would return a list of
+ strings.
+
+Fri Dec 15 12:14:08 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-configure-frame): New function.
+
+Thu Dec 14 20:16:30 1995 Jason L. Tibbitts, III <tibbs@uh.edu>
+
+ * gnus.el (gnus-simplify-subject-fully): New function.
+
+Thu Dec 14 17:55:03 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-update-missing-marks): New function.
+ (gnus-select-newsgroup): Use it.
+
+ * gnus-uu.el (gnus-uu-grabbed-file-functions): New variable.
+ (gnus-uu-grab-articles): Use it.
+ (gnus-uu-grab-view, gnus-uu-grab-move): New functions.
+
+ * gnus-score.el (gnus-possibly-score-headers): Allow a
+ `score-file' group parameter.
+
+Thu Dec 14 12:42:20 1995 Lars Magne Ingebrigtsen <larsi@narfi.ifi.uio.no>
+
+ * nnheader.el (nnheader-file-to-number): Returned strings instead
+ of numbers.
+
Thu Dec 14 10:48:51 1995 Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
* gnus.el (gnus-summary-toggle-header): Don't do that hook dance.
+ * gnus.el: 0.22 is released.
+
Thu Dec 14 10:02:08 1995 Lars Magne Ingebrigtsen <larsi@narfi.ifi.uio.no>
* gnus-xmas.el (gnus-xmas-read-event-char): New function.
(defvar gnus-cache-active-hashtb nil)
(defvar gnus-cache-active-altered nil)
+(eval-and-compile
+ (autoload 'nnml-generate-nov-databases-1 "nnml"))
+
\f
;;; Functions called from Gnus.
"Generate the cache active file."
(interactive)
(let* ((top (null directory))
- (directory (or directory (expand-file-name gnus-cache-directory)))
+ (directory (expand-file-name (or directory gnus-cache-directory)))
(files (directory-files directory 'full))
(group
(progn
- (string-match (concat "^" (expand-file-name gnus-cache-directory))
- directory)
+ (string-match
+ (concat "^" (file-name-as-directory
+ (expand-file-name gnus-cache-directory)))
+ directory)
(gnus-replace-chars-in-string
(substring directory (match-end 0))
?/ ?.)))
"Generate NOV files recursively starting in DIR."
(interactive (list gnus-cache-directory))
(gnus-cache-close)
- (require 'nnml)
(let ((nnml-generate-active-function 'identity))
- (nnml-generate-nov-databases dir)))
+ (nnml-generate-nov-databases-1 dir)))
(provide 'gnus-cache)
'(gnus-deletable t face italic) (current-buffer)))))))
;; Insert new Sender if the From is strange.
(let ((from (mail-fetch-field "from"))
- (sender (mail-fetch-field "sender")))
- (if (and from
- (not (gnus-check-before-posting 'sender))
- (not (string=
- (downcase (car (cdr (gnus-extract-address-components from))))
- (downcase (gnus-inews-real-user-address))))
- (or (null sender)
- (not
- (string=
- (downcase (car (cdr (gnus-extract-address-components sender))))
- (downcase (gnus-inews-real-user-address))))))
- (progn
- (goto-char (point-min))
- (and (re-search-forward "^Sender:" nil t)
- (progn
- (beginning-of-line)
- (insert "Original-")
- (beginning-of-line)))
- (insert "Sender: " (gnus-inews-real-user-address) "\n"))))))
-
+ (sender (mail-fetch-field "sender"))
+ (secure-sender (gnus-inews-real-user-address)))
+ (when (and from
+ (not (gnus-check-before-posting 'sender))
+ (not (string=
+ (downcase (car (cdr (gnus-extract-address-components
+ from))))
+ (downcase (gnus-inews-real-user-address))))
+ (or (null sender)
+ (not
+ (string=
+ (downcase (car (cdr (gnus-extract-address-components
+ sender))))
+ (downcase secure-sender)))))
+ (goto-char (point-min))
+ ;; Rename any old Sender headers to Original-Sender.
+ (when (re-search-forward "^Sender:" nil t)
+ (beginning-of-line)
+ (insert "Original-")
+ (beginning-of-line))
+ (insert "Sender: " secure-sender "\n")))))
(defun gnus-inews-insert-signature ()
"Insert a signature file.
--- /dev/null
+;; gnus-picon.el: Copyright (C) 1995 Wes Hardaker
+;; Icon hacks for displaying pretty icons in Gnus.
+;;
+;; Author: Wes hardaker
+;; hardaker@ece.ucdavis.edu
+;;
+;; Usage:
+;; - You must have XEmacs to use this.
+;; - (add-hook 'gnus-article-display-hook 'gnus-article-display-picons t)
+;; This HAS to have the 't' flag above to make sure it appends the hook.
+;; - Read the variable descriptions below.
+;;
+;; Warnings:
+;; - I'm not even close to being a lisp expert.
+;;
+;; TODO:
+;; - Following the Gnus motto: We've got to build him bigger,
+;; better, stronger, faster than before... errr.... sorry.
+;; - Create a seperate frame to store icons in so icons are
+;; visibile immediately upon entering a group rather than just
+;; at the top of the article buffer.
+;;
+;;
+
+(require 'xpm)
+(require 'annotations)
+
+(defvar gnus-picons-database "/usr/local/faces"
+ "defines the location of the faces database. For information on
+ obtaining this database of pretty pictures, please see
+ http://www.cs.indiana.edu/picons/ftp/index.html"
+)
+
+(defvar gnus-picons-news-directory "news"
+ "Sub-directory of the faces database containing the icons for
+ newsgroups."
+)
+
+(defvar gnus-picons-user-directories '("local" "users" "usenix" "misc/MISC")
+ "List of directories to search for user faces."
+)
+
+(defvar gnus-picons-domain-directories '("domains")
+ "List of directories to search for domain faces. Some people may
+ want to add \"unknown\" to this list."
+)
+
+(defun gnus-article-display-picons ()
+ "prepare article buffer with pretty pictures"
+ (interactive)
+ (if (featurep 'xpm)
+ (save-excursion
+ (beginning-of-buffer)
+ (open-line 1)
+ (let* ((iconpoint (point)) (from (mail-fetch-field "from"))
+ (username
+ (progn
+ (string-match "\\([-_a-zA-Z0-9]+\\)@" from)
+ (match-string 1 from)))
+ (hostpath
+ (gnus-picons-reverse-domain-path
+ (replace-in-string
+ (replace-in-string from ".*@\\([_a-zA-Z0-9-.]+\\).*" "\\1")
+ "\\." "/"))))
+ (if (equal username from)
+ (setq username (replace-in-string from
+ ".*<\\([_a-zA-Z0-9-.]+\\)>.*"
+ "\\1")))
+ (insert username)
+ (gnus-picons-insert-face-if-exists
+ (concat gnus-picons-database "/" gnus-picons-news-directory)
+ (concat (replace-in-string gnus-newsgroup-name "\\." "/") "/unknown")
+ iconpoint)
+ (mapcar '(lambda (pathpart)
+ (gnus-picons-insert-face-if-exists
+ (concat gnus-picons-database "/" pathpart)
+ (concat hostpath "/" username)
+ iconpoint))
+ gnus-picons-user-directories)
+ (mapcar '(lambda (pathpart)
+ (gnus-picons-insert-face-if-exists
+ (concat gnus-picons-database "/" pathpart)
+ (concat hostpath "/" "unknown")
+ iconpoint))
+ gnus-picons-domain-directories)
+ ))))
+
+(defun gnus-picons-insert-face-if-exists (path filename ipoint)
+ "inserts a face at point if I can find one"
+ (let ((pathfile (concat path "/" filename "/face")))
+ (let ((newfilename
+ (replace-in-string filename
+ "[_a-zA-Z0-9-]+/\\([_A-Za-z0-9-]+\\)$" "\\1")))
+ (if (not (equal filename newfilename))
+ (gnus-picons-insert-face-if-exists path newfilename ipoint)))
+ (if (not (gnus-picons-try-to-find-face (concat pathfile ".xpm") ipoint))
+ (gnus-picons-try-to-find-face (concat pathfile ".xbm") ipoint))
+ )
+ )
+
+
+(defun gnus-picons-try-to-find-face (path ipoint)
+ "if path exists, display it as a bitmap. Returns t if succedded."
+ (if (file-exists-p path)
+ (progn
+ (setq gl (make-glyph path))
+ (set-glyph-face gl 'default)
+ (setq annot (make-annotation gl ipoint 'text))
+ t)
+; (insert (format "no: %s\n" path))
+ nil))
+
+(defun gnus-picons-reverse-domain-path (str)
+ "a/b/c/d -> d/c/b/a"
+ (if (equal (replace-in-string str "^[^/]*$" "") "")
+ str
+ (concat (replace-in-string str "^.*/\\([_a-zA-Z0-9-]+\\)$" "\\1") "/"
+ (gnus-picons-reverse-domain-path
+ (replace-in-string str "^\\(.*\\)/[_a-zA-Z0-9-]+$" "\\1")))))
+
+
score-files)))
(defun gnus-possibly-score-headers (&optional trace)
- (let ((func gnus-score-find-score-files-function)
+ (let ((funcs gnus-score-find-score-files-function)
score-files)
- (and func
- (not (listp func))
- (setq func (list func)))
+ ;; Make sure funcs is a list.
+ (and funcs
+ (not (listp funcs))
+ (setq funcs (list funcs)))
+ ;; Get the initial score files for this group.
+ (when funcs
+ (setq score-files (gnus-score-find-alist gnus-newsgroup-name)))
;; Go through all the functions for finding score files (or actual
;; scores) and add them to a list.
- (and func (setq score-files (gnus-score-find-alist gnus-newsgroup-name)))
- (while func
- (and (symbolp (car func))
- (fboundp (car func))
- (setq score-files
- (nconc score-files (funcall (car func) gnus-newsgroup-name))))
- (setq func (cdr func)))
- (if score-files (gnus-score-headers score-files trace))))
+ (while funcs
+ (when (gnus-functionp (car funcs))
+ (setq score-files
+ (nconc score-files (funcall (car funcs) gnus-newsgroup-name))))
+ (setq funcs (cdr funcs)))
+ ;; Check whether there is a `score-file' group parameter.
+ (let ((param-file (gnus-group-get-parameter
+ gnus-newsgroup-name 'score-file)))
+ (when param-file
+ (push param-file score-files)))
+ ;; Do the scoring if there are any score files for this group.
+ (when score-files
+ (gnus-score-headers score-files trace))))
(defun gnus-score-file-name (newsgroup &optional suffix)
"Return the name of a score file for NEWSGROUP."
"*Non-nil means that gnus-uu will ignore the default viewing rules.
Only the user viewing rules will be consulted. Default is nil.")
+(defvar gnus-uu-grabbed-file-functions nil
+ "*Functions run on each file after successful decoding.
+They will be called with the name of the file as the argument.
+Likely functions you can use in this list are `gnus-uu-grab-view'
+and `gnus-uu-grab-move'.")
+
(defvar gnus-uu-ignore-default-archive-rules nil
"*Non-nil means that gnus-uu will ignore the default archive unpacking commands.
Only the user unpacking commands will be consulted. Default is nil.")
(setq end-char (point))
(set-buffer (get-buffer-create gnus-uu-output-buffer-name))
(insert-buffer-substring process-buffer start-char end-char)
- (setq file-name (concat gnus-uu-work-dir (cdr gnus-article-current) ".ps"))
+ (setq file-name (concat gnus-uu-work-dir
+ (cdr gnus-article-current) ".ps"))
(write-region (point-min) (point-max) file-name)
- (setq state (list file-name'begin 'end))
-
- ))
- )
+ (setq state (list file-name 'begin 'end)))))
state))
(defun gnus-uu-grab-articles
(articles process-function &optional sloppy limit no-errors)
(let ((state 'first)
- has-been-begin article result-file result-files process-state)
+ has-been-begin article result-file result-files process-state
+ article-series)
- (if (not (gnus-server-opened gnus-current-select-method))
- (progn
- (gnus-start-news-server)
- (gnus-request-group gnus-newsgroup-name)))
-
- (setq gnus-uu-has-been-grabbed nil)
-
(while (and articles
(not (memq 'error process-state))
(or sloppy
(not (memq 'end process-state))))
- (setq article (car articles))
- (setq articles (cdr articles))
- (setq gnus-uu-has-been-grabbed (cons article gnus-uu-has-been-grabbed))
+ (setq article (pop articles))
+ (push article article-series)
- (if (eq articles ())
- (if (eq state 'first)
- (setq state 'first-and-last)
- (setq state 'last)))
+ (unless articles
+ (if (eq state 'first)
+ (setq state 'first-and-last)
+ (setq state 'last)))
(message "Getting article %d, %s" article (gnus-uu-part-number article))
(gnus-summary-display-article article)
+
+ ;; Push the article to the processing function.
(save-excursion
(set-buffer gnus-original-article-buffer)
(let ((buffer-read-only nil))
(setq process-state
(funcall process-function
gnus-original-article-buffer state)))))
+
(gnus-summary-remove-process-mark article)
- (if (or (memq 'begin process-state)
- (and (or (eq state 'first) (eq state 'first-and-last))
- (memq 'ok process-state)))
- (progn
- (if has-been-begin
- (if (and result-file (file-exists-p result-file))
- (delete-file result-file)))
- (if (memq 'begin process-state)
- (setq result-file (car process-state)))
- (setq has-been-begin t)))
-
- (if (memq 'end process-state)
- (progn
- (setq gnus-uu-has-been-grabbed nil)
- (setq result-files (cons (list (cons 'name result-file)
- (cons 'article article))
- result-files))
- (setq has-been-begin nil)
- (and limit (= (length result-files) limit)
- (setq articles nil))))
-
- (if (and (or (eq state 'last) (eq state 'first-and-last))
- (not (memq 'end process-state)))
- (if (and result-file (file-exists-p result-file))
+ ;; If this is the beginning of a decoded file, we push it
+ ;; on to a list.
+ (when (or (memq 'begin process-state)
+ (and (or (eq state 'first)
+ (eq state 'first-and-last))
+ (memq 'ok process-state)))
+ (if has-been-begin
+ ;; If there is a `result-file' here, that means that the
+ ;; file was unsuccessfully decoded, so we delete it.
+ (when (and result-file
+ (file-exists-p result-file))
(delete-file result-file)))
+ (when (memq 'begin process-state)
+ (setq result-file (car process-state)))
+ (setq has-been-begin t))
+
+ ;; Check whether we have decoded one complete file.
+ (when (memq 'end process-state)
+ (setq article-series nil)
+ (setq has-been-begin nil)
+ (push (list (cons 'name result-file)
+ (cons 'article article))
+ result-files)
+ ;; Allow user-defined functions to be run on this file.
+ (when gnus-uu-grabbed-file-functions
+ (let ((funcs gnus-uu-grabbed-file-functions))
+ (unless (listp funcs)
+ (setq funcs (list funcs)))
+ (while funcs
+ (funcall (pop funcs) result-file))))
+ ;; Check whether we have decoded enough articles.
+ (and limit (= (length result-files) limit)
+ (setq articles nil)))
+
+ ;; If this is the last article to be decoded, and
+ ;; we still haven't reached the end, then we delete
+ ;; the partially decoded file.
+ (and (or (eq state 'last) (eq state 'first-and-last)
+ (not (memq 'end process-state)))
+ result-file
+ (file-exists-p result-file)
+ (delete-file result-file))
- (if (not (memq 'wrong-type process-state))
- ()
- (if gnus-uu-unmark-articles-not-decoded
- (gnus-summary-tick-article article t)))
+ ;; If this was a file of the wrong sort, then
+ (when (and (or (memq 'wrong-type process-state)
+ (memq 'error process-state))
+ gnus-uu-unmark-articles-not-decoded)
+ (gnus-summary-tick-article article t))
+ ;; Set the new series state.
(if (and (not has-been-begin)
(not sloppy)
(or (memq 'end process-state)
(sleep-for 2))
(setq state 'middle)))
- ;; Make sure the last article is put in the article buffer & fix
- ;; windows etc.
+ ;; When there are no result-files, then something must be wrong.
+ (unless result-files
+ (cond
+ ((not has-been-begin)
+ (message "Wrong type file"))
+ ((memq 'error process-state)
+ (message "An error occurred during decoding"))
+ ((not (or (memq 'ok process-state)
+ (memq 'end process-state)))
+ (message "End of articles reached before end of file")))
+ ;; Make unsuccessfully decoded articles unread.
+ (when gnus-uu-unmark-articles-not-decoded
+ (while article-series
+ (gnus-summary-tick-article (pop article-series) t))))
- (if result-files
- ()
- (if (not has-been-begin)
- (if (not no-errors) (message "Wrong type file"))
- (if (memq 'error process-state)
- (setq result-files nil)
- (if (not (or (memq 'ok process-state)
- (memq 'end process-state)))
- (progn
- (if (not no-errors)
- (message "End of articles reached before end of file"))
- (setq result-files nil))
- (gnus-uu-unmark-list-of-grabbed)))))
result-files))
+(defun gnus-uu-grab-view (file)
+ "View FILE using the gnus-uu methods."
+ (let ((action (gnus-uu-get-action file)))
+ (gnus-execute-command
+ (if (string-match "%" action)
+ (format action file)
+ (concat action " " file))
+ (eq gnus-view-pseudos 'not-confirm))))
+
+(defun gnus-uu-grab-move (file)
+ "Move FILE to somewhere."
+ (when gnus-uu-default-dir
+ (let ((to-file (concat (file-name-as-directory gnus-uu-default-dir)
+ (file-name-nondirectory file))))
+ (rename-file file to-file)
+ (unless (file-exists-p file)
+ (make-symbolic-link to-file file)))))
+
(defun gnus-uu-part-number (article)
(let ((subject (mail-header-subject (gnus-summary-article-header article))))
(if (string-match "[0-9]+ */[0-9]+\\|[0-9]+ * of *[0-9]+"
(defvar gnus-use-scoring t
"*If non-nil, enable scoring.")
+(defvar gnus-use-picon nil
+ "*If non-nil, display picons.")
+
(defvar gnus-fetch-old-headers nil
"*Non-nil means that Gnus will try to build threads by grabbing old headers.
If an unread article in the group refers to an older, already read (or
(defvar gnus-simplify-subject-fuzzy-regexp nil
"*Strings to be removed when doing fuzzy matches.
-This can either be a egular expression or list of regular expressions
+This can either be a regular expression or list of regular expressions
that will be removed from subject strings if fuzzy subject
simplification is selected.")
"Obsolete variable. See `gnus-buffer-configuration'.")
(defvar gnus-buffer-configuration
- '((group ([group 1.0 point]
- (if gnus-carpal [group-carpal 4])))
- (summary ([summary 1.0 point]
- (if gnus-carpal [summary-carpal 4])))
- (article ([summary 0.25 point]
- (if gnus-carpal [summary-carpal 4])
- [article 1.0]))
- (server ([server 1.0 point]
- (if gnus-carpal [server-carpal 2])))
- (browse ([browse 1.0 point]
- (if gnus-carpal [browse-carpal 2])))
- (group-mail ([mail 1.0 point]))
- (summary-mail ([mail 1.0 point]))
- (summary-reply ([article 0.5]
- [mail 1.0 point]))
- (info ([nil 1.0 point]))
- (summary-faq ([summary 0.25]
- [faq 1.0 point]))
- (edit-group ([group 0.5]
- [edit-group 1.0 point]))
- (edit-server ([server 0.5]
- [edit-server 1.0 point]))
- (edit-score ([summary 0.25]
- [edit-score 1.0 point]))
- (post ([post 1.0 point]))
- (reply ([article 0.5]
- [mail 1.0 point]))
- (mail-forward ([mail 1.0 point]))
- (post-forward ([post 1.0 point]))
- (reply-yank ([mail 1.0 point]))
- (mail-bounce ([article 0.5]
- [mail 1.0 point]))
- (draft ([draft 1.0 point]))
- (pipe ([summary 0.25 point]
- (if gnus-carpal [summary-carpal 4])
- ["*Shell Command Output*" 1.0]))
- (followup ([article 0.5]
- [post 1.0 point]))
- (followup-yank ([post 1.0 point])))
+ '((group
+ (vertical 1.0
+ (group 1.0 point)
+ (if gnus-carpal (group-carpal 4))))
+ (summary
+ (vertical 1.0
+ (summary 1.0 point)
+ (if gnus-carpal (summary-carpal 4))))
+ (article
+ (vertical 1.0
+ (if gnus-use-picon
+ '(horizontal 0.25
+ (summary 1.0 point)
+ (picon 10))
+ '(summary 0.25 point))
+ (if gnus-carpal (summary-carpal 4))
+ (article 1.0)))
+ (server
+ (vertical 1.0
+ (server 1.0 point)
+ (if gnus-carpal (server-carpal 2))))
+ (browse
+ (vertical 1.0
+ (browse 1.0 point)
+ (if gnus-carpal (browse-carpal 2))))
+ (group-mail
+ (vertical 1.0
+ (mail 1.0 point)))
+ (summary-mail
+ (vertical 1.0
+ (mail 1.0 point)))
+ (summary-reply
+ (vertical 1.0
+ (article 0.5)
+ (mail 1.0 point)))
+ (info
+ (vertical 1.0
+ (nil 1.0 point)))
+ (summary-faq
+ (vertical 1.0
+ (summary 0.25)
+ (faq 1.0 point)))
+ (edit-group
+ (vertical 1.0
+ (group 0.5)
+ (edit-group 1.0 point)))
+ (edit-server
+ (vertical 1.0
+ (server 0.5)
+ (edit-server 1.0 point)))
+ (edit-score
+ (vertical 1.0
+ (summary 0.25)
+ (edit-score 1.0 point)))
+ (post
+ (vertical 1.0
+ (post 1.0 point)))
+ (reply
+ (vertical 1.0
+ (article 0.5)
+ (mail 1.0 point)))
+ (mail-forward
+ (vertical 1.0
+ (mail 1.0 point)))
+ (post-forward
+ (vertical 1.0
+ (post 1.0 point)))
+ (reply-yank
+ (vertical 1.0
+ (mail 1.0 point)))
+ (mail-bounce
+ (vertical 1.0
+ (article 0.5)
+ (mail 1.0 point)))
+ (draft
+ (vertical 1.0
+ (draft 1.0 point)))
+ (pipe
+ (vertical 1.0
+ (summary 0.25 point)
+ (if gnus-carpal (summary-carpal 4))
+ ("*Shell Command Output*" 1.0)))
+ (followup
+ (vertical 1.0
+ (article 0.5)
+ (post 1.0 point)))
+ (followup-yank
+ (vertical 1.0
+ (post 1.0 point))))
"Window configuration for all possible Gnus buffers.
This variable is a list of lists. Each of these lists has a NAME and
a RULE. The NAMEs are commonsense names like `group', which names a
(mail . gnus-mail-buffer)
(post . gnus-post-news-buffer)
(faq . gnus-faq-buffer)
+ (picon . gnus-picon-buffer)
(draft . gnus-draft-buffer))
"Mapping from short symbols to buffer names or buffer variables.")
\f
;; Internal variables
+(defconst gnus-article-mark-lists
+ '((marked . tick) (replied . reply)
+ (expirable . expire) (killed . killed)
+ (bookmarks . bookmark) (dormant . dormant)
+ (scored . score) (saved . save)))
+
;; Avoid highlighting in kill files.
(defvar gnus-summary-inhibit-highlight nil)
(defvar gnus-newsgroup-selected-overlay nil)
"gnus-bug@ifi.uio.no (The Gnus Bugfixing Girls + Boys)"
"The mail address of the Gnus maintainers.")
-(defconst gnus-version "September Gnus v0.22"
+(defconst gnus-version "September Gnus v0.23"
"Version number for this version of Gnus.")
(defvar gnus-info-nodes
gnus-summary-reply gnus-summary-reply-with-original
gnus-summary-mail-forward gnus-summary-mail-other-window
gnus-bug)
+ ("gnus-picon" gnus-article-display-picon)
("gnus-vm" gnus-vm-mail-setup)
("gnus-vm" :interactive t gnus-summary-save-in-vm
gnus-summary-save-article-vm gnus-yank-article))))
(setq flist (cons (gnus-max-width-function el max-width)
flist))
(setq newspec ?s))
- (setq flist (cons (car elem) flist)))
- (setq newspec (car (cdr elem))))
+ (progn
+ (setq flist (cons (car elem) flist))
+ (setq newspec (car (cdr elem))))))
;; Remove the old specification (and possibly a ",12" string).
(delete-region beg (match-end 2))
;; Insert the new specification.
(cons conf (delq (assq (car conf) gnus-buffer-configuration)
gnus-buffer-configuration))))
+(defun gnus-configure-frame (split &optional window)
+ "Split WINDOW according to SPLIT."
+ (unless window
+ (setq window (get-buffer-window (current-buffer))))
+ (select-window window)
+ ;; This might be an old-stylee buffer config.
+ (when (vectorp split)
+ (setq split (append split nil)))
+ (when (or (consp (car split))
+ (vectorp (car split)))
+ (push 1.0 split)
+ (push 'vertical split))
+ ;; The SPLIT might be something that is to be evaled to
+ ;; return a new SPLIT.
+ (while (and (not (assq (car split) gnus-window-to-buffer))
+ (gnus-functionp (car split)))
+ (setq split (eval split)))
+ (let* ((type (car split))
+ (subs (cdr (cdr split)))
+ (len (if (eq type 'horizontal) (window-width) (window-height) ))
+ (total 0)
+ s result new-win rest comp-subs size sub)
+ (cond
+ ;; Nothing to do here.
+ ((null split))
+ ;; This is a buffer to be selected.
+ ((not (or (eq type 'horizontal) (eq type 'vertical)))
+ (let ((buffer (cdr (assq type gnus-window-to-buffer))))
+ (unless buffer
+ (error "Illegal buffer type: %s" type))
+ (switch-to-buffer (get-buffer (if (symbolp buffer)
+ (symbol-value buffer)
+ buffer)))
+ ;; We return the window if it has the `point' spec.
+ (and (memq 'point split) window)))
+ ;; This is a normal split
+ (t
+ (when (> (length subs) 0)
+ ;; First we have to compute the sizes of all new windows.
+ (while subs
+ (setq sub (append (pop subs) nil))
+ (while (and (not (assq (car sub) gnus-window-to-buffer))
+ (gnus-functionp (car sub)))
+ (setq sub (eval sub)))
+ (when sub
+ (push sub comp-subs)
+ (setq size (cadar comp-subs))
+ (cond ((equal size 1.0)
+ (setq rest (car comp-subs))
+ (setq s 0))
+ ((floatp size)
+ (setq s (floor (* size len))))
+ ((integerp size)
+ (setq s size))
+ (t
+ (error "Illegal size: %s" size)))
+ ;; Try to make sure that we are inside the safe limits.
+ (cond ((zerop s))
+ ((and (eq type 'horizontal)
+ (< s 10))
+ (setq s 10))
+ ((and (eq type 'vertical)
+ (< s 4))
+ (setq s 4)))
+ (setcar (cdar comp-subs) s)
+ (incf total s)))
+ ;; Take care of the "1.0" spec.
+ (if rest
+ (setcar (cdr rest) (- len total))
+ (error "No 1.0 specs in %s" split))
+ ;; The we do the actual splitting in a nice recursive
+ ;; fashion.
+ (setq comp-subs (nreverse comp-subs))
+ (while comp-subs
+ (if (null (cdr comp-subs))
+ (setq new-win window)
+ (setq new-win
+ (split-window window (cadar comp-subs)
+ (eq type 'horizontal))))
+ (setq result (or (gnus-configure-frame
+ (car comp-subs) window) result))
+ (select-window new-win)
+ (setq window new-win)
+ (setq comp-subs (cdr comp-subs))))
+ ;; Return the proper window, if any.
+ (when result
+ (select-window result))))))
+
(defun gnus-configure-windows (setting &optional force)
(setq setting (gnus-windows-old-to-new setting))
- (let ((r (if (symbolp setting)
- (cdr (assq setting gnus-buffer-configuration))
- setting))
+ (let ((split (if (symbolp setting)
+ (car (cdr (assq setting gnus-buffer-configuration)))
+ setting))
(in-buf (current-buffer))
rule val w height hor ohor heights sub jump-buffer
rel total to-buf all-visible)
- (or r (error "No such setting: %s" setting))
- (if (and (not force) (setq all-visible (gnus-all-windows-visible-p r)))
+ (unless split
+ (error "No such setting: %s" setting))
+
+ (if (and (not force) (setq all-visible (gnus-all-windows-visible-p split)))
;; All the windows mentioned are already visible, so we just
;; put point in the assigned buffer, and do not touch the
;; winconf.
- (select-window (get-buffer-window all-visible t))
-
+ (select-window all-visible)
;; Either remove all windows or just remove all Gnus windows.
(if gnus-use-full-window
(gnus-remove-some-windows)
(switch-to-buffer nntp-server-buffer))
- (while r
- (setq hor (car r)
- ohor nil)
-
- ;; We have to do the (possible) horizontal splitting before the
- ;; vertical.
- (if (and (listp (car hor))
- (eq (car (car hor)) 'horizontal))
- (progn
- (split-window
- nil
- (if (integerp (nth 1 (car hor)))
- (nth 1 (car hor))
- (- (frame-width) (floor (* (frame-width) (nth 1 (car hor))))))
- t)
- (setq hor (cdr hor))))
-
- ;; Go through the rules and eval the elements that are to be
- ;; evaled.
- (while hor
- (if (setq val (if (vectorp (car hor)) (car hor) (eval (car hor))))
- (progn
- ;; Expand short buffer name.
- (setq w (aref val 0))
- (and (setq w (cdr (assq w gnus-window-to-buffer)))
- (progn
- (setq val (apply 'vector (mapcar 'identity val)))
- (aset val 0 w)))
- (setq ohor (cons val ohor))))
- (setq hor (cdr hor)))
- (setq rule (cons (nreverse ohor) rule))
- (setq r (cdr r)))
- (setq rule (nreverse rule))
-
- ;; We tally the window sizes.
- (setq total (window-height))
- (while rule
- (setq hor (car rule))
- (if (and (listp (car hor)) (eq (car (car hor)) 'horizontal))
- (setq hor (cdr hor)))
- (setq sub 0)
- (while hor
- (setq rel (aref (car hor) 1)
- heights (cons
- (cond ((and (floatp rel) (= 1.0 rel))
- 'x)
- ((integerp rel)
- rel)
- (t
- (max (floor (* total rel)) 4)))
- heights)
- sub (+ sub (if (numberp (car heights)) (car heights) 0))
- hor (cdr hor)))
- (setq heights (nreverse heights)
- hor (car rule))
-
- ;; We then go through these heighs and create windows for them.
- (while heights
- (setq height (car heights)
- heights (cdr heights))
- (and (eq height 'x)
- (setq height (- total sub)))
- (and heights
- (split-window nil height))
- (setq to-buf (aref (car hor) 0))
- (switch-to-buffer
- (cond ((not to-buf)
- in-buf)
- ((symbolp to-buf)
- (symbol-value (aref (car hor) 0)))
- (t
- (aref (car hor) 0))))
- (and (> (length (car hor)) 2)
- (eq (aref (car hor) 2) 'point)
- (setq jump-buffer (current-buffer)))
- (other-window 1)
- (setq hor (cdr hor)))
-
- (setq rule (cdr rule)))
-
- ;; Finally, we pop to the buffer that's supposed to have point.
- (or jump-buffer (error "Missing `point' in spec for %s" setting))
-
- (select-window (get-buffer-window jump-buffer t))
- (set-buffer jump-buffer))))
-
-(defun gnus-all-windows-visible-p (rule)
- (let (invisible hor jump-buffer val buffer)
- ;; Go through the rules and eval the elements that are to be
- ;; evaled.
- (while (and rule (not invisible))
- (setq hor (car rule)
- rule (cdr rule))
- (while (and hor (not invisible))
- (if (setq val (if (vectorp (car hor))
- (car hor)
- (if (not (eq (car (car hor)) 'horizontal))
- (eval (car hor)))))
- (progn
- ;; Expand short buffer name.
- (setq buffer (or (cdr (assq (aref val 0) gnus-window-to-buffer))
- (aref val 0)))
- (setq buffer (if (symbolp buffer) (symbol-value buffer)
- buffer))
- (and (> (length val) 2) (eq 'point (aref val 2))
- (setq jump-buffer buffer))
- (setq invisible (not (and buffer (get-buffer-window buffer))))))
- (setq hor (cdr hor))))
- (and (not invisible) jump-buffer)))
+ (switch-to-buffer nntp-server-buffer)
+ (gnus-configure-frame split (get-buffer-window (current-buffer))))))
+
+(defun gnus-all-windows-visible-p (split)
+ (when (vectorp split)
+ (setq split (append split nil)))
+ (when (or (consp (car split))
+ (vectorp (car split)))
+ (push 1.0 split)
+ (push 'vertical split))
+ ;; The SPLIT might be something that is to be evaled to
+ ;; return a new SPLIT.
+ (while (and (not (assq (car split) gnus-window-to-buffer))
+ (gnus-functionp (car split)))
+ (setq split (eval split)))
+ (let* ((type (elt split 0)))
+ (cond
+ ((null split)
+ t)
+ ((not (or (eq type 'horizontal) (eq type 'vertical)))
+ (let ((buffer (cdr (assq type gnus-window-to-buffer)))
+ win)
+ (unless buffer
+ (error "Illegal buffer type: %s" type))
+ (setq win
+ (get-buffer-window (get-buffer (if (symbolp buffer)
+ (symbol-value buffer)
+ buffer))))
+ (when win
+ (if (memq 'point split)
+ win
+ t))))
+ (t
+ (let ((n (mapcar 'gnus-all-windows-visible-p
+ (cdr (cdr split))))
+ (win t))
+ (while n
+ (cond ((windowp (car n))
+ (setq win (car n)))
+ ((null (car n))
+ (setq win nil)))
+ (setq n (cdr n)))
+ win)))))
(defun gnus-window-top-edge (&optional window)
(nth 1 (window-edges window)))
(and gnus-auto-expirable-newsgroups ; Check var.
(string-match gnus-auto-expirable-newsgroups group)))))
-(defun gnus-subject-equal (s1 s2)
- "Check whether two subjects are equal."
+(defsubst gnus-simplify-subject-fully (subject)
+ "Simplify a subject string according to the user's wishes."
(cond
((null gnus-summary-gather-subject-limit)
- (equal (gnus-simplify-subject-re s1)
- (gnus-simplify-subject-re s2)))
+ (gnus-simplify-subject-re subject))
((eq gnus-summary-gather-subject-limit 'fuzzy)
- (equal (gnus-simplify-subject-fuzzy s1)
- (gnus-simplify-subject-fuzzy s2)))
+ (gnus-simplify-subject-fuzzy subject))
((numberp gnus-summary-gather-subject-limit)
- (equal (gnus-limit-string s1 gnus-summary-gather-subject-limit)
- (gnus-limit-string s2 gnus-summary-gather-subject-limit)))
+ (gnus-limit-string subject gnus-summary-gather-subject-limit))
(t
- (equal s1 s2))))
+ subject)))
+
+(defsubst gnus-subject-equal (s1 s2 &optional simple-first)
+ "Check whether two subjects are equal. If optional argument
+simple-first is t, first argument is already simplified."
+ (cond
+ ((null simple-first)
+ (equal (gnus-simplify-subject-fully s1)
+ (gnus-simplify-subject-fully s2)))
+ (t
+ (equal s1
+ (gnus-simplify-subject-fully s2)))))
;; Returns a list of writable groups.
(defun gnus-writable-groups ()
If READ-ALL is non-nil, all articles in the group are selected."
(let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
(info (nth 2 entry))
- articles)
+ articles fetched-articles)
(or (gnus-check-server
(setq gnus-current-select-method (gnus-find-method-for-group group)))
(setq gnus-newsgroup-unreads
(gnus-set-sorted-intersection
gnus-newsgroup-unreads
- (mapcar (lambda (headers) (mail-header-number headers))
- gnus-newsgroup-headers)))
+ (setq fetched-articles
+ (mapcar (lambda (headers) (mail-header-number headers))
+ gnus-newsgroup-headers))))
+ ;; Removed marked articles that do not exist.
+ (gnus-update-missing-marks
+ (gnus-sorted-complement fetched-articles articles))
;; We might want to build some more threads first.
(and gnus-fetch-old-headers
(eq gnus-headers-retrieved-by 'nov)
(active (gnus-active (gnus-info-group info)))
(min (car active))
(max (cdr active))
- (types '((marked . tick) (replied . reply)
- (expirable . expire) (killed . killed)
- (bookmarks . bookmark) (dormant . dormant)
- (scored . score) (saved . save)))
+ (types gnus-article-mark-lists)
(uncompressed '(score bookmark))
marks var articles article mark)
(> (car article) max))
(set var (delq article (symbol-value var))))))))))
+(defun gnus-update-missing-marks (missing)
+ "Go through the list of MISSING articles and remove them mark lists."
+ (when missing
+ (let ((types gnus-article-mark-lists)
+ var m)
+ ;; Go through all types.
+ (while types
+ (setq var (intern (format "gnus-newsgroup-%s" (car (pop types)))))
+ (when (symbol-value var)
+ ;; This list has articles. So we delete all missing articles
+ ;; from it.
+ (setq m missing)
+ (while m
+ (set var (delq (pop m) (symbol-value var)))))))))
+
(defun gnus-update-marks ()
"Enter the various lists of marked articles into the newsgroup info list."
- (let ((types '((marked . tick) (replied . reply)
- (expirable . expire) (killed . killed)
- (bookmarks . bookmark) (dormant . dormant)
- (scored . score) (saved . save)))
+ (let ((types gnus-article-mark-lists)
(info (gnus-get-info gnus-newsgroup-name))
(uncompressed '(score bookmark killed))
var type list newmarked symbol)
(gnus-data-number result)))))
(defun gnus-summary-find-subject (subject &optional unread backward article)
- (let* ((article (or article (gnus-summary-article-number)))
+ (let* ((simp-subject (gnus-simplify-subject-fully subject))
+ (article (or article (gnus-summary-article-number)))
(articles (gnus-data-list backward))
(arts (gnus-data-find-list article articles))
result)
(gnus-data-unread-p (car arts)))
(vectorp (gnus-data-header (car arts)))
(gnus-subject-equal
- subject (mail-header-subject (gnus-data-header (car arts))))
+ simp-subject (mail-header-subject (gnus-data-header (car arts))) t)
(setq result (car arts)
arts nil))
(setq arts (cdr arts)))
(setq gnus-current-select-method gnus-select-method)
(pop-to-buffer gnus-group-buffer)
;; Clear the current group name.
- (setq gnus-newsgroup-name nil)
(if (not quit-config)
(progn
(gnus-group-jump-to-group group)
(and (eq major-mode 'gnus-summary-mode)
(gnus-set-global-variables))
(gnus-configure-windows (cdr quit-config))))
- (run-hooks 'gnus-summary-exit-hook))))
+ (run-hooks 'gnus-summary-exit-hook)
+ (unless quit-config
+ (setq gnus-newsgroup-name nil)))))
(defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
(defun gnus-summary-exit-no-update (&optional no-questions)
(if (not (boundp 'jka-compr-compression-info-list))
(string-to-int file)
(string-match nnheader-numerical-short-files file)
- (match-string 1 file)))
+ (string-to-int (match-string 0 file))))
(defun nnheader-directory-articles (dir)
(mapcar 'nnheader-file-to-number
(require 'nnheader)
(require 'nnmail)
+(eval-when-compile (require 'cl))
+
+(require 'cl)
(defvar nnml-directory "~/Mail/"
"Mail spool directory.")
(let ((group (nnmail-replace-chars-in-string
(substring dir (length nnml-directory))
?/ ?.)))
- (setq nnml-group-alist (delq (assoc group nnml-group-alist)))
+ (setq nnml-group-alist (delq (assoc group nnml-group-alist) nnml-group-alist))
(push (list group
(cons (car files)
(let ((f files))
nnml-group-alist)))
(defun nnml-generate-nov-file (dir files)
- (let ((nov (concat dir "/" nnml-nov-file-name))
- (nov-buffer (get-buffer-create " *nov*"))
- nov-line chars)
+ (let* ((dir (file-name-as-directory dir))
+ (nov (concat dir nnml-nov-file-name))
+ (nov-buffer (get-buffer-create " *nov*"))
+ nov-line chars)
(save-excursion
;; Init the nov buffer.
(set-buffer nov-buffer)
(funcall nnmail-delete-file-function nov))
(while files
(erase-buffer)
- (insert-file-contents (concat dir "/" (int-to-string (car files))))
+ (insert-file-contents (concat dir (int-to-string (car files))))
(narrow-to-region
(goto-char (point-min))
(progn
(search-forward "\n\n" nil t)
(setq chars (- (point-max) (point)))
- (1- (point))))
+ (max 1 (1- (point)))))
(when (and (not (= 0 chars)) ; none of them empty files...
(not (= (point-min) (point-max))))
(goto-char (point-min))
+Fri Dec 15 13:53:02 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Windows Configuration): Addition, change.
+
+Thu Dec 14 17:57:19 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Group Parameters): Addition.
+ (Other Decode Variables): Addition.
+
Wed Dec 13 16:13:56 1995 Lars Ingebrigtsen <lars@eyesore.no>
* gnus.texi (Selecting a Group): Addition.
The value can either be a number of days (not necessarily an integer) or
the symbols @code{never} or @code{immediate}.
+@item score-file
+Elements that look like @samp{(score-file . "file")} will make
+@samp{file} into the current score file for the group in question. This
+means that all score commands you issue will end up in that file.
+
@item @var{(variable form)}
You can use the group parameters to set variables local to the group you
are entering. Say you want to turn threading off in
archives.
@end table
+
@node Other Decode Variables
@subsubsection Other Decode Variables
@table @code
+@vindex gnus-uu-grabbed-file-functions
+@item gnus-uu-grabbed-file-functions
+All functions in this list will be called right each file has been
+successfully decoded---so that you can move or view files right away,
+and don't have to wait for all files to be decoded before you can do
+anything. Ready-made functions you can put in this list are:
+
+@table @code
+@item gnus-uu-grab-view
+@findex gnus-uu-grab-view
+View the file.
+
+@item gnus-uu-grab-move
+@findex gnus-uu-grab-move
+Move the file (if you're using a saving function.)
+@end table
+
@item gnus-uu-ignore-files-by-name
@vindex gnus-uu-ignore-files-by-name
Files with name matching this regular expression won't be viewed.
No, there's nothing here about X, so be quiet.
-@table @code
-@item gnus-use-full-window
@vindex gnus-use-full-window
-If non-@code{nil}, Gnus will delete all other windows and occupy the
-entire Emacs screen by itself. It is @code{t} by default.
+If @code{gnus-use-full-window} non-@code{nil}, Gnus will delete all
+other windows and occupy the entire Emacs screen by itself. It is
+@code{t} by default.
-@item gnus-buffer-configuration
-@vindex gnus-buffer-configuration
-This variable describes how much space each Gnus buffer should be given.
-Here's an excerpt of this variable:
+@code{gnus-buffer-configuration} describes how much space each Gnus
+buffer should be given. Here's an excerpt of this variable:
@lisp
-((group ([group 1.0 point]
- (if gnus-carpal [group-carpal 4])))
- (article ([summary 0.25 point]
- [article 1.0])))
+((group (vertical 1.0 (group 1.0 point)
+ (if gnus-carpal (group-carpal 4))))
+ (article (vertical 1.0 (summary 0.25 point)
+ (article 1.0))))
@end lisp
This is an alist. The @dfn{key} is a symbol that names some action or
configuration function will use @code{group} as the key. A full list of
possible names is listed below.
-The @dfn{value} is a @dfn{rule} that says how much space each buffer
-should occupy. To take the @code{article} rule as an example -
+The @dfn{value} (i. e., the @dfn{split}) says how much space each buffer
+should occupy. To take the @code{article} split as an example -
@lisp
-(article ([summary 0.25 point]
- [article 1.0]))
+(article (vertical 1.0 (summary 0.25 point)
+ (article 1.0)))
@end lisp
-This rule says that the summary buffer should occupy 25% of the screen,
-and that it is placed over the article buffer. As you may have noticed,
-100% + 25% is actually 125% (yup, I saw y'all reaching for that
-calculator there). However, the special number @code{1.0} is used to
-signal that this buffer should soak up all the rest of the space
-avaiable after the rest of the buffers have taken whatever they need.
-There should be only one buffer with the @code{1.0} size spec.
+This @dfn{split} says that the summary buffer should occupy 25% of upper
+half of the screen, and that it is placed over the article buffer. As
+you may have noticed, 100% + 25% is actually 125% (yup, I saw y'all
+reaching for that calculator there). However, the special number
+@code{1.0} is used to signal that this buffer should soak up all the
+rest of the space avaiable after the rest of the buffers have taken
+whatever they need. There should be only one buffer with the @code{1.0}
+size spec per split.
Point will be put in the buffer that has the optional third element
@code{point}.
Here's a more complicated example:
@lisp
-(article ([group 4]
- [summary 0.25 point]
- (if gnus-carpal [summary-carpal 4])
- [article 1.0])
+(article (vertical 1.0 (group 4)
+ (summary 0.25 point)
+ (if gnus-carpal (summary-carpal 4))
+ (article 1.0)))
@end lisp
If the size spec is an integer instead of a floating point number,
then that number will be used to say how many lines a buffer should
occupy, not a percentage.
-If an element is a list instead of a vector, this list will be
-@code{eval}ed. If the result is non-@code{nil}, it will be used. This
-means that there will be three buffers if @code{gnus-carpal} is
-@code{nil}, and four buffers if @code{gnus-carpal} is non-@code{nil}.
+If the @dfn{split} looks like something that can be @code{eval}ed (to be
+precise---if the @code{car} of the split is a function or a subr), this
+split will be @code{eval}ed. If the result is non-@code{nil}, it will
+be used as a split. This means that there will be three buffers if
+@code{gnus-carpal} is @code{nil}, and four buffers if @code{gnus-carpal}
+is non-@code{nil}.
Not complicated enough for you? Well, try this on for size:
@lisp
-(article ([group 1.0]
- [gnus-carpal 4])
- ((horizontal 0.5)
- [summary 0.25 point]
- [summary-carpal 4]
- [article 1.0]))
+(article (horizontal 1.0
+ (vertical 0.5
+ (group 1.0)
+ (gnus-carpal 4))
+ (vertical 1.0
+ (summary 0.25 point)
+ (summary-carpal 4)
+ (article 1.0))))
@end lisp
Whoops. Two buffers with the mystery 100% tag. And what's that
@code{horizontal} thingie?
-If the first element in one of the rule lists is a list with
-@code{horizontal} as the first element, Gnus will split the window
-horizontally, giving you two windows side-by-side. Inside each of these
-strips you may carry on all you like in the normal fashion. The number
-following @code{horizontal} says what percentage of the screen is to be
-given to this strip.
+If the first element in one of the split is @code{horizontal}, Gnus will
+split the window horizontally, giving you two windows side-by-side.
+Inside each of these strips you may carry on all you like in the normal
+fashion. The number following @code{horizontal} says what percentage of
+the screen is to be given to this strip.
-For each horizontal split, there @emph{must} be one element that has the
-100% tag. The splitting is never accurate, and this buffer will eat any
-leftover lines from the splits.
+For each split, there @emph{must} be one element that has the 100% tag.
+The splitting is never accurate, and this buffer will eat any leftover
+lines from the splits.
-Here's a list of all possible keys:
+To be slightly more formal, here's a definition of what a legal split
+may look like:
+
+@example
+split = frame | horizontal | vertical | buffer | form
+frame = "(frame " frame-size *split ")"
+horizontal = "(horizontal " size *split ")"
+vertical = "(vertical " size *split ")"
+buffer = "(" buffer-name " " size *[ "point" ] ")"
+frame-size = "(" number " . " number ")"
+size = number
+buffer-name = group | article | summary ...
+@end example
+
+The limitations are that the @samp{frame} split can only appear as the
+top-level split. @samp{form} should be an Emacs Lisp form that should
+return a valid split. We see that each split is fully recursive, and
+may contain any number of @samp{vertical} and @samp{horizontal} splits.
+
+Finding the right sizes can be a bit complicated. No window may be less
+than 4 characters high, and all windows must be at least 10 characters
+wide. Gnus will try to enforce this before applying the splits.
+
+If you're not familiar with Emacs terminology, @samp{horizontal} and
+@samp{vertical} splits may work the opposite way of what you'd expect.
+Windows inside a @samp{horizontal} split are shown side-by-side, and
+windows within a @samp{vertical} split are shown above each other.
+
+@findex gnus-configure-frame
+If you want to experiment with window placement, a good tip is to call
+@code{gnus-configure-frame} directly with a split. This is the function
+that does all the real work when splitting buffers. Below is a pretty
+nonsensical configuration with 5 windows; two for the group buffer and
+three for the article buffer. (I said it was nonsensical.) If you
+@code{eval} the statement below, you can get an idea of how that would
+look straight away, without going through the normal Gnus channels.
+Play with it until you're satisfied, and then use
+@code{gnus-add-configuration} to add your new creation to the buffer
+configuration list.
+
+@example
+(gnus-configure-frame
+ '(horizontal 1.0
+ (vertical 10
+ (group 1.0)
+ (article 0.3 point))
+ (vertical 1.0
+ (article 1.0)
+ (horizontal 4
+ (group 1.0)
+ (article 10)))))
+@end example
+
+Here's a list of all possible keys for
+@code{gnus-buffer-configuaration}:
@code{group}, @code{summary}, @code{article}, @code{server},
@code{browse}, @code{group-mail}, @code{summary-mail},
@code{followup}, @code{followup-yank}, @code{edit-score}.
@findex gnus-add-configuration
-Since this variable is so long and complicated, there's a function you
-can use to ease changing the config of a single setting:
-@code{gnus-add-configuration}. If, for instance, you want to change the
-@code{article} setting, you could say:
+Since the @code{gnus-buffer-configuration} variable is so long and
+complicated, there's a function you can use to ease changing the config
+of a single setting: @code{gnus-add-configuration}. If, for instance,
+you want to change the @code{article} setting, you could say:
@lisp
(gnus-add-configuration
- '(article ([group 4]
- [summary .25 point]
- [article 1.0])))
+ '(article (vertical 1.0
+ (group 4)
+ (summary .25 point)
+ (article 1.0))))
@end lisp
-@end table
+
@node Buttons
@section Buttons