| 1 | ;;; gnus-sum.el --- summary mode commands for Gnus |
| 2 | |
| 3 | ;; Copyright (C) 1996-2013 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> |
| 6 | ;; Keywords: news |
| 7 | |
| 8 | ;; This file is part of GNU Emacs. |
| 9 | |
| 10 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
| 11 | ;; it under the terms of the GNU General Public License as published by |
| 12 | ;; the Free Software Foundation, either version 3 of the License, or |
| 13 | ;; (at your option) any later version. |
| 14 | |
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | ;; GNU General Public License for more details. |
| 19 | |
| 20 | ;; You should have received a copy of the GNU General Public License |
| 21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
| 22 | |
| 23 | ;;; Commentary: |
| 24 | |
| 25 | ;;; Code: |
| 26 | |
| 27 | ;; For Emacs <22.2 and XEmacs. |
| 28 | (eval-and-compile |
| 29 | (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))) |
| 30 | (eval-when-compile |
| 31 | (require 'cl)) |
| 32 | (eval-when-compile |
| 33 | (when (featurep 'xemacs) |
| 34 | (require 'easy-mmode))) ; for `define-minor-mode' |
| 35 | |
| 36 | (defvar tool-bar-mode) |
| 37 | (defvar gnus-tmp-header) |
| 38 | |
| 39 | (require 'gnus) |
| 40 | (require 'gnus-group) |
| 41 | (require 'gnus-spec) |
| 42 | (require 'gnus-range) |
| 43 | (require 'gnus-int) |
| 44 | (require 'gnus-undo) |
| 45 | (require 'gnus-util) |
| 46 | (require 'gmm-utils) |
| 47 | (require 'mm-decode) |
| 48 | (require 'nnoo) |
| 49 | |
| 50 | (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t) |
| 51 | (autoload 'gnus-cache-write-active "gnus-cache") |
| 52 | (autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t) |
| 53 | (autoload 'turn-on-gnus-mailing-list-mode "gnus-ml" nil t) |
| 54 | (autoload 'gnus-pick-line-number "gnus-salt" nil t) |
| 55 | (autoload 'mm-uu-dissect "mm-uu") |
| 56 | (autoload 'gnus-article-outlook-deuglify-article "deuglify" |
| 57 | "Deuglify broken Outlook (Express) articles and redisplay." |
| 58 | t) |
| 59 | (autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t) |
| 60 | (autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t) |
| 61 | (autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t) |
| 62 | (autoload 'nnir-article-rsv "nnir" nil nil 'macro) |
| 63 | (autoload 'nnir-article-group "nnir" nil nil 'macro) |
| 64 | |
| 65 | (defcustom gnus-kill-summary-on-exit t |
| 66 | "*If non-nil, kill the summary buffer when you exit from it. |
| 67 | If nil, the summary will become a \"*Dead Summary*\" buffer, and |
| 68 | it will be killed sometime later." |
| 69 | :group 'gnus-summary-exit |
| 70 | :type 'boolean) |
| 71 | |
| 72 | (defcustom gnus-summary-next-group-on-exit t |
| 73 | "If non-nil, go to the next unread newsgroup on summary exit. |
| 74 | See `gnus-group-goto-unread'." |
| 75 | :link '(custom-manual "(gnus)Group Maneuvering") |
| 76 | :group 'gnus-summary-exit |
| 77 | :version "23.1" ;; No Gnus |
| 78 | :type 'boolean) |
| 79 | |
| 80 | (defcustom gnus-summary-stop-at-end-of-message nil |
| 81 | "If non-nil, don't select the next message when using `SPC'." |
| 82 | :link '(custom-manual "(gnus)Group Maneuvering") |
| 83 | :group 'gnus-summary-maneuvering |
| 84 | :version "24.1" |
| 85 | :type 'boolean) |
| 86 | |
| 87 | (defcustom gnus-fetch-old-headers nil |
| 88 | "*Non-nil means that Gnus will try to build threads by grabbing old headers. |
| 89 | If an unread article in the group refers to an older, already |
| 90 | read (or just marked as read) article, the old article will not |
| 91 | normally be displayed in the Summary buffer. If this variable is |
| 92 | t, Gnus will attempt to grab the headers to the old articles, and |
| 93 | thereby build complete threads. If it has the value `some', all |
| 94 | old headers will be fetched but only enough headers to connect |
| 95 | otherwise loose threads will be displayed. This variable can |
| 96 | also be a number. In that case, no more than that number of old |
| 97 | headers will be fetched. If it has the value `invisible', all |
| 98 | old headers will be fetched, but none will be displayed. |
| 99 | |
| 100 | The server has to support NOV for any of this to work. |
| 101 | |
| 102 | This feature can seriously impact performance it ignores all |
| 103 | locally cached header entries. Setting it to t for groups for a |
| 104 | server that doesn't expire articles (such as news.gmane.org), |
| 105 | leads to very slow summary generation." |
| 106 | :group 'gnus-thread |
| 107 | :type '(choice (const :tag "off" nil) |
| 108 | (const :tag "on" t) |
| 109 | (const some) |
| 110 | (const invisible) |
| 111 | number |
| 112 | (sexp :menu-tag "other" t))) |
| 113 | |
| 114 | (defcustom gnus-refer-thread-limit 500 |
| 115 | "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread]. |
| 116 | If t, fetch all the available old headers." |
| 117 | :group 'gnus-thread |
| 118 | :type '(choice number |
| 119 | (sexp :menu-tag "other" t))) |
| 120 | |
| 121 | (defcustom gnus-refer-thread-use-nnir nil |
| 122 | "*Use nnir to search an entire server when referring threads. A |
| 123 | nil value will only search for thread-related articles in the |
| 124 | current group." |
| 125 | :version "24.1" |
| 126 | :group 'gnus-thread |
| 127 | :type 'boolean) |
| 128 | |
| 129 | (defcustom gnus-summary-make-false-root 'adopt |
| 130 | "*nil means that Gnus won't gather loose threads. |
| 131 | If the root of a thread has expired or been read in a previous |
| 132 | session, the information necessary to build a complete thread has been |
| 133 | lost. Instead of having many small sub-threads from this original thread |
| 134 | scattered all over the summary buffer, Gnus can gather them. |
| 135 | |
| 136 | If non-nil, Gnus will try to gather all loose sub-threads from an |
| 137 | original thread into one large thread. |
| 138 | |
| 139 | If this variable is non-nil, it should be one of `none', `adopt', |
| 140 | `dummy' or `empty'. |
| 141 | |
| 142 | If this variable is `none', Gnus will not make a false root, but just |
| 143 | present the sub-threads after another. |
| 144 | If this variable is `dummy', Gnus will create a dummy root that will |
| 145 | have all the sub-threads as children. |
| 146 | If this variable is `adopt', Gnus will make one of the \"children\" |
| 147 | the parent and mark all the step-children as such. |
| 148 | If this variable is `empty', the \"children\" are printed with empty |
| 149 | subject fields. (Or rather, they will be printed with a string |
| 150 | given by the `gnus-summary-same-subject' variable.)" |
| 151 | :group 'gnus-thread |
| 152 | :type '(choice (const :tag "off" nil) |
| 153 | (const none) |
| 154 | (const dummy) |
| 155 | (const adopt) |
| 156 | (const empty))) |
| 157 | |
| 158 | (defcustom gnus-summary-make-false-root-always nil |
| 159 | "Always make a false dummy root." |
| 160 | :version "22.1" |
| 161 | :group 'gnus-thread |
| 162 | :type 'boolean) |
| 163 | |
| 164 | (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$" |
| 165 | "*A regexp to match subjects to be excluded from loose thread gathering. |
| 166 | As loose thread gathering is done on subjects only, that means that |
| 167 | there can be many false gatherings performed. By rooting out certain |
| 168 | common subjects, gathering might become saner." |
| 169 | :group 'gnus-thread |
| 170 | :type 'regexp) |
| 171 | |
| 172 | (defcustom gnus-summary-gather-subject-limit nil |
| 173 | "*Maximum length of subject comparisons when gathering loose threads. |
| 174 | Use nil to compare full subjects. Setting this variable to a low |
| 175 | number will help gather threads that have been corrupted by |
| 176 | newsreaders chopping off subject lines, but it might also mean that |
| 177 | unrelated articles that have subject that happen to begin with the |
| 178 | same few characters will be incorrectly gathered. |
| 179 | |
| 180 | If this variable is `fuzzy', Gnus will use a fuzzy algorithm when |
| 181 | comparing subjects." |
| 182 | :group 'gnus-thread |
| 183 | :type '(choice (const :tag "off" nil) |
| 184 | (const fuzzy) |
| 185 | (sexp :menu-tag "on" t))) |
| 186 | |
| 187 | (defcustom gnus-simplify-subject-functions nil |
| 188 | "List of functions taking a string argument that simplify subjects. |
| 189 | The functions are applied recursively. |
| 190 | |
| 191 | Useful functions to put in this list include: |
| 192 | `gnus-simplify-subject-re', `gnus-simplify-subject-fuzzy', |
| 193 | `gnus-simplify-whitespace', and `gnus-simplify-all-whitespace'." |
| 194 | :group 'gnus-thread |
| 195 | :type '(repeat function)) |
| 196 | |
| 197 | (defcustom gnus-simplify-ignored-prefixes nil |
| 198 | "*Remove matches for this regexp from subject lines when simplifying fuzzily." |
| 199 | :group 'gnus-thread |
| 200 | :type '(choice (const :tag "off" nil) |
| 201 | regexp)) |
| 202 | |
| 203 | (defcustom gnus-build-sparse-threads nil |
| 204 | "*If non-nil, fill in the gaps in threads. |
| 205 | If `some', only fill in the gaps that are needed to tie loose threads |
| 206 | together. If `more', fill in all leaf nodes that Gnus can find. If |
| 207 | non-nil and non-`some', fill in all gaps that Gnus manages to guess." |
| 208 | :group 'gnus-thread |
| 209 | :type '(choice (const :tag "off" nil) |
| 210 | (const some) |
| 211 | (const more) |
| 212 | (sexp :menu-tag "all" t))) |
| 213 | |
| 214 | (defcustom gnus-summary-thread-gathering-function |
| 215 | 'gnus-gather-threads-by-subject |
| 216 | "*Function used for gathering loose threads. |
| 217 | There are two pre-defined functions: `gnus-gather-threads-by-subject', |
| 218 | which only takes Subjects into consideration; and |
| 219 | `gnus-gather-threads-by-references', which compared the References |
| 220 | headers of the articles to find matches." |
| 221 | :group 'gnus-thread |
| 222 | :type '(radio (function-item gnus-gather-threads-by-subject) |
| 223 | (function-item gnus-gather-threads-by-references) |
| 224 | (function :tag "other"))) |
| 225 | |
| 226 | (defcustom gnus-summary-same-subject "" |
| 227 | "*String indicating that the current article has the same subject as the previous. |
| 228 | This variable will only be used if the value of |
| 229 | `gnus-summary-make-false-root' is `empty'." |
| 230 | :group 'gnus-summary-format |
| 231 | :type 'string) |
| 232 | |
| 233 | (defcustom gnus-summary-goto-unread nil |
| 234 | "*If t, many commands will go to the next unread article. |
| 235 | This applies to marking commands as well as other commands that |
| 236 | \"naturally\" select the next article, like, for instance, `SPC' at |
| 237 | the end of an article. |
| 238 | |
| 239 | If nil, the marking commands do NOT go to the next unread article |
| 240 | \(they go to the next article instead). If `never', commands that |
| 241 | usually go to the next unread article, will go to the next article, |
| 242 | whether it is read or not." |
| 243 | :version "24.1" |
| 244 | :group 'gnus-summary-marks |
| 245 | :link '(custom-manual "(gnus)Setting Marks") |
| 246 | :type '(choice (const :tag "off" nil) |
| 247 | (const never) |
| 248 | (sexp :menu-tag "on" t))) |
| 249 | |
| 250 | (defcustom gnus-summary-default-score 0 |
| 251 | "*Default article score level. |
| 252 | All scores generated by the score files will be added to this score. |
| 253 | If this variable is nil, scoring will be disabled." |
| 254 | :group 'gnus-score-default |
| 255 | :type '(choice (const :tag "disable") |
| 256 | integer)) |
| 257 | |
| 258 | (defcustom gnus-summary-default-high-score 0 |
| 259 | "*Default threshold for a high scored article. |
| 260 | An article will be highlighted as high scored if its score is greater |
| 261 | than this score." |
| 262 | :version "22.1" |
| 263 | :group 'gnus-score-default |
| 264 | :type 'integer) |
| 265 | |
| 266 | (defcustom gnus-summary-default-low-score 0 |
| 267 | "*Default threshold for a low scored article. |
| 268 | An article will be highlighted as low scored if its score is smaller |
| 269 | than this score." |
| 270 | :version "22.1" |
| 271 | :group 'gnus-score-default |
| 272 | :type 'integer) |
| 273 | |
| 274 | (defcustom gnus-summary-zcore-fuzz 0 |
| 275 | "*Fuzziness factor for the zcore in the summary buffer. |
| 276 | Articles with scores closer than this to `gnus-summary-default-score' |
| 277 | will not be marked." |
| 278 | :group 'gnus-summary-format |
| 279 | :type 'integer) |
| 280 | |
| 281 | (defcustom gnus-simplify-subject-fuzzy-regexp nil |
| 282 | "*Strings to be removed when doing fuzzy matches. |
| 283 | This can either be a regular expression or list of regular expressions |
| 284 | that will be removed from subject strings if fuzzy subject |
| 285 | simplification is selected." |
| 286 | :group 'gnus-thread |
| 287 | :type '(repeat regexp)) |
| 288 | |
| 289 | (defcustom gnus-show-threads t |
| 290 | "*If non-nil, display threads in summary mode." |
| 291 | :group 'gnus-thread |
| 292 | :type 'boolean) |
| 293 | |
| 294 | (defcustom gnus-thread-hide-subtree nil |
| 295 | "*If non-nil, hide all threads initially. |
| 296 | This can be a predicate specifier which says which threads to hide. |
| 297 | If threads are hidden, you have to run the command |
| 298 | `gnus-summary-show-thread' by hand or select an article." |
| 299 | :group 'gnus-thread |
| 300 | :type '(radio (sexp :format "Non-nil\n" |
| 301 | :match (lambda (widget value) |
| 302 | (not (or (consp value) (functionp value)))) |
| 303 | :value t) |
| 304 | (const nil) |
| 305 | (sexp :tag "Predicate specifier"))) |
| 306 | |
| 307 | (defcustom gnus-thread-hide-killed t |
| 308 | "*If non-nil, hide killed threads automatically." |
| 309 | :group 'gnus-thread |
| 310 | :type 'boolean) |
| 311 | |
| 312 | (defcustom gnus-thread-ignore-subject t |
| 313 | "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header. |
| 314 | If nil, articles that have different subjects from their parents will |
| 315 | start separate threads." |
| 316 | :group 'gnus-thread |
| 317 | :type 'boolean) |
| 318 | |
| 319 | (defcustom gnus-thread-operation-ignore-subject t |
| 320 | "*If non-nil, subjects will be ignored when doing thread commands. |
| 321 | This affects commands like `gnus-summary-kill-thread' and |
| 322 | `gnus-summary-lower-thread'. |
| 323 | |
| 324 | If this variable is nil, articles in the same thread with different |
| 325 | subjects will not be included in the operation in question. If this |
| 326 | variable is `fuzzy', only articles that have subjects that are fuzzily |
| 327 | equal will be included." |
| 328 | :group 'gnus-thread |
| 329 | :type '(choice (const :tag "off" nil) |
| 330 | (const fuzzy) |
| 331 | (sexp :tag "on" t))) |
| 332 | |
| 333 | (defcustom gnus-thread-indent-level 4 |
| 334 | "*Number that says how much each sub-thread should be indented." |
| 335 | :group 'gnus-thread |
| 336 | :type 'integer) |
| 337 | |
| 338 | (defcustom gnus-auto-extend-newsgroup t |
| 339 | "*If non-nil, extend newsgroup forward and backward when requested." |
| 340 | :group 'gnus-summary-choose |
| 341 | :type 'boolean) |
| 342 | |
| 343 | (defcustom gnus-auto-select-first t |
| 344 | "If non-nil, select an article on group entry. |
| 345 | An article is selected automatically when entering a group |
| 346 | e.g. with \\<gnus-group-mode-map>\\[gnus-group-read-group], or via `gnus-summary-next-page' or |
| 347 | `gnus-summary-catchup-and-goto-next-group'. |
| 348 | |
| 349 | Which article is selected is controlled by the variable |
| 350 | `gnus-auto-select-subject'. |
| 351 | |
| 352 | If you want to prevent automatic selection of articles in some |
| 353 | newsgroups, set the variable to nil in `gnus-select-group-hook'." |
| 354 | ;; Commands include... |
| 355 | ;; \\<gnus-group-mode-map>\\[gnus-group-read-group] |
| 356 | ;; \\<gnus-summary-mode-map>\\[gnus-summary-next-page] |
| 357 | ;; \\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group] |
| 358 | :group 'gnus-group-select |
| 359 | :type '(choice (const :tag "none" nil) |
| 360 | (sexp :menu-tag "first" t))) |
| 361 | |
| 362 | (defcustom gnus-auto-select-subject 'unseen-or-unread |
| 363 | "*Says what subject to place under point when entering a group. |
| 364 | |
| 365 | This variable can either be the symbols `first' (place point on the |
| 366 | first subject), `unread' (place point on the subject line of the first |
| 367 | unread article), `best' (place point on the subject line of the |
| 368 | highest-scored article), `unseen' (place point on the subject line of |
| 369 | the first unseen article), `unseen-or-unread' (place point on the subject |
| 370 | line of the first unseen article or, if all articles have been seen, on the |
| 371 | subject line of the first unread article), or a function to be called to |
| 372 | place point on some subject line." |
| 373 | :version "24.1" |
| 374 | :group 'gnus-group-select |
| 375 | :type '(choice (const best) |
| 376 | (const unread) |
| 377 | (const first) |
| 378 | (const unseen) |
| 379 | (const unseen-or-unread) |
| 380 | (function :tag "Function to call"))) |
| 381 | |
| 382 | (defcustom gnus-auto-select-next t |
| 383 | "*If non-nil, offer to go to the next group from the end of the previous. |
| 384 | If the value is t and the next newsgroup is empty, Gnus will exit |
| 385 | summary mode and go back to group mode. If the value is neither nil |
| 386 | nor t, Gnus will select the following unread newsgroup. In |
| 387 | particular, if the value is the symbol `quietly', the next unread |
| 388 | newsgroup will be selected without any confirmation, and if it is |
| 389 | `almost-quietly', the next group will be selected without any |
| 390 | confirmation if you are located on the last article in the group. |
| 391 | Finally, if this variable is `slightly-quietly', the `\\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]' command |
| 392 | will go to the next group without confirmation." |
| 393 | :group 'gnus-summary-maneuvering |
| 394 | :type '(choice (const :tag "off" nil) |
| 395 | (const quietly) |
| 396 | (const almost-quietly) |
| 397 | (const slightly-quietly) |
| 398 | (sexp :menu-tag "on" t))) |
| 399 | |
| 400 | (defcustom gnus-auto-select-same nil |
| 401 | "*If non-nil, select the next article with the same subject. |
| 402 | If there are no more articles with the same subject, go to |
| 403 | the first unread article." |
| 404 | :group 'gnus-summary-maneuvering |
| 405 | :type 'boolean) |
| 406 | |
| 407 | (defcustom gnus-auto-select-on-ephemeral-exit 'next-noselect |
| 408 | "What article should be selected after exiting an ephemeral group. |
| 409 | Valid values include: |
| 410 | |
| 411 | `next' |
| 412 | Select the next article. |
| 413 | `next-unread' |
| 414 | Select the next unread article. |
| 415 | `next-noselect' |
| 416 | Move the cursor to the next article. This is the default. |
| 417 | `next-unread-noselect' |
| 418 | Move the cursor to the next unread article. |
| 419 | |
| 420 | If it has any other value or there is no next (unread) article, the |
| 421 | article selected before entering to the ephemeral group will appear." |
| 422 | :version "23.1" ;; No Gnus |
| 423 | :group 'gnus-summary-maneuvering |
| 424 | :type '(choice :format "%{%t%}:\n %[Value Menu%] %v" |
| 425 | (const next) (const next-unread) |
| 426 | (const next-noselect) (const next-unread-noselect) |
| 427 | (sexp :tag "other" :value nil))) |
| 428 | |
| 429 | (defcustom gnus-auto-goto-ignores 'unfetched |
| 430 | "*Says how to handle unfetched articles when maneuvering. |
| 431 | |
| 432 | This variable can either be the symbols nil (maneuver to any |
| 433 | article), `undownloaded' (maneuvering while unplugged ignores articles |
| 434 | that have not been fetched), `always-undownloaded' (maneuvering always |
| 435 | ignores articles that have not been fetched), `unfetched' (maneuvering |
| 436 | ignores articles whose headers have not been fetched). |
| 437 | |
| 438 | NOTE: The list of unfetched articles will always be nil when plugged |
| 439 | and, when unplugged, a subset of the undownloaded article list." |
| 440 | :version "22.1" |
| 441 | :group 'gnus-summary-maneuvering |
| 442 | :type '(choice (const :tag "None" nil) |
| 443 | (const :tag "Undownloaded when unplugged" undownloaded) |
| 444 | (const :tag "Undownloaded" always-undownloaded) |
| 445 | (const :tag "Unfetched" unfetched))) |
| 446 | |
| 447 | (defcustom gnus-summary-check-current nil |
| 448 | "*If non-nil, consider the current article when moving. |
| 449 | The \"unread\" movement commands will stay on the same line if the |
| 450 | current article is unread." |
| 451 | :group 'gnus-summary-maneuvering |
| 452 | :type 'boolean) |
| 453 | |
| 454 | (defcustom gnus-auto-center-summary |
| 455 | (max (or (bound-and-true-p scroll-margin) 0) 2) |
| 456 | "*If non-nil, always center the current summary buffer. |
| 457 | In particular, if `vertical' do only vertical recentering. If non-nil |
| 458 | and non-`vertical', do both horizontal and vertical recentering." |
| 459 | :group 'gnus-summary-maneuvering |
| 460 | :type '(choice (const :tag "none" nil) |
| 461 | (const vertical) |
| 462 | (integer :tag "height") |
| 463 | (sexp :menu-tag "both" t))) |
| 464 | |
| 465 | (defcustom gnus-auto-center-group t |
| 466 | "If non-nil, always center the group buffer." |
| 467 | :group 'gnus-summary-maneuvering |
| 468 | :type 'boolean) |
| 469 | |
| 470 | (defcustom gnus-show-all-headers nil |
| 471 | "*If non-nil, don't hide any headers." |
| 472 | :group 'gnus-article-hiding |
| 473 | :group 'gnus-article-headers |
| 474 | :type 'boolean) |
| 475 | |
| 476 | (defcustom gnus-summary-ignore-duplicates nil |
| 477 | "*If non-nil, ignore articles with identical Message-ID headers." |
| 478 | :group 'gnus-summary |
| 479 | :type 'boolean) |
| 480 | |
| 481 | (defcustom gnus-single-article-buffer nil |
| 482 | "*If non-nil, display all articles in the same buffer. |
| 483 | If nil, each group will get its own article buffer." |
| 484 | :version "24.1" |
| 485 | :group 'gnus-article-various |
| 486 | :type 'boolean) |
| 487 | |
| 488 | (defcustom gnus-widen-article-window nil |
| 489 | "If non-nil, selecting the article buffer will display only the article buffer." |
| 490 | :version "24.1" |
| 491 | :group 'gnus-article-various |
| 492 | :type 'boolean) |
| 493 | |
| 494 | (defcustom gnus-break-pages t |
| 495 | "*If non-nil, do page breaking on articles. |
| 496 | The page delimiter is specified by the `gnus-page-delimiter' |
| 497 | variable." |
| 498 | :group 'gnus-article-various |
| 499 | :type 'boolean) |
| 500 | |
| 501 | (defcustom gnus-move-split-methods nil |
| 502 | "*Variable used to suggest where articles are to be moved to. |
| 503 | It uses the same syntax as the `gnus-split-methods' variable. |
| 504 | However, whereas `gnus-split-methods' specifies file names as targets, |
| 505 | this variable specifies group names." |
| 506 | :group 'gnus-summary-mail |
| 507 | :type '(repeat (choice (list :value (fun) function) |
| 508 | (cons :value ("" "") regexp (repeat string)) |
| 509 | (sexp :value nil)))) |
| 510 | |
| 511 | (defcustom gnus-move-group-prefix-function 'gnus-group-real-prefix |
| 512 | "Function used to compute default prefix for article move/copy/etc prompts. |
| 513 | The function should take one argument, a group name, and return a |
| 514 | string with the suggested prefix." |
| 515 | :group 'gnus-summary-mail |
| 516 | :type 'function) |
| 517 | |
| 518 | ;; FIXME: Although the custom type is `character' for the following variables, |
| 519 | ;; using multibyte characters (Latin-1, UTF-8) doesn't work. -- rs |
| 520 | |
| 521 | (defcustom gnus-unread-mark ? ;Whitespace |
| 522 | "*Mark used for unread articles." |
| 523 | :group 'gnus-summary-marks |
| 524 | :type 'character) |
| 525 | |
| 526 | (defcustom gnus-ticked-mark ?! |
| 527 | "*Mark used for ticked articles." |
| 528 | :group 'gnus-summary-marks |
| 529 | :type 'character) |
| 530 | |
| 531 | (defcustom gnus-dormant-mark ?? |
| 532 | "*Mark used for dormant articles." |
| 533 | :group 'gnus-summary-marks |
| 534 | :type 'character) |
| 535 | |
| 536 | (defcustom gnus-del-mark ?r |
| 537 | "*Mark used for del'd articles." |
| 538 | :group 'gnus-summary-marks |
| 539 | :type 'character) |
| 540 | |
| 541 | (defcustom gnus-read-mark ?R |
| 542 | "*Mark used for read articles." |
| 543 | :group 'gnus-summary-marks |
| 544 | :type 'character) |
| 545 | |
| 546 | (defcustom gnus-expirable-mark ?E |
| 547 | "*Mark used for expirable articles." |
| 548 | :group 'gnus-summary-marks |
| 549 | :type 'character) |
| 550 | |
| 551 | (defcustom gnus-killed-mark ?K |
| 552 | "*Mark used for killed articles." |
| 553 | :group 'gnus-summary-marks |
| 554 | :type 'character) |
| 555 | |
| 556 | (defcustom gnus-spam-mark ?$ |
| 557 | "*Mark used for spam articles." |
| 558 | :version "22.1" |
| 559 | :group 'gnus-summary-marks |
| 560 | :type 'character) |
| 561 | |
| 562 | (defcustom gnus-kill-file-mark ?X |
| 563 | "*Mark used for articles killed by kill files." |
| 564 | :group 'gnus-summary-marks |
| 565 | :type 'character) |
| 566 | |
| 567 | (defcustom gnus-low-score-mark ?Y |
| 568 | "*Mark used for articles with a low score." |
| 569 | :group 'gnus-summary-marks |
| 570 | :type 'character) |
| 571 | |
| 572 | (defcustom gnus-catchup-mark ?C |
| 573 | "*Mark used for articles that are caught up." |
| 574 | :group 'gnus-summary-marks |
| 575 | :type 'character) |
| 576 | |
| 577 | (defcustom gnus-replied-mark ?A |
| 578 | "*Mark used for articles that have been replied to." |
| 579 | :group 'gnus-summary-marks |
| 580 | :type 'character) |
| 581 | |
| 582 | (defcustom gnus-forwarded-mark ?F |
| 583 | "*Mark used for articles that have been forwarded." |
| 584 | :version "22.1" |
| 585 | :group 'gnus-summary-marks |
| 586 | :type 'character) |
| 587 | |
| 588 | (defcustom gnus-recent-mark ?N |
| 589 | "*Mark used for articles that are recent." |
| 590 | :version "22.1" |
| 591 | :group 'gnus-summary-marks |
| 592 | :type 'character) |
| 593 | |
| 594 | (defcustom gnus-cached-mark ?* |
| 595 | "*Mark used for articles that are in the cache." |
| 596 | :group 'gnus-summary-marks |
| 597 | :type 'character) |
| 598 | |
| 599 | (defcustom gnus-saved-mark ?S |
| 600 | "*Mark used for articles that have been saved." |
| 601 | :group 'gnus-summary-marks |
| 602 | :type 'character) |
| 603 | |
| 604 | (defcustom gnus-unseen-mark ?. |
| 605 | "*Mark used for articles that haven't been seen." |
| 606 | :version "22.1" |
| 607 | :group 'gnus-summary-marks |
| 608 | :type 'character) |
| 609 | |
| 610 | (defcustom gnus-no-mark ? ;Whitespace |
| 611 | "*Mark used for articles that have no other secondary mark." |
| 612 | :version "22.1" |
| 613 | :group 'gnus-summary-marks |
| 614 | :type 'character) |
| 615 | |
| 616 | (defcustom gnus-ancient-mark ?O |
| 617 | "*Mark used for ancient articles." |
| 618 | :group 'gnus-summary-marks |
| 619 | :type 'character) |
| 620 | |
| 621 | (defcustom gnus-sparse-mark ?Q |
| 622 | "*Mark used for sparsely reffed articles." |
| 623 | :group 'gnus-summary-marks |
| 624 | :type 'character) |
| 625 | |
| 626 | (defcustom gnus-canceled-mark ?G |
| 627 | "*Mark used for canceled articles." |
| 628 | :group 'gnus-summary-marks |
| 629 | :type 'character) |
| 630 | |
| 631 | (defcustom gnus-duplicate-mark ?M |
| 632 | "*Mark used for duplicate articles." |
| 633 | :group 'gnus-summary-marks |
| 634 | :type 'character) |
| 635 | |
| 636 | (defcustom gnus-undownloaded-mark ?- |
| 637 | "*Mark used for articles that weren't downloaded." |
| 638 | :version "22.1" |
| 639 | :group 'gnus-summary-marks |
| 640 | :type 'character) |
| 641 | |
| 642 | (defcustom gnus-downloaded-mark ?+ |
| 643 | "*Mark used for articles that were downloaded." |
| 644 | :group 'gnus-summary-marks |
| 645 | :type 'character) |
| 646 | |
| 647 | (defcustom gnus-downloadable-mark ?% |
| 648 | "*Mark used for articles that are to be downloaded." |
| 649 | :group 'gnus-summary-marks |
| 650 | :type 'character) |
| 651 | |
| 652 | (defcustom gnus-unsendable-mark ?= |
| 653 | "*Mark used for articles that won't be sent." |
| 654 | :group 'gnus-summary-marks |
| 655 | :type 'character) |
| 656 | |
| 657 | (defcustom gnus-score-over-mark ?+ |
| 658 | "*Score mark used for articles with high scores." |
| 659 | :group 'gnus-summary-marks |
| 660 | :type 'character) |
| 661 | |
| 662 | (defcustom gnus-score-below-mark ?- |
| 663 | "*Score mark used for articles with low scores." |
| 664 | :group 'gnus-summary-marks |
| 665 | :type 'character) |
| 666 | |
| 667 | (defcustom gnus-empty-thread-mark ? ;Whitespace |
| 668 | "*There is no thread under the article." |
| 669 | :group 'gnus-summary-marks |
| 670 | :type 'character) |
| 671 | |
| 672 | (defcustom gnus-not-empty-thread-mark ?= |
| 673 | "*There is a thread under the article." |
| 674 | :group 'gnus-summary-marks |
| 675 | :type 'character) |
| 676 | |
| 677 | (defcustom gnus-view-pseudo-asynchronously nil |
| 678 | "*If non-nil, Gnus will view pseudo-articles asynchronously." |
| 679 | :group 'gnus-extract-view |
| 680 | :type 'boolean) |
| 681 | |
| 682 | (defcustom gnus-auto-expirable-marks |
| 683 | (list gnus-killed-mark gnus-del-mark gnus-catchup-mark |
| 684 | gnus-low-score-mark gnus-ancient-mark gnus-read-mark |
| 685 | gnus-duplicate-mark) |
| 686 | "*The list of marks converted into expiration if a group is auto-expirable." |
| 687 | :version "24.1" |
| 688 | :group 'gnus-summary |
| 689 | :type '(repeat character)) |
| 690 | |
| 691 | (defcustom gnus-inhibit-user-auto-expire t |
| 692 | "*If non-nil, user marking commands will not mark an article as expirable, even if the group has auto-expire turned on." |
| 693 | :version "21.1" |
| 694 | :group 'gnus-summary |
| 695 | :type 'boolean) |
| 696 | |
| 697 | (defcustom gnus-mark-copied-or-moved-articles-as-expirable nil |
| 698 | "If non-nil, mark articles copied or moved to auto-expire group as expirable. |
| 699 | If nil, the expirable marks will be unchanged except that the marks |
| 700 | will be removed when copying or moving articles to a group that has |
| 701 | not turned auto-expire on. If non-nil, articles that have been read |
| 702 | will be marked as expirable when being copied or moved to a group in |
| 703 | which auto-expire is turned on." |
| 704 | :version "23.2" |
| 705 | :type 'boolean |
| 706 | :group 'gnus-summary-marks) |
| 707 | |
| 708 | (defcustom gnus-view-pseudos nil |
| 709 | "*If `automatic', pseudo-articles will be viewed automatically. |
| 710 | If `not-confirm', pseudos will be viewed automatically, and the user |
| 711 | will not be asked to confirm the command." |
| 712 | :group 'gnus-extract-view |
| 713 | :type '(choice (const :tag "off" nil) |
| 714 | (const automatic) |
| 715 | (const not-confirm))) |
| 716 | |
| 717 | (defcustom gnus-view-pseudos-separately t |
| 718 | "*If non-nil, one pseudo-article will be created for each file to be viewed. |
| 719 | If nil, all files that use the same viewing command will be given as a |
| 720 | list of parameters to that command." |
| 721 | :group 'gnus-extract-view |
| 722 | :type 'boolean) |
| 723 | |
| 724 | (defcustom gnus-insert-pseudo-articles t |
| 725 | "*If non-nil, insert pseudo-articles when decoding articles." |
| 726 | :group 'gnus-extract-view |
| 727 | :type 'boolean) |
| 728 | |
| 729 | (defcustom gnus-summary-dummy-line-format |
| 730 | " %(: :%) %S\n" |
| 731 | "*The format specification for the dummy roots in the summary buffer. |
| 732 | It works along the same lines as a normal formatting string, |
| 733 | with some simple extensions. |
| 734 | |
| 735 | %S The subject |
| 736 | |
| 737 | General format specifiers can also be used. |
| 738 | See `(gnus)Formatting Variables'." |
| 739 | :link '(custom-manual "(gnus)Formatting Variables") |
| 740 | :group 'gnus-threading |
| 741 | :type 'string) |
| 742 | |
| 743 | (defcustom gnus-summary-mode-line-format "Gnus: %g [%A] %Z" |
| 744 | "*The format specification for the summary mode line. |
| 745 | It works along the same lines as a normal formatting string, |
| 746 | with some simple extensions: |
| 747 | |
| 748 | %G Group name |
| 749 | %p Unprefixed group name |
| 750 | %A Current article number |
| 751 | %z Current article score |
| 752 | %V Gnus version |
| 753 | %U Number of unread articles in the group |
| 754 | %e Number of unselected articles in the group |
| 755 | %Z A string with unread/unselected article counts |
| 756 | %g Shortish group name |
| 757 | %S Subject of the current article |
| 758 | %u User-defined spec |
| 759 | %s Current score file name |
| 760 | %d Number of dormant articles |
| 761 | %r Number of articles that have been marked as read in this session |
| 762 | %E Number of articles expunged by the score files" |
| 763 | :group 'gnus-summary-format |
| 764 | :type 'string) |
| 765 | |
| 766 | (defcustom gnus-list-identifiers nil |
| 767 | "Regexp that matches list identifiers to be removed from subject. |
| 768 | This can also be a list of regexps." |
| 769 | :version "21.1" |
| 770 | :group 'gnus-summary-format |
| 771 | :group 'gnus-article-hiding |
| 772 | :type '(choice (const :tag "none" nil) |
| 773 | (regexp :value ".*") |
| 774 | (repeat :value (".*") regexp))) |
| 775 | |
| 776 | (defcustom gnus-summary-mark-below 0 |
| 777 | "*Mark all articles with a score below this variable as read. |
| 778 | This variable is local to each summary buffer and usually set by the |
| 779 | score file." |
| 780 | :group 'gnus-score-default |
| 781 | :type 'integer) |
| 782 | |
| 783 | (defun gnus-widget-reversible-match (widget value) |
| 784 | "Ignoring WIDGET, convert VALUE to internal form. |
| 785 | VALUE should have the form `FOO' or `(not FOO)', where FOO is an symbol." |
| 786 | ;; (debug value) |
| 787 | (or (symbolp value) |
| 788 | (and (listp value) |
| 789 | (eq (length value) 2) |
| 790 | (eq (nth 0 value) 'not) |
| 791 | (symbolp (nth 1 value))))) |
| 792 | |
| 793 | (defun gnus-widget-reversible-to-internal (widget value) |
| 794 | "Ignoring WIDGET, convert VALUE to internal form. |
| 795 | VALUE should have the form `FOO' or `(not FOO)', where FOO is an atom. |
| 796 | FOO is converted to (FOO nil) and (not FOO) is converted to (FOO t)." |
| 797 | ;; (debug value) |
| 798 | (if (atom value) |
| 799 | (list value nil) |
| 800 | (list (nth 1 value) t))) |
| 801 | |
| 802 | (defun gnus-widget-reversible-to-external (widget value) |
| 803 | "Ignoring WIDGET, convert VALUE to external form. |
| 804 | VALUE should have the form `(FOO nil)' or `(FOO t)', where FOO is an atom. |
| 805 | \(FOO nil) is converted to FOO and (FOO t) is converted to (not FOO)." |
| 806 | ;; (debug value) |
| 807 | (if (nth 1 value) |
| 808 | (list 'not (nth 0 value)) |
| 809 | (nth 0 value))) |
| 810 | |
| 811 | (define-widget 'gnus-widget-reversible 'group |
| 812 | "A `group' that convert values." |
| 813 | :match 'gnus-widget-reversible-match |
| 814 | :value-to-internal 'gnus-widget-reversible-to-internal |
| 815 | :value-to-external 'gnus-widget-reversible-to-external) |
| 816 | |
| 817 | (defcustom gnus-article-sort-functions '(gnus-article-sort-by-number) |
| 818 | "*List of functions used for sorting articles in the summary buffer. |
| 819 | |
| 820 | Each function takes two articles and returns non-nil if the first |
| 821 | article should be sorted before the other. If you use more than one |
| 822 | function, the primary sort function should be the last. You should |
| 823 | probably always include `gnus-article-sort-by-number' in the list of |
| 824 | sorting functions -- preferably first. Also note that sorting by date |
| 825 | is often much slower than sorting by number, and the sorting order is |
| 826 | very similar. (Sorting by date means sorting by the time the message |
| 827 | was sent, sorting by number means sorting by arrival time.) |
| 828 | |
| 829 | Each item can also be a list `(not F)' where F is a function; |
| 830 | this reverses the sort order. |
| 831 | |
| 832 | Ready-made functions include `gnus-article-sort-by-number', |
| 833 | `gnus-article-sort-by-author', `gnus-article-sort-by-subject', |
| 834 | `gnus-article-sort-by-date', `gnus-article-sort-by-random' |
| 835 | and `gnus-article-sort-by-score'. |
| 836 | |
| 837 | When threading is turned on, the variable `gnus-thread-sort-functions' |
| 838 | controls how articles are sorted." |
| 839 | :group 'gnus-summary-sort |
| 840 | :type '(repeat (gnus-widget-reversible |
| 841 | (choice (function-item gnus-article-sort-by-number) |
| 842 | (function-item gnus-article-sort-by-author) |
| 843 | (function-item gnus-article-sort-by-subject) |
| 844 | (function-item gnus-article-sort-by-date) |
| 845 | (function-item gnus-article-sort-by-score) |
| 846 | (function-item gnus-article-sort-by-random) |
| 847 | (function :tag "other")) |
| 848 | (boolean :tag "Reverse order")))) |
| 849 | |
| 850 | |
| 851 | (defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number) |
| 852 | "*List of functions used for sorting threads in the summary buffer. |
| 853 | By default, threads are sorted by article number. |
| 854 | |
| 855 | Each function takes two threads and returns non-nil if the first |
| 856 | thread should be sorted before the other. If you use more than one |
| 857 | function, the primary sort function should be the last. You should |
| 858 | probably always include `gnus-thread-sort-by-number' in the list of |
| 859 | sorting functions -- preferably first. Also note that sorting by date |
| 860 | is often much slower than sorting by number, and the sorting order is |
| 861 | very similar. (Sorting by date means sorting by the time the message |
| 862 | was sent, sorting by number means sorting by arrival time.) |
| 863 | |
| 864 | Each list item can also be a list `(not F)' where F is a |
| 865 | function; this specifies reversed sort order. |
| 866 | |
| 867 | Ready-made functions include `gnus-thread-sort-by-number', |
| 868 | `gnus-thread-sort-by-author', `gnus-thread-sort-by-recipient' |
| 869 | `gnus-thread-sort-by-subject', `gnus-thread-sort-by-date', |
| 870 | `gnus-thread-sort-by-score', `gnus-thread-sort-by-most-recent-number', |
| 871 | `gnus-thread-sort-by-most-recent-date', `gnus-thread-sort-by-random', |
| 872 | and `gnus-thread-sort-by-total-score' (see |
| 873 | `gnus-thread-score-function'). |
| 874 | |
| 875 | When threading is turned off, the variable |
| 876 | `gnus-article-sort-functions' controls how articles are sorted." |
| 877 | :group 'gnus-summary-sort |
| 878 | :type '(repeat |
| 879 | (gnus-widget-reversible |
| 880 | (choice (function-item gnus-thread-sort-by-number) |
| 881 | (function-item gnus-thread-sort-by-author) |
| 882 | (function-item gnus-thread-sort-by-recipient) |
| 883 | (function-item gnus-thread-sort-by-subject) |
| 884 | (function-item gnus-thread-sort-by-date) |
| 885 | (function-item gnus-thread-sort-by-score) |
| 886 | (function-item gnus-thread-sort-by-most-recent-number) |
| 887 | (function-item gnus-thread-sort-by-most-recent-date) |
| 888 | (function-item gnus-thread-sort-by-random) |
| 889 | (function-item gnus-thread-sort-by-total-score) |
| 890 | (function :tag "other")) |
| 891 | (boolean :tag "Reverse order")))) |
| 892 | |
| 893 | (defcustom gnus-thread-score-function '+ |
| 894 | "*Function used for calculating the total score of a thread. |
| 895 | |
| 896 | The function is called with the scores of the article and each |
| 897 | subthread and should then return the score of the thread. |
| 898 | |
| 899 | Some functions you can use are `+', `max', or `min'." |
| 900 | :group 'gnus-summary-sort |
| 901 | :type 'function) |
| 902 | |
| 903 | (defcustom gnus-summary-expunge-below nil |
| 904 | "All articles that have a score less than this variable will be expunged. |
| 905 | This variable is local to the summary buffers." |
| 906 | :group 'gnus-score-default |
| 907 | :type '(choice (const :tag "off" nil) |
| 908 | integer)) |
| 909 | |
| 910 | (defcustom gnus-thread-expunge-below nil |
| 911 | "All threads that have a total score less than this variable will be expunged. |
| 912 | See `gnus-thread-score-function' for en explanation of what a |
| 913 | \"thread score\" is. |
| 914 | |
| 915 | This variable is local to the summary buffers." |
| 916 | :group 'gnus-threading |
| 917 | :group 'gnus-score-default |
| 918 | :type '(choice (const :tag "off" nil) |
| 919 | integer)) |
| 920 | |
| 921 | (defcustom gnus-summary-mode-hook nil |
| 922 | "*A hook for Gnus summary mode. |
| 923 | This hook is run before any variables are set in the summary buffer." |
| 924 | :options '(turn-on-gnus-mailing-list-mode gnus-pick-mode) |
| 925 | :group 'gnus-summary-various |
| 926 | :type 'hook) |
| 927 | |
| 928 | ;; Extracted from gnus-xmas-redefine in order to preserve user settings |
| 929 | (when (featurep 'xemacs) |
| 930 | (add-hook 'gnus-summary-mode-hook 'gnus-xmas-summary-menu-add) |
| 931 | (add-hook 'gnus-summary-mode-hook 'gnus-xmas-setup-summary-toolbar) |
| 932 | (add-hook 'gnus-summary-mode-hook |
| 933 | 'gnus-xmas-switch-horizontal-scrollbar-off)) |
| 934 | |
| 935 | (defcustom gnus-summary-menu-hook nil |
| 936 | "*Hook run after the creation of the summary mode menu." |
| 937 | :group 'gnus-summary-visual |
| 938 | :type 'hook) |
| 939 | |
| 940 | (defcustom gnus-summary-exit-hook nil |
| 941 | "*A hook called on exit from the summary buffer. |
| 942 | It will be called with point in the group buffer." |
| 943 | :group 'gnus-summary-exit |
| 944 | :type 'hook) |
| 945 | |
| 946 | (defcustom gnus-summary-prepare-hook nil |
| 947 | "*A hook called after the summary buffer has been generated. |
| 948 | If you want to modify the summary buffer, you can use this hook." |
| 949 | :group 'gnus-summary-various |
| 950 | :type 'hook) |
| 951 | |
| 952 | (defcustom gnus-summary-prepared-hook nil |
| 953 | "*A hook called as the last thing after the summary buffer has been generated." |
| 954 | :group 'gnus-summary-various |
| 955 | :type 'hook) |
| 956 | |
| 957 | (defcustom gnus-summary-generate-hook nil |
| 958 | "*A hook run just before generating the summary buffer. |
| 959 | This hook is commonly used to customize threading variables and the |
| 960 | like." |
| 961 | :group 'gnus-summary-various |
| 962 | :type 'hook) |
| 963 | |
| 964 | (defcustom gnus-select-group-hook nil |
| 965 | "*A hook called when a newsgroup is selected. |
| 966 | |
| 967 | If you'd like to simplify subjects like the |
| 968 | `gnus-summary-next-same-subject' command does, you can use the |
| 969 | following hook: |
| 970 | |
| 971 | (add-hook gnus-select-group-hook |
| 972 | (lambda () |
| 973 | (mapcar (lambda (header) |
| 974 | (mail-header-set-subject |
| 975 | header |
| 976 | (gnus-simplify-subject |
| 977 | (mail-header-subject header) 're-only))) |
| 978 | gnus-newsgroup-headers)))" |
| 979 | :group 'gnus-group-select |
| 980 | :type 'hook) |
| 981 | |
| 982 | (defcustom gnus-select-article-hook nil |
| 983 | "*A hook called when an article is selected." |
| 984 | :group 'gnus-summary-choose |
| 985 | :options '(gnus-agent-fetch-selected-article) |
| 986 | :type 'hook) |
| 987 | |
| 988 | (defcustom gnus-visual-mark-article-hook |
| 989 | (list 'gnus-highlight-selected-summary) |
| 990 | "*Hook run after selecting an article in the summary buffer. |
| 991 | It is meant to be used for highlighting the article in some way. It |
| 992 | is not run if `gnus-visual' is nil." |
| 993 | :group 'gnus-summary-visual |
| 994 | :type 'hook) |
| 995 | |
| 996 | (defcustom gnus-parse-headers-hook nil |
| 997 | "*A hook called before parsing the headers." |
| 998 | :group 'gnus-various |
| 999 | :type 'hook) |
| 1000 | |
| 1001 | (defcustom gnus-exit-group-hook nil |
| 1002 | "*A hook called when exiting summary mode. |
| 1003 | This hook is not called from the non-updating exit commands like `Q'." |
| 1004 | :group 'gnus-various |
| 1005 | :type 'hook) |
| 1006 | |
| 1007 | (defcustom gnus-summary-update-hook nil |
| 1008 | "*A hook called when a summary line is changed. |
| 1009 | The hook will not be called if `gnus-visual' is nil. |
| 1010 | |
| 1011 | The default function `gnus-summary-highlight-line' will |
| 1012 | highlight the line according to the `gnus-summary-highlight' |
| 1013 | variable." |
| 1014 | :group 'gnus-summary-visual |
| 1015 | :type 'hook) |
| 1016 | |
| 1017 | (defcustom gnus-mark-article-hook '(gnus-summary-mark-read-and-unread-as-read) |
| 1018 | "*A hook called when an article is selected for the first time. |
| 1019 | The hook is intended to mark an article as read (or unread) |
| 1020 | automatically when it is selected." |
| 1021 | :group 'gnus-summary-choose |
| 1022 | :type 'hook) |
| 1023 | |
| 1024 | (defcustom gnus-group-no-more-groups-hook nil |
| 1025 | "*A hook run when returning to group mode having no more (unread) groups." |
| 1026 | :group 'gnus-group-select |
| 1027 | :type 'hook) |
| 1028 | |
| 1029 | (defcustom gnus-ps-print-hook nil |
| 1030 | "*A hook run before ps-printing something from Gnus." |
| 1031 | :group 'gnus-summary |
| 1032 | :type 'hook) |
| 1033 | |
| 1034 | (defcustom gnus-summary-article-move-hook nil |
| 1035 | "*A hook called after an article is moved, copied, respooled, or crossposted." |
| 1036 | :version "22.1" |
| 1037 | :group 'gnus-summary |
| 1038 | :type 'hook) |
| 1039 | |
| 1040 | (defcustom gnus-summary-article-delete-hook nil |
| 1041 | "*A hook called after an article is deleted." |
| 1042 | :version "22.1" |
| 1043 | :group 'gnus-summary |
| 1044 | :type 'hook) |
| 1045 | |
| 1046 | (defcustom gnus-summary-article-expire-hook nil |
| 1047 | "*A hook called after an article is expired." |
| 1048 | :version "22.1" |
| 1049 | :group 'gnus-summary |
| 1050 | :type 'hook) |
| 1051 | |
| 1052 | (defcustom gnus-summary-display-arrow |
| 1053 | (and (fboundp 'display-graphic-p) |
| 1054 | (display-graphic-p)) |
| 1055 | "*If non-nil, display an arrow highlighting the current article." |
| 1056 | :version "22.1" |
| 1057 | :group 'gnus-summary |
| 1058 | :type 'boolean) |
| 1059 | |
| 1060 | (defcustom gnus-summary-selected-face 'gnus-summary-selected |
| 1061 | "Face used for highlighting the current article in the summary buffer." |
| 1062 | :group 'gnus-summary-visual |
| 1063 | :type 'face) |
| 1064 | |
| 1065 | (defvar gnus-tmp-downloaded nil) |
| 1066 | |
| 1067 | (defcustom gnus-summary-highlight |
| 1068 | '(((eq mark gnus-canceled-mark) |
| 1069 | . gnus-summary-cancelled) |
| 1070 | ((and uncached (> score default-high)) |
| 1071 | . gnus-summary-high-undownloaded) |
| 1072 | ((and uncached (< score default-low)) |
| 1073 | . gnus-summary-low-undownloaded) |
| 1074 | (uncached |
| 1075 | . gnus-summary-normal-undownloaded) |
| 1076 | ((and (> score default-high) |
| 1077 | (or (eq mark gnus-dormant-mark) |
| 1078 | (eq mark gnus-ticked-mark))) |
| 1079 | . gnus-summary-high-ticked) |
| 1080 | ((and (< score default-low) |
| 1081 | (or (eq mark gnus-dormant-mark) |
| 1082 | (eq mark gnus-ticked-mark))) |
| 1083 | . gnus-summary-low-ticked) |
| 1084 | ((or (eq mark gnus-dormant-mark) |
| 1085 | (eq mark gnus-ticked-mark)) |
| 1086 | . gnus-summary-normal-ticked) |
| 1087 | ((and (> score default-high) (eq mark gnus-ancient-mark)) |
| 1088 | . gnus-summary-high-ancient) |
| 1089 | ((and (< score default-low) (eq mark gnus-ancient-mark)) |
| 1090 | . gnus-summary-low-ancient) |
| 1091 | ((eq mark gnus-ancient-mark) |
| 1092 | . gnus-summary-normal-ancient) |
| 1093 | ((and (> score default-high) (eq mark gnus-unread-mark)) |
| 1094 | . gnus-summary-high-unread) |
| 1095 | ((and (< score default-low) (eq mark gnus-unread-mark)) |
| 1096 | . gnus-summary-low-unread) |
| 1097 | ((eq mark gnus-unread-mark) |
| 1098 | . gnus-summary-normal-unread) |
| 1099 | ((> score default-high) |
| 1100 | . gnus-summary-high-read) |
| 1101 | ((< score default-low) |
| 1102 | . gnus-summary-low-read) |
| 1103 | (t |
| 1104 | . gnus-summary-normal-read)) |
| 1105 | "*Controls the highlighting of summary buffer lines. |
| 1106 | |
| 1107 | A list of (FORM . FACE) pairs. When deciding how a particular |
| 1108 | summary line should be displayed, each form is evaluated. The content |
| 1109 | of the face field after the first true form is used. You can change |
| 1110 | how those summary lines are displayed, by editing the face field. |
| 1111 | |
| 1112 | You can use the following variables in the FORM field. |
| 1113 | |
| 1114 | score: The article's score. |
| 1115 | default: The default article score. |
| 1116 | default-high: The default score for high scored articles. |
| 1117 | default-low: The default score for low scored articles. |
| 1118 | below: The score below which articles are automatically marked as read. |
| 1119 | mark: The article's mark. |
| 1120 | uncached: Non-nil if the article is uncached." |
| 1121 | :group 'gnus-summary-visual |
| 1122 | :type '(repeat (cons (sexp :tag "Form" nil) |
| 1123 | face))) |
| 1124 | (put 'gnus-summary-highlight 'risky-local-variable t) |
| 1125 | |
| 1126 | (defcustom gnus-alter-header-function nil |
| 1127 | "Function called to allow alteration of article header structures. |
| 1128 | The function is called with one parameter, the article header vector, |
| 1129 | which it may alter in any way." |
| 1130 | :type '(choice (const :tag "None" nil) |
| 1131 | function) |
| 1132 | :group 'gnus-summary) |
| 1133 | |
| 1134 | (defvar gnus-decode-encoded-word-function 'mail-decode-encoded-word-string |
| 1135 | "Function used to decode a string with encoded words.") |
| 1136 | |
| 1137 | (defvar gnus-decode-encoded-address-function |
| 1138 | 'mail-decode-encoded-address-string |
| 1139 | "Function used to decode addresses with encoded words.") |
| 1140 | |
| 1141 | (defcustom gnus-extra-headers '(To Cc Keywords Gcc Newsgroups) |
| 1142 | "*Extra headers to parse." |
| 1143 | :version "24.1" ; added Cc Keywords Gcc |
| 1144 | :group 'gnus-summary |
| 1145 | :type '(repeat symbol)) |
| 1146 | |
| 1147 | (defcustom gnus-ignored-from-addresses |
| 1148 | (and user-mail-address |
| 1149 | (not (string= user-mail-address "")) |
| 1150 | (regexp-quote user-mail-address)) |
| 1151 | "*From headers that may be suppressed in favor of To headers. |
| 1152 | This can be a regexp or a list of regexps." |
| 1153 | :version "21.1" |
| 1154 | :group 'gnus-summary |
| 1155 | :type '(choice regexp |
| 1156 | (repeat :tag "Regexp List" regexp))) |
| 1157 | |
| 1158 | (defsubst gnus-ignored-from-addresses () |
| 1159 | (gmm-regexp-concat gnus-ignored-from-addresses)) |
| 1160 | |
| 1161 | (defcustom gnus-summary-to-prefix "-> " |
| 1162 | "*String prefixed to the To field in the summary line when |
| 1163 | using `gnus-ignored-from-addresses'." |
| 1164 | :version "22.1" |
| 1165 | :group 'gnus-summary |
| 1166 | :type 'string) |
| 1167 | |
| 1168 | (defcustom gnus-summary-newsgroup-prefix "=> " |
| 1169 | "*String prefixed to the Newsgroup field in the summary |
| 1170 | line when using the option `gnus-ignored-from-addresses'." |
| 1171 | :version "22.1" |
| 1172 | :group 'gnus-summary |
| 1173 | :type 'string) |
| 1174 | |
| 1175 | (defcustom gnus-newsgroup-ignored-charsets '(unknown-8bit x-unknown) |
| 1176 | "List of charsets that should be ignored. |
| 1177 | When these charsets are used in the \"charset\" parameter, the |
| 1178 | default charset will be used instead." |
| 1179 | :version "21.1" |
| 1180 | :type '(repeat symbol) |
| 1181 | :group 'gnus-charset) |
| 1182 | |
| 1183 | (defcustom gnus-newsgroup-maximum-articles nil |
| 1184 | "The maximum number of articles a newsgroup. |
| 1185 | If this is a number, old articles in a newsgroup exceeding this number |
| 1186 | are silently ignored. If it is nil, no article is ignored. Note that |
| 1187 | setting this variable to a number might prevent you from reading very |
| 1188 | old articles." |
| 1189 | :group 'gnus-group-select |
| 1190 | :version "22.2" |
| 1191 | :type '(choice (const :tag "No limit" nil) |
| 1192 | integer)) |
| 1193 | |
| 1194 | (gnus-define-group-parameter |
| 1195 | ignored-charsets |
| 1196 | :type list |
| 1197 | :function-document |
| 1198 | "Return the ignored charsets of GROUP." |
| 1199 | :variable gnus-group-ignored-charsets-alist |
| 1200 | :variable-default |
| 1201 | '(("alt\\.chinese\\.text" iso-8859-1)) |
| 1202 | :variable-document |
| 1203 | "Alist of regexps (to match group names) and charsets that should be ignored. |
| 1204 | When these charsets are used in the \"charset\" parameter, the |
| 1205 | default charset will be used instead." |
| 1206 | :variable-group gnus-charset |
| 1207 | :variable-type '(repeat (cons (regexp :tag "Group") |
| 1208 | (repeat symbol))) |
| 1209 | :parameter-type '(choice :tag "Ignored charsets" |
| 1210 | :value nil |
| 1211 | (repeat (symbol))) |
| 1212 | :parameter-document "\ |
| 1213 | List of charsets that should be ignored. |
| 1214 | |
| 1215 | When these charsets are used in the \"charset\" parameter, the |
| 1216 | default charset will be used instead.") |
| 1217 | |
| 1218 | (defcustom gnus-group-highlight-words-alist nil |
| 1219 | "Alist of group regexps and highlight regexps. |
| 1220 | This variable uses the same syntax as `gnus-emphasis-alist'." |
| 1221 | :version "21.1" |
| 1222 | :type '(repeat (cons (regexp :tag "Group") |
| 1223 | (repeat (list (regexp :tag "Highlight regexp") |
| 1224 | (number :tag "Group for entire word" 0) |
| 1225 | (number :tag "Group for displayed part" 0) |
| 1226 | (symbol :tag "Face" |
| 1227 | gnus-emphasis-highlight-words))))) |
| 1228 | :group 'gnus-summary-visual) |
| 1229 | |
| 1230 | (defcustom gnus-summary-show-article-charset-alist |
| 1231 | nil |
| 1232 | "Alist of number and charset. |
| 1233 | The article will be shown with the charset corresponding to the |
| 1234 | numbered argument. |
| 1235 | For example: ((1 . cn-gb-2312) (2 . big5))." |
| 1236 | :version "21.1" |
| 1237 | :type '(repeat (cons (number :tag "Argument" 1) |
| 1238 | (symbol :tag "Charset"))) |
| 1239 | :group 'gnus-charset) |
| 1240 | |
| 1241 | (defcustom gnus-preserve-marks t |
| 1242 | "Whether marks are preserved when moving, copying and respooling messages." |
| 1243 | :version "21.1" |
| 1244 | :type 'boolean |
| 1245 | :group 'gnus-summary-marks) |
| 1246 | |
| 1247 | (defcustom gnus-alter-articles-to-read-function nil |
| 1248 | "Function to be called to alter the list of articles to be selected." |
| 1249 | :type '(choice (const nil) function) |
| 1250 | :group 'gnus-summary) |
| 1251 | |
| 1252 | (defcustom gnus-orphan-score nil |
| 1253 | "*All orphans get this score added. Set in the score file." |
| 1254 | :group 'gnus-score-default |
| 1255 | :type '(choice (const nil) |
| 1256 | integer)) |
| 1257 | |
| 1258 | (defcustom gnus-summary-save-parts-default-mime "image/.*" |
| 1259 | "*A regexp to match MIME parts when saving multiple parts of a |
| 1260 | message with `gnus-summary-save-parts' (\\<gnus-summary-mode-map>\\[gnus-summary-save-parts]). |
| 1261 | This regexp will be used by default when prompting the user for which |
| 1262 | type of files to save." |
| 1263 | :group 'gnus-summary |
| 1264 | :type 'regexp) |
| 1265 | |
| 1266 | (defcustom gnus-read-all-available-headers nil |
| 1267 | "Whether Gnus should parse all headers made available to it. |
| 1268 | This is mostly relevant for slow back ends where the user may |
| 1269 | wish to widen the summary buffer to include all headers |
| 1270 | that were fetched." |
| 1271 | :version "22.1" |
| 1272 | :group 'gnus-summary |
| 1273 | :type '(choice boolean regexp)) |
| 1274 | |
| 1275 | (defcustom gnus-summary-pipe-output-default-command nil |
| 1276 | "Command (and optional arguments) used to pipe article to subprocess. |
| 1277 | This will be used as the default command if it is non-nil. The value |
| 1278 | will be updated if you modify it when executing the command |
| 1279 | `gnus-summary-pipe-output' or the function `gnus-summary-save-in-pipe'." |
| 1280 | :version "23.1" ;; No Gnus |
| 1281 | :group 'gnus-summary |
| 1282 | :type '(radio (const :tag "None" nil) (string :tag "Command"))) |
| 1283 | |
| 1284 | (defcustom gnus-summary-muttprint-program "muttprint" |
| 1285 | "Command (and optional arguments) used to run Muttprint. |
| 1286 | The value will be updated if you modify it when executing the command |
| 1287 | `gnus-summary-muttprint'." |
| 1288 | :version "22.1" |
| 1289 | :group 'gnus-summary |
| 1290 | :type 'string) |
| 1291 | |
| 1292 | (defcustom gnus-article-loose-mime t |
| 1293 | "If non-nil, don't require MIME-Version header. |
| 1294 | Some brain-damaged MUA/MTA, e.g. Lotus Domino 5.0.6 clients, does not |
| 1295 | supply the MIME-Version header or deliberately strip it from the mail. |
| 1296 | If non-nil (the default), Gnus will treat some articles as MIME |
| 1297 | even if the MIME-Version header is missing." |
| 1298 | :version "22.1" |
| 1299 | :type 'boolean |
| 1300 | :group 'gnus-article-mime) |
| 1301 | |
| 1302 | (defcustom gnus-article-emulate-mime t |
| 1303 | "If non-nil, use MIME emulation for uuencode and the like. |
| 1304 | This means that Gnus will search message bodies for text that look |
| 1305 | like uuencoded bits, yEncoded bits, and so on, and present that using |
| 1306 | the normal Gnus MIME machinery." |
| 1307 | :version "22.1" |
| 1308 | :type 'boolean |
| 1309 | :group 'gnus-article-mime) |
| 1310 | |
| 1311 | ;;; Internal variables |
| 1312 | |
| 1313 | (defvar gnus-summary-display-cache nil) |
| 1314 | (defvar gnus-article-mime-handles nil) |
| 1315 | (defvar gnus-article-decoded-p nil) |
| 1316 | (defvar gnus-article-charset nil) |
| 1317 | (defvar gnus-article-ignored-charsets nil) |
| 1318 | (defvar gnus-scores-exclude-files nil) |
| 1319 | (defvar gnus-page-broken nil) |
| 1320 | |
| 1321 | (defvar gnus-original-article nil) |
| 1322 | (defvar gnus-article-internal-prepare-hook nil) |
| 1323 | (defvar gnus-newsgroup-process-stack nil) |
| 1324 | |
| 1325 | (defvar gnus-thread-indent-array nil) |
| 1326 | (defvar gnus-thread-indent-array-level gnus-thread-indent-level) |
| 1327 | (defvar gnus-sort-gathered-threads-function 'gnus-thread-sort-by-number |
| 1328 | "Function called to sort the articles within a thread after it has been gathered together.") |
| 1329 | |
| 1330 | (defvar gnus-summary-save-parts-type-history nil) |
| 1331 | (defvar gnus-summary-save-parts-last-directory mm-default-directory) |
| 1332 | |
| 1333 | ;; Avoid highlighting in kill files. |
| 1334 | (defvar gnus-summary-inhibit-highlight nil) |
| 1335 | (defvar gnus-newsgroup-selected-overlay nil) |
| 1336 | (defvar gnus-inhibit-limiting nil) |
| 1337 | (defvar gnus-newsgroup-adaptive-score-file nil) |
| 1338 | (defvar gnus-current-score-file nil) |
| 1339 | (defvar gnus-current-move-group nil) |
| 1340 | (defvar gnus-current-copy-group nil) |
| 1341 | (defvar gnus-current-crosspost-group nil) |
| 1342 | (defvar gnus-newsgroup-display nil) |
| 1343 | |
| 1344 | (defvar gnus-newsgroup-dependencies nil) |
| 1345 | (defvar gnus-newsgroup-adaptive nil) |
| 1346 | (defvar gnus-summary-display-article-function nil) |
| 1347 | (defvar gnus-summary-highlight-line-function nil |
| 1348 | "Function called after highlighting a summary line.") |
| 1349 | |
| 1350 | (defvar gnus-summary-line-format-alist |
| 1351 | `((?N ,(macroexpand '(mail-header-number gnus-tmp-header)) ?d) |
| 1352 | (?S ,(macroexpand '(mail-header-subject gnus-tmp-header)) ?s) |
| 1353 | (?s gnus-tmp-subject-or-nil ?s) |
| 1354 | (?n gnus-tmp-name ?s) |
| 1355 | (?A (car (cdr (funcall gnus-extract-address-components gnus-tmp-from))) |
| 1356 | ?s) |
| 1357 | (?a (or (car (funcall gnus-extract-address-components gnus-tmp-from)) |
| 1358 | gnus-tmp-from) ?s) |
| 1359 | (?F gnus-tmp-from ?s) |
| 1360 | (?x ,(macroexpand '(mail-header-xref gnus-tmp-header)) ?s) |
| 1361 | (?D ,(macroexpand '(mail-header-date gnus-tmp-header)) ?s) |
| 1362 | (?d (gnus-dd-mmm (mail-header-date gnus-tmp-header)) ?s) |
| 1363 | (?o (gnus-date-iso8601 (mail-header-date gnus-tmp-header)) ?s) |
| 1364 | (?M ,(macroexpand '(mail-header-id gnus-tmp-header)) ?s) |
| 1365 | (?r ,(macroexpand '(mail-header-references gnus-tmp-header)) ?s) |
| 1366 | (?c (or (mail-header-chars gnus-tmp-header) 0) ?d) |
| 1367 | (?k (gnus-summary-line-message-size gnus-tmp-header) ?s) |
| 1368 | (?L gnus-tmp-lines ?s) |
| 1369 | (?Z (or (nnir-article-rsv (mail-header-number gnus-tmp-header)) |
| 1370 | 0) ?d) |
| 1371 | (?G (or (nnir-article-group (mail-header-number gnus-tmp-header)) |
| 1372 | "") ?s) |
| 1373 | (?g (or (gnus-group-short-name |
| 1374 | (nnir-article-group (mail-header-number gnus-tmp-header))) |
| 1375 | "") ?s) |
| 1376 | (?O gnus-tmp-downloaded ?c) |
| 1377 | (?I gnus-tmp-indentation ?s) |
| 1378 | (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s) |
| 1379 | (?R gnus-tmp-replied ?c) |
| 1380 | (?\[ gnus-tmp-opening-bracket ?c) |
| 1381 | (?\] gnus-tmp-closing-bracket ?c) |
| 1382 | (?\> (make-string gnus-tmp-level ? ) ?s) |
| 1383 | (?\< (make-string (max 0 (- 20 gnus-tmp-level)) ? ) ?s) |
| 1384 | (?i gnus-tmp-score ?d) |
| 1385 | (?z gnus-tmp-score-char ?c) |
| 1386 | (?V (gnus-thread-total-score (and (boundp 'thread) (car thread))) ?d) |
| 1387 | (?U gnus-tmp-unread ?c) |
| 1388 | (?f (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from) |
| 1389 | ?s) |
| 1390 | (?t (gnus-summary-number-of-articles-in-thread |
| 1391 | (and (boundp 'thread) (car thread)) gnus-tmp-level) |
| 1392 | ?d) |
| 1393 | (?e (gnus-summary-number-of-articles-in-thread |
| 1394 | (and (boundp 'thread) (car thread)) gnus-tmp-level t) |
| 1395 | ?c) |
| 1396 | (?u gnus-tmp-user-defined ?s) |
| 1397 | (?P (gnus-pick-line-number) ?d) |
| 1398 | (?B gnus-tmp-thread-tree-header-string ?s) |
| 1399 | (user-date (gnus-user-date |
| 1400 | ,(macroexpand '(mail-header-date gnus-tmp-header))) ?s)) |
| 1401 | "An alist of format specifications that can appear in summary lines. |
| 1402 | These are paired with what variables they correspond with, along with |
| 1403 | the type of the variable (string, integer, character, etc).") |
| 1404 | |
| 1405 | (defvar gnus-summary-dummy-line-format-alist |
| 1406 | `((?S gnus-tmp-subject ?s) |
| 1407 | (?N gnus-tmp-number ?d) |
| 1408 | (?u gnus-tmp-user-defined ?s))) |
| 1409 | |
| 1410 | (defvar gnus-summary-mode-line-format-alist |
| 1411 | `((?G gnus-tmp-group-name ?s) |
| 1412 | (?g (gnus-short-group-name gnus-tmp-group-name) ?s) |
| 1413 | (?p (gnus-group-real-name gnus-tmp-group-name) ?s) |
| 1414 | (?A gnus-tmp-article-number ?d) |
| 1415 | (?Z gnus-tmp-unread-and-unselected ?s) |
| 1416 | (?V gnus-version ?s) |
| 1417 | (?U gnus-tmp-unread-and-unticked ?d) |
| 1418 | (?S gnus-tmp-subject ?s) |
| 1419 | (?e gnus-tmp-unselected ?d) |
| 1420 | (?u gnus-tmp-user-defined ?s) |
| 1421 | (?d (length gnus-newsgroup-dormant) ?d) |
| 1422 | (?t (length gnus-newsgroup-marked) ?d) |
| 1423 | (?h (length gnus-newsgroup-spam-marked) ?d) |
| 1424 | (?r (length gnus-newsgroup-reads) ?d) |
| 1425 | (?z (gnus-summary-article-score gnus-tmp-article-number) ?d) |
| 1426 | (?E gnus-newsgroup-expunged-tally ?d) |
| 1427 | (?s (gnus-current-score-file-nondirectory) ?s))) |
| 1428 | |
| 1429 | ;; This is here rather than in gnus-art for compilation reasons. |
| 1430 | (defvar gnus-article-mode-line-format-alist |
| 1431 | (nconc '((?w (gnus-article-wash-status) ?s) |
| 1432 | (?m (gnus-article-mime-part-status) ?s)) |
| 1433 | gnus-summary-mode-line-format-alist)) |
| 1434 | |
| 1435 | (defvar gnus-last-search-regexp nil |
| 1436 | "Default regexp for article search command.") |
| 1437 | |
| 1438 | (defvar gnus-last-shell-command nil |
| 1439 | "Default shell command on article.") |
| 1440 | |
| 1441 | (defvar gnus-newsgroup-agentized nil |
| 1442 | "Locally bound in each summary buffer to indicate whether the server has been agentized.") |
| 1443 | (defvar gnus-newsgroup-begin nil) |
| 1444 | (defvar gnus-newsgroup-end nil) |
| 1445 | (defvar gnus-newsgroup-last-rmail nil) |
| 1446 | (defvar gnus-newsgroup-last-mail nil) |
| 1447 | (defvar gnus-newsgroup-last-folder nil) |
| 1448 | (defvar gnus-newsgroup-last-file nil) |
| 1449 | (defvar gnus-newsgroup-last-directory nil) |
| 1450 | (defvar gnus-newsgroup-auto-expire nil) |
| 1451 | (defvar gnus-newsgroup-active nil) |
| 1452 | (defvar gnus-newsgroup-highest nil) |
| 1453 | |
| 1454 | (defvar gnus-newsgroup-data nil) |
| 1455 | (defvar gnus-newsgroup-data-reverse nil) |
| 1456 | (defvar gnus-newsgroup-limit nil) |
| 1457 | (defvar gnus-newsgroup-limits nil) |
| 1458 | (defvar gnus-summary-use-undownloaded-faces nil) |
| 1459 | |
| 1460 | (defvar gnus-newsgroup-unreads nil |
| 1461 | "Sorted list of unread articles in the current newsgroup.") |
| 1462 | |
| 1463 | (defvar gnus-newsgroup-unselected nil |
| 1464 | "Sorted list of unselected unread articles in the current newsgroup.") |
| 1465 | |
| 1466 | (defvar gnus-newsgroup-reads nil |
| 1467 | "Alist of read articles and article marks in the current newsgroup.") |
| 1468 | |
| 1469 | (defvar gnus-newsgroup-expunged-tally nil) |
| 1470 | |
| 1471 | (defvar gnus-newsgroup-marked nil |
| 1472 | "Sorted list of ticked articles in the current newsgroup (a subset of unread art).") |
| 1473 | |
| 1474 | (defvar gnus-newsgroup-spam-marked nil |
| 1475 | "List of ranges of articles that have been marked as spam.") |
| 1476 | |
| 1477 | (defvar gnus-newsgroup-killed nil |
| 1478 | "List of ranges of articles that have been through the scoring process.") |
| 1479 | |
| 1480 | (defvar gnus-newsgroup-cached nil |
| 1481 | "Sorted list of articles that come from the article cache.") |
| 1482 | |
| 1483 | (defvar gnus-newsgroup-saved nil |
| 1484 | "List of articles that have been saved.") |
| 1485 | |
| 1486 | (defvar gnus-newsgroup-kill-headers nil) |
| 1487 | |
| 1488 | (defvar gnus-newsgroup-replied nil |
| 1489 | "List of articles that have been replied to in the current newsgroup.") |
| 1490 | |
| 1491 | (defvar gnus-newsgroup-forwarded nil |
| 1492 | "List of articles that have been forwarded in the current newsgroup.") |
| 1493 | |
| 1494 | (defvar gnus-newsgroup-expirable nil |
| 1495 | "Sorted list of articles in the current newsgroup that can be expired.") |
| 1496 | |
| 1497 | (defvar gnus-newsgroup-processable nil |
| 1498 | "List of articles in the current newsgroup that can be processed.") |
| 1499 | |
| 1500 | (defvar gnus-newsgroup-downloadable nil |
| 1501 | "Sorted list of articles in the current newsgroup that can be processed.") |
| 1502 | |
| 1503 | (defvar gnus-newsgroup-unfetched nil |
| 1504 | "Sorted list of articles in the current newsgroup whose headers have |
| 1505 | not been fetched into the agent. |
| 1506 | |
| 1507 | This list will always be a subset of gnus-newsgroup-undownloaded.") |
| 1508 | |
| 1509 | (defvar gnus-newsgroup-undownloaded nil |
| 1510 | "List of articles in the current newsgroup that haven't been downloaded.") |
| 1511 | |
| 1512 | (defvar gnus-newsgroup-unsendable nil |
| 1513 | "List of articles in the current newsgroup that won't be sent.") |
| 1514 | |
| 1515 | (defvar gnus-newsgroup-bookmarks nil |
| 1516 | "List of articles in the current newsgroup that have bookmarks.") |
| 1517 | |
| 1518 | (defvar gnus-newsgroup-dormant nil |
| 1519 | "Sorted list of dormant articles in the current newsgroup.") |
| 1520 | |
| 1521 | (defvar gnus-newsgroup-unseen nil |
| 1522 | "List of unseen articles in the current newsgroup.") |
| 1523 | |
| 1524 | (defvar gnus-newsgroup-seen nil |
| 1525 | "Range of seen articles in the current newsgroup.") |
| 1526 | |
| 1527 | (defvar gnus-newsgroup-unexist nil |
| 1528 | "Range of unexisting articles in the current newsgroup.") |
| 1529 | |
| 1530 | (defvar gnus-newsgroup-articles nil |
| 1531 | "List of articles in the current newsgroup.") |
| 1532 | |
| 1533 | (defvar gnus-newsgroup-scored nil |
| 1534 | "List of scored articles in the current newsgroup.") |
| 1535 | |
| 1536 | (defvar gnus-newsgroup-headers nil |
| 1537 | "List of article headers in the current newsgroup.") |
| 1538 | |
| 1539 | (defvar gnus-newsgroup-threads nil) |
| 1540 | |
| 1541 | (defvar gnus-newsgroup-prepared nil |
| 1542 | "Whether the current group has been prepared properly.") |
| 1543 | |
| 1544 | (defvar gnus-newsgroup-ancient nil |
| 1545 | "List of `gnus-fetch-old-headers' articles in the current newsgroup.") |
| 1546 | |
| 1547 | (defvar gnus-newsgroup-sparse nil) |
| 1548 | |
| 1549 | (defvar gnus-current-article nil) |
| 1550 | (defvar gnus-article-current nil) |
| 1551 | (defvar gnus-current-headers nil) |
| 1552 | (defvar gnus-have-all-headers nil) |
| 1553 | (defvar gnus-last-article nil) |
| 1554 | (defvar gnus-newsgroup-history nil) |
| 1555 | (defvar gnus-newsgroup-charset nil) |
| 1556 | (defvar gnus-newsgroup-ephemeral-charset nil) |
| 1557 | (defvar gnus-newsgroup-ephemeral-ignored-charsets nil) |
| 1558 | |
| 1559 | (defvar gnus-article-before-search nil) |
| 1560 | |
| 1561 | (defvar gnus-summary-local-variables |
| 1562 | '(gnus-newsgroup-name |
| 1563 | |
| 1564 | ;; Marks lists |
| 1565 | gnus-newsgroup-unreads |
| 1566 | gnus-newsgroup-unselected |
| 1567 | gnus-newsgroup-marked |
| 1568 | gnus-newsgroup-spam-marked |
| 1569 | gnus-newsgroup-reads |
| 1570 | gnus-newsgroup-saved |
| 1571 | gnus-newsgroup-replied |
| 1572 | gnus-newsgroup-forwarded |
| 1573 | gnus-newsgroup-expirable |
| 1574 | gnus-newsgroup-killed |
| 1575 | gnus-newsgroup-unseen |
| 1576 | gnus-newsgroup-seen |
| 1577 | gnus-newsgroup-unexist |
| 1578 | gnus-newsgroup-cached |
| 1579 | gnus-newsgroup-downloadable |
| 1580 | gnus-newsgroup-undownloaded |
| 1581 | gnus-newsgroup-unsendable |
| 1582 | |
| 1583 | gnus-newsgroup-begin gnus-newsgroup-end |
| 1584 | gnus-newsgroup-last-rmail gnus-newsgroup-last-mail |
| 1585 | gnus-newsgroup-last-folder gnus-newsgroup-last-file |
| 1586 | gnus-newsgroup-last-directory |
| 1587 | gnus-newsgroup-auto-expire |
| 1588 | gnus-newsgroup-processable |
| 1589 | gnus-newsgroup-unfetched |
| 1590 | gnus-newsgroup-articles |
| 1591 | gnus-newsgroup-bookmarks gnus-newsgroup-dormant |
| 1592 | gnus-newsgroup-headers gnus-newsgroup-threads |
| 1593 | gnus-newsgroup-prepared gnus-summary-highlight-line-function |
| 1594 | gnus-current-article gnus-current-headers gnus-have-all-headers |
| 1595 | gnus-last-article gnus-article-internal-prepare-hook |
| 1596 | (gnus-summary-article-delete-hook . global) |
| 1597 | (gnus-summary-article-move-hook . global) |
| 1598 | gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay |
| 1599 | gnus-newsgroup-scored gnus-newsgroup-kill-headers |
| 1600 | gnus-thread-expunge-below |
| 1601 | gnus-score-alist gnus-current-score-file |
| 1602 | (gnus-summary-expunge-below . global) |
| 1603 | (gnus-summary-mark-below . global) |
| 1604 | (gnus-orphan-score . global) |
| 1605 | gnus-newsgroup-active gnus-scores-exclude-files |
| 1606 | gnus-newsgroup-highest |
| 1607 | gnus-newsgroup-history gnus-newsgroup-ancient |
| 1608 | gnus-newsgroup-sparse gnus-newsgroup-process-stack |
| 1609 | (gnus-newsgroup-adaptive . gnus-use-adaptive-scoring) |
| 1610 | gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1) |
| 1611 | (gnus-newsgroup-expunged-tally . 0) |
| 1612 | gnus-cache-removable-articles |
| 1613 | gnus-newsgroup-data gnus-newsgroup-data-reverse |
| 1614 | gnus-newsgroup-limit gnus-newsgroup-limits |
| 1615 | gnus-newsgroup-charset gnus-newsgroup-display |
| 1616 | gnus-summary-use-undownloaded-faces) |
| 1617 | "Variables that are buffer-local to the summary buffers.") |
| 1618 | |
| 1619 | (defvar gnus-newsgroup-variables nil |
| 1620 | "A list of variables that have separate values in different newsgroups. |
| 1621 | A list of newsgroup (summary buffer) local variables, or cons of |
| 1622 | variables and their default expressions to be evalled (when the default |
| 1623 | values are not nil), that should be made global while the summary buffer |
| 1624 | is active. |
| 1625 | |
| 1626 | Note: The default expressions will be evaluated (using function `eval') |
| 1627 | before assignment to the local variable rather than just assigned to it. |
| 1628 | If the default expression is the symbol `global', that symbol will not |
| 1629 | be evaluated but the global value of the local variable will be used |
| 1630 | instead. |
| 1631 | |
| 1632 | These variables can be used to set variables in the group parameters |
| 1633 | while still allowing them to affect operations done in other buffers. |
| 1634 | For example: |
| 1635 | |
| 1636 | \(setq gnus-newsgroup-variables |
| 1637 | '(message-use-followup-to |
| 1638 | (gnus-visible-headers . |
| 1639 | \"^From:\\\\|^Newsgroups:\\\\|^Subject:\\\\|^Date:\\\\|^To:\"))) |
| 1640 | ") |
| 1641 | |
| 1642 | (eval-when-compile |
| 1643 | ;; Bind features so that require will believe that gnus-sum has |
| 1644 | ;; already been loaded (avoids infinite recursion) |
| 1645 | (let ((features (cons 'gnus-sum features))) |
| 1646 | (require 'gnus-art))) |
| 1647 | |
| 1648 | ;; MIME stuff. |
| 1649 | |
| 1650 | (defvar gnus-decode-encoded-word-methods |
| 1651 | '(mail-decode-encoded-word-string) |
| 1652 | "List of methods used to decode encoded words. |
| 1653 | |
| 1654 | This variable is a list of FUNCTION or (REGEXP . FUNCTION). If item |
| 1655 | is FUNCTION, FUNCTION will be apply to all newsgroups. If item is a |
| 1656 | \(REGEXP . FUNCTION), FUNCTION will be applied only to the newsgroups |
| 1657 | whose names match REGEXP. |
| 1658 | |
| 1659 | For example: |
| 1660 | \((\"chinese\" . gnus-decode-encoded-word-string-by-guess) |
| 1661 | mail-decode-encoded-word-string |
| 1662 | (\"chinese\" . rfc1843-decode-string))") |
| 1663 | |
| 1664 | (defvar gnus-decode-encoded-word-methods-cache nil) |
| 1665 | |
| 1666 | (defun gnus-multi-decode-encoded-word-string (string) |
| 1667 | "Apply the functions from `gnus-encoded-word-methods' that match." |
| 1668 | (unless (and gnus-decode-encoded-word-methods-cache |
| 1669 | (eq gnus-newsgroup-name |
| 1670 | (car gnus-decode-encoded-word-methods-cache))) |
| 1671 | (setq gnus-decode-encoded-word-methods-cache (list gnus-newsgroup-name)) |
| 1672 | (dolist (method gnus-decode-encoded-word-methods) |
| 1673 | (if (symbolp method) |
| 1674 | (nconc gnus-decode-encoded-word-methods-cache (list method)) |
| 1675 | (if (and gnus-newsgroup-name |
| 1676 | (string-match (car method) gnus-newsgroup-name)) |
| 1677 | (nconc gnus-decode-encoded-word-methods-cache |
| 1678 | (list (cdr method))))))) |
| 1679 | (dolist (method (cdr gnus-decode-encoded-word-methods-cache) string) |
| 1680 | (setq string (funcall method string)))) |
| 1681 | |
| 1682 | ;; Subject simplification. |
| 1683 | |
| 1684 | (defun gnus-simplify-whitespace (str) |
| 1685 | "Remove excessive whitespace from STR." |
| 1686 | ;; Multiple spaces. |
| 1687 | (while (string-match "[ \t][ \t]+" str) |
| 1688 | (setq str (concat (substring str 0 (match-beginning 0)) |
| 1689 | " " |
| 1690 | (substring str (match-end 0))))) |
| 1691 | ;; Leading spaces. |
| 1692 | (when (string-match "^[ \t]+" str) |
| 1693 | (setq str (substring str (match-end 0)))) |
| 1694 | ;; Trailing spaces. |
| 1695 | (when (string-match "[ \t]+$" str) |
| 1696 | (setq str (substring str 0 (match-beginning 0)))) |
| 1697 | str) |
| 1698 | |
| 1699 | (defun gnus-simplify-all-whitespace (str) |
| 1700 | "Remove all whitespace from STR." |
| 1701 | (while (string-match "[ \t\n]+" str) |
| 1702 | (setq str (replace-match "" nil nil str))) |
| 1703 | str) |
| 1704 | |
| 1705 | (defsubst gnus-simplify-subject-re (subject) |
| 1706 | "Remove \"Re:\" from subject lines." |
| 1707 | (if (string-match message-subject-re-regexp subject) |
| 1708 | (substring subject (match-end 0)) |
| 1709 | subject)) |
| 1710 | |
| 1711 | (defun gnus-simplify-subject (subject &optional re-only) |
| 1712 | "Remove `Re:' and words in parentheses. |
| 1713 | If RE-ONLY is non-nil, strip leading `Re:'s only." |
| 1714 | (let ((case-fold-search t)) ;Ignore case. |
| 1715 | ;; Remove `Re:', `Re^N:', `Re(n)', and `Re[n]:'. |
| 1716 | (when (string-match "\\`\\(re\\([[(^][0-9]+[])]?\\)?:[ \t]*\\)+" subject) |
| 1717 | (setq subject (substring subject (match-end 0)))) |
| 1718 | ;; Remove uninteresting prefixes. |
| 1719 | (when (and (not re-only) |
| 1720 | gnus-simplify-ignored-prefixes |
| 1721 | (string-match gnus-simplify-ignored-prefixes subject)) |
| 1722 | (setq subject (substring subject (match-end 0)))) |
| 1723 | ;; Remove words in parentheses from end. |
| 1724 | (unless re-only |
| 1725 | (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject) |
| 1726 | (setq subject (substring subject 0 (match-beginning 0))))) |
| 1727 | ;; Return subject string. |
| 1728 | subject)) |
| 1729 | |
| 1730 | ;; Remove any leading "re:"s, any trailing paren phrases, and simplify |
| 1731 | ;; all whitespace. |
| 1732 | (defsubst gnus-simplify-buffer-fuzzy-step (regexp &optional newtext) |
| 1733 | (goto-char (point-min)) |
| 1734 | (while (re-search-forward regexp nil t) |
| 1735 | (replace-match (or newtext "")))) |
| 1736 | |
| 1737 | (defun gnus-simplify-buffer-fuzzy (regexp) |
| 1738 | "Simplify string in the buffer fuzzily. |
| 1739 | The string in the accessible portion of the current buffer is simplified. |
| 1740 | It is assumed to be a single-line subject. |
| 1741 | Whitespace is generally cleaned up, and miscellaneous leading/trailing |
| 1742 | matter is removed. Additional things can be deleted by setting |
| 1743 | `gnus-simplify-subject-fuzzy-regexp'." |
| 1744 | (let ((case-fold-search t) |
| 1745 | (modified-tick)) |
| 1746 | (gnus-simplify-buffer-fuzzy-step "\t" " ") |
| 1747 | |
| 1748 | (while (not (eq modified-tick (buffer-modified-tick))) |
| 1749 | (setq modified-tick (buffer-modified-tick)) |
| 1750 | (cond |
| 1751 | ((listp regexp) |
| 1752 | (mapc 'gnus-simplify-buffer-fuzzy-step regexp)) |
| 1753 | (regexp |
| 1754 | (gnus-simplify-buffer-fuzzy-step regexp))) |
| 1755 | (gnus-simplify-buffer-fuzzy-step "^ *\\[[-+?*!][-+?*!]\\] *") |
| 1756 | (gnus-simplify-buffer-fuzzy-step |
| 1757 | "^ *\\(re\\|fw\\|fwd\\)[[{(^0-9]*[])}]?[:;] *") |
| 1758 | (gnus-simplify-buffer-fuzzy-step "^[[].*:\\( .*\\)[]]$" "\\1")) |
| 1759 | |
| 1760 | (gnus-simplify-buffer-fuzzy-step " *[[{(][^()\n]*[]})] *$") |
| 1761 | (gnus-simplify-buffer-fuzzy-step " +" " ") |
| 1762 | (gnus-simplify-buffer-fuzzy-step " $") |
| 1763 | (gnus-simplify-buffer-fuzzy-step "^ +"))) |
| 1764 | |
| 1765 | (defun gnus-simplify-subject-fuzzy (subject) |
| 1766 | "Simplify a subject string fuzzily. |
| 1767 | See `gnus-simplify-buffer-fuzzy' for details." |
| 1768 | (save-excursion |
| 1769 | (let ((regexp gnus-simplify-subject-fuzzy-regexp)) |
| 1770 | (gnus-set-work-buffer) |
| 1771 | (let ((case-fold-search t)) |
| 1772 | ;; Remove uninteresting prefixes. |
| 1773 | (when (and gnus-simplify-ignored-prefixes |
| 1774 | (string-match gnus-simplify-ignored-prefixes subject)) |
| 1775 | (setq subject (substring subject (match-end 0)))) |
| 1776 | (insert subject) |
| 1777 | (inline (gnus-simplify-buffer-fuzzy regexp)) |
| 1778 | (buffer-string))))) |
| 1779 | |
| 1780 | (defsubst gnus-simplify-subject-fully (subject) |
| 1781 | "Simplify a subject string according to `gnus-summary-gather-subject-limit'." |
| 1782 | (cond |
| 1783 | (gnus-simplify-subject-functions |
| 1784 | (gnus-map-function gnus-simplify-subject-functions subject)) |
| 1785 | ((null gnus-summary-gather-subject-limit) |
| 1786 | (gnus-simplify-subject-re subject)) |
| 1787 | ((eq gnus-summary-gather-subject-limit 'fuzzy) |
| 1788 | (gnus-simplify-subject-fuzzy subject)) |
| 1789 | ((numberp gnus-summary-gather-subject-limit) |
| 1790 | (truncate-string-to-width (gnus-simplify-subject-re subject) |
| 1791 | gnus-summary-gather-subject-limit)) |
| 1792 | (t |
| 1793 | subject))) |
| 1794 | |
| 1795 | (defsubst gnus-subject-equal (s1 s2 &optional simple-first) |
| 1796 | "Check whether two subjects are equal. |
| 1797 | If optional argument SIMPLE-FIRST is t, first argument is already |
| 1798 | simplified." |
| 1799 | (cond |
| 1800 | ((null simple-first) |
| 1801 | (equal (gnus-simplify-subject-fully s1) |
| 1802 | (gnus-simplify-subject-fully s2))) |
| 1803 | (t |
| 1804 | (equal s1 |
| 1805 | (gnus-simplify-subject-fully s2))))) |
| 1806 | |
| 1807 | (defun gnus-summary-bubble-group () |
| 1808 | "Increase the score of the current group. |
| 1809 | This is a handy function to add to `gnus-summary-exit-hook' to |
| 1810 | increase the score of each group you read." |
| 1811 | (gnus-group-add-score gnus-newsgroup-name)) |
| 1812 | |
| 1813 | \f |
| 1814 | ;;; |
| 1815 | ;;; Gnus summary mode |
| 1816 | ;;; |
| 1817 | |
| 1818 | (put 'gnus-summary-mode 'mode-class 'special) |
| 1819 | |
| 1820 | (defvar gnus-article-commands-menu) |
| 1821 | |
| 1822 | ;; Non-orthogonal keys |
| 1823 | |
| 1824 | (gnus-define-keys gnus-summary-mode-map |
| 1825 | " " gnus-summary-next-page |
| 1826 | [?\S-\ ] gnus-summary-prev-page |
| 1827 | "\177" gnus-summary-prev-page |
| 1828 | [delete] gnus-summary-prev-page |
| 1829 | [backspace] gnus-summary-prev-page |
| 1830 | "\r" gnus-summary-scroll-up |
| 1831 | "\M-\r" gnus-summary-scroll-down |
| 1832 | "n" gnus-summary-next-unread-article |
| 1833 | "p" gnus-summary-prev-unread-article |
| 1834 | "N" gnus-summary-next-article |
| 1835 | "P" gnus-summary-prev-article |
| 1836 | "\M-\C-n" gnus-summary-next-same-subject |
| 1837 | "\M-\C-p" gnus-summary-prev-same-subject |
| 1838 | "\M-n" gnus-summary-next-unread-subject |
| 1839 | "\M-p" gnus-summary-prev-unread-subject |
| 1840 | "." gnus-summary-first-unread-article |
| 1841 | "," gnus-summary-best-unread-article |
| 1842 | "\M-s" gnus-summary-search-article-forward |
| 1843 | "\M-r" gnus-summary-search-article-backward |
| 1844 | "\M-S" gnus-summary-repeat-search-article-forward |
| 1845 | "\M-R" gnus-summary-repeat-search-article-backward |
| 1846 | "<" gnus-summary-beginning-of-article |
| 1847 | ">" gnus-summary-end-of-article |
| 1848 | "j" gnus-summary-goto-article |
| 1849 | "^" gnus-summary-refer-parent-article |
| 1850 | "\M-^" gnus-summary-refer-article |
| 1851 | "u" gnus-summary-tick-article-forward |
| 1852 | "!" gnus-summary-tick-article-forward |
| 1853 | "U" gnus-summary-tick-article-backward |
| 1854 | "d" gnus-summary-mark-as-read-forward |
| 1855 | "D" gnus-summary-mark-as-read-backward |
| 1856 | "E" gnus-summary-mark-as-expirable |
| 1857 | "\M-u" gnus-summary-clear-mark-forward |
| 1858 | "\M-U" gnus-summary-clear-mark-backward |
| 1859 | "k" gnus-summary-kill-same-subject-and-select |
| 1860 | "\C-k" gnus-summary-kill-same-subject |
| 1861 | "\M-\C-k" gnus-summary-kill-thread |
| 1862 | "\M-\C-l" gnus-summary-lower-thread |
| 1863 | "e" gnus-summary-edit-article |
| 1864 | "#" gnus-summary-mark-as-processable |
| 1865 | "\M-#" gnus-summary-unmark-as-processable |
| 1866 | "\M-\C-t" gnus-summary-toggle-threads |
| 1867 | "\M-\C-s" gnus-summary-show-thread |
| 1868 | "\M-\C-h" gnus-summary-hide-thread |
| 1869 | "\M-\C-f" gnus-summary-next-thread |
| 1870 | "\M-\C-b" gnus-summary-prev-thread |
| 1871 | [(meta down)] gnus-summary-next-thread |
| 1872 | [(meta up)] gnus-summary-prev-thread |
| 1873 | "\M-\C-u" gnus-summary-up-thread |
| 1874 | "\M-\C-d" gnus-summary-down-thread |
| 1875 | "&" gnus-summary-execute-command |
| 1876 | "c" gnus-summary-catchup-and-exit |
| 1877 | "\C-w" gnus-summary-mark-region-as-read |
| 1878 | "\C-t" gnus-summary-toggle-truncation |
| 1879 | "?" gnus-summary-mark-as-dormant |
| 1880 | "\C-c\M-\C-s" gnus-summary-limit-include-expunged |
| 1881 | "\C-c\C-s\C-n" gnus-summary-sort-by-number |
| 1882 | "\C-c\C-s\C-m\C-n" gnus-summary-sort-by-most-recent-number |
| 1883 | "\C-c\C-s\C-l" gnus-summary-sort-by-lines |
| 1884 | "\C-c\C-s\C-c" gnus-summary-sort-by-chars |
| 1885 | "\C-c\C-s\C-a" gnus-summary-sort-by-author |
| 1886 | "\C-c\C-s\C-t" gnus-summary-sort-by-recipient |
| 1887 | "\C-c\C-s\C-s" gnus-summary-sort-by-subject |
| 1888 | "\C-c\C-s\C-d" gnus-summary-sort-by-date |
| 1889 | "\C-c\C-s\C-m\C-d" gnus-summary-sort-by-most-recent-date |
| 1890 | "\C-c\C-s\C-i" gnus-summary-sort-by-score |
| 1891 | "\C-c\C-s\C-o" gnus-summary-sort-by-original |
| 1892 | "\C-c\C-s\C-r" gnus-summary-sort-by-random |
| 1893 | "=" gnus-summary-expand-window |
| 1894 | "\C-x\C-s" gnus-summary-reselect-current-group |
| 1895 | "\M-g" gnus-summary-rescan-group |
| 1896 | "\C-c\C-r" gnus-summary-caesar-message |
| 1897 | "f" gnus-summary-followup |
| 1898 | "F" gnus-summary-followup-with-original |
| 1899 | "C" gnus-summary-cancel-article |
| 1900 | "r" gnus-summary-reply |
| 1901 | "R" gnus-summary-reply-with-original |
| 1902 | "\C-c\C-f" gnus-summary-mail-forward |
| 1903 | "o" gnus-summary-save-article |
| 1904 | "\C-o" gnus-summary-save-article-mail |
| 1905 | "|" gnus-summary-pipe-output |
| 1906 | "\M-k" gnus-summary-edit-local-kill |
| 1907 | "\M-K" gnus-summary-edit-global-kill |
| 1908 | ;; "V" gnus-version |
| 1909 | "\C-c\C-d" gnus-summary-describe-group |
| 1910 | "q" gnus-summary-exit |
| 1911 | "Q" gnus-summary-exit-no-update |
| 1912 | "\C-c\C-i" gnus-info-find-node |
| 1913 | gnus-mouse-2 gnus-mouse-pick-article |
| 1914 | [follow-link] mouse-face |
| 1915 | "m" gnus-summary-mail-other-window |
| 1916 | "a" gnus-summary-post-news |
| 1917 | "x" gnus-summary-limit-to-unread |
| 1918 | "s" gnus-summary-isearch-article |
| 1919 | "\t" gnus-summary-widget-forward |
| 1920 | [backtab] gnus-summary-widget-backward |
| 1921 | "t" gnus-summary-toggle-header |
| 1922 | "g" gnus-summary-show-article |
| 1923 | "l" gnus-summary-goto-last-article |
| 1924 | "\C-c\C-v\C-v" gnus-uu-decode-uu-view |
| 1925 | "\C-d" gnus-summary-enter-digest-group |
| 1926 | "\M-\C-d" gnus-summary-read-document |
| 1927 | "\M-\C-e" gnus-summary-edit-parameters |
| 1928 | "\M-\C-a" gnus-summary-customize-parameters |
| 1929 | "\C-c\C-b" gnus-bug |
| 1930 | "*" gnus-cache-enter-article |
| 1931 | "\M-*" gnus-cache-remove-article |
| 1932 | "\M-&" gnus-summary-universal-argument |
| 1933 | "\C-l" gnus-recenter |
| 1934 | "I" gnus-summary-increase-score |
| 1935 | "L" gnus-summary-lower-score |
| 1936 | "\M-i" gnus-symbolic-argument |
| 1937 | "h" gnus-summary-select-article-buffer |
| 1938 | |
| 1939 | "b" gnus-article-view-part |
| 1940 | "\M-t" gnus-summary-toggle-display-buttonized |
| 1941 | |
| 1942 | "V" gnus-summary-score-map |
| 1943 | "X" gnus-uu-extract-map |
| 1944 | "S" gnus-summary-send-map) |
| 1945 | |
| 1946 | ;; Sort of orthogonal keymap |
| 1947 | (gnus-define-keys (gnus-summary-mark-map "M" gnus-summary-mode-map) |
| 1948 | "t" gnus-summary-tick-article-forward |
| 1949 | "!" gnus-summary-tick-article-forward |
| 1950 | "d" gnus-summary-mark-as-read-forward |
| 1951 | "r" gnus-summary-mark-as-read-forward |
| 1952 | "c" gnus-summary-clear-mark-forward |
| 1953 | " " gnus-summary-clear-mark-forward |
| 1954 | "e" gnus-summary-mark-as-expirable |
| 1955 | "x" gnus-summary-mark-as-expirable |
| 1956 | "?" gnus-summary-mark-as-dormant |
| 1957 | "b" gnus-summary-set-bookmark |
| 1958 | "B" gnus-summary-remove-bookmark |
| 1959 | "#" gnus-summary-mark-as-processable |
| 1960 | "\M-#" gnus-summary-unmark-as-processable |
| 1961 | "S" gnus-summary-limit-include-expunged |
| 1962 | "C" gnus-summary-catchup |
| 1963 | "H" gnus-summary-catchup-to-here |
| 1964 | "h" gnus-summary-catchup-from-here |
| 1965 | "\C-c" gnus-summary-catchup-all |
| 1966 | "k" gnus-summary-kill-same-subject-and-select |
| 1967 | "K" gnus-summary-kill-same-subject |
| 1968 | "P" gnus-uu-mark-map) |
| 1969 | |
| 1970 | (gnus-define-keys (gnus-summary-mscore-map "V" gnus-summary-mark-map) |
| 1971 | "c" gnus-summary-clear-above |
| 1972 | "u" gnus-summary-tick-above |
| 1973 | "m" gnus-summary-mark-above |
| 1974 | "k" gnus-summary-kill-below) |
| 1975 | |
| 1976 | (gnus-define-keys (gnus-summary-limit-map "/" gnus-summary-mode-map) |
| 1977 | "/" gnus-summary-limit-to-subject |
| 1978 | "n" gnus-summary-limit-to-articles |
| 1979 | "b" gnus-summary-limit-to-bodies |
| 1980 | "h" gnus-summary-limit-to-headers |
| 1981 | "w" gnus-summary-pop-limit |
| 1982 | "s" gnus-summary-limit-to-subject |
| 1983 | "a" gnus-summary-limit-to-author |
| 1984 | "u" gnus-summary-limit-to-unread |
| 1985 | "m" gnus-summary-limit-to-marks |
| 1986 | "M" gnus-summary-limit-exclude-marks |
| 1987 | "v" gnus-summary-limit-to-score |
| 1988 | "*" gnus-summary-limit-include-cached |
| 1989 | "D" gnus-summary-limit-include-dormant |
| 1990 | "T" gnus-summary-limit-include-thread |
| 1991 | "d" gnus-summary-limit-exclude-dormant |
| 1992 | "t" gnus-summary-limit-to-age |
| 1993 | "." gnus-summary-limit-to-unseen |
| 1994 | "x" gnus-summary-limit-to-extra |
| 1995 | "p" gnus-summary-limit-to-display-predicate |
| 1996 | "E" gnus-summary-limit-include-expunged |
| 1997 | "c" gnus-summary-limit-exclude-childless-dormant |
| 1998 | "C" gnus-summary-limit-mark-excluded-as-read |
| 1999 | "o" gnus-summary-insert-old-articles |
| 2000 | "N" gnus-summary-insert-new-articles |
| 2001 | "S" gnus-summary-limit-to-singletons |
| 2002 | "r" gnus-summary-limit-to-replied |
| 2003 | "R" gnus-summary-limit-to-recipient |
| 2004 | "A" gnus-summary-limit-to-address) |
| 2005 | |
| 2006 | (gnus-define-keys (gnus-summary-goto-map "G" gnus-summary-mode-map) |
| 2007 | "n" gnus-summary-next-unread-article |
| 2008 | "p" gnus-summary-prev-unread-article |
| 2009 | "N" gnus-summary-next-article |
| 2010 | "P" gnus-summary-prev-article |
| 2011 | "\C-n" gnus-summary-next-same-subject |
| 2012 | "\C-p" gnus-summary-prev-same-subject |
| 2013 | "\M-n" gnus-summary-next-unread-subject |
| 2014 | "\M-p" gnus-summary-prev-unread-subject |
| 2015 | "f" gnus-summary-first-unread-article |
| 2016 | "b" gnus-summary-best-unread-article |
| 2017 | "j" gnus-summary-goto-article |
| 2018 | "g" gnus-summary-goto-subject |
| 2019 | "l" gnus-summary-goto-last-article |
| 2020 | "o" gnus-summary-pop-article) |
| 2021 | |
| 2022 | (gnus-define-keys (gnus-summary-thread-map "T" gnus-summary-mode-map) |
| 2023 | "k" gnus-summary-kill-thread |
| 2024 | "E" gnus-summary-expire-thread |
| 2025 | "l" gnus-summary-lower-thread |
| 2026 | "i" gnus-summary-raise-thread |
| 2027 | "T" gnus-summary-toggle-threads |
| 2028 | "t" gnus-summary-rethread-current |
| 2029 | "^" gnus-summary-reparent-thread |
| 2030 | "\M-^" gnus-summary-reparent-children |
| 2031 | "s" gnus-summary-show-thread |
| 2032 | "S" gnus-summary-show-all-threads |
| 2033 | "h" gnus-summary-hide-thread |
| 2034 | "H" gnus-summary-hide-all-threads |
| 2035 | "n" gnus-summary-next-thread |
| 2036 | "p" gnus-summary-prev-thread |
| 2037 | "u" gnus-summary-up-thread |
| 2038 | "o" gnus-summary-top-thread |
| 2039 | "d" gnus-summary-down-thread |
| 2040 | "#" gnus-uu-mark-thread |
| 2041 | "\M-#" gnus-uu-unmark-thread) |
| 2042 | |
| 2043 | (gnus-define-keys (gnus-summary-buffer-map "Y" gnus-summary-mode-map) |
| 2044 | "g" gnus-summary-prepare |
| 2045 | "c" gnus-summary-insert-cached-articles |
| 2046 | "d" gnus-summary-insert-dormant-articles |
| 2047 | "t" gnus-summary-insert-ticked-articles) |
| 2048 | |
| 2049 | (gnus-define-keys (gnus-summary-exit-map "Z" gnus-summary-mode-map) |
| 2050 | "c" gnus-summary-catchup-and-exit |
| 2051 | "C" gnus-summary-catchup-all-and-exit |
| 2052 | "E" gnus-summary-exit-no-update |
| 2053 | "Q" gnus-summary-exit |
| 2054 | "Z" gnus-summary-exit |
| 2055 | "n" gnus-summary-catchup-and-goto-next-group |
| 2056 | "p" gnus-summary-catchup-and-goto-prev-group |
| 2057 | "R" gnus-summary-reselect-current-group |
| 2058 | "G" gnus-summary-rescan-group |
| 2059 | "N" gnus-summary-next-group |
| 2060 | "s" gnus-summary-save-newsrc |
| 2061 | "P" gnus-summary-prev-group) |
| 2062 | |
| 2063 | (gnus-define-keys (gnus-summary-article-map "A" gnus-summary-mode-map) |
| 2064 | " " gnus-summary-next-page |
| 2065 | "n" gnus-summary-next-page |
| 2066 | [?\S-\ ] gnus-summary-prev-page |
| 2067 | "\177" gnus-summary-prev-page |
| 2068 | [delete] gnus-summary-prev-page |
| 2069 | "p" gnus-summary-prev-page |
| 2070 | "\r" gnus-summary-scroll-up |
| 2071 | "\M-\r" gnus-summary-scroll-down |
| 2072 | "<" gnus-summary-beginning-of-article |
| 2073 | ">" gnus-summary-end-of-article |
| 2074 | "b" gnus-summary-beginning-of-article |
| 2075 | "e" gnus-summary-end-of-article |
| 2076 | "^" gnus-summary-refer-parent-article |
| 2077 | "r" gnus-summary-refer-parent-article |
| 2078 | "C" gnus-summary-show-complete-article |
| 2079 | "D" gnus-summary-enter-digest-group |
| 2080 | "R" gnus-summary-refer-references |
| 2081 | "T" gnus-summary-refer-thread |
| 2082 | "W" gnus-warp-to-article |
| 2083 | "g" gnus-summary-show-article |
| 2084 | "s" gnus-summary-isearch-article |
| 2085 | "\t" gnus-summary-widget-forward |
| 2086 | [backtab] gnus-summary-widget-backward |
| 2087 | "P" gnus-summary-print-article |
| 2088 | "S" gnus-sticky-article |
| 2089 | "M" gnus-mailing-list-insinuate |
| 2090 | "t" gnus-article-babel) |
| 2091 | |
| 2092 | (gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map) |
| 2093 | "b" gnus-article-add-buttons |
| 2094 | "B" gnus-article-add-buttons-to-head |
| 2095 | "o" gnus-article-treat-overstrike |
| 2096 | "e" gnus-article-emphasize |
| 2097 | "w" gnus-article-fill-cited-article |
| 2098 | "Q" gnus-article-fill-long-lines |
| 2099 | "L" gnus-article-toggle-truncate-lines |
| 2100 | "C" gnus-article-capitalize-sentences |
| 2101 | "c" gnus-article-remove-cr |
| 2102 | "q" gnus-article-de-quoted-unreadable |
| 2103 | "6" gnus-article-de-base64-unreadable |
| 2104 | "Z" gnus-article-decode-HZ |
| 2105 | "A" gnus-article-treat-ansi-sequences |
| 2106 | "h" gnus-article-wash-html |
| 2107 | "u" gnus-article-unsplit-urls |
| 2108 | "s" gnus-summary-force-verify-and-decrypt |
| 2109 | "f" gnus-article-display-x-face |
| 2110 | "l" gnus-summary-stop-page-breaking |
| 2111 | "r" gnus-summary-caesar-message |
| 2112 | "m" gnus-summary-morse-message |
| 2113 | "t" gnus-summary-toggle-header |
| 2114 | "g" gnus-treat-smiley |
| 2115 | "v" gnus-summary-verbose-headers |
| 2116 | "a" gnus-article-strip-headers-in-body ;; mnemonic: wash archive |
| 2117 | "p" gnus-article-verify-x-pgp-sig |
| 2118 | "d" gnus-article-treat-dumbquotes |
| 2119 | "U" gnus-article-treat-non-ascii |
| 2120 | "i" gnus-summary-idna-message) |
| 2121 | |
| 2122 | (gnus-define-keys (gnus-summary-wash-deuglify-map "Y" gnus-summary-wash-map) |
| 2123 | ;; mnemonic: deuglif*Y* |
| 2124 | "u" gnus-article-outlook-unwrap-lines |
| 2125 | "a" gnus-article-outlook-repair-attribution |
| 2126 | "c" gnus-article-outlook-rearrange-citation |
| 2127 | "f" gnus-article-outlook-deuglify-article) ;; mnemonic: full deuglify |
| 2128 | |
| 2129 | (gnus-define-keys (gnus-summary-wash-hide-map "W" gnus-summary-wash-map) |
| 2130 | "a" gnus-article-hide |
| 2131 | "h" gnus-article-hide-headers |
| 2132 | "b" gnus-article-hide-boring-headers |
| 2133 | "s" gnus-article-hide-signature |
| 2134 | "c" gnus-article-hide-citation |
| 2135 | "C" gnus-article-hide-citation-in-followups |
| 2136 | "l" gnus-article-hide-list-identifiers |
| 2137 | "B" gnus-article-strip-banner |
| 2138 | "P" gnus-article-hide-pem |
| 2139 | "\C-c" gnus-article-hide-citation-maybe) |
| 2140 | |
| 2141 | (gnus-define-keys (gnus-summary-wash-highlight-map "H" gnus-summary-wash-map) |
| 2142 | "a" gnus-article-highlight |
| 2143 | "h" gnus-article-highlight-headers |
| 2144 | "c" gnus-article-highlight-citation |
| 2145 | "s" gnus-article-highlight-signature) |
| 2146 | |
| 2147 | (gnus-define-keys (gnus-summary-wash-header-map "G" gnus-summary-wash-map) |
| 2148 | "f" gnus-article-treat-fold-headers |
| 2149 | "u" gnus-article-treat-unfold-headers |
| 2150 | "n" gnus-article-treat-fold-newsgroups) |
| 2151 | |
| 2152 | (gnus-define-keys (gnus-summary-wash-display-map "D" gnus-summary-wash-map) |
| 2153 | "x" gnus-article-display-x-face |
| 2154 | "d" gnus-article-display-face |
| 2155 | "s" gnus-treat-smiley |
| 2156 | "D" gnus-article-remove-images |
| 2157 | "W" gnus-article-show-images |
| 2158 | "f" gnus-treat-from-picon |
| 2159 | "m" gnus-treat-mail-picon |
| 2160 | "n" gnus-treat-newsgroups-picon |
| 2161 | "g" gnus-treat-from-gravatar |
| 2162 | "h" gnus-treat-mail-gravatar) |
| 2163 | |
| 2164 | (gnus-define-keys (gnus-summary-wash-mime-map "M" gnus-summary-wash-map) |
| 2165 | "w" gnus-article-decode-mime-words |
| 2166 | "c" gnus-article-decode-charset |
| 2167 | "v" gnus-mime-view-all-parts |
| 2168 | "b" gnus-article-view-part) |
| 2169 | |
| 2170 | (gnus-define-keys (gnus-summary-wash-time-map "T" gnus-summary-wash-map) |
| 2171 | "z" gnus-article-date-ut |
| 2172 | "u" gnus-article-date-ut |
| 2173 | "l" gnus-article-date-local |
| 2174 | "p" gnus-article-date-english |
| 2175 | "e" gnus-article-date-lapsed |
| 2176 | "o" gnus-article-date-original |
| 2177 | "i" gnus-article-date-iso8601 |
| 2178 | "s" gnus-article-date-user) |
| 2179 | |
| 2180 | (gnus-define-keys (gnus-summary-wash-empty-map "E" gnus-summary-wash-map) |
| 2181 | "t" gnus-article-remove-trailing-blank-lines |
| 2182 | "l" gnus-article-strip-leading-blank-lines |
| 2183 | "m" gnus-article-strip-multiple-blank-lines |
| 2184 | "a" gnus-article-strip-blank-lines |
| 2185 | "A" gnus-article-strip-all-blank-lines |
| 2186 | "s" gnus-article-strip-leading-space |
| 2187 | "e" gnus-article-strip-trailing-space |
| 2188 | "w" gnus-article-remove-leading-whitespace) |
| 2189 | |
| 2190 | (gnus-define-keys (gnus-summary-help-map "H" gnus-summary-mode-map) |
| 2191 | "v" gnus-version |
| 2192 | "d" gnus-summary-describe-group |
| 2193 | "h" gnus-summary-describe-briefly |
| 2194 | "i" gnus-info-find-node) |
| 2195 | |
| 2196 | (gnus-define-keys (gnus-summary-backend-map "B" gnus-summary-mode-map) |
| 2197 | "e" gnus-summary-expire-articles |
| 2198 | "\M-\C-e" gnus-summary-expire-articles-now |
| 2199 | "\177" gnus-summary-delete-article |
| 2200 | [delete] gnus-summary-delete-article |
| 2201 | [backspace] gnus-summary-delete-article |
| 2202 | "m" gnus-summary-move-article |
| 2203 | "r" gnus-summary-respool-article |
| 2204 | "w" gnus-summary-edit-article |
| 2205 | "c" gnus-summary-copy-article |
| 2206 | "B" gnus-summary-crosspost-article |
| 2207 | "q" gnus-summary-respool-query |
| 2208 | "t" gnus-summary-respool-trace |
| 2209 | "i" gnus-summary-import-article |
| 2210 | "I" gnus-summary-create-article |
| 2211 | "p" gnus-summary-article-posted-p) |
| 2212 | |
| 2213 | (gnus-define-keys (gnus-summary-save-map "O" gnus-summary-mode-map) |
| 2214 | "o" gnus-summary-save-article |
| 2215 | "m" gnus-summary-save-article-mail |
| 2216 | "F" gnus-summary-write-article-file |
| 2217 | "r" gnus-summary-save-article-rmail |
| 2218 | "f" gnus-summary-save-article-file |
| 2219 | "b" gnus-summary-save-article-body-file |
| 2220 | "B" gnus-summary-write-article-body-file |
| 2221 | "h" gnus-summary-save-article-folder |
| 2222 | "v" gnus-summary-save-article-vm |
| 2223 | "p" gnus-summary-pipe-output |
| 2224 | "P" gnus-summary-muttprint) |
| 2225 | |
| 2226 | (gnus-define-keys (gnus-summary-mime-map "K" gnus-summary-mode-map) |
| 2227 | "b" gnus-summary-display-buttonized |
| 2228 | "m" gnus-summary-repair-multipart |
| 2229 | "v" gnus-article-view-part |
| 2230 | "o" gnus-article-save-part |
| 2231 | "O" gnus-article-save-part-and-strip |
| 2232 | "r" gnus-article-replace-part |
| 2233 | "d" gnus-article-delete-part |
| 2234 | "t" gnus-article-view-part-as-type |
| 2235 | "j" gnus-article-jump-to-part |
| 2236 | "c" gnus-article-copy-part |
| 2237 | "C" gnus-article-view-part-as-charset |
| 2238 | "e" gnus-article-view-part-externally |
| 2239 | "H" gnus-article-browse-html-article |
| 2240 | "E" gnus-article-encrypt-body |
| 2241 | "i" gnus-article-inline-part |
| 2242 | "|" gnus-article-pipe-part) |
| 2243 | |
| 2244 | (gnus-define-keys (gnus-uu-mark-map "P" gnus-summary-mark-map) |
| 2245 | "p" gnus-summary-mark-as-processable |
| 2246 | "u" gnus-summary-unmark-as-processable |
| 2247 | "U" gnus-summary-unmark-all-processable |
| 2248 | "v" gnus-uu-mark-over |
| 2249 | "s" gnus-uu-mark-series |
| 2250 | "r" gnus-uu-mark-region |
| 2251 | "g" gnus-uu-unmark-region |
| 2252 | "R" gnus-uu-mark-by-regexp |
| 2253 | "G" gnus-uu-unmark-by-regexp |
| 2254 | "t" gnus-uu-mark-thread |
| 2255 | "T" gnus-uu-unmark-thread |
| 2256 | "a" gnus-uu-mark-all |
| 2257 | "b" gnus-uu-mark-buffer |
| 2258 | "S" gnus-uu-mark-sparse |
| 2259 | "k" gnus-summary-kill-process-mark |
| 2260 | "y" gnus-summary-yank-process-mark |
| 2261 | "w" gnus-summary-save-process-mark |
| 2262 | "i" gnus-uu-invert-processable) |
| 2263 | |
| 2264 | (gnus-define-keys (gnus-uu-extract-map "X" gnus-summary-mode-map) |
| 2265 | ;;"x" gnus-uu-extract-any |
| 2266 | "m" gnus-summary-save-parts |
| 2267 | "u" gnus-uu-decode-uu |
| 2268 | "U" gnus-uu-decode-uu-and-save |
| 2269 | "s" gnus-uu-decode-unshar |
| 2270 | "S" gnus-uu-decode-unshar-and-save |
| 2271 | "o" gnus-uu-decode-save |
| 2272 | "O" gnus-uu-decode-save |
| 2273 | "b" gnus-uu-decode-binhex |
| 2274 | "B" gnus-uu-decode-binhex |
| 2275 | "Y" gnus-uu-decode-yenc |
| 2276 | "p" gnus-uu-decode-postscript |
| 2277 | "P" gnus-uu-decode-postscript-and-save) |
| 2278 | |
| 2279 | (gnus-define-keys |
| 2280 | (gnus-uu-extract-view-map "v" gnus-uu-extract-map) |
| 2281 | "u" gnus-uu-decode-uu-view |
| 2282 | "U" gnus-uu-decode-uu-and-save-view |
| 2283 | "s" gnus-uu-decode-unshar-view |
| 2284 | "S" gnus-uu-decode-unshar-and-save-view |
| 2285 | "o" gnus-uu-decode-save-view |
| 2286 | "O" gnus-uu-decode-save-view |
| 2287 | "b" gnus-uu-decode-binhex-view |
| 2288 | "B" gnus-uu-decode-binhex-view |
| 2289 | "p" gnus-uu-decode-postscript-view |
| 2290 | "P" gnus-uu-decode-postscript-and-save-view) |
| 2291 | |
| 2292 | (defvar gnus-article-post-menu nil) |
| 2293 | |
| 2294 | (defconst gnus-summary-menu-maxlen 20) |
| 2295 | |
| 2296 | (defun gnus-summary-menu-split (menu) |
| 2297 | ;; If we have lots of elements, divide them into groups of 20 |
| 2298 | ;; and make a pane (or submenu) for each one. |
| 2299 | (if (> (length menu) (/ (* gnus-summary-menu-maxlen 3) 2)) |
| 2300 | (let ((menu menu) sublists next |
| 2301 | (i 1)) |
| 2302 | (while menu |
| 2303 | ;; Pull off the next gnus-summary-menu-maxlen elements |
| 2304 | ;; and make them the next element of sublist. |
| 2305 | (setq next (nthcdr gnus-summary-menu-maxlen menu)) |
| 2306 | (if next |
| 2307 | (setcdr (nthcdr (1- gnus-summary-menu-maxlen) menu) |
| 2308 | nil)) |
| 2309 | (setq sublists (cons (cons (format "%s ... %s" (aref (car menu) 0) |
| 2310 | (aref (car (last menu)) 0)) menu) |
| 2311 | sublists)) |
| 2312 | (setq i (1+ i)) |
| 2313 | (setq menu next)) |
| 2314 | (nreverse sublists)) |
| 2315 | ;; Few elements--put them all in one pane. |
| 2316 | menu)) |
| 2317 | |
| 2318 | (defun gnus-summary-make-menu-bar () |
| 2319 | (gnus-turn-off-edit-menu 'summary) |
| 2320 | |
| 2321 | (unless (boundp 'gnus-summary-misc-menu) |
| 2322 | |
| 2323 | (easy-menu-define |
| 2324 | gnus-summary-kill-menu gnus-summary-mode-map "" |
| 2325 | (cons |
| 2326 | "Score" |
| 2327 | (nconc |
| 2328 | (list |
| 2329 | ["Customize" gnus-score-customize t]) |
| 2330 | (gnus-make-score-map 'increase) |
| 2331 | (gnus-make-score-map 'lower) |
| 2332 | '(("Mark" |
| 2333 | ["Kill below" gnus-summary-kill-below t] |
| 2334 | ["Mark above" gnus-summary-mark-above t] |
| 2335 | ["Tick above" gnus-summary-tick-above t] |
| 2336 | ["Clear above" gnus-summary-clear-above t]) |
| 2337 | ["Current score" gnus-summary-current-score t] |
| 2338 | ["Set score" gnus-summary-set-score t] |
| 2339 | ["Switch current score file..." gnus-score-change-score-file t] |
| 2340 | ["Set mark below..." gnus-score-set-mark-below t] |
| 2341 | ["Set expunge below..." gnus-score-set-expunge-below t] |
| 2342 | ["Edit current score file" gnus-score-edit-current-scores t] |
| 2343 | ["Edit score file..." gnus-score-edit-file t] |
| 2344 | ["Trace score" gnus-score-find-trace t] |
| 2345 | ["Find words" gnus-score-find-favourite-words t] |
| 2346 | ["Rescore buffer" gnus-summary-rescore t] |
| 2347 | ["Increase score..." gnus-summary-increase-score t] |
| 2348 | ["Lower score..." gnus-summary-lower-score t])))) |
| 2349 | |
| 2350 | ;; Define both the Article menu in the summary buffer and the |
| 2351 | ;; equivalent Commands menu in the article buffer here for |
| 2352 | ;; consistency. |
| 2353 | (let ((innards |
| 2354 | `(("Hide" |
| 2355 | ["All" gnus-article-hide t] |
| 2356 | ["Headers" gnus-article-hide-headers t] |
| 2357 | ["Signature" gnus-article-hide-signature t] |
| 2358 | ["Citation" gnus-article-hide-citation t] |
| 2359 | ["List identifiers" gnus-article-hide-list-identifiers t] |
| 2360 | ["Banner" gnus-article-strip-banner t] |
| 2361 | ["Boring headers" gnus-article-hide-boring-headers t]) |
| 2362 | ("Highlight" |
| 2363 | ["All" gnus-article-highlight t] |
| 2364 | ["Headers" gnus-article-highlight-headers t] |
| 2365 | ["Signature" gnus-article-highlight-signature t] |
| 2366 | ["Citation" gnus-article-highlight-citation t]) |
| 2367 | ("MIME" |
| 2368 | ["Words" gnus-article-decode-mime-words t] |
| 2369 | ["Charset" gnus-article-decode-charset t] |
| 2370 | ["QP" gnus-article-de-quoted-unreadable t] |
| 2371 | ["Base64" gnus-article-de-base64-unreadable t] |
| 2372 | ["View MIME buttons" gnus-summary-display-buttonized t] |
| 2373 | ["View all" gnus-mime-view-all-parts t] |
| 2374 | ["Verify and Decrypt" gnus-summary-force-verify-and-decrypt t] |
| 2375 | ["Encrypt body" gnus-article-encrypt-body |
| 2376 | :active (not (gnus-group-read-only-p)) |
| 2377 | ,@(if (featurep 'xemacs) nil |
| 2378 | '(:help "Encrypt the message body on disk"))] |
| 2379 | ["Extract all parts..." gnus-summary-save-parts t] |
| 2380 | ("Multipart" |
| 2381 | ["Repair multipart" gnus-summary-repair-multipart t] |
| 2382 | ["Pipe part..." gnus-article-pipe-part t] |
| 2383 | ["Inline part" gnus-article-inline-part t] |
| 2384 | ["View part as type..." gnus-article-view-part-as-type t] |
| 2385 | ["Encrypt body" gnus-article-encrypt-body |
| 2386 | :active (not (gnus-group-read-only-p)) |
| 2387 | ,@(if (featurep 'xemacs) nil |
| 2388 | '(:help "Encrypt the message body on disk"))] |
| 2389 | ["View part externally" gnus-article-view-part-externally t] |
| 2390 | ["View HTML parts in browser" gnus-article-browse-html-article t] |
| 2391 | ["View part with charset..." gnus-article-view-part-as-charset t] |
| 2392 | ["Copy part" gnus-article-copy-part t] |
| 2393 | ["Save part..." gnus-article-save-part t] |
| 2394 | ["View part" gnus-article-view-part t])) |
| 2395 | ("Date" |
| 2396 | ["Local" gnus-article-date-local t] |
| 2397 | ["ISO8601" gnus-article-date-iso8601 t] |
| 2398 | ["UT" gnus-article-date-ut t] |
| 2399 | ["Original" gnus-article-date-original t] |
| 2400 | ["Lapsed" gnus-article-date-lapsed t] |
| 2401 | ["User-defined" gnus-article-date-user t]) |
| 2402 | ("Display" |
| 2403 | ["Remove images" gnus-article-remove-images t] |
| 2404 | ["Toggle smiley" gnus-treat-smiley t] |
| 2405 | ["Show X-Face" gnus-article-display-x-face t] |
| 2406 | ["Show picons in From" gnus-treat-from-picon t] |
| 2407 | ["Show picons in mail headers" gnus-treat-mail-picon t] |
| 2408 | ["Show picons in news headers" gnus-treat-newsgroups-picon t] |
| 2409 | ["Show Gravatars in From" gnus-treat-from-gravatar t] |
| 2410 | ["Show Gravatars in mail headers" gnus-treat-mail-gravatar t] |
| 2411 | ("View as different encoding" |
| 2412 | ,@(gnus-summary-menu-split |
| 2413 | (mapcar |
| 2414 | (lambda (cs) |
| 2415 | ;; Since easymenu under Emacs doesn't allow |
| 2416 | ;; lambda forms for menu commands, we should |
| 2417 | ;; provide intern'ed function symbols. |
| 2418 | (let ((command (intern (format "\ |
| 2419 | gnus-summary-show-article-from-menu-as-charset-%s" cs)))) |
| 2420 | (fset command |
| 2421 | `(lambda () |
| 2422 | (interactive) |
| 2423 | (let ((gnus-summary-show-article-charset-alist |
| 2424 | '((1 . ,cs)))) |
| 2425 | (gnus-summary-show-article 1)))) |
| 2426 | `[,(symbol-name cs) ,command t])) |
| 2427 | (sort (if (fboundp 'coding-system-list) |
| 2428 | (coding-system-list) |
| 2429 | (mapcar 'car mm-mime-mule-charset-alist)) |
| 2430 | 'string<))))) |
| 2431 | ("Washing" |
| 2432 | ("Remove Blanks" |
| 2433 | ["Leading" gnus-article-strip-leading-blank-lines t] |
| 2434 | ["Multiple" gnus-article-strip-multiple-blank-lines t] |
| 2435 | ["Trailing" gnus-article-remove-trailing-blank-lines t] |
| 2436 | ["All of the above" gnus-article-strip-blank-lines t] |
| 2437 | ["All" gnus-article-strip-all-blank-lines t] |
| 2438 | ["Leading space" gnus-article-strip-leading-space t] |
| 2439 | ["Trailing space" gnus-article-strip-trailing-space t] |
| 2440 | ["Leading space in headers" |
| 2441 | gnus-article-remove-leading-whitespace t]) |
| 2442 | ["Overstrike" gnus-article-treat-overstrike t] |
| 2443 | ["Dumb quotes" gnus-article-treat-dumbquotes t] |
| 2444 | ["Non-ASCII" gnus-article-treat-non-ascii t] |
| 2445 | ["Emphasis" gnus-article-emphasize t] |
| 2446 | ["Word wrap" gnus-article-fill-cited-article t] |
| 2447 | ["Fill long lines" gnus-article-fill-long-lines t] |
| 2448 | ["Toggle truncate long lines" gnus-article-toggle-truncate-lines t] |
| 2449 | ["Capitalize sentences" gnus-article-capitalize-sentences t] |
| 2450 | ["Remove CR" gnus-article-remove-cr t] |
| 2451 | ["Quoted-Printable" gnus-article-de-quoted-unreadable t] |
| 2452 | ["Base64" gnus-article-de-base64-unreadable t] |
| 2453 | ["Rot 13" gnus-summary-caesar-message |
| 2454 | ,@(if (featurep 'xemacs) '(t) |
| 2455 | '(:help "\"Caesar rotate\" article by 13"))] |
| 2456 | ["De-IDNA" gnus-summary-idna-message t] |
| 2457 | ["Morse decode" gnus-summary-morse-message t] |
| 2458 | ["Unix pipe..." gnus-summary-pipe-message t] |
| 2459 | ["Add buttons" gnus-article-add-buttons t] |
| 2460 | ["Add buttons to head" gnus-article-add-buttons-to-head t] |
| 2461 | ["Stop page breaking" gnus-summary-stop-page-breaking t] |
| 2462 | ["Verbose header" gnus-summary-verbose-headers t] |
| 2463 | ["Toggle header" gnus-summary-toggle-header t] |
| 2464 | ["Unfold headers" gnus-article-treat-unfold-headers t] |
| 2465 | ["Fold newsgroups" gnus-article-treat-fold-newsgroups t] |
| 2466 | ["Html" gnus-article-wash-html t] |
| 2467 | ["Unsplit URLs" gnus-article-unsplit-urls t] |
| 2468 | ["Verify X-PGP-Sig" gnus-article-verify-x-pgp-sig t] |
| 2469 | ["Decode HZ" gnus-article-decode-HZ t] |
| 2470 | ["ANSI sequences" gnus-article-treat-ansi-sequences t] |
| 2471 | ("(Outlook) Deuglify" |
| 2472 | ["Unwrap lines" gnus-article-outlook-unwrap-lines t] |
| 2473 | ["Repair attribution" gnus-article-outlook-repair-attribution t] |
| 2474 | ["Rearrange citation" gnus-article-outlook-rearrange-citation t] |
| 2475 | ["Full (Outlook) deuglify" |
| 2476 | gnus-article-outlook-deuglify-article t]) |
| 2477 | ) |
| 2478 | ("Output" |
| 2479 | ["Save in default format..." gnus-summary-save-article |
| 2480 | ,@(if (featurep 'xemacs) '(t) |
| 2481 | '(:help "Save article using default method"))] |
| 2482 | ["Save in file..." gnus-summary-save-article-file |
| 2483 | ,@(if (featurep 'xemacs) '(t) |
| 2484 | '(:help "Save article in file"))] |
| 2485 | ["Save in Unix mail format..." gnus-summary-save-article-mail t] |
| 2486 | ["Save in MH folder..." gnus-summary-save-article-folder t] |
| 2487 | ["Save in VM folder..." gnus-summary-save-article-vm t] |
| 2488 | ["Save in RMAIL mbox..." gnus-summary-save-article-rmail t] |
| 2489 | ["Save body in file..." gnus-summary-save-article-body-file t] |
| 2490 | ["Pipe through a filter..." gnus-summary-pipe-output t] |
| 2491 | ["Print with Muttprint..." gnus-summary-muttprint t] |
| 2492 | ["Print" gnus-summary-print-article |
| 2493 | ,@(if (featurep 'xemacs) '(t) |
| 2494 | '(:help "Generate and print a PostScript image"))]) |
| 2495 | ("Copy, move,... (Backend)" |
| 2496 | ,@(if (featurep 'xemacs) nil |
| 2497 | '(:help "Copying, moving, expiring articles...")) |
| 2498 | ["Respool article..." gnus-summary-respool-article t] |
| 2499 | ["Move article..." gnus-summary-move-article |
| 2500 | (gnus-check-backend-function |
| 2501 | 'request-move-article gnus-newsgroup-name)] |
| 2502 | ["Copy article..." gnus-summary-copy-article t] |
| 2503 | ["Crosspost article..." gnus-summary-crosspost-article |
| 2504 | (gnus-check-backend-function |
| 2505 | 'request-replace-article gnus-newsgroup-name)] |
| 2506 | ["Import file..." gnus-summary-import-article |
| 2507 | (gnus-check-backend-function |
| 2508 | 'request-accept-article gnus-newsgroup-name)] |
| 2509 | ["Create article..." gnus-summary-create-article |
| 2510 | (gnus-check-backend-function |
| 2511 | 'request-accept-article gnus-newsgroup-name)] |
| 2512 | ["Check if posted" gnus-summary-article-posted-p t] |
| 2513 | ["Edit article" gnus-summary-edit-article |
| 2514 | (not (gnus-group-read-only-p))] |
| 2515 | ["Delete article" gnus-summary-delete-article |
| 2516 | (gnus-check-backend-function |
| 2517 | 'request-expire-articles gnus-newsgroup-name)] |
| 2518 | ["Query respool" gnus-summary-respool-query t] |
| 2519 | ["Trace respool" gnus-summary-respool-trace t] |
| 2520 | ["Delete expirable articles" gnus-summary-expire-articles-now |
| 2521 | (gnus-check-backend-function |
| 2522 | 'request-expire-articles gnus-newsgroup-name)]) |
| 2523 | ("Extract" |
| 2524 | ["Uudecode" gnus-uu-decode-uu |
| 2525 | ,@(if (featurep 'xemacs) '(t) |
| 2526 | '(:help "Decode uuencoded article(s)"))] |
| 2527 | ["Uudecode and save" gnus-uu-decode-uu-and-save t] |
| 2528 | ["Unshar" gnus-uu-decode-unshar t] |
| 2529 | ["Unshar and save" gnus-uu-decode-unshar-and-save t] |
| 2530 | ["Save" gnus-uu-decode-save t] |
| 2531 | ["Binhex" gnus-uu-decode-binhex t] |
| 2532 | ["PostScript" gnus-uu-decode-postscript t] |
| 2533 | ["All MIME parts" gnus-summary-save-parts t]) |
| 2534 | ("Cache" |
| 2535 | ["Enter article" gnus-cache-enter-article t] |
| 2536 | ["Remove article" gnus-cache-remove-article t]) |
| 2537 | ["Translate" gnus-article-babel t] |
| 2538 | ["Select article buffer" gnus-summary-select-article-buffer t] |
| 2539 | ["Make article buffer sticky" gnus-sticky-article t] |
| 2540 | ["Enter digest buffer" gnus-summary-enter-digest-group t] |
| 2541 | ["Isearch article..." gnus-summary-isearch-article t] |
| 2542 | ["Beginning of the article" gnus-summary-beginning-of-article t] |
| 2543 | ["End of the article" gnus-summary-end-of-article t] |
| 2544 | ["Fetch parent of article" gnus-summary-refer-parent-article t] |
| 2545 | ["Fetch referenced articles" gnus-summary-refer-references t] |
| 2546 | ["Fetch current thread" gnus-summary-refer-thread t] |
| 2547 | ["Fetch article with id..." gnus-summary-refer-article t] |
| 2548 | ["Setup Mailing List Params" gnus-mailing-list-insinuate t] |
| 2549 | ["Redisplay" gnus-summary-show-article t] |
| 2550 | ["Raw article" gnus-summary-show-raw-article :keys "C-u g"]))) |
| 2551 | (easy-menu-define |
| 2552 | gnus-summary-article-menu gnus-summary-mode-map "" |
| 2553 | (cons "Article" innards)) |
| 2554 | |
| 2555 | (if (not (keymapp gnus-summary-article-menu)) |
| 2556 | (easy-menu-define |
| 2557 | gnus-article-commands-menu gnus-article-mode-map "" |
| 2558 | (cons "Commands" innards)) |
| 2559 | ;; in Emacs, don't share menu. |
| 2560 | (setq gnus-article-commands-menu |
| 2561 | (copy-keymap gnus-summary-article-menu)) |
| 2562 | (define-key gnus-article-mode-map [menu-bar commands] |
| 2563 | (cons "Commands" gnus-article-commands-menu)))) |
| 2564 | |
| 2565 | (easy-menu-define |
| 2566 | gnus-summary-thread-menu gnus-summary-mode-map "" |
| 2567 | '("Threads" |
| 2568 | ["Find all messages in thread" gnus-summary-refer-thread t] |
| 2569 | ["Toggle threading" gnus-summary-toggle-threads t] |
| 2570 | ["Hide threads" gnus-summary-hide-all-threads t] |
| 2571 | ["Show threads" gnus-summary-show-all-threads t] |
| 2572 | ["Hide thread" gnus-summary-hide-thread t] |
| 2573 | ["Show thread" gnus-summary-show-thread t] |
| 2574 | ["Go to next thread" gnus-summary-next-thread t] |
| 2575 | ["Go to previous thread" gnus-summary-prev-thread t] |
| 2576 | ["Go down thread" gnus-summary-down-thread t] |
| 2577 | ["Go up thread" gnus-summary-up-thread t] |
| 2578 | ["Top of thread" gnus-summary-top-thread t] |
| 2579 | ["Mark thread as read" gnus-summary-kill-thread t] |
| 2580 | ["Mark thread as expired" gnus-summary-expire-thread t] |
| 2581 | ["Lower thread score" gnus-summary-lower-thread t] |
| 2582 | ["Raise thread score" gnus-summary-raise-thread t] |
| 2583 | ["Rethread current" gnus-summary-rethread-current t])) |
| 2584 | |
| 2585 | (easy-menu-define |
| 2586 | gnus-summary-post-menu gnus-summary-mode-map "" |
| 2587 | `("Post" |
| 2588 | ["Send a message (mail or news)" gnus-summary-post-news |
| 2589 | ,@(if (featurep 'xemacs) '(t) |
| 2590 | '(:help "Compose a new message (mail or news)"))] |
| 2591 | ["Followup" gnus-summary-followup |
| 2592 | ,@(if (featurep 'xemacs) '(t) |
| 2593 | '(:help "Post followup to this article"))] |
| 2594 | ["Followup and yank" gnus-summary-followup-with-original |
| 2595 | ,@(if (featurep 'xemacs) '(t) |
| 2596 | '(:help "Post followup to this article, quoting its contents"))] |
| 2597 | ["Supersede article" gnus-summary-supersede-article t] |
| 2598 | ["Cancel article" gnus-summary-cancel-article |
| 2599 | ,@(if (featurep 'xemacs) '(t) |
| 2600 | '(:help "Cancel an article you posted"))] |
| 2601 | ["Reply" gnus-summary-reply t] |
| 2602 | ["Reply and yank" gnus-summary-reply-with-original t] |
| 2603 | ["Wide reply" gnus-summary-wide-reply t] |
| 2604 | ["Wide reply and yank" gnus-summary-wide-reply-with-original |
| 2605 | ,@(if (featurep 'xemacs) '(t) |
| 2606 | '(:help "Mail a reply, quoting this article"))] |
| 2607 | ["Very wide reply" gnus-summary-very-wide-reply t] |
| 2608 | ["Very wide reply and yank" gnus-summary-very-wide-reply-with-original |
| 2609 | ,@(if (featurep 'xemacs) '(t) |
| 2610 | '(:help "Mail a very wide reply, quoting this article"))] |
| 2611 | ["Mail forward" gnus-summary-mail-forward t] |
| 2612 | ["Post forward" gnus-summary-post-forward t] |
| 2613 | ["Digest and mail" gnus-uu-digest-mail-forward t] |
| 2614 | ["Digest and post" gnus-uu-digest-post-forward t] |
| 2615 | ["Resend message" gnus-summary-resend-message t] |
| 2616 | ["Resend message edit" gnus-summary-resend-message-edit t] |
| 2617 | ["Send bounced mail" gnus-summary-resend-bounced-mail t] |
| 2618 | ["Send a mail" gnus-summary-mail-other-window t] |
| 2619 | ["Create a local message" gnus-summary-news-other-window t] |
| 2620 | ["Uuencode and post" gnus-uu-post-news |
| 2621 | ,@(if (featurep 'xemacs) '(t) |
| 2622 | '(:help "Post a uuencoded article"))] |
| 2623 | ["Followup via news" gnus-summary-followup-to-mail t] |
| 2624 | ["Followup via news and yank" |
| 2625 | gnus-summary-followup-to-mail-with-original t] |
| 2626 | ["Strip signature on reply" |
| 2627 | (lambda () |
| 2628 | (interactive) |
| 2629 | (if (not (memq message-cite-function |
| 2630 | '(message-cite-original-without-signature |
| 2631 | message-cite-original))) |
| 2632 | ;; Stupid workaround for XEmacs not honoring :visible. |
| 2633 | (message "Can't toggle this value of `message-cite-function'") |
| 2634 | (setq message-cite-function |
| 2635 | (if (eq message-cite-function |
| 2636 | 'message-cite-original-without-signature) |
| 2637 | 'message-cite-original |
| 2638 | 'message-cite-original-without-signature)))) |
| 2639 | ;; XEmacs barfs on :visible. |
| 2640 | ,@(if (featurep 'xemacs) nil |
| 2641 | '(:visible (memq message-cite-function |
| 2642 | '(message-cite-original-without-signature |
| 2643 | message-cite-original)))) |
| 2644 | :style toggle |
| 2645 | :selected (eq message-cite-function |
| 2646 | 'message-cite-original-without-signature) |
| 2647 | ,@(if (featurep 'xemacs) nil |
| 2648 | '(:help "Strip signature from cited article when replying."))] |
| 2649 | ;;("Draft" |
| 2650 | ;;["Send" gnus-summary-send-draft t] |
| 2651 | ;;["Send bounced" gnus-resend-bounced-mail t]) |
| 2652 | )) |
| 2653 | |
| 2654 | (cond |
| 2655 | ((not (keymapp gnus-summary-post-menu)) |
| 2656 | (setq gnus-article-post-menu gnus-summary-post-menu)) |
| 2657 | ((not gnus-article-post-menu) |
| 2658 | ;; Don't share post menu. |
| 2659 | (setq gnus-article-post-menu |
| 2660 | (copy-keymap gnus-summary-post-menu)))) |
| 2661 | (define-key gnus-article-mode-map [menu-bar post] |
| 2662 | (cons "Post" gnus-article-post-menu)) |
| 2663 | |
| 2664 | (easy-menu-define |
| 2665 | gnus-summary-misc-menu gnus-summary-mode-map "" |
| 2666 | `("Gnus" |
| 2667 | ("Mark Read" |
| 2668 | ["Mark as read" gnus-summary-mark-as-read-forward t] |
| 2669 | ["Mark same subject and select" |
| 2670 | gnus-summary-kill-same-subject-and-select t] |
| 2671 | ["Mark same subject" gnus-summary-kill-same-subject t] |
| 2672 | ["Catchup" gnus-summary-catchup |
| 2673 | ,@(if (featurep 'xemacs) '(t) |
| 2674 | '(:help "Mark unread articles in this group as read"))] |
| 2675 | ["Catchup all" gnus-summary-catchup-all t] |
| 2676 | ["Catchup to here" gnus-summary-catchup-to-here t] |
| 2677 | ["Catchup from here" gnus-summary-catchup-from-here t] |
| 2678 | ["Catchup region" gnus-summary-mark-region-as-read |
| 2679 | (gnus-mark-active-p)] |
| 2680 | ["Mark excluded" gnus-summary-limit-mark-excluded-as-read t]) |
| 2681 | ("Mark Various" |
| 2682 | ["Tick" gnus-summary-tick-article-forward t] |
| 2683 | ["Mark as dormant" gnus-summary-mark-as-dormant t] |
| 2684 | ["Remove marks" gnus-summary-clear-mark-forward t] |
| 2685 | ["Set expirable mark" gnus-summary-mark-as-expirable t] |
| 2686 | ["Set bookmark" gnus-summary-set-bookmark t] |
| 2687 | ["Remove bookmark" gnus-summary-remove-bookmark t]) |
| 2688 | ("Limit to" |
| 2689 | ["Marks..." gnus-summary-limit-to-marks t] |
| 2690 | ["Subject..." gnus-summary-limit-to-subject t] |
| 2691 | ["Author..." gnus-summary-limit-to-author t] |
| 2692 | ["Recipient..." gnus-summary-limit-to-recipient t] |
| 2693 | ["Address..." gnus-summary-limit-to-address t] |
| 2694 | ["Age..." gnus-summary-limit-to-age t] |
| 2695 | ["Extra..." gnus-summary-limit-to-extra t] |
| 2696 | ["Score..." gnus-summary-limit-to-score t] |
| 2697 | ["Display Predicate" gnus-summary-limit-to-display-predicate t] |
| 2698 | ["Unread" gnus-summary-limit-to-unread t] |
| 2699 | ["Unseen" gnus-summary-limit-to-unseen t] |
| 2700 | ["Singletons" gnus-summary-limit-to-singletons t] |
| 2701 | ["Replied" gnus-summary-limit-to-replied t] |
| 2702 | ["Non-dormant" gnus-summary-limit-exclude-dormant t] |
| 2703 | ["Next or process marked articles" gnus-summary-limit-to-articles t] |
| 2704 | ["Pop limit" gnus-summary-pop-limit t] |
| 2705 | ["Show dormant" gnus-summary-limit-include-dormant t] |
| 2706 | ["Hide childless dormant" |
| 2707 | gnus-summary-limit-exclude-childless-dormant t] |
| 2708 | ;;["Hide thread" gnus-summary-limit-exclude-thread t] |
| 2709 | ["Hide marked" gnus-summary-limit-exclude-marks t] |
| 2710 | ["Show expunged" gnus-summary-limit-include-expunged t]) |
| 2711 | ("Process Mark" |
| 2712 | ["Set mark" gnus-summary-mark-as-processable t] |
| 2713 | ["Remove mark" gnus-summary-unmark-as-processable t] |
| 2714 | ["Remove all marks" gnus-summary-unmark-all-processable t] |
| 2715 | ["Invert marks" gnus-uu-invert-processable t] |
| 2716 | ["Mark above" gnus-uu-mark-over t] |
| 2717 | ["Mark series" gnus-uu-mark-series t] |
| 2718 | ["Mark region" gnus-uu-mark-region (gnus-mark-active-p)] |
| 2719 | ["Unmark region" gnus-uu-unmark-region (gnus-mark-active-p)] |
| 2720 | ["Mark by regexp..." gnus-uu-mark-by-regexp t] |
| 2721 | ["Unmark by regexp..." gnus-uu-unmark-by-regexp t] |
| 2722 | ["Mark all" gnus-uu-mark-all t] |
| 2723 | ["Mark buffer" gnus-uu-mark-buffer t] |
| 2724 | ["Mark sparse" gnus-uu-mark-sparse t] |
| 2725 | ["Mark thread" gnus-uu-mark-thread t] |
| 2726 | ["Unmark thread" gnus-uu-unmark-thread t] |
| 2727 | ("Process Mark Sets" |
| 2728 | ["Kill" gnus-summary-kill-process-mark t] |
| 2729 | ["Yank" gnus-summary-yank-process-mark |
| 2730 | gnus-newsgroup-process-stack] |
| 2731 | ["Save" gnus-summary-save-process-mark t] |
| 2732 | ["Run command on marked..." gnus-summary-universal-argument t])) |
| 2733 | ("Registry Marks") |
| 2734 | ("Scroll article" |
| 2735 | ["Page forward" gnus-summary-next-page |
| 2736 | ,@(if (featurep 'xemacs) '(t) |
| 2737 | '(:help "Show next page of article"))] |
| 2738 | ["Page backward" gnus-summary-prev-page |
| 2739 | ,@(if (featurep 'xemacs) '(t) |
| 2740 | '(:help "Show previous page of article"))] |
| 2741 | ["Line forward" gnus-summary-scroll-up t]) |
| 2742 | ("Move" |
| 2743 | ["Next unread article" gnus-summary-next-unread-article t] |
| 2744 | ["Previous unread article" gnus-summary-prev-unread-article t] |
| 2745 | ["Next article" gnus-summary-next-article t] |
| 2746 | ["Previous article" gnus-summary-prev-article t] |
| 2747 | ["Next unread subject" gnus-summary-next-unread-subject t] |
| 2748 | ["Previous unread subject" gnus-summary-prev-unread-subject t] |
| 2749 | ["Next article same subject" gnus-summary-next-same-subject t] |
| 2750 | ["Previous article same subject" gnus-summary-prev-same-subject t] |
| 2751 | ["First unread article" gnus-summary-first-unread-article t] |
| 2752 | ["Best unread article" gnus-summary-best-unread-article t] |
| 2753 | ["Go to subject number..." gnus-summary-goto-subject t] |
| 2754 | ["Go to article number..." gnus-summary-goto-article t] |
| 2755 | ["Go to the last article" gnus-summary-goto-last-article t] |
| 2756 | ["Pop article off history" gnus-summary-pop-article t]) |
| 2757 | ("Sort" |
| 2758 | ["Sort by number" gnus-summary-sort-by-number t] |
| 2759 | ["Sort by most recent number" gnus-summary-sort-by-most-recent-number t] |
| 2760 | ["Sort by author" gnus-summary-sort-by-author t] |
| 2761 | ["Sort by recipient" gnus-summary-sort-by-recipient t] |
| 2762 | ["Sort by subject" gnus-summary-sort-by-subject t] |
| 2763 | ["Sort by date" gnus-summary-sort-by-date t] |
| 2764 | ["Sort by most recent date" gnus-summary-sort-by-most-recent-date t] |
| 2765 | ["Sort by score" gnus-summary-sort-by-score t] |
| 2766 | ["Sort by lines" gnus-summary-sort-by-lines t] |
| 2767 | ["Sort by characters" gnus-summary-sort-by-chars t] |
| 2768 | ["Randomize" gnus-summary-sort-by-random t] |
| 2769 | ["Original sort" gnus-summary-sort-by-original t]) |
| 2770 | ("Help" |
| 2771 | ["Describe group" gnus-summary-describe-group t] |
| 2772 | ["Read manual" gnus-info-find-node t]) |
| 2773 | ("Modes" |
| 2774 | ["Pick and read" gnus-pick-mode t] |
| 2775 | ["Binary" gnus-binary-mode t]) |
| 2776 | ("Regeneration" |
| 2777 | ["Regenerate" gnus-summary-prepare t] |
| 2778 | ["Insert cached articles" gnus-summary-insert-cached-articles t] |
| 2779 | ["Insert dormant articles" gnus-summary-insert-dormant-articles t] |
| 2780 | ["Insert ticked articles" gnus-summary-insert-ticked-articles t] |
| 2781 | ["Toggle threading" gnus-summary-toggle-threads t]) |
| 2782 | ["See old articles" gnus-summary-insert-old-articles t] |
| 2783 | ["See new articles" gnus-summary-insert-new-articles t] |
| 2784 | ["Filter articles..." gnus-summary-execute-command t] |
| 2785 | ["Run command on articles..." gnus-summary-universal-argument t] |
| 2786 | ["Search articles forward..." gnus-summary-search-article-forward t] |
| 2787 | ["Search articles backward..." gnus-summary-search-article-backward t] |
| 2788 | ["Toggle line truncation" gnus-summary-toggle-truncation t] |
| 2789 | ["Expand window" gnus-summary-expand-window t] |
| 2790 | ["Expire expirable articles" gnus-summary-expire-articles |
| 2791 | (gnus-check-backend-function |
| 2792 | 'request-expire-articles gnus-newsgroup-name)] |
| 2793 | ["Edit local kill file" gnus-summary-edit-local-kill t] |
| 2794 | ["Edit main kill file" gnus-summary-edit-global-kill t] |
| 2795 | ["Edit group parameters" gnus-summary-edit-parameters t] |
| 2796 | ["Customize group parameters" gnus-summary-customize-parameters t] |
| 2797 | ["Send a bug report" gnus-bug t] |
| 2798 | ("Exit" |
| 2799 | ["Catchup and exit" gnus-summary-catchup-and-exit |
| 2800 | ,@(if (featurep 'xemacs) '(t) |
| 2801 | '(:help "Mark unread articles in this group as read, then exit"))] |
| 2802 | ["Catchup all and exit" gnus-summary-catchup-all-and-exit t] |
| 2803 | ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t] |
| 2804 | ["Catchup and goto prev" gnus-summary-catchup-and-goto-prev-group t] |
| 2805 | ["Exit group" gnus-summary-exit |
| 2806 | ,@(if (featurep 'xemacs) '(t) |
| 2807 | '(:help "Exit current group, return to group selection mode"))] |
| 2808 | ["Exit group without updating" gnus-summary-exit-no-update t] |
| 2809 | ["Exit and goto next group" gnus-summary-next-group t] |
| 2810 | ["Exit and goto prev group" gnus-summary-prev-group t] |
| 2811 | ["Reselect group" gnus-summary-reselect-current-group t] |
| 2812 | ["Rescan group" gnus-summary-rescan-group t] |
| 2813 | ["Update dribble" gnus-summary-save-newsrc t]))) |
| 2814 | |
| 2815 | (gnus-run-hooks 'gnus-summary-menu-hook))) |
| 2816 | |
| 2817 | (defvar gnus-summary-tool-bar-map nil) |
| 2818 | |
| 2819 | ;; Note: The :set function in the `gnus-summary-tool-bar*' variables will only |
| 2820 | ;; affect _new_ message buffers. We might add a function that walks thru all |
| 2821 | ;; summary-mode buffers and force the update. |
| 2822 | (defun gnus-summary-tool-bar-update (&optional symbol value) |
| 2823 | "Update summary mode toolbar. |
| 2824 | Setter function for custom variables." |
| 2825 | (setq-default gnus-summary-tool-bar-map nil) |
| 2826 | (when symbol |
| 2827 | ;; When used as ":set" function: |
| 2828 | (set-default symbol value)) |
| 2829 | (when (gnus-buffer-live-p gnus-summary-buffer) |
| 2830 | (with-current-buffer gnus-summary-buffer |
| 2831 | (gnus-summary-make-tool-bar)))) |
| 2832 | |
| 2833 | (defcustom gnus-summary-tool-bar (if (eq gmm-tool-bar-style 'gnome) |
| 2834 | 'gnus-summary-tool-bar-gnome |
| 2835 | 'gnus-summary-tool-bar-retro) |
| 2836 | "Specifies the Gnus summary tool bar. |
| 2837 | |
| 2838 | It can be either a list or a symbol referring to a list. See |
| 2839 | `gmm-tool-bar-from-list' for the format of the list. The |
| 2840 | default key map is `gnus-summary-mode-map'. |
| 2841 | |
| 2842 | Pre-defined symbols include `gnus-summary-tool-bar-gnome' and |
| 2843 | `gnus-summary-tool-bar-retro'." |
| 2844 | :type '(choice (const :tag "GNOME style" gnus-summary-tool-bar-gnome) |
| 2845 | (const :tag "Retro look" gnus-summary-tool-bar-retro) |
| 2846 | (repeat :tag "User defined list" gmm-tool-bar-item) |
| 2847 | (symbol)) |
| 2848 | :version "23.1" ;; No Gnus |
| 2849 | :initialize 'custom-initialize-default |
| 2850 | :set 'gnus-summary-tool-bar-update |
| 2851 | :group 'gnus-summary) |
| 2852 | |
| 2853 | (defcustom gnus-summary-tool-bar-gnome |
| 2854 | '((gnus-summary-post-news "mail/compose" nil) |
| 2855 | (gnus-summary-insert-new-articles "mail/inbox" nil |
| 2856 | :visible (or (not gnus-agent) |
| 2857 | gnus-plugged)) |
| 2858 | (gnus-summary-reply-with-original "mail/reply") |
| 2859 | (gnus-summary-reply "mail/reply" nil :visible nil) |
| 2860 | (gnus-summary-followup-with-original "mail/reply-all") |
| 2861 | (gnus-summary-followup "mail/reply-all" nil :visible nil) |
| 2862 | (gnus-summary-mail-forward "mail/forward") |
| 2863 | (gnus-summary-save-article "mail/save") |
| 2864 | (gnus-summary-search-article-forward "search" nil :visible nil) |
| 2865 | (gnus-summary-print-article "print") |
| 2866 | (gnus-summary-tick-article-forward "flag-followup" nil :visible nil) |
| 2867 | ;; Some new commands that may need more suitable icons: |
| 2868 | (gnus-summary-save-newsrc "save" nil :visible nil) |
| 2869 | ;; (gnus-summary-show-article "stock_message-display" nil :visible nil) |
| 2870 | (gnus-summary-prev-article "left-arrow") |
| 2871 | (gnus-summary-next-article "right-arrow") |
| 2872 | (gnus-summary-next-page "next-page") |
| 2873 | ;; (gnus-summary-enter-digest-group "right_arrow" nil :visible nil) |
| 2874 | ;; |
| 2875 | ;; Maybe some sort-by-... could be added: |
| 2876 | ;; (gnus-summary-sort-by-author "sort-a-z" nil :visible nil) |
| 2877 | ;; (gnus-summary-sort-by-date "sort-1-9" nil :visible nil) |
| 2878 | (gnus-summary-mark-as-expirable |
| 2879 | "delete" nil |
| 2880 | :visible (gnus-check-backend-function 'request-expire-articles |
| 2881 | gnus-newsgroup-name)) |
| 2882 | (gnus-summary-mark-as-spam |
| 2883 | "mail/spam" t |
| 2884 | :visible (and (fboundp 'spam-group-ham-contents-p) |
| 2885 | (spam-group-ham-contents-p gnus-newsgroup-name)) |
| 2886 | :help "Mark as spam") |
| 2887 | (gnus-summary-mark-as-read-forward |
| 2888 | "mail/not-spam" nil |
| 2889 | :visible (and (fboundp 'spam-group-spam-contents-p) |
| 2890 | (spam-group-spam-contents-p gnus-newsgroup-name))) |
| 2891 | ;; |
| 2892 | (gnus-summary-exit "exit") |
| 2893 | (gmm-customize-mode "preferences" t :help "Edit mode preferences") |
| 2894 | (gnus-info-find-node "help")) |
| 2895 | "List of functions for the summary tool bar (GNOME style). |
| 2896 | |
| 2897 | See `gmm-tool-bar-from-list' for the format of the list." |
| 2898 | :type '(repeat gmm-tool-bar-item) |
| 2899 | :version "23.1" ;; No Gnus |
| 2900 | :initialize 'custom-initialize-default |
| 2901 | :set 'gnus-summary-tool-bar-update |
| 2902 | :group 'gnus-summary) |
| 2903 | |
| 2904 | (defcustom gnus-summary-tool-bar-retro |
| 2905 | '((gnus-summary-prev-unread-article "gnus/prev-ur") |
| 2906 | (gnus-summary-next-unread-article "gnus/next-ur") |
| 2907 | (gnus-summary-post-news "gnus/post") |
| 2908 | (gnus-summary-followup-with-original "gnus/fuwo") |
| 2909 | (gnus-summary-followup "gnus/followup") |
| 2910 | (gnus-summary-reply-with-original "gnus/reply-wo") |
| 2911 | (gnus-summary-reply "gnus/reply") |
| 2912 | (gnus-summary-caesar-message "gnus/rot13") |
| 2913 | (gnus-uu-decode-uu "gnus/uu-decode") |
| 2914 | (gnus-summary-save-article-file "gnus/save-aif") |
| 2915 | (gnus-summary-save-article "gnus/save-art") |
| 2916 | (gnus-uu-post-news "gnus/uu-post") |
| 2917 | (gnus-summary-catchup "gnus/catchup") |
| 2918 | (gnus-summary-catchup-and-exit "gnus/cu-exit") |
| 2919 | (gnus-summary-exit "gnus/exit-summ") |
| 2920 | ;; Some new command that may need more suitable icons: |
| 2921 | (gnus-summary-print-article "gnus/print" nil :visible nil) |
| 2922 | (gnus-summary-mark-as-expirable "gnus/close" nil :visible nil) |
| 2923 | (gnus-summary-save-newsrc "gnus/save" nil :visible nil) |
| 2924 | ;; (gnus-summary-enter-digest-group "gnus/right_arrow" nil :visible nil) |
| 2925 | (gnus-summary-search-article-forward "gnus/search" nil :visible nil) |
| 2926 | ;; (gnus-summary-insert-new-articles "gnus/paste" nil :visible nil) |
| 2927 | ;; (gnus-summary-toggle-threads "gnus/open" nil :visible nil) |
| 2928 | ;; |
| 2929 | (gnus-info-find-node "gnus/help" nil :visible nil)) |
| 2930 | "List of functions for the summary tool bar (retro look). |
| 2931 | |
| 2932 | See `gmm-tool-bar-from-list' for the format of the list." |
| 2933 | :type '(repeat gmm-tool-bar-item) |
| 2934 | :version "23.1" ;; No Gnus |
| 2935 | :initialize 'custom-initialize-default |
| 2936 | :set 'gnus-summary-tool-bar-update |
| 2937 | :group 'gnus-summary) |
| 2938 | |
| 2939 | (defcustom gnus-summary-tool-bar-zap-list t |
| 2940 | "List of icon items from the global tool bar. |
| 2941 | These items are not displayed in the Gnus summary mode tool bar. |
| 2942 | |
| 2943 | See `gmm-tool-bar-from-list' for the format of the list." |
| 2944 | :type 'gmm-tool-bar-zap-list |
| 2945 | :version "23.1" ;; No Gnus |
| 2946 | :initialize 'custom-initialize-default |
| 2947 | :set 'gnus-summary-tool-bar-update |
| 2948 | :group 'gnus-summary) |
| 2949 | |
| 2950 | (defvar image-load-path) |
| 2951 | (defvar tool-bar-map) |
| 2952 | |
| 2953 | (defun gnus-summary-make-tool-bar (&optional force) |
| 2954 | "Make a summary mode tool bar from `gnus-summary-tool-bar'. |
| 2955 | When FORCE, rebuild the tool bar." |
| 2956 | (when (and (not (featurep 'xemacs)) |
| 2957 | (boundp 'tool-bar-mode) |
| 2958 | tool-bar-mode |
| 2959 | (or (not gnus-summary-tool-bar-map) force)) |
| 2960 | (let* ((load-path |
| 2961 | (gmm-image-load-path-for-library "gnus" |
| 2962 | "mail/save.xpm" |
| 2963 | nil t)) |
| 2964 | (image-load-path (cons (car load-path) |
| 2965 | (when (boundp 'image-load-path) |
| 2966 | image-load-path))) |
| 2967 | (map (gmm-tool-bar-from-list gnus-summary-tool-bar |
| 2968 | gnus-summary-tool-bar-zap-list |
| 2969 | 'gnus-summary-mode-map))) |
| 2970 | (when map |
| 2971 | ;; Need to set `gnus-summary-tool-bar-map' because `gnus-article-mode' |
| 2972 | ;; uses its value. |
| 2973 | (setq gnus-summary-tool-bar-map map)))) |
| 2974 | (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)) |
| 2975 | |
| 2976 | (defun gnus-make-score-map (type) |
| 2977 | "Make a summary score map of type TYPE." |
| 2978 | (if t |
| 2979 | nil |
| 2980 | (let ((headers '(("author" "from" string) |
| 2981 | ("subject" "subject" string) |
| 2982 | ("article body" "body" string) |
| 2983 | ("article head" "head" string) |
| 2984 | ("xref" "xref" string) |
| 2985 | ("extra header" "extra" string) |
| 2986 | ("lines" "lines" number) |
| 2987 | ("followups to author" "followup" string))) |
| 2988 | (types '((number ("less than" <) |
| 2989 | ("greater than" >) |
| 2990 | ("equal" =)) |
| 2991 | (string ("substring" s) |
| 2992 | ("exact string" e) |
| 2993 | ("fuzzy string" f) |
| 2994 | ("regexp" r)))) |
| 2995 | (perms '(("temporary" (current-time-string)) |
| 2996 | ("permanent" nil) |
| 2997 | ("immediate" now))) |
| 2998 | header) |
| 2999 | (list |
| 3000 | (apply |
| 3001 | 'nconc |
| 3002 | (list |
| 3003 | (if (eq type 'lower) |
| 3004 | "Lower score" |
| 3005 | "Increase score")) |
| 3006 | (let (outh) |
| 3007 | (while headers |
| 3008 | (setq header (car headers)) |
| 3009 | (setq outh |
| 3010 | (cons |
| 3011 | (apply |
| 3012 | 'nconc |
| 3013 | (list (car header)) |
| 3014 | (let ((ts (cdr (assoc (nth 2 header) types))) |
| 3015 | outt) |
| 3016 | (while ts |
| 3017 | (setq outt |
| 3018 | (cons |
| 3019 | (apply |
| 3020 | 'nconc |
| 3021 | (list (caar ts)) |
| 3022 | (let ((ps perms) |
| 3023 | outp) |
| 3024 | (while ps |
| 3025 | (setq outp |
| 3026 | (cons |
| 3027 | (vector |
| 3028 | (caar ps) |
| 3029 | (list |
| 3030 | 'gnus-summary-score-entry |
| 3031 | (nth 1 header) |
| 3032 | (if (or (string= (nth 1 header) |
| 3033 | "head") |
| 3034 | (string= (nth 1 header) |
| 3035 | "body")) |
| 3036 | "" |
| 3037 | (list 'gnus-summary-header |
| 3038 | (nth 1 header))) |
| 3039 | (list 'quote (nth 1 (car ts))) |
| 3040 | (list 'gnus-score-delta-default |
| 3041 | nil) |
| 3042 | (nth 1 (car ps)) |
| 3043 | t) |
| 3044 | t) |
| 3045 | outp)) |
| 3046 | (setq ps (cdr ps))) |
| 3047 | (list (nreverse outp)))) |
| 3048 | outt)) |
| 3049 | (setq ts (cdr ts))) |
| 3050 | (list (nreverse outt)))) |
| 3051 | outh)) |
| 3052 | (setq headers (cdr headers))) |
| 3053 | (list (nreverse outh)))))))) |
| 3054 | |
| 3055 | |
| 3056 | (declare-function turn-on-gnus-mailing-list-mode "gnus-ml" ()) |
| 3057 | (defvar bookmark-make-record-function) |
| 3058 | \f |
| 3059 | (defvar bidi-paragraph-direction) |
| 3060 | |
| 3061 | (defun gnus-summary-mode (&optional group) |
| 3062 | "Major mode for reading articles. |
| 3063 | |
| 3064 | All normal editing commands are switched off. |
| 3065 | \\<gnus-summary-mode-map> |
| 3066 | Each line in this buffer represents one article. To read an |
| 3067 | article, you can, for instance, type `\\[gnus-summary-next-page]'. To move forwards |
| 3068 | and backwards while displaying articles, type `\\[gnus-summary-next-unread-article]' and `\\[gnus-summary-prev-unread-article]', |
| 3069 | respectively. |
| 3070 | |
| 3071 | You can also post articles and send mail from this buffer. To |
| 3072 | follow up an article, type `\\[gnus-summary-followup]'. To mail a reply to the author |
| 3073 | of an article, type `\\[gnus-summary-reply]'. |
| 3074 | |
| 3075 | There are approx. one gazillion commands you can execute in this |
| 3076 | buffer; read the info pages for more information (`\\[gnus-info-find-node]'). |
| 3077 | |
| 3078 | The following commands are available: |
| 3079 | |
| 3080 | \\{gnus-summary-mode-map}" |
| 3081 | (interactive) |
| 3082 | (kill-all-local-variables) |
| 3083 | (let ((gnus-summary-local-variables gnus-newsgroup-variables)) |
| 3084 | (gnus-summary-make-local-variables)) |
| 3085 | (gnus-summary-make-local-variables) |
| 3086 | (setq gnus-newsgroup-name group) |
| 3087 | (when (gnus-visual-p 'summary-menu 'menu) |
| 3088 | (gnus-summary-make-menu-bar) |
| 3089 | (gnus-summary-make-tool-bar)) |
| 3090 | (gnus-make-thread-indent-array) |
| 3091 | (gnus-simplify-mode-line) |
| 3092 | (setq major-mode 'gnus-summary-mode) |
| 3093 | (setq mode-name "Summary") |
| 3094 | (use-local-map gnus-summary-mode-map) |
| 3095 | (buffer-disable-undo) |
| 3096 | (setq buffer-read-only t ;Disable modification |
| 3097 | show-trailing-whitespace nil) |
| 3098 | (setq truncate-lines t) |
| 3099 | ;; Force paragraph direction to be left-to-right. Don't make it |
| 3100 | ;; bound globally in old Emacsen and XEmacsen. |
| 3101 | (set (make-local-variable 'bidi-paragraph-direction) 'left-to-right) |
| 3102 | (add-to-invisibility-spec '(gnus-sum . t)) |
| 3103 | (gnus-summary-set-display-table) |
| 3104 | (gnus-set-default-directory) |
| 3105 | (make-local-variable 'gnus-summary-line-format) |
| 3106 | (make-local-variable 'gnus-summary-line-format-spec) |
| 3107 | (make-local-variable 'gnus-summary-dummy-line-format) |
| 3108 | (make-local-variable 'gnus-summary-dummy-line-format-spec) |
| 3109 | (make-local-variable 'gnus-summary-mark-positions) |
| 3110 | (gnus-make-local-hook 'pre-command-hook) |
| 3111 | (add-hook 'pre-command-hook 'gnus-set-global-variables nil t) |
| 3112 | (gnus-run-mode-hooks 'gnus-summary-mode-hook) |
| 3113 | (turn-on-gnus-mailing-list-mode) |
| 3114 | (mm-enable-multibyte) |
| 3115 | (set (make-local-variable 'bookmark-make-record-function) |
| 3116 | 'gnus-summary-bookmark-make-record) |
| 3117 | (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy) |
| 3118 | (gnus-update-summary-mark-positions)) |
| 3119 | |
| 3120 | (defun gnus-summary-make-local-variables () |
| 3121 | "Make all the local summary buffer variables." |
| 3122 | (let (global) |
| 3123 | (dolist (local gnus-summary-local-variables) |
| 3124 | (if (consp local) |
| 3125 | (progn |
| 3126 | (if (eq (cdr local) 'global) |
| 3127 | ;; Copy the global value of the variable. |
| 3128 | (setq global (symbol-value (car local))) |
| 3129 | ;; Use the value from the list. |
| 3130 | (setq global (eval (cdr local)))) |
| 3131 | (set (make-local-variable (car local)) global)) |
| 3132 | ;; Simple nil-valued local variable. |
| 3133 | (set (make-local-variable local) nil))))) |
| 3134 | |
| 3135 | ;; Summary data functions. |
| 3136 | |
| 3137 | (defmacro gnus-data-number (data) |
| 3138 | `(car ,data)) |
| 3139 | |
| 3140 | (defmacro gnus-data-set-number (data number) |
| 3141 | `(setcar ,data ,number)) |
| 3142 | |
| 3143 | (defmacro gnus-data-mark (data) |
| 3144 | `(nth 1 ,data)) |
| 3145 | |
| 3146 | (defmacro gnus-data-set-mark (data mark) |
| 3147 | `(setcar (nthcdr 1 ,data) ,mark)) |
| 3148 | |
| 3149 | (defmacro gnus-data-pos (data) |
| 3150 | `(nth 2 ,data)) |
| 3151 | |
| 3152 | (defmacro gnus-data-set-pos (data pos) |
| 3153 | `(setcar (nthcdr 2 ,data) ,pos)) |
| 3154 | |
| 3155 | (defmacro gnus-data-header (data) |
| 3156 | `(nth 3 ,data)) |
| 3157 | |
| 3158 | (defmacro gnus-data-set-header (data header) |
| 3159 | `(setf (nth 3 ,data) ,header)) |
| 3160 | |
| 3161 | (defmacro gnus-data-level (data) |
| 3162 | `(nth 4 ,data)) |
| 3163 | |
| 3164 | (defmacro gnus-data-unread-p (data) |
| 3165 | `(= (nth 1 ,data) gnus-unread-mark)) |
| 3166 | |
| 3167 | (defmacro gnus-data-read-p (data) |
| 3168 | `(/= (nth 1 ,data) gnus-unread-mark)) |
| 3169 | |
| 3170 | (defmacro gnus-data-pseudo-p (data) |
| 3171 | `(consp (nth 3 ,data))) |
| 3172 | |
| 3173 | (defmacro gnus-data-find (number) |
| 3174 | `(assq ,number gnus-newsgroup-data)) |
| 3175 | |
| 3176 | (defmacro gnus-data-find-list (number &optional data) |
| 3177 | `(let ((bdata ,(or data 'gnus-newsgroup-data))) |
| 3178 | (memq (assq ,number bdata) |
| 3179 | bdata))) |
| 3180 | |
| 3181 | (defmacro gnus-data-make (number mark pos header level) |
| 3182 | `(list ,number ,mark ,pos ,header ,level)) |
| 3183 | |
| 3184 | (defun gnus-data-enter (after-article number mark pos header level offset) |
| 3185 | (let ((data (gnus-data-find-list after-article))) |
| 3186 | (unless data |
| 3187 | (error "No such article: %d" after-article)) |
| 3188 | (setcdr data (cons (gnus-data-make number mark pos header level) |
| 3189 | (cdr data))) |
| 3190 | (setq gnus-newsgroup-data-reverse nil) |
| 3191 | (gnus-data-update-list (cddr data) offset))) |
| 3192 | |
| 3193 | (defun gnus-data-enter-list (after-article list &optional offset) |
| 3194 | (when list |
| 3195 | (let ((data (and after-article (gnus-data-find-list after-article))) |
| 3196 | (ilist list)) |
| 3197 | (if (not (or data |
| 3198 | after-article)) |
| 3199 | (let ((odata gnus-newsgroup-data)) |
| 3200 | (setq gnus-newsgroup-data (nconc list gnus-newsgroup-data)) |
| 3201 | (when offset |
| 3202 | (gnus-data-update-list odata offset))) |
| 3203 | ;; Find the last element in the list to be spliced into the main |
| 3204 | ;; list. |
| 3205 | (setq list (last list)) |
| 3206 | (if (not data) |
| 3207 | (progn |
| 3208 | (setcdr list gnus-newsgroup-data) |
| 3209 | (setq gnus-newsgroup-data ilist) |
| 3210 | (when offset |
| 3211 | (gnus-data-update-list (cdr list) offset))) |
| 3212 | (setcdr list (cdr data)) |
| 3213 | (setcdr data ilist) |
| 3214 | (when offset |
| 3215 | (gnus-data-update-list (cdr list) offset)))) |
| 3216 | (setq gnus-newsgroup-data-reverse nil)))) |
| 3217 | |
| 3218 | (defun gnus-data-remove (article &optional offset) |
| 3219 | (let ((data gnus-newsgroup-data)) |
| 3220 | (if (= (gnus-data-number (car data)) article) |
| 3221 | (progn |
| 3222 | (setq gnus-newsgroup-data (cdr gnus-newsgroup-data) |
| 3223 | gnus-newsgroup-data-reverse nil) |
| 3224 | (when offset |
| 3225 | (gnus-data-update-list gnus-newsgroup-data offset))) |
| 3226 | (while (cdr data) |
| 3227 | (when (= (gnus-data-number (cadr data)) article) |
| 3228 | (setcdr data (cddr data)) |
| 3229 | (when offset |
| 3230 | (gnus-data-update-list (cdr data) offset)) |
| 3231 | (setq data nil |
| 3232 | gnus-newsgroup-data-reverse nil)) |
| 3233 | (setq data (cdr data)))))) |
| 3234 | |
| 3235 | (defmacro gnus-data-list (backward) |
| 3236 | `(if ,backward |
| 3237 | (or gnus-newsgroup-data-reverse |
| 3238 | (setq gnus-newsgroup-data-reverse |
| 3239 | (reverse gnus-newsgroup-data))) |
| 3240 | gnus-newsgroup-data)) |
| 3241 | |
| 3242 | (defun gnus-data-update-list (data offset) |
| 3243 | "Add OFFSET to the POS of all data entries in DATA." |
| 3244 | (setq gnus-newsgroup-data-reverse nil) |
| 3245 | (while data |
| 3246 | (setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data)))) |
| 3247 | (setq data (cdr data)))) |
| 3248 | |
| 3249 | (defun gnus-summary-article-pseudo-p (article) |
| 3250 | "Say whether this article is a pseudo article or not." |
| 3251 | (not (vectorp (gnus-data-header (gnus-data-find article))))) |
| 3252 | |
| 3253 | (defmacro gnus-summary-article-sparse-p (article) |
| 3254 | "Say whether this article is a sparse article or not." |
| 3255 | `(memq ,article gnus-newsgroup-sparse)) |
| 3256 | |
| 3257 | (defmacro gnus-summary-article-ancient-p (article) |
| 3258 | "Say whether this article is a sparse article or not." |
| 3259 | `(memq ,article gnus-newsgroup-ancient)) |
| 3260 | |
| 3261 | (defun gnus-article-children (number) |
| 3262 | "Return a list of all children to NUMBER." |
| 3263 | (let* ((data (gnus-data-find-list number)) |
| 3264 | (level (gnus-data-level (car data))) |
| 3265 | children) |
| 3266 | (setq data (cdr data)) |
| 3267 | (while (and data |
| 3268 | (= (gnus-data-level (car data)) (1+ level))) |
| 3269 | (push (gnus-data-number (car data)) children) |
| 3270 | (setq data (cdr data))) |
| 3271 | children)) |
| 3272 | |
| 3273 | (defmacro gnus-summary-skip-intangible () |
| 3274 | "If the current article is intangible, then jump to a different article." |
| 3275 | '(let ((to (get-text-property (point) 'gnus-intangible))) |
| 3276 | (and to (gnus-summary-goto-subject to)))) |
| 3277 | |
| 3278 | (defmacro gnus-summary-article-intangible-p () |
| 3279 | "Say whether this article is intangible or not." |
| 3280 | '(get-text-property (point) 'gnus-intangible)) |
| 3281 | |
| 3282 | ;; Some summary mode macros. |
| 3283 | |
| 3284 | (defmacro gnus-summary-article-number () |
| 3285 | "The article number of the article on the current line. |
| 3286 | If there isn't an article number here, then we return the current |
| 3287 | article number." |
| 3288 | '(progn |
| 3289 | (gnus-summary-skip-intangible) |
| 3290 | (or (get-text-property (point) 'gnus-number) |
| 3291 | (gnus-summary-last-subject)))) |
| 3292 | |
| 3293 | (defmacro gnus-summary-article-header (&optional number) |
| 3294 | "Return the header of article NUMBER." |
| 3295 | `(gnus-data-header (gnus-data-find |
| 3296 | ,(or number '(gnus-summary-article-number))))) |
| 3297 | |
| 3298 | (defmacro gnus-summary-thread-level (&optional number) |
| 3299 | "Return the level of thread that starts with article NUMBER." |
| 3300 | `(if (and (eq gnus-summary-make-false-root 'dummy) |
| 3301 | (get-text-property (point) 'gnus-intangible)) |
| 3302 | 0 |
| 3303 | (gnus-data-level (gnus-data-find |
| 3304 | ,(or number '(gnus-summary-article-number)))))) |
| 3305 | |
| 3306 | (defmacro gnus-summary-article-mark (&optional number) |
| 3307 | "Return the mark of article NUMBER." |
| 3308 | `(gnus-data-mark (gnus-data-find |
| 3309 | ,(or number '(gnus-summary-article-number))))) |
| 3310 | |
| 3311 | (defmacro gnus-summary-article-pos (&optional number) |
| 3312 | "Return the position of the line of article NUMBER." |
| 3313 | `(gnus-data-pos (gnus-data-find |
| 3314 | ,(or number '(gnus-summary-article-number))))) |
| 3315 | |
| 3316 | (defalias 'gnus-summary-subject-string 'gnus-summary-article-subject) |
| 3317 | (defmacro gnus-summary-article-subject (&optional number) |
| 3318 | "Return current subject string or nil if nothing." |
| 3319 | `(let ((headers |
| 3320 | ,(if number |
| 3321 | `(gnus-data-header (assq ,number gnus-newsgroup-data)) |
| 3322 | '(gnus-data-header (assq (gnus-summary-article-number) |
| 3323 | gnus-newsgroup-data))))) |
| 3324 | (and headers |
| 3325 | (vectorp headers) |
| 3326 | (mail-header-subject headers)))) |
| 3327 | |
| 3328 | (defmacro gnus-summary-article-score (&optional number) |
| 3329 | "Return current article score." |
| 3330 | `(or (cdr (assq ,(or number '(gnus-summary-article-number)) |
| 3331 | gnus-newsgroup-scored)) |
| 3332 | gnus-summary-default-score 0)) |
| 3333 | |
| 3334 | (defun gnus-summary-article-children (&optional number) |
| 3335 | "Return a list of article numbers that are children of article NUMBER." |
| 3336 | (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number)))) |
| 3337 | (level (gnus-data-level (car data))) |
| 3338 | l children) |
| 3339 | (while (and (setq data (cdr data)) |
| 3340 | (> (setq l (gnus-data-level (car data))) level)) |
| 3341 | (and (= (1+ level) l) |
| 3342 | (push (gnus-data-number (car data)) |
| 3343 | children))) |
| 3344 | (nreverse children))) |
| 3345 | |
| 3346 | (defun gnus-summary-article-parent (&optional number) |
| 3347 | "Return the article number of the parent of article NUMBER." |
| 3348 | (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number)) |
| 3349 | (gnus-data-list t))) |
| 3350 | (level (gnus-data-level (car data)))) |
| 3351 | (if (zerop level) |
| 3352 | () ; This is a root. |
| 3353 | ;; We search until we find an article with a level less than |
| 3354 | ;; this one. That function has to be the parent. |
| 3355 | (while (and (setq data (cdr data)) |
| 3356 | (not (< (gnus-data-level (car data)) level)))) |
| 3357 | (and data (gnus-data-number (car data)))))) |
| 3358 | |
| 3359 | (defun gnus-unread-mark-p (mark) |
| 3360 | "Say whether MARK is the unread mark." |
| 3361 | (= mark gnus-unread-mark)) |
| 3362 | |
| 3363 | (defun gnus-read-mark-p (mark) |
| 3364 | "Say whether MARK is one of the marks that mark as read. |
| 3365 | This is all marks except unread, ticked, dormant, and expirable." |
| 3366 | (not (or (= mark gnus-unread-mark) |
| 3367 | (= mark gnus-ticked-mark) |
| 3368 | (= mark gnus-spam-mark) |
| 3369 | (= mark gnus-dormant-mark) |
| 3370 | (= mark gnus-expirable-mark)))) |
| 3371 | |
| 3372 | (defmacro gnus-article-mark (number) |
| 3373 | "Return the MARK of article NUMBER. |
| 3374 | This macro should only be used when computing the mark the \"first\" |
| 3375 | time; i.e., when generating the summary lines. After that, |
| 3376 | `gnus-summary-article-mark' should be used to examine the |
| 3377 | marks of articles." |
| 3378 | `(cond |
| 3379 | ((memq ,number gnus-newsgroup-unsendable) gnus-unsendable-mark) |
| 3380 | ((memq ,number gnus-newsgroup-downloadable) gnus-downloadable-mark) |
| 3381 | ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark) |
| 3382 | ((memq ,number gnus-newsgroup-marked) gnus-ticked-mark) |
| 3383 | ((memq ,number gnus-newsgroup-spam-marked) gnus-spam-mark) |
| 3384 | ((memq ,number gnus-newsgroup-dormant) gnus-dormant-mark) |
| 3385 | ((memq ,number gnus-newsgroup-expirable) gnus-expirable-mark) |
| 3386 | (t (or (cdr (assq ,number gnus-newsgroup-reads)) |
| 3387 | gnus-ancient-mark)))) |
| 3388 | |
| 3389 | ;; Saving hidden threads. |
| 3390 | |
| 3391 | (defmacro gnus-save-hidden-threads (&rest forms) |
| 3392 | "Save hidden threads, eval FORMS, and restore the hidden threads." |
| 3393 | (let ((config (make-symbol "config"))) |
| 3394 | `(let ((,config (gnus-hidden-threads-configuration))) |
| 3395 | (unwind-protect |
| 3396 | (save-excursion |
| 3397 | ,@forms) |
| 3398 | (gnus-restore-hidden-threads-configuration ,config))))) |
| 3399 | (put 'gnus-save-hidden-threads 'lisp-indent-function 0) |
| 3400 | (put 'gnus-save-hidden-threads 'edebug-form-spec '(body)) |
| 3401 | |
| 3402 | (defun gnus-data-compute-positions () |
| 3403 | "Compute the positions of all articles." |
| 3404 | (setq gnus-newsgroup-data-reverse nil) |
| 3405 | (let ((data gnus-newsgroup-data)) |
| 3406 | (save-excursion |
| 3407 | (gnus-save-hidden-threads |
| 3408 | (gnus-summary-show-all-threads) |
| 3409 | (goto-char (point-min)) |
| 3410 | (while data |
| 3411 | (while (get-text-property (point) 'gnus-intangible) |
| 3412 | (forward-line 1)) |
| 3413 | (gnus-data-set-pos (car data) (+ (point) 3)) |
| 3414 | (setq data (cdr data)) |
| 3415 | (forward-line 1)))))) |
| 3416 | |
| 3417 | (defun gnus-hidden-threads-configuration () |
| 3418 | "Return the current hidden threads configuration." |
| 3419 | (save-excursion |
| 3420 | (let (config) |
| 3421 | (goto-char (point-min)) |
| 3422 | (while (not (eobp)) |
| 3423 | (when (eq (get-char-property (point-at-eol) 'invisible) 'gnus-sum) |
| 3424 | (push (save-excursion (forward-line 0) (point)) config)) |
| 3425 | (forward-line 1)) |
| 3426 | config))) |
| 3427 | |
| 3428 | (defun gnus-restore-hidden-threads-configuration (config) |
| 3429 | "Restore hidden threads configuration from CONFIG." |
| 3430 | (save-excursion |
| 3431 | (let (point (inhibit-read-only t)) |
| 3432 | (while (setq point (pop config)) |
| 3433 | (goto-char point) |
| 3434 | (gnus-summary-hide-thread))))) |
| 3435 | |
| 3436 | ;; Various summary mode internalish functions. |
| 3437 | |
| 3438 | (defun gnus-mouse-pick-article (e) |
| 3439 | (interactive "e") |
| 3440 | (mouse-set-point e) |
| 3441 | (gnus-summary-next-page nil t)) |
| 3442 | |
| 3443 | (defun gnus-summary-set-display-table () |
| 3444 | "Change the display table. |
| 3445 | Odd characters have a tendency to mess |
| 3446 | up nicely formatted displays - we make all possible glyphs |
| 3447 | display only a single character." |
| 3448 | |
| 3449 | ;; We start from the standard display table, if any. |
| 3450 | (let ((table (or (copy-sequence standard-display-table) |
| 3451 | (make-display-table))) |
| 3452 | (i 32)) |
| 3453 | ;; Nix out all the control chars... |
| 3454 | (while (>= (setq i (1- i)) 0) |
| 3455 | (gnus-put-display-table i [??] table)) |
| 3456 | ;; ... but not newline and cr, of course. (cr is necessary for the |
| 3457 | ;; selective display). |
| 3458 | (gnus-put-display-table ?\n nil table) |
| 3459 | (gnus-put-display-table ?\r nil table) |
| 3460 | ;; We keep TAB as well. |
| 3461 | (gnus-put-display-table ?\t nil table) |
| 3462 | ;; We nix out any glyphs 127 through 255, or 127 through 159 in |
| 3463 | ;; Emacs 23 (unicode), that are not set already. |
| 3464 | (let ((i (if (ignore-errors (= (make-char 'latin-iso8859-1 160) 160)) |
| 3465 | 160 |
| 3466 | 256))) |
| 3467 | (while (>= (setq i (1- i)) 127) |
| 3468 | ;; Only modify if the entry is nil. |
| 3469 | (unless (gnus-get-display-table i table) |
| 3470 | (gnus-put-display-table i [??] table)))) |
| 3471 | (setq buffer-display-table table))) |
| 3472 | |
| 3473 | (defun gnus-summary-set-article-display-arrow (pos) |
| 3474 | "Update the overlay arrow to point to line at position POS." |
| 3475 | (when gnus-summary-display-arrow |
| 3476 | (make-local-variable 'overlay-arrow-position) |
| 3477 | (make-local-variable 'overlay-arrow-string) |
| 3478 | (save-excursion |
| 3479 | (goto-char pos) |
| 3480 | (beginning-of-line) |
| 3481 | (unless overlay-arrow-position |
| 3482 | (setq overlay-arrow-position (make-marker))) |
| 3483 | (setq overlay-arrow-string "=>" |
| 3484 | overlay-arrow-position (set-marker overlay-arrow-position |
| 3485 | (point) |
| 3486 | (current-buffer)))))) |
| 3487 | |
| 3488 | (defun gnus-summary-setup-buffer (group) |
| 3489 | "Initialize summary buffer. |
| 3490 | If the setup was successful, non-nil is returned." |
| 3491 | (let ((buffer (gnus-summary-buffer-name group)) |
| 3492 | (dead-name (concat "*Dead Summary " |
| 3493 | (gnus-group-decoded-name group) "*"))) |
| 3494 | ;; If a dead summary buffer exists, we kill it. |
| 3495 | (when (gnus-buffer-live-p dead-name) |
| 3496 | (gnus-kill-buffer dead-name)) |
| 3497 | (if (get-buffer buffer) |
| 3498 | (progn |
| 3499 | (set-buffer buffer) |
| 3500 | (setq gnus-summary-buffer (current-buffer)) |
| 3501 | (not gnus-newsgroup-prepared)) |
| 3502 | (set-buffer (gnus-get-buffer-create buffer)) |
| 3503 | (setq gnus-summary-buffer (current-buffer)) |
| 3504 | (gnus-summary-mode group) |
| 3505 | (when (gnus-group-quit-config group) |
| 3506 | (set (make-local-variable 'gnus-single-article-buffer) nil)) |
| 3507 | (make-local-variable 'gnus-article-buffer) |
| 3508 | (make-local-variable 'gnus-article-current) |
| 3509 | (make-local-variable 'gnus-original-article-buffer) |
| 3510 | (setq gnus-newsgroup-name group) |
| 3511 | ;; Set any local variables in the group parameters. |
| 3512 | (gnus-summary-set-local-parameters gnus-newsgroup-name) |
| 3513 | t))) |
| 3514 | |
| 3515 | (defun gnus-set-global-variables () |
| 3516 | "Set the global equivalents of the buffer-local variables. |
| 3517 | They are set to the latest values they had. These reflect the summary |
| 3518 | buffer that was in action when the last article was fetched." |
| 3519 | (when (eq major-mode 'gnus-summary-mode) |
| 3520 | (setq gnus-summary-buffer (current-buffer)) |
| 3521 | (let ((name gnus-newsgroup-name) |
| 3522 | (marked gnus-newsgroup-marked) |
| 3523 | (spam gnus-newsgroup-spam-marked) |
| 3524 | (unread gnus-newsgroup-unreads) |
| 3525 | (headers gnus-current-headers) |
| 3526 | (data gnus-newsgroup-data) |
| 3527 | (summary gnus-summary-buffer) |
| 3528 | (article-buffer gnus-article-buffer) |
| 3529 | (original gnus-original-article-buffer) |
| 3530 | (gac gnus-article-current) |
| 3531 | (reffed gnus-reffed-article-number) |
| 3532 | (score-file gnus-current-score-file) |
| 3533 | (default-charset gnus-newsgroup-charset) |
| 3534 | vlist) |
| 3535 | (let ((locals gnus-newsgroup-variables)) |
| 3536 | (while locals |
| 3537 | (if (consp (car locals)) |
| 3538 | (push (eval (caar locals)) vlist) |
| 3539 | (push (eval (car locals)) vlist)) |
| 3540 | (setq locals (cdr locals))) |
| 3541 | (setq vlist (nreverse vlist))) |
| 3542 | (with-temp-buffer |
| 3543 | (setq gnus-newsgroup-name name |
| 3544 | gnus-newsgroup-marked marked |
| 3545 | gnus-newsgroup-spam-marked spam |
| 3546 | gnus-newsgroup-unreads unread |
| 3547 | gnus-current-headers headers |
| 3548 | gnus-newsgroup-data data |
| 3549 | gnus-article-current gac |
| 3550 | gnus-summary-buffer summary |
| 3551 | gnus-article-buffer article-buffer |
| 3552 | gnus-original-article-buffer original |
| 3553 | gnus-reffed-article-number reffed |
| 3554 | gnus-current-score-file score-file |
| 3555 | gnus-newsgroup-charset default-charset) |
| 3556 | (let ((locals gnus-newsgroup-variables)) |
| 3557 | (while locals |
| 3558 | (if (consp (car locals)) |
| 3559 | (set (caar locals) (pop vlist)) |
| 3560 | (set (car locals) (pop vlist))) |
| 3561 | (setq locals (cdr locals)))))))) |
| 3562 | |
| 3563 | (defun gnus-summary-article-unread-p (article) |
| 3564 | "Say whether ARTICLE is unread or not." |
| 3565 | (memq article gnus-newsgroup-unreads)) |
| 3566 | |
| 3567 | (defun gnus-summary-first-article-p (&optional article) |
| 3568 | "Return whether ARTICLE is the first article in the buffer." |
| 3569 | (if (not (setq article (or article (gnus-summary-article-number)))) |
| 3570 | nil |
| 3571 | (eq article (caar gnus-newsgroup-data)))) |
| 3572 | |
| 3573 | (defun gnus-summary-last-article-p (&optional article) |
| 3574 | "Return whether ARTICLE is the last article in the buffer." |
| 3575 | (if (not (setq article (or article (gnus-summary-article-number)))) |
| 3576 | ;; All non-existent numbers are the last article. :-) |
| 3577 | t |
| 3578 | (not (cdr (gnus-data-find-list article))))) |
| 3579 | |
| 3580 | (defun gnus-make-thread-indent-array (&optional n) |
| 3581 | (when (or n |
| 3582 | (progn (setq n 200) nil) |
| 3583 | (null gnus-thread-indent-array) |
| 3584 | (/= gnus-thread-indent-level gnus-thread-indent-array-level)) |
| 3585 | (setq gnus-thread-indent-array (make-vector (1+ n) "") |
| 3586 | gnus-thread-indent-array-level gnus-thread-indent-level) |
| 3587 | (while (>= n 0) |
| 3588 | (aset gnus-thread-indent-array n |
| 3589 | (make-string (* n gnus-thread-indent-level) ? )) |
| 3590 | (setq n (1- n))))) |
| 3591 | |
| 3592 | (defun gnus-update-summary-mark-positions () |
| 3593 | "Compute where the summary marks are to go." |
| 3594 | (save-excursion |
| 3595 | (when (gnus-buffer-exists-p gnus-summary-buffer) |
| 3596 | (set-buffer gnus-summary-buffer)) |
| 3597 | (let ((spec gnus-summary-line-format-spec) |
| 3598 | pos) |
| 3599 | (save-excursion |
| 3600 | (gnus-set-work-buffer) |
| 3601 | (let ((gnus-tmp-unread ?Z) |
| 3602 | (gnus-replied-mark ?Z) |
| 3603 | (gnus-score-below-mark ?Z) |
| 3604 | (gnus-score-over-mark ?Z) |
| 3605 | (gnus-undownloaded-mark ?Z) |
| 3606 | (gnus-summary-line-format-spec spec) |
| 3607 | (gnus-newsgroup-downloadable '(0)) |
| 3608 | (header [0 "" "" "05 Apr 2001 23:33:09 +0400" "" "" 0 0 "" nil]) |
| 3609 | case-fold-search ignores) |
| 3610 | ;; Here, all marks are bound to Z. |
| 3611 | (gnus-summary-insert-line header |
| 3612 | 0 nil t gnus-tmp-unread t nil "" nil 1) |
| 3613 | (goto-char (point-min)) |
| 3614 | ;; Memorize the positions of the same characters as dummy marks. |
| 3615 | (while (re-search-forward "[A-D]" nil t) |
| 3616 | (push (point) ignores)) |
| 3617 | (erase-buffer) |
| 3618 | ;; We use A-D as dummy marks in order to know column positions |
| 3619 | ;; where marks should be inserted. |
| 3620 | (setq gnus-tmp-unread ?A |
| 3621 | gnus-replied-mark ?B |
| 3622 | gnus-score-below-mark ?C |
| 3623 | gnus-score-over-mark ?C |
| 3624 | gnus-undownloaded-mark ?D) |
| 3625 | (gnus-summary-insert-line header |
| 3626 | 0 nil t gnus-tmp-unread t nil "" nil 1) |
| 3627 | ;; Ignore characters which aren't dummy marks. |
| 3628 | (dolist (p ignores) |
| 3629 | (delete-region (goto-char (1- p)) p) |
| 3630 | (insert ?Z)) |
| 3631 | (goto-char (point-min)) |
| 3632 | (setq pos (list (cons 'unread |
| 3633 | (and (search-forward "A" nil t) |
| 3634 | (- (point) (point-min) 1))))) |
| 3635 | (goto-char (point-min)) |
| 3636 | (push (cons 'replied (and (search-forward "B" nil t) |
| 3637 | (- (point) (point-min) 1))) |
| 3638 | pos) |
| 3639 | (goto-char (point-min)) |
| 3640 | (push (cons 'score (and (search-forward "C" nil t) |
| 3641 | (- (point) (point-min) 1))) |
| 3642 | pos) |
| 3643 | (goto-char (point-min)) |
| 3644 | (push (cons 'download (and (search-forward "D" nil t) |
| 3645 | (- (point) (point-min) 1))) |
| 3646 | pos))) |
| 3647 | (setq gnus-summary-mark-positions pos)))) |
| 3648 | |
| 3649 | (defun gnus-summary-insert-dummy-line (gnus-tmp-subject gnus-tmp-number) |
| 3650 | "Insert a dummy root in the summary buffer." |
| 3651 | (beginning-of-line) |
| 3652 | (gnus-add-text-properties |
| 3653 | (point) (progn (eval gnus-summary-dummy-line-format-spec) (point)) |
| 3654 | (list 'gnus-number gnus-tmp-number 'gnus-intangible gnus-tmp-number))) |
| 3655 | |
| 3656 | (defun gnus-summary-extract-address-component (from) |
| 3657 | (or (car (funcall gnus-extract-address-components from)) |
| 3658 | from)) |
| 3659 | |
| 3660 | (defun gnus-summary-from-or-to-or-newsgroups (header gnus-tmp-from) |
| 3661 | (let ((mail-parse-charset gnus-newsgroup-charset) |
| 3662 | (ignored-from-addresses (gnus-ignored-from-addresses)) |
| 3663 | ;; Is it really necessary to do this next part for each summary line? |
| 3664 | ;; Luckily, doesn't seem to slow things down much. |
| 3665 | (mail-parse-ignored-charsets |
| 3666 | (with-current-buffer gnus-summary-buffer |
| 3667 | gnus-newsgroup-ignored-charsets))) |
| 3668 | (or |
| 3669 | (and ignored-from-addresses |
| 3670 | (string-match ignored-from-addresses gnus-tmp-from) |
| 3671 | (let ((extra-headers (mail-header-extra header)) |
| 3672 | to |
| 3673 | newsgroups) |
| 3674 | (cond |
| 3675 | ((setq to (cdr (assq 'To extra-headers))) |
| 3676 | (concat gnus-summary-to-prefix |
| 3677 | (inline |
| 3678 | (gnus-summary-extract-address-component |
| 3679 | (funcall gnus-decode-encoded-address-function to))))) |
| 3680 | ((setq newsgroups |
| 3681 | (or |
| 3682 | (cdr (assq 'Newsgroups extra-headers)) |
| 3683 | (and |
| 3684 | (memq 'Newsgroups gnus-extra-headers) |
| 3685 | (eq (car (gnus-find-method-for-group |
| 3686 | gnus-newsgroup-name)) 'nntp) |
| 3687 | (gnus-group-real-name gnus-newsgroup-name)))) |
| 3688 | (concat gnus-summary-newsgroup-prefix newsgroups))))) |
| 3689 | (gnus-string-mark-left-to-right |
| 3690 | (inline |
| 3691 | (gnus-summary-extract-address-component gnus-tmp-from)))))) |
| 3692 | |
| 3693 | (defun gnus-summary-insert-line (gnus-tmp-header |
| 3694 | gnus-tmp-level gnus-tmp-current |
| 3695 | undownloaded gnus-tmp-unread gnus-tmp-replied |
| 3696 | gnus-tmp-expirable gnus-tmp-subject-or-nil |
| 3697 | &optional gnus-tmp-dummy gnus-tmp-score |
| 3698 | gnus-tmp-process) |
| 3699 | (if (>= gnus-tmp-level (length gnus-thread-indent-array)) |
| 3700 | (gnus-make-thread-indent-array (max (* 2 (length gnus-thread-indent-array)) |
| 3701 | gnus-tmp-level))) |
| 3702 | (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level)) |
| 3703 | (gnus-tmp-lines (mail-header-lines gnus-tmp-header)) |
| 3704 | (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0)) |
| 3705 | (gnus-tmp-score-char |
| 3706 | (if (or (null gnus-summary-default-score) |
| 3707 | (<= (abs (- gnus-tmp-score gnus-summary-default-score)) |
| 3708 | gnus-summary-zcore-fuzz)) |
| 3709 | ? ;Whitespace |
| 3710 | (if (< gnus-tmp-score gnus-summary-default-score) |
| 3711 | gnus-score-below-mark gnus-score-over-mark))) |
| 3712 | (gnus-tmp-number (mail-header-number gnus-tmp-header)) |
| 3713 | (gnus-tmp-replied |
| 3714 | (cond (gnus-tmp-process gnus-process-mark) |
| 3715 | ((memq gnus-tmp-current gnus-newsgroup-cached) |
| 3716 | gnus-cached-mark) |
| 3717 | (gnus-tmp-replied gnus-replied-mark) |
| 3718 | ((memq gnus-tmp-current gnus-newsgroup-forwarded) |
| 3719 | gnus-forwarded-mark) |
| 3720 | ((memq gnus-tmp-current gnus-newsgroup-saved) |
| 3721 | gnus-saved-mark) |
| 3722 | ((memq gnus-tmp-number gnus-newsgroup-unseen) |
| 3723 | gnus-unseen-mark) |
| 3724 | (t gnus-no-mark))) |
| 3725 | (gnus-tmp-downloaded |
| 3726 | (cond (undownloaded |
| 3727 | gnus-undownloaded-mark) |
| 3728 | (gnus-newsgroup-agentized |
| 3729 | gnus-downloaded-mark) |
| 3730 | (t |
| 3731 | gnus-no-mark))) |
| 3732 | (gnus-tmp-from (mail-header-from gnus-tmp-header)) |
| 3733 | (gnus-tmp-name |
| 3734 | (cond |
| 3735 | ((string-match "<[^>]+> *$" gnus-tmp-from) |
| 3736 | (let ((beg (match-beginning 0))) |
| 3737 | (or (and (string-match "^\".+\"" gnus-tmp-from) |
| 3738 | (substring gnus-tmp-from 1 (1- (match-end 0)))) |
| 3739 | (substring gnus-tmp-from 0 beg)))) |
| 3740 | ((string-match "(.+)" gnus-tmp-from) |
| 3741 | (substring gnus-tmp-from |
| 3742 | (1+ (match-beginning 0)) (1- (match-end 0)))) |
| 3743 | (t gnus-tmp-from))) |
| 3744 | (gnus-tmp-subject (mail-header-subject gnus-tmp-header)) |
| 3745 | (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[)) |
| 3746 | (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\])) |
| 3747 | (inhibit-read-only t)) |
| 3748 | (when (string= gnus-tmp-name "") |
| 3749 | (setq gnus-tmp-name gnus-tmp-from)) |
| 3750 | (unless (numberp gnus-tmp-lines) |
| 3751 | (setq gnus-tmp-lines -1)) |
| 3752 | (if (= gnus-tmp-lines -1) |
| 3753 | (setq gnus-tmp-lines "?") |
| 3754 | (setq gnus-tmp-lines (number-to-string gnus-tmp-lines))) |
| 3755 | (condition-case () |
| 3756 | (gnus-put-text-property |
| 3757 | (point) |
| 3758 | (progn (eval gnus-summary-line-format-spec) (point)) |
| 3759 | 'gnus-number gnus-tmp-number) |
| 3760 | (error (gnus-message 5 "Error updating the summary line"))) |
| 3761 | (when (gnus-visual-p 'summary-highlight 'highlight) |
| 3762 | (forward-line -1) |
| 3763 | (gnus-summary-highlight-line) |
| 3764 | (gnus-run-hooks 'gnus-summary-update-hook) |
| 3765 | (forward-line 1)))) |
| 3766 | |
| 3767 | (defun gnus-summary-update-line (&optional dont-update) |
| 3768 | "Update summary line after change." |
| 3769 | (when (and gnus-summary-default-score |
| 3770 | (not gnus-summary-inhibit-highlight)) |
| 3771 | (let* ((gnus-summary-inhibit-highlight t) ; Prevent recursion. |
| 3772 | (article (gnus-summary-article-number)) |
| 3773 | (score (gnus-summary-article-score article))) |
| 3774 | (unless dont-update |
| 3775 | (if (and gnus-summary-mark-below |
| 3776 | (< (gnus-summary-article-score) |
| 3777 | gnus-summary-mark-below)) |
| 3778 | ;; This article has a low score, so we mark it as read. |
| 3779 | (when (memq article gnus-newsgroup-unreads) |
| 3780 | (gnus-summary-mark-article-as-read gnus-low-score-mark)) |
| 3781 | (when (eq (gnus-summary-article-mark) gnus-low-score-mark) |
| 3782 | ;; This article was previously marked as read on account |
| 3783 | ;; of a low score, but now it has risen, so we mark it as |
| 3784 | ;; unread. |
| 3785 | (gnus-summary-mark-article-as-unread gnus-unread-mark))) |
| 3786 | (gnus-summary-update-mark |
| 3787 | (if (or (null gnus-summary-default-score) |
| 3788 | (<= (abs (- score gnus-summary-default-score)) |
| 3789 | gnus-summary-zcore-fuzz)) |
| 3790 | ? ;Whitespace |
| 3791 | (if (< score gnus-summary-default-score) |
| 3792 | gnus-score-below-mark gnus-score-over-mark)) |
| 3793 | 'score)) |
| 3794 | ;; Do visual highlighting. |
| 3795 | (when (gnus-visual-p 'summary-highlight 'highlight) |
| 3796 | (gnus-summary-highlight-line) |
| 3797 | (gnus-run-hooks 'gnus-summary-update-hook))))) |
| 3798 | |
| 3799 | (defvar gnus-tmp-new-adopts nil) |
| 3800 | |
| 3801 | (defun gnus-summary-number-of-articles-in-thread (thread &optional level char) |
| 3802 | "Return the number of articles in THREAD. |
| 3803 | This may be 0 in some cases -- if none of the articles in |
| 3804 | the thread are to be displayed." |
| 3805 | (let* ((number |
| 3806 | ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>. |
| 3807 | (cond |
| 3808 | ((not (listp thread)) |
| 3809 | 1) |
| 3810 | ((and (consp thread) (cdr thread)) |
| 3811 | (apply |
| 3812 | '+ 1 (mapcar |
| 3813 | 'gnus-summary-number-of-articles-in-thread (cdr thread)))) |
| 3814 | ((null thread) |
| 3815 | 1) |
| 3816 | ((memq (mail-header-number (car thread)) gnus-newsgroup-limit) |
| 3817 | 1) |
| 3818 | (t 0)))) |
| 3819 | (when (and level (zerop level) gnus-tmp-new-adopts) |
| 3820 | (incf number |
| 3821 | (apply '+ (mapcar |
| 3822 | 'gnus-summary-number-of-articles-in-thread |
| 3823 | gnus-tmp-new-adopts)))) |
| 3824 | (if char |
| 3825 | (if (> number 1) gnus-not-empty-thread-mark |
| 3826 | gnus-empty-thread-mark) |
| 3827 | number))) |
| 3828 | |
| 3829 | (defsubst gnus-summary-line-message-size (head) |
| 3830 | "Return pretty-printed version of message size. |
| 3831 | This function is intended to be used in |
| 3832 | `gnus-summary-line-format-alist'." |
| 3833 | (let ((c (or (mail-header-chars head) -1))) |
| 3834 | (cond ((< c 0) "n/a") ; chars not available |
| 3835 | ((< c (* 1000 10)) (format "%1.1fk" (/ c 1024.0))) |
| 3836 | ((< c (* 1000 100)) (format "%dk" (/ c 1024.0))) |
| 3837 | ((< c (* 1000 10000)) (format "%1.1fM" (/ c (* 1024.0 1024)))) |
| 3838 | (t (format "%dM" (/ c (* 1024.0 1024))))))) |
| 3839 | |
| 3840 | (defcustom gnus-user-date-format-alist |
| 3841 | '(((gnus-seconds-today) . "Today, %H:%M") |
| 3842 | ((+ 86400 (gnus-seconds-today)) . "Yesterday, %H:%M") |
| 3843 | (604800 . "%A %H:%M") ; That's one week |
| 3844 | ((gnus-seconds-month) . "%A %d") |
| 3845 | ((gnus-seconds-year) . "%B %d") |
| 3846 | (t . "%b %d %Y")) ; This one is used when no other |
| 3847 | ; does match |
| 3848 | "Specifies date format depending on age of article. |
| 3849 | This is an alist of items (AGE . FORMAT). AGE can be a number (of |
| 3850 | seconds) or a Lisp expression evaluating to a number. When the age of |
| 3851 | the article is less than this number, then use `format-time-string' |
| 3852 | with the corresponding FORMAT for displaying the date of the article. |
| 3853 | If AGE is not a number or a Lisp expression evaluating to a |
| 3854 | non-number, then the corresponding FORMAT is used as a default value. |
| 3855 | |
| 3856 | Note that the list is processed from the beginning, so it should be |
| 3857 | sorted by ascending AGE. Also note that items following the first |
| 3858 | non-number AGE will be ignored. |
| 3859 | |
| 3860 | You can use the functions `gnus-seconds-today', `gnus-seconds-month' |
| 3861 | and `gnus-seconds-year' in the AGE spec. They return the number of |
| 3862 | seconds passed since the start of today, of this month, of this year, |
| 3863 | respectively." |
| 3864 | :version "24.1" |
| 3865 | :group 'gnus-summary-format |
| 3866 | :type '(alist :key-type sexp :value-type string)) |
| 3867 | |
| 3868 | (defun gnus-user-date (messy-date) |
| 3869 | "Format the messy-date according to `gnus-user-date-format-alist'. |
| 3870 | Returns \" ? \" if there's bad input or if another error occurs. |
| 3871 | Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"." |
| 3872 | (condition-case () |
| 3873 | (let* ((messy-date (gnus-float-time (gnus-date-get-time messy-date))) |
| 3874 | (now (gnus-float-time)) |
| 3875 | ;;If we don't find something suitable we'll use this one |
| 3876 | (my-format "%b %d '%y")) |
| 3877 | (let* ((difference (- now messy-date)) |
| 3878 | (templist gnus-user-date-format-alist) |
| 3879 | (top (eval (caar templist)))) |
| 3880 | (while (if (numberp top) (< top difference) (not top)) |
| 3881 | (progn |
| 3882 | (setq templist (cdr templist)) |
| 3883 | (setq top (eval (caar templist))))) |
| 3884 | (if (stringp (cdr (car templist))) |
| 3885 | (setq my-format (cdr (car templist))))) |
| 3886 | (format-time-string (eval my-format) (seconds-to-time messy-date))) |
| 3887 | (error " ? "))) |
| 3888 | |
| 3889 | (defun gnus-summary-set-local-parameters (group) |
| 3890 | "Go through the local params of GROUP and set all variable specs in that list." |
| 3891 | (let ((vars '(quit-config active))) ; Ignore things that aren't |
| 3892 | ; really variables. |
| 3893 | (dolist (elem (gnus-group-find-parameter group)) |
| 3894 | (and (consp elem) ; Has to be a cons. |
| 3895 | (consp (cdr elem)) ; The cdr has to be a list. |
| 3896 | (symbolp (car elem)) ; Has to be a symbol in there. |
| 3897 | (not (memq (car elem) vars)) |
| 3898 | (ignore-errors |
| 3899 | (push (car elem) vars) |
| 3900 | ;; Variables like `gnus-show-threads' that are globally |
| 3901 | ;; bound, if used as group parameters, need to get to be |
| 3902 | ;; buffer-local, whereas just parameters like `gcc-self', |
| 3903 | ;; `timestamp', etc. should not be bound as variables. |
| 3904 | (if (boundp (car elem)) |
| 3905 | (set (make-local-variable (car elem)) (eval (nth 1 elem))) |
| 3906 | (eval (nth 1 elem)))))))) |
| 3907 | |
| 3908 | (defun gnus-summary-read-group (group &optional show-all no-article |
| 3909 | kill-buffer no-display backward |
| 3910 | select-articles) |
| 3911 | "Start reading news in newsgroup GROUP. |
| 3912 | If SHOW-ALL is non-nil, already read articles are also listed. |
| 3913 | If NO-ARTICLE is non-nil, no article is selected initially. |
| 3914 | If NO-DISPLAY, don't generate the summary buffer contents. |
| 3915 | If KILL-BUFFER, it should be a buffer that's killed once the new |
| 3916 | summary buffer has been generated. |
| 3917 | If BACKWARD, move point to the previous group in the group buffer |
| 3918 | If SELECT-ARTICLES, only select those articles from GROUP." |
| 3919 | (let (result) |
| 3920 | (while (and group |
| 3921 | (null (setq result |
| 3922 | (let ((gnus-auto-select-next nil)) |
| 3923 | (or (gnus-summary-read-group-1 |
| 3924 | group show-all no-article |
| 3925 | kill-buffer no-display |
| 3926 | select-articles) |
| 3927 | (setq show-all nil |
| 3928 | select-articles nil))))) |
| 3929 | (eq gnus-auto-select-next 'quietly)) |
| 3930 | (set-buffer gnus-group-buffer) |
| 3931 | ;; The entry function called above goes to the next |
| 3932 | ;; group automatically, so we go two groups back |
| 3933 | ;; if we are searching for the previous group. |
| 3934 | (when backward |
| 3935 | (gnus-group-prev-unread-group 2)) |
| 3936 | (if (not (equal group (gnus-group-group-name))) |
| 3937 | (setq group (gnus-group-group-name)) |
| 3938 | (setq group nil))) |
| 3939 | result)) |
| 3940 | |
| 3941 | (defun gnus-summary-read-group-1 (group show-all no-article |
| 3942 | kill-buffer no-display |
| 3943 | &optional select-articles) |
| 3944 | ;; Killed foreign groups can't be entered. |
| 3945 | ;; (when (and (not (gnus-group-native-p group)) |
| 3946 | ;; (not (gnus-gethash group gnus-newsrc-hashtb))) |
| 3947 | ;; (error "Dead non-native groups can't be entered")) |
| 3948 | (gnus-message 7 "Retrieving newsgroup: %s..." |
| 3949 | (gnus-group-decoded-name group)) |
| 3950 | (let* ((new-group (gnus-summary-setup-buffer group)) |
| 3951 | (quit-config (gnus-group-quit-config group)) |
| 3952 | (did-select (and new-group (gnus-select-newsgroup |
| 3953 | group show-all select-articles)))) |
| 3954 | (cond |
| 3955 | ;; This summary buffer exists already, so we just select it. |
| 3956 | ((not new-group) |
| 3957 | (gnus-set-global-variables) |
| 3958 | (when kill-buffer |
| 3959 | (gnus-kill-or-deaden-summary kill-buffer)) |
| 3960 | (gnus-configure-windows 'summary 'force) |
| 3961 | (gnus-set-mode-line 'summary) |
| 3962 | (gnus-summary-position-point) |
| 3963 | (message "") |
| 3964 | t) |
| 3965 | ;; We couldn't select this group. |
| 3966 | ((null did-select) |
| 3967 | (when (and (eq major-mode 'gnus-summary-mode) |
| 3968 | (not (equal (current-buffer) kill-buffer))) |
| 3969 | (kill-buffer (current-buffer)) |
| 3970 | (if (not quit-config) |
| 3971 | (progn |
| 3972 | ;; Update the info -- marks might need to be removed, |
| 3973 | ;; for instance. |
| 3974 | (gnus-summary-update-info) |
| 3975 | (set-buffer gnus-group-buffer) |
| 3976 | (gnus-group-jump-to-group group) |
| 3977 | (gnus-group-next-unread-group 1)) |
| 3978 | (gnus-handle-ephemeral-exit quit-config))) |
| 3979 | (if (null (gnus-list-of-unread-articles group)) |
| 3980 | (gnus-message 3 "Group %s contains no messages" group) |
| 3981 | (gnus-message 3 "Can't select group")) |
| 3982 | nil) |
| 3983 | ;; The user did a `C-g' while prompting for number of articles, |
| 3984 | ;; so we exit this group. |
| 3985 | ((eq did-select 'quit) |
| 3986 | (and (eq major-mode 'gnus-summary-mode) |
| 3987 | (not (equal (current-buffer) kill-buffer)) |
| 3988 | (kill-buffer (current-buffer))) |
| 3989 | (when kill-buffer |
| 3990 | (gnus-kill-or-deaden-summary kill-buffer)) |
| 3991 | (if (not quit-config) |
| 3992 | (progn |
| 3993 | (set-buffer gnus-group-buffer) |
| 3994 | (gnus-group-jump-to-group group) |
| 3995 | (gnus-configure-windows 'group 'force)) |
| 3996 | (gnus-handle-ephemeral-exit quit-config)) |
| 3997 | ;; Finally signal the quit. |
| 3998 | (signal 'quit nil)) |
| 3999 | ;; The group was successfully selected. |
| 4000 | (t |
| 4001 | (gnus-set-global-variables) |
| 4002 | ;; Save the active value in effect when the group was entered. |
| 4003 | (setq gnus-newsgroup-active |
| 4004 | (gnus-copy-sequence |
| 4005 | (gnus-active gnus-newsgroup-name))) |
| 4006 | (setq gnus-newsgroup-highest (cdr gnus-newsgroup-active)) |
| 4007 | ;; You can change the summary buffer in some way with this hook. |
| 4008 | (gnus-run-hooks 'gnus-select-group-hook) |
| 4009 | (when (memq 'summary (gnus-update-format-specifications |
| 4010 | nil 'summary 'summary-mode 'summary-dummy)) |
| 4011 | ;; The format specification for the summary line was updated, |
| 4012 | ;; so we need to update the mark positions as well. |
| 4013 | (gnus-update-summary-mark-positions)) |
| 4014 | ;; Do score processing. |
| 4015 | (when gnus-use-scoring |
| 4016 | (gnus-possibly-score-headers)) |
| 4017 | ;; Check whether to fill in the gaps in the threads. |
| 4018 | (when gnus-build-sparse-threads |
| 4019 | (gnus-build-sparse-threads)) |
| 4020 | ;; Find the initial limit. |
| 4021 | (if show-all |
| 4022 | (let ((gnus-newsgroup-dormant nil)) |
| 4023 | (gnus-summary-initial-limit show-all)) |
| 4024 | (gnus-summary-initial-limit show-all)) |
| 4025 | ;; Generate the summary buffer. |
| 4026 | (unless no-display |
| 4027 | (gnus-summary-prepare)) |
| 4028 | (when gnus-use-trees |
| 4029 | (gnus-tree-open group) |
| 4030 | (setq gnus-summary-highlight-line-function |
| 4031 | 'gnus-tree-highlight-article)) |
| 4032 | ;; If the summary buffer is empty, but there are some low-scored |
| 4033 | ;; articles or some excluded dormants, we include these in the |
| 4034 | ;; buffer. |
| 4035 | (when (and (zerop (buffer-size)) |
| 4036 | (not no-display)) |
| 4037 | (cond (gnus-newsgroup-dormant |
| 4038 | (gnus-summary-limit-include-dormant)) |
| 4039 | ((and gnus-newsgroup-scored show-all) |
| 4040 | (gnus-summary-limit-include-expunged t)))) |
| 4041 | ;; Function `gnus-apply-kill-file' must be called in this hook. |
| 4042 | (gnus-run-hooks 'gnus-apply-kill-hook) |
| 4043 | (if (and (zerop (buffer-size)) |
| 4044 | (not no-display)) |
| 4045 | (progn |
| 4046 | ;; This newsgroup is empty. |
| 4047 | (gnus-summary-catchup-and-exit nil t) |
| 4048 | (gnus-message 6 "No unread news") |
| 4049 | (when kill-buffer |
| 4050 | (gnus-kill-or-deaden-summary kill-buffer)) |
| 4051 | ;; Return nil from this function. |
| 4052 | nil) |
| 4053 | ;; Hide conversation thread subtrees. We cannot do this in |
| 4054 | ;; gnus-summary-prepare-hook since kill processing may not |
| 4055 | ;; work with hidden articles. |
| 4056 | (gnus-summary-maybe-hide-threads) |
| 4057 | (gnus-configure-windows 'summary) |
| 4058 | (when kill-buffer |
| 4059 | (gnus-kill-or-deaden-summary kill-buffer)) |
| 4060 | (gnus-summary-auto-select-subject) |
| 4061 | ;; Show first unread article if requested. |
| 4062 | (if (and (not no-article) |
| 4063 | (not no-display) |
| 4064 | gnus-newsgroup-unreads |
| 4065 | gnus-auto-select-first) |
| 4066 | (progn |
| 4067 | (let ((art (gnus-summary-article-number))) |
| 4068 | (when (and art |
| 4069 | gnus-plugged |
| 4070 | (not (memq art gnus-newsgroup-undownloaded)) |
| 4071 | (not (memq art gnus-newsgroup-downloadable))) |
| 4072 | (gnus-summary-goto-article art)))) |
| 4073 | ;; Don't select any articles. |
| 4074 | (gnus-summary-position-point) |
| 4075 | (gnus-configure-windows 'summary 'force) |
| 4076 | (gnus-set-mode-line 'summary)) |
| 4077 | (when (and gnus-auto-center-group |
| 4078 | (get-buffer-window gnus-group-buffer t)) |
| 4079 | ;; Gotta use windows, because recenter does weird stuff if |
| 4080 | ;; the current buffer ain't the displayed window. |
| 4081 | (let ((owin (selected-window))) |
| 4082 | (select-window (get-buffer-window gnus-group-buffer t)) |
| 4083 | (when (gnus-group-goto-group group) |
| 4084 | (recenter)) |
| 4085 | (select-window owin))) |
| 4086 | ;; Mark this buffer as "prepared". |
| 4087 | (setq gnus-newsgroup-prepared t) |
| 4088 | (gnus-run-hooks 'gnus-summary-prepared-hook) |
| 4089 | (unless (gnus-ephemeral-group-p group) |
| 4090 | (gnus-group-update-group group nil t)) |
| 4091 | t))))) |
| 4092 | |
| 4093 | (defun gnus-summary-auto-select-subject () |
| 4094 | "Select the subject line on initial group entry." |
| 4095 | (goto-char (point-min)) |
| 4096 | (cond |
| 4097 | ((eq gnus-auto-select-subject 'best) |
| 4098 | (gnus-summary-best-unread-subject)) |
| 4099 | ((eq gnus-auto-select-subject 'unread) |
| 4100 | (gnus-summary-first-unread-subject)) |
| 4101 | ((eq gnus-auto-select-subject 'unseen) |
| 4102 | (gnus-summary-first-unseen-subject)) |
| 4103 | ((eq gnus-auto-select-subject 'unseen-or-unread) |
| 4104 | (gnus-summary-first-unseen-or-unread-subject)) |
| 4105 | ((eq gnus-auto-select-subject 'first) |
| 4106 | ;; Do nothing. |
| 4107 | ) |
| 4108 | ((functionp gnus-auto-select-subject) |
| 4109 | (funcall gnus-auto-select-subject)))) |
| 4110 | |
| 4111 | (defun gnus-summary-prepare () |
| 4112 | "Generate the summary buffer." |
| 4113 | (interactive) |
| 4114 | (let ((inhibit-read-only t)) |
| 4115 | (erase-buffer) |
| 4116 | (setq gnus-newsgroup-data nil |
| 4117 | gnus-newsgroup-data-reverse nil) |
| 4118 | (gnus-run-hooks 'gnus-summary-generate-hook) |
| 4119 | ;; Generate the buffer, either with threads or without. |
| 4120 | (when gnus-newsgroup-headers |
| 4121 | (gnus-summary-prepare-threads |
| 4122 | (if gnus-show-threads |
| 4123 | (gnus-sort-gathered-threads |
| 4124 | (funcall gnus-summary-thread-gathering-function |
| 4125 | (gnus-sort-threads |
| 4126 | (gnus-cut-threads (gnus-make-threads))))) |
| 4127 | ;; Unthreaded display. |
| 4128 | (gnus-sort-articles gnus-newsgroup-headers)))) |
| 4129 | (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data)) |
| 4130 | ;; Call hooks for modifying summary buffer. |
| 4131 | (goto-char (point-min)) |
| 4132 | (gnus-run-hooks 'gnus-summary-prepare-hook))) |
| 4133 | |
| 4134 | (defsubst gnus-general-simplify-subject (subject) |
| 4135 | "Simplify subject by the same rules as `gnus-gather-threads-by-subject'." |
| 4136 | (setq subject |
| 4137 | (cond |
| 4138 | ;; Truncate the subject. |
| 4139 | (gnus-simplify-subject-functions |
| 4140 | (gnus-map-function gnus-simplify-subject-functions subject)) |
| 4141 | ((numberp gnus-summary-gather-subject-limit) |
| 4142 | (setq subject (gnus-simplify-subject-re subject)) |
| 4143 | (if (> (length subject) gnus-summary-gather-subject-limit) |
| 4144 | (substring subject 0 gnus-summary-gather-subject-limit) |
| 4145 | subject)) |
| 4146 | ;; Fuzzily simplify it. |
| 4147 | ((eq 'fuzzy gnus-summary-gather-subject-limit) |
| 4148 | (gnus-simplify-subject-fuzzy subject)) |
| 4149 | ;; Just remove the leading "Re:". |
| 4150 | (t |
| 4151 | (gnus-simplify-subject-re subject)))) |
| 4152 | |
| 4153 | (if (and gnus-summary-gather-exclude-subject |
| 4154 | (string-match gnus-summary-gather-exclude-subject subject)) |
| 4155 | nil ; This article shouldn't be gathered |
| 4156 | subject)) |
| 4157 | |
| 4158 | (defun gnus-summary-simplify-subject-query () |
| 4159 | "Query where the respool algorithm would put this article." |
| 4160 | (interactive) |
| 4161 | (gnus-summary-select-article) |
| 4162 | (message "%s" (gnus-general-simplify-subject (gnus-summary-article-subject)))) |
| 4163 | |
| 4164 | (defun gnus-gather-threads-by-subject (threads) |
| 4165 | "Gather threads by looking at Subject headers." |
| 4166 | (if (not gnus-summary-make-false-root) |
| 4167 | threads |
| 4168 | (let ((hashtb (gnus-make-hashtable 1024)) |
| 4169 | (prev threads) |
| 4170 | (result threads) |
| 4171 | subject hthread whole-subject) |
| 4172 | (while threads |
| 4173 | (setq subject (gnus-general-simplify-subject |
| 4174 | (setq whole-subject (mail-header-subject |
| 4175 | (caar threads))))) |
| 4176 | (when subject |
| 4177 | (if (setq hthread (gnus-gethash subject hashtb)) |
| 4178 | (progn |
| 4179 | ;; We enter a dummy root into the thread, if we |
| 4180 | ;; haven't done that already. |
| 4181 | (unless (stringp (caar hthread)) |
| 4182 | (setcar hthread (list whole-subject (car hthread)))) |
| 4183 | ;; We add this new gathered thread to this gathered |
| 4184 | ;; thread. |
| 4185 | (setcdr (car hthread) |
| 4186 | (nconc (cdar hthread) (list (car threads)))) |
| 4187 | ;; Remove it from the list of threads. |
| 4188 | (setcdr prev (cdr threads)) |
| 4189 | (setq threads prev)) |
| 4190 | ;; Enter this thread into the hash table. |
| 4191 | (gnus-sethash subject |
| 4192 | (if gnus-summary-make-false-root-always |
| 4193 | (progn |
| 4194 | ;; If you want a dummy root above all |
| 4195 | ;; threads... |
| 4196 | (setcar threads (list whole-subject |
| 4197 | (car threads))) |
| 4198 | threads) |
| 4199 | threads) |
| 4200 | hashtb))) |
| 4201 | (setq prev threads) |
| 4202 | (setq threads (cdr threads))) |
| 4203 | result))) |
| 4204 | |
| 4205 | (defun gnus-gather-threads-by-references (threads) |
| 4206 | "Gather threads by looking at References headers." |
| 4207 | (let ((idhashtb (gnus-make-hashtable 1024)) |
| 4208 | (thhashtb (gnus-make-hashtable 1024)) |
| 4209 | (prev threads) |
| 4210 | (result threads) |
| 4211 | ids references id gthread gid entered ref) |
| 4212 | (while threads |
| 4213 | (when (setq references (mail-header-references (caar threads))) |
| 4214 | (setq id (mail-header-id (caar threads)) |
| 4215 | ids (inline (gnus-split-references references)) |
| 4216 | entered nil) |
| 4217 | (while (setq ref (pop ids)) |
| 4218 | (setq ids (delete ref ids)) |
| 4219 | (if (not (setq gid (gnus-gethash ref idhashtb))) |
| 4220 | (progn |
| 4221 | (gnus-sethash ref id idhashtb) |
| 4222 | (gnus-sethash id threads thhashtb)) |
| 4223 | (setq gthread (gnus-gethash gid thhashtb)) |
| 4224 | (unless entered |
| 4225 | ;; We enter a dummy root into the thread, if we |
| 4226 | ;; haven't done that already. |
| 4227 | (unless (stringp (caar gthread)) |
| 4228 | (setcar gthread (list (mail-header-subject (caar gthread)) |
| 4229 | (car gthread)))) |
| 4230 | ;; We add this new gathered thread to this gathered |
| 4231 | ;; thread. |
| 4232 | (setcdr (car gthread) |
| 4233 | (nconc (cdar gthread) (list (car threads))))) |
| 4234 | ;; Add it into the thread hash table. |
| 4235 | (gnus-sethash id gthread thhashtb) |
| 4236 | (setq entered t) |
| 4237 | ;; Remove it from the list of threads. |
| 4238 | (setcdr prev (cdr threads)) |
| 4239 | (setq threads prev)))) |
| 4240 | (setq prev threads) |
| 4241 | (setq threads (cdr threads))) |
| 4242 | result)) |
| 4243 | |
| 4244 | (defun gnus-sort-gathered-threads (threads) |
| 4245 | "Sort subthreads inside each gathered thread by `gnus-sort-gathered-threads-function'." |
| 4246 | (let ((result threads)) |
| 4247 | (while threads |
| 4248 | (when (stringp (caar threads)) |
| 4249 | (setcdr (car threads) |
| 4250 | (sort (cdar threads) gnus-sort-gathered-threads-function))) |
| 4251 | (setq threads (cdr threads))) |
| 4252 | result)) |
| 4253 | |
| 4254 | (defun gnus-thread-loop-p (root thread) |
| 4255 | "Say whether ROOT is in THREAD." |
| 4256 | (let ((stack (list thread)) |
| 4257 | (infloop 0) |
| 4258 | th) |
| 4259 | (while (setq thread (pop stack)) |
| 4260 | (setq th (cdr thread)) |
| 4261 | (while (and th |
| 4262 | (not (eq (caar th) root))) |
| 4263 | (pop th)) |
| 4264 | (if th |
| 4265 | ;; We have found a loop. |
| 4266 | (let (ref-dep) |
| 4267 | (setcdr thread (delq (car th) (cdr thread))) |
| 4268 | (if (boundp (setq ref-dep (intern "none" |
| 4269 | gnus-newsgroup-dependencies))) |
| 4270 | (setcdr (symbol-value ref-dep) |
| 4271 | (nconc (cdr (symbol-value ref-dep)) |
| 4272 | (list (car th)))) |
| 4273 | (set ref-dep (list nil (car th)))) |
| 4274 | (setq infloop 1 |
| 4275 | stack nil)) |
| 4276 | ;; Push all the subthreads onto the stack. |
| 4277 | (push (cdr thread) stack))) |
| 4278 | infloop)) |
| 4279 | |
| 4280 | (defun gnus-make-threads () |
| 4281 | "Go through the dependency hashtb and find the roots. Return all threads." |
| 4282 | (let (threads) |
| 4283 | (while (catch 'infloop |
| 4284 | (mapatoms |
| 4285 | (lambda (refs) |
| 4286 | ;; Deal with self-referencing References loops. |
| 4287 | (when (and (car (symbol-value refs)) |
| 4288 | (not (zerop |
| 4289 | (apply |
| 4290 | '+ |
| 4291 | (mapcar |
| 4292 | (lambda (thread) |
| 4293 | (gnus-thread-loop-p |
| 4294 | (car (symbol-value refs)) thread)) |
| 4295 | (cdr (symbol-value refs))))))) |
| 4296 | (setq threads nil) |
| 4297 | (throw 'infloop t)) |
| 4298 | (unless (car (symbol-value refs)) |
| 4299 | ;; These threads do not refer back to any other |
| 4300 | ;; articles, so they're roots. |
| 4301 | (setq threads (append (cdr (symbol-value refs)) threads)))) |
| 4302 | gnus-newsgroup-dependencies))) |
| 4303 | threads)) |
| 4304 | |
| 4305 | ;; Build the thread tree. |
| 4306 | (defsubst gnus-dependencies-add-header (header dependencies force-new) |
| 4307 | "Enter HEADER into the DEPENDENCIES table if it is not already there. |
| 4308 | |
| 4309 | If FORCE-NEW is not nil, enter HEADER into the DEPENDENCIES table even |
| 4310 | if it was already present. |
| 4311 | |
| 4312 | If `gnus-summary-ignore-duplicates' is nil then duplicate Message-IDs |
| 4313 | will not be entered in the DEPENDENCIES table. Otherwise duplicate |
| 4314 | Message-IDs will be renamed to a unique Message-ID before being |
| 4315 | entered. |
| 4316 | |
| 4317 | Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise." |
| 4318 | (let* ((id (mail-header-id header)) |
| 4319 | (id-dep (and id (intern id dependencies))) |
| 4320 | parent-id ref ref-dep ref-header replaced) |
| 4321 | ;; Enter this `header' in the `dependencies' table. |
| 4322 | (cond |
| 4323 | ((not id-dep) |
| 4324 | (setq header nil)) |
| 4325 | ;; The first two cases do the normal part: enter a new `header' |
| 4326 | ;; in the `dependencies' table. |
| 4327 | ((not (boundp id-dep)) |
| 4328 | (set id-dep (list header))) |
| 4329 | ((null (car (symbol-value id-dep))) |
| 4330 | (setcar (symbol-value id-dep) header)) |
| 4331 | |
| 4332 | ;; From here the `header' was already present in the |
| 4333 | ;; `dependencies' table. |
| 4334 | (force-new |
| 4335 | ;; Overrides an existing entry; |
| 4336 | ;; just set the header part of the entry. |
| 4337 | (setcar (symbol-value id-dep) header) |
| 4338 | (setq replaced t)) |
| 4339 | |
| 4340 | ;; Renames the existing `header' to a unique Message-ID. |
| 4341 | ((not gnus-summary-ignore-duplicates) |
| 4342 | ;; An article with this Message-ID has already been seen. |
| 4343 | ;; We rename the Message-ID. |
| 4344 | (set (setq id-dep (intern (setq id (nnmail-message-id)) dependencies)) |
| 4345 | (list header)) |
| 4346 | (mail-header-set-id header id)) |
| 4347 | |
| 4348 | ;; The last case ignores an existing entry, except it adds any |
| 4349 | ;; additional Xrefs (in case the two articles came from different |
| 4350 | ;; servers. |
| 4351 | ;; Also sets `header' to `nil' meaning that the `dependencies' |
| 4352 | ;; table was *not* modified. |
| 4353 | (t |
| 4354 | (mail-header-set-xref |
| 4355 | (car (symbol-value id-dep)) |
| 4356 | (concat (or (mail-header-xref (car (symbol-value id-dep))) |
| 4357 | "") |
| 4358 | (or (mail-header-xref header) ""))) |
| 4359 | (setq header nil))) |
| 4360 | |
| 4361 | (when (and header (not replaced)) |
| 4362 | ;; First check that we are not creating a References loop. |
| 4363 | (setq parent-id (gnus-parent-id (mail-header-references header))) |
| 4364 | (setq ref parent-id) |
| 4365 | (while (and ref |
| 4366 | (setq ref-dep (intern-soft ref dependencies)) |
| 4367 | (boundp ref-dep) |
| 4368 | (setq ref-header (car (symbol-value ref-dep)))) |
| 4369 | (if (string= id ref) |
| 4370 | ;; Yuk! This is a reference loop. Make the article be a |
| 4371 | ;; root article. |
| 4372 | (progn |
| 4373 | (mail-header-set-references (car (symbol-value id-dep)) "none") |
| 4374 | (setq ref nil) |
| 4375 | (setq parent-id nil)) |
| 4376 | (setq ref (gnus-parent-id (mail-header-references ref-header))))) |
| 4377 | (setq ref-dep (intern (or parent-id "none") dependencies)) |
| 4378 | (if (boundp ref-dep) |
| 4379 | (setcdr (symbol-value ref-dep) |
| 4380 | (nconc (cdr (symbol-value ref-dep)) |
| 4381 | (list (symbol-value id-dep)))) |
| 4382 | (set ref-dep (list nil (symbol-value id-dep))))) |
| 4383 | header)) |
| 4384 | |
| 4385 | (defun gnus-extract-message-id-from-in-reply-to (string) |
| 4386 | (if (string-match "<[^>]+>" string) |
| 4387 | (substring string (match-beginning 0) (match-end 0)) |
| 4388 | nil)) |
| 4389 | |
| 4390 | (defun gnus-build-sparse-threads () |
| 4391 | (let ((headers gnus-newsgroup-headers) |
| 4392 | (mail-parse-charset gnus-newsgroup-charset) |
| 4393 | (gnus-summary-ignore-duplicates t) |
| 4394 | header references generation relations |
| 4395 | subject child end new-child date) |
| 4396 | ;; First we create an alist of generations/relations, where |
| 4397 | ;; generations is how much we trust the relation, and the relation |
| 4398 | ;; is parent/child. |
| 4399 | (gnus-message 7 "Making sparse threads...") |
| 4400 | (save-excursion |
| 4401 | (nnheader-set-temp-buffer " *gnus sparse threads*") |
| 4402 | (while (setq header (pop headers)) |
| 4403 | (when (and (setq references (mail-header-references header)) |
| 4404 | (not (string= references ""))) |
| 4405 | (insert references) |
| 4406 | (setq child (mail-header-id header) |
| 4407 | subject (mail-header-subject header) |
| 4408 | date (mail-header-date header) |
| 4409 | generation 0) |
| 4410 | (while (search-backward ">" nil t) |
| 4411 | (setq end (1+ (point))) |
| 4412 | (when (search-backward "<" nil t) |
| 4413 | (setq new-child (buffer-substring (point) end)) |
| 4414 | (push (list (incf generation) |
| 4415 | child (setq child new-child) |
| 4416 | subject date) |
| 4417 | relations))) |
| 4418 | (when child |
| 4419 | (push (list (1+ generation) child nil subject) relations)) |
| 4420 | (erase-buffer))) |
| 4421 | (kill-buffer (current-buffer))) |
| 4422 | ;; Sort over trustworthiness. |
| 4423 | (dolist (relation (sort relations 'car-less-than-car)) |
| 4424 | (when (gnus-dependencies-add-header |
| 4425 | (make-full-mail-header |
| 4426 | gnus-reffed-article-number |
| 4427 | (nth 3 relation) "" (or (nth 4 relation) "") |
| 4428 | (nth 1 relation) |
| 4429 | (or (nth 2 relation) "") 0 0 "") |
| 4430 | gnus-newsgroup-dependencies nil) |
| 4431 | (push gnus-reffed-article-number gnus-newsgroup-limit) |
| 4432 | (push gnus-reffed-article-number gnus-newsgroup-sparse) |
| 4433 | (push (cons gnus-reffed-article-number gnus-sparse-mark) |
| 4434 | gnus-newsgroup-reads) |
| 4435 | (decf gnus-reffed-article-number))) |
| 4436 | (gnus-message 7 "Making sparse threads...done"))) |
| 4437 | |
| 4438 | (defun gnus-build-old-threads () |
| 4439 | ;; Look at all the articles that refer back to old articles, and |
| 4440 | ;; fetch the headers for the articles that aren't there. This will |
| 4441 | ;; build complete threads - if the roots haven't been expired by the |
| 4442 | ;; server, that is. |
| 4443 | (let ((mail-parse-charset gnus-newsgroup-charset) |
| 4444 | id heads) |
| 4445 | (mapatoms |
| 4446 | (lambda (refs) |
| 4447 | (when (not (car (symbol-value refs))) |
| 4448 | (setq heads (cdr (symbol-value refs))) |
| 4449 | (while heads |
| 4450 | (if (memq (mail-header-number (caar heads)) |
| 4451 | gnus-newsgroup-dormant) |
| 4452 | (setq heads (cdr heads)) |
| 4453 | (setq id (symbol-name refs)) |
| 4454 | (while (and (setq id (gnus-build-get-header id)) |
| 4455 | (not (car (gnus-id-to-thread id))))) |
| 4456 | (setq heads nil))))) |
| 4457 | gnus-newsgroup-dependencies))) |
| 4458 | |
| 4459 | (defsubst gnus-remove-odd-characters (string) |
| 4460 | "Translate STRING into something that doesn't contain weird characters." |
| 4461 | (mm-subst-char-in-string |
| 4462 | ?\r ?\- |
| 4463 | (mm-subst-char-in-string ?\n ?\- string t) t)) |
| 4464 | |
| 4465 | ;; This function has to be called with point after the article number |
| 4466 | ;; on the beginning of the line. |
| 4467 | (defsubst gnus-nov-parse-line (number dependencies &optional force-new) |
| 4468 | (let ((eol (point-at-eol)) |
| 4469 | (buffer (current-buffer)) |
| 4470 | header references in-reply-to) |
| 4471 | |
| 4472 | ;; overview: [num subject from date id refs chars lines misc] |
| 4473 | (unwind-protect |
| 4474 | (let (x) |
| 4475 | (narrow-to-region (point) eol) |
| 4476 | (unless (eobp) |
| 4477 | (forward-char)) |
| 4478 | |
| 4479 | (setq header |
| 4480 | (make-full-mail-header |
| 4481 | number ; number |
| 4482 | (condition-case () ; subject |
| 4483 | (gnus-remove-odd-characters |
| 4484 | (funcall gnus-decode-encoded-word-function |
| 4485 | (setq x (nnheader-nov-field)))) |
| 4486 | (error x)) |
| 4487 | (condition-case () ; from |
| 4488 | (gnus-remove-odd-characters |
| 4489 | (funcall gnus-decode-encoded-address-function |
| 4490 | (setq x (nnheader-nov-field)))) |
| 4491 | (error x)) |
| 4492 | (nnheader-nov-field) ; date |
| 4493 | (nnheader-nov-read-message-id number) ; id |
| 4494 | (setq references (nnheader-nov-field)) ; refs |
| 4495 | (nnheader-nov-read-integer) ; chars |
| 4496 | (nnheader-nov-read-integer) ; lines |
| 4497 | (unless (eobp) |
| 4498 | (if (looking-at "Xref: ") |
| 4499 | (goto-char (match-end 0))) |
| 4500 | (nnheader-nov-field)) ; Xref |
| 4501 | (nnheader-nov-parse-extra)))) ; extra |
| 4502 | |
| 4503 | (widen)) |
| 4504 | |
| 4505 | (when (and (string= references "") |
| 4506 | (setq in-reply-to (mail-header-extra header)) |
| 4507 | (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to)))) |
| 4508 | (mail-header-set-references |
| 4509 | header (gnus-extract-message-id-from-in-reply-to in-reply-to))) |
| 4510 | |
| 4511 | (when gnus-alter-header-function |
| 4512 | (funcall gnus-alter-header-function header)) |
| 4513 | (gnus-dependencies-add-header header dependencies force-new))) |
| 4514 | |
| 4515 | (defun gnus-build-get-header (id) |
| 4516 | "Look through the buffer of NOV lines and find the header to ID. |
| 4517 | Enter this line into the dependencies hash table, and return |
| 4518 | the id of the parent article (if any)." |
| 4519 | (let ((deps gnus-newsgroup-dependencies) |
| 4520 | found header) |
| 4521 | (prog1 |
| 4522 | (with-current-buffer nntp-server-buffer |
| 4523 | (let ((case-fold-search nil)) |
| 4524 | (goto-char (point-min)) |
| 4525 | (while (and (not found) |
| 4526 | (search-forward id nil t)) |
| 4527 | (beginning-of-line) |
| 4528 | (setq found (looking-at |
| 4529 | (format "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t%s" |
| 4530 | (regexp-quote id)))) |
| 4531 | (or found (beginning-of-line 2))) |
| 4532 | (when found |
| 4533 | (beginning-of-line) |
| 4534 | (and |
| 4535 | (setq header (gnus-nov-parse-line |
| 4536 | (read (current-buffer)) deps)) |
| 4537 | (gnus-parent-id (mail-header-references header)))))) |
| 4538 | (when header |
| 4539 | (let ((number (mail-header-number header))) |
| 4540 | (push number gnus-newsgroup-limit) |
| 4541 | (push header gnus-newsgroup-headers) |
| 4542 | (if (memq number gnus-newsgroup-unselected) |
| 4543 | (progn |
| 4544 | (setq gnus-newsgroup-unreads |
| 4545 | (gnus-add-to-sorted-list gnus-newsgroup-unreads |
| 4546 | number)) |
| 4547 | (setq gnus-newsgroup-unselected |
| 4548 | (delq number gnus-newsgroup-unselected))) |
| 4549 | (push number gnus-newsgroup-ancient))))))) |
| 4550 | |
| 4551 | (defun gnus-build-all-threads () |
| 4552 | "Read all the headers." |
| 4553 | (let ((gnus-summary-ignore-duplicates t) |
| 4554 | (mail-parse-charset gnus-newsgroup-charset) |
| 4555 | (dependencies gnus-newsgroup-dependencies) |
| 4556 | header article) |
| 4557 | (with-current-buffer nntp-server-buffer |
| 4558 | (let ((case-fold-search nil)) |
| 4559 | (goto-char (point-min)) |
| 4560 | (while (not (eobp)) |
| 4561 | (ignore-errors |
| 4562 | (setq article (read (current-buffer)) |
| 4563 | header (gnus-nov-parse-line article dependencies t))) |
| 4564 | (when header |
| 4565 | (with-current-buffer gnus-summary-buffer |
| 4566 | (push header gnus-newsgroup-headers) |
| 4567 | (if (memq (setq article (mail-header-number header)) |
| 4568 | gnus-newsgroup-unselected) |
| 4569 | (progn |
| 4570 | (setq gnus-newsgroup-unreads |
| 4571 | (gnus-add-to-sorted-list |
| 4572 | gnus-newsgroup-unreads article)) |
| 4573 | (setq gnus-newsgroup-unselected |
| 4574 | (delq article gnus-newsgroup-unselected))) |
| 4575 | (push article gnus-newsgroup-ancient))) |
| 4576 | (forward-line 1))))))) |
| 4577 | |
| 4578 | (defun gnus-summary-update-article-line (article header) |
| 4579 | "Update the line for ARTICLE using HEADER." |
| 4580 | (let* ((id (mail-header-id header)) |
| 4581 | (thread (gnus-id-to-thread id))) |
| 4582 | (unless thread |
| 4583 | (error "Article in no thread")) |
| 4584 | ;; Update the thread. |
| 4585 | (setcar thread header) |
| 4586 | (gnus-summary-goto-subject article) |
| 4587 | (let* ((datal (gnus-data-find-list article)) |
| 4588 | (data (car datal)) |
| 4589 | (inhibit-read-only t) |
| 4590 | (level (gnus-summary-thread-level))) |
| 4591 | (gnus-delete-line) |
| 4592 | (let ((inserted (- (point) |
| 4593 | (progn |
| 4594 | (gnus-summary-insert-line |
| 4595 | header level nil |
| 4596 | (memq article gnus-newsgroup-undownloaded) |
| 4597 | (gnus-article-mark article) |
| 4598 | (memq article gnus-newsgroup-replied) |
| 4599 | (memq article gnus-newsgroup-expirable) |
| 4600 | ;; Only insert the Subject string when it's different |
| 4601 | ;; from the previous Subject string. |
| 4602 | (if (and |
| 4603 | gnus-show-threads |
| 4604 | (gnus-subject-equal |
| 4605 | (condition-case () |
| 4606 | (mail-header-subject |
| 4607 | (gnus-data-header |
| 4608 | (cadr |
| 4609 | (gnus-data-find-list |
| 4610 | article |
| 4611 | (gnus-data-list t))))) |
| 4612 | ;; Error on the side of excessive subjects. |
| 4613 | (error "")) |
| 4614 | (mail-header-subject header))) |
| 4615 | "" |
| 4616 | (mail-header-subject header)) |
| 4617 | nil (cdr (assq article gnus-newsgroup-scored)) |
| 4618 | (memq article gnus-newsgroup-processable)) |
| 4619 | (point))))) |
| 4620 | (when (cdr datal) |
| 4621 | (gnus-data-update-list |
| 4622 | (cdr datal) |
| 4623 | (- (gnus-data-pos data) (gnus-data-pos (cadr datal)) inserted))))))) |
| 4624 | |
| 4625 | (defun gnus-summary-update-article (article &optional iheader) |
| 4626 | "Update ARTICLE in the summary buffer." |
| 4627 | (set-buffer gnus-summary-buffer) |
| 4628 | (let* ((header (gnus-summary-article-header article)) |
| 4629 | (id (mail-header-id header)) |
| 4630 | (data (gnus-data-find article)) |
| 4631 | (thread (gnus-id-to-thread id)) |
| 4632 | (references (mail-header-references header)) |
| 4633 | (parent |
| 4634 | (gnus-id-to-thread |
| 4635 | (or (gnus-parent-id |
| 4636 | (when (and references |
| 4637 | (not (equal "" references))) |
| 4638 | references)) |
| 4639 | "none"))) |
| 4640 | (inhibit-read-only t) |
| 4641 | (old (car thread))) |
| 4642 | (when thread |
| 4643 | (unless iheader |
| 4644 | (setcar thread nil) |
| 4645 | (when parent |
| 4646 | (delq thread parent))) |
| 4647 | (if (gnus-summary-insert-subject id header) |
| 4648 | ;; Set the (possibly) new article number in the data structure. |
| 4649 | (gnus-data-set-number data (gnus-id-to-article id)) |
| 4650 | (setcar thread old) |
| 4651 | nil)))) |
| 4652 | |
| 4653 | (defun gnus-rebuild-thread (id &optional line) |
| 4654 | "Rebuild the thread containing ID. |
| 4655 | If LINE, insert the rebuilt thread starting on line LINE." |
| 4656 | (let ((inhibit-read-only t) |
| 4657 | old-pos current thread data) |
| 4658 | (if (not gnus-show-threads) |
| 4659 | (setq thread (list (car (gnus-id-to-thread id)))) |
| 4660 | ;; Get the thread this article is part of. |
| 4661 | (setq thread (gnus-remove-thread id))) |
| 4662 | (setq old-pos (point-at-bol)) |
| 4663 | (setq current (save-excursion |
| 4664 | (and (re-search-backward "[\r\n]" nil t) |
| 4665 | (gnus-summary-article-number)))) |
| 4666 | ;; If this is a gathered thread, we have to go some re-gathering. |
| 4667 | (when (stringp (car thread)) |
| 4668 | (let ((subject (car thread)) |
| 4669 | roots thr) |
| 4670 | (setq thread (cdr thread)) |
| 4671 | (while thread |
| 4672 | (unless (memq (setq thr (gnus-id-to-thread |
| 4673 | (gnus-root-id |
| 4674 | (mail-header-id (caar thread))))) |
| 4675 | roots) |
| 4676 | (push thr roots)) |
| 4677 | (setq thread (cdr thread))) |
| 4678 | ;; We now have all (unique) roots. |
| 4679 | (if (= (length roots) 1) |
| 4680 | ;; All the loose roots are now one solid root. |
| 4681 | (setq thread (car roots)) |
| 4682 | (setq thread (cons subject (gnus-sort-threads roots)))))) |
| 4683 | (let (threads) |
| 4684 | ;; We then insert this thread into the summary buffer. |
| 4685 | (when line |
| 4686 | (goto-char (point-min)) |
| 4687 | (forward-line (1- line))) |
| 4688 | (let (gnus-newsgroup-data gnus-newsgroup-threads) |
| 4689 | (if gnus-show-threads |
| 4690 | (gnus-summary-prepare-threads (gnus-cut-threads (list thread))) |
| 4691 | (gnus-summary-prepare-unthreaded thread)) |
| 4692 | (setq data (nreverse gnus-newsgroup-data)) |
| 4693 | (setq threads gnus-newsgroup-threads)) |
| 4694 | ;; We splice the new data into the data structure. |
| 4695 | ;;!!! This is kinda bogus. We assume that in LINE is non-nil, |
| 4696 | ;;!!! then we want to insert at the beginning of the buffer. |
| 4697 | ;;!!! That happens to be true with Gnus now, but that may |
| 4698 | ;;!!! change in the future. Perhaps. |
| 4699 | (gnus-data-enter-list |
| 4700 | (if line nil current) data (- (point) old-pos)) |
| 4701 | (setq gnus-newsgroup-threads |
| 4702 | (nconc threads gnus-newsgroup-threads)) |
| 4703 | (gnus-data-compute-positions)))) |
| 4704 | |
| 4705 | (defun gnus-number-to-header (number) |
| 4706 | "Return the header for article NUMBER." |
| 4707 | (let ((headers gnus-newsgroup-headers)) |
| 4708 | (while (and headers |
| 4709 | (not (= number (mail-header-number (car headers))))) |
| 4710 | (pop headers)) |
| 4711 | (when headers |
| 4712 | (car headers)))) |
| 4713 | |
| 4714 | (defun gnus-parent-headers (in-headers &optional generation) |
| 4715 | "Return the headers of the GENERATIONth parent of HEADERS." |
| 4716 | (unless generation |
| 4717 | (setq generation 1)) |
| 4718 | (let ((parent t) |
| 4719 | (headers in-headers) |
| 4720 | references) |
| 4721 | (while (and parent |
| 4722 | (not (zerop generation)) |
| 4723 | (setq references (mail-header-references headers))) |
| 4724 | (setq headers (if (and references |
| 4725 | (setq parent (gnus-parent-id references))) |
| 4726 | (car (gnus-id-to-thread parent)) |
| 4727 | nil)) |
| 4728 | (decf generation)) |
| 4729 | (and (not (eq headers in-headers)) |
| 4730 | headers))) |
| 4731 | |
| 4732 | (defun gnus-id-to-thread (id) |
| 4733 | "Return the (sub-)thread where ID appears." |
| 4734 | (gnus-gethash id gnus-newsgroup-dependencies)) |
| 4735 | |
| 4736 | (defun gnus-id-to-article (id) |
| 4737 | "Return the article number of ID." |
| 4738 | (let ((thread (gnus-id-to-thread id))) |
| 4739 | (when (and thread |
| 4740 | (car thread)) |
| 4741 | (mail-header-number (car thread))))) |
| 4742 | |
| 4743 | (defun gnus-id-to-header (id) |
| 4744 | "Return the article headers of ID." |
| 4745 | (car (gnus-id-to-thread id))) |
| 4746 | |
| 4747 | (defun gnus-article-displayed-root-p (article) |
| 4748 | "Say whether ARTICLE is a root(ish) article." |
| 4749 | (let ((level (gnus-summary-thread-level article)) |
| 4750 | (refs (mail-header-references (gnus-summary-article-header article))) |
| 4751 | particle) |
| 4752 | (cond |
| 4753 | ((null level) nil) |
| 4754 | ((zerop level) t) |
| 4755 | ((null refs) t) |
| 4756 | ((null (gnus-parent-id refs)) t) |
| 4757 | ((and (= 1 level) |
| 4758 | (null (setq particle (gnus-id-to-article |
| 4759 | (gnus-parent-id refs)))) |
| 4760 | (null (gnus-summary-thread-level particle))))))) |
| 4761 | |
| 4762 | (defun gnus-root-id (id) |
| 4763 | "Return the id of the root of the thread where ID appears." |
| 4764 | (let (last-id prev) |
| 4765 | (while (and id (setq prev (car (gnus-id-to-thread id)))) |
| 4766 | (setq last-id id |
| 4767 | id (gnus-parent-id (mail-header-references prev)))) |
| 4768 | last-id)) |
| 4769 | |
| 4770 | (defun gnus-articles-in-thread (thread) |
| 4771 | "Return the list of articles in THREAD." |
| 4772 | (cons (mail-header-number (car thread)) |
| 4773 | (apply 'nconc (mapcar 'gnus-articles-in-thread (cdr thread))))) |
| 4774 | |
| 4775 | (defun gnus-remove-thread (id &optional dont-remove) |
| 4776 | "Remove the thread that has ID in it." |
| 4777 | (let (headers thread last-id) |
| 4778 | ;; First go up in this thread until we find the root. |
| 4779 | (setq last-id (gnus-root-id id) |
| 4780 | headers (message-flatten-list (gnus-id-to-thread last-id))) |
| 4781 | ;; We have now found the real root of this thread. It might have |
| 4782 | ;; been gathered into some loose thread, so we have to search |
| 4783 | ;; through the threads to find the thread we wanted. |
| 4784 | (let ((threads gnus-newsgroup-threads) |
| 4785 | sub) |
| 4786 | (while threads |
| 4787 | (setq sub (car threads)) |
| 4788 | (if (stringp (car sub)) |
| 4789 | ;; This is a gathered thread, so we look at the roots |
| 4790 | ;; below it to find whether this article is in this |
| 4791 | ;; gathered root. |
| 4792 | (progn |
| 4793 | (setq sub (cdr sub)) |
| 4794 | (while sub |
| 4795 | (when (member (caar sub) headers) |
| 4796 | (setq thread (car threads) |
| 4797 | threads nil |
| 4798 | sub nil)) |
| 4799 | (setq sub (cdr sub)))) |
| 4800 | ;; It's an ordinary thread, so we check it. |
| 4801 | (when (eq (car sub) (car headers)) |
| 4802 | (setq thread sub |
| 4803 | threads nil))) |
| 4804 | (setq threads (cdr threads))) |
| 4805 | ;; If this article is in no thread, then it's a root. |
| 4806 | (if thread |
| 4807 | (unless dont-remove |
| 4808 | (setq gnus-newsgroup-threads (delq thread gnus-newsgroup-threads))) |
| 4809 | (setq thread (gnus-id-to-thread last-id))) |
| 4810 | (when thread |
| 4811 | (prog1 |
| 4812 | thread ; We return this thread. |
| 4813 | (unless dont-remove |
| 4814 | (if (stringp (car thread)) |
| 4815 | (progn |
| 4816 | ;; If we use dummy roots, then we have to remove the |
| 4817 | ;; dummy root as well. |
| 4818 | (when (eq gnus-summary-make-false-root 'dummy) |
| 4819 | ;; We go to the dummy root by going to |
| 4820 | ;; the first sub-"thread", and then one line up. |
| 4821 | (gnus-summary-goto-article |
| 4822 | (mail-header-number (caadr thread))) |
| 4823 | (forward-line -1) |
| 4824 | (gnus-delete-line) |
| 4825 | (gnus-data-compute-positions)) |
| 4826 | (setq thread (cdr thread)) |
| 4827 | (while thread |
| 4828 | (gnus-remove-thread-1 (car thread)) |
| 4829 | (setq thread (cdr thread)))) |
| 4830 | (gnus-remove-thread-1 thread)))))))) |
| 4831 | |
| 4832 | (defun gnus-remove-thread-1 (thread) |
| 4833 | "Remove the thread THREAD recursively." |
| 4834 | (let ((number (mail-header-number (pop thread))) |
| 4835 | d) |
| 4836 | (setq thread (reverse thread)) |
| 4837 | (while thread |
| 4838 | (gnus-remove-thread-1 (pop thread))) |
| 4839 | (when (setq d (gnus-data-find number)) |
| 4840 | (goto-char (gnus-data-pos d)) |
| 4841 | (gnus-summary-show-thread) |
| 4842 | (gnus-data-remove |
| 4843 | number |
| 4844 | (- (point-at-bol) |
| 4845 | (prog1 |
| 4846 | (1+ (point-at-eol)) |
| 4847 | (gnus-delete-line))))))) |
| 4848 | |
| 4849 | (defun gnus-sort-threads-recursive (threads func) |
| 4850 | (sort (mapcar (lambda (thread) |
| 4851 | (cons (car thread) |
| 4852 | (and (cdr thread) |
| 4853 | (gnus-sort-threads-recursive (cdr thread) func)))) |
| 4854 | threads) func)) |
| 4855 | |
| 4856 | (defun gnus-sort-threads-loop (threads func) |
| 4857 | (let* ((superthread (cons nil threads)) |
| 4858 | (stack (list (cons superthread threads))) |
| 4859 | remaining-threads thread) |
| 4860 | (while stack |
| 4861 | (setq remaining-threads (cdr (car stack))) |
| 4862 | (if remaining-threads |
| 4863 | (progn (setq thread (car remaining-threads)) |
| 4864 | (setcdr (car stack) (cdr remaining-threads)) |
| 4865 | (if (cdr thread) |
| 4866 | (push (cons thread (cdr thread)) stack))) |
| 4867 | (setq thread (caar stack)) |
| 4868 | (setcdr thread (sort (cdr thread) func)) |
| 4869 | (pop stack))) |
| 4870 | (cdr superthread))) |
| 4871 | |
| 4872 | (defun gnus-sort-threads (threads) |
| 4873 | "Sort THREADS." |
| 4874 | (if (not gnus-thread-sort-functions) |
| 4875 | threads |
| 4876 | (gnus-message 8 "Sorting threads...") |
| 4877 | (prog1 |
| 4878 | (condition-case nil |
| 4879 | (let ((max-lisp-eval-depth (max max-lisp-eval-depth 5000))) |
| 4880 | (gnus-sort-threads-recursive |
| 4881 | threads (gnus-make-sort-function gnus-thread-sort-functions))) |
| 4882 | ;; Even after binding max-lisp-eval-depth, the recursive |
| 4883 | ;; sorter might fail for very long threads. In that case, |
| 4884 | ;; try using a (less well-tested) non-recursive sorter. |
| 4885 | (error (gnus-message 9 "Sorting threads with loop...") |
| 4886 | (gnus-sort-threads-loop |
| 4887 | threads (gnus-make-sort-function |
| 4888 | gnus-thread-sort-functions)))) |
| 4889 | (gnus-message 8 "Sorting threads...done")))) |
| 4890 | |
| 4891 | (defun gnus-sort-articles (articles) |
| 4892 | "Sort ARTICLES." |
| 4893 | (when gnus-article-sort-functions |
| 4894 | (gnus-message 7 "Sorting articles...") |
| 4895 | (prog1 |
| 4896 | (setq gnus-newsgroup-headers |
| 4897 | (sort articles (gnus-make-sort-function |
| 4898 | gnus-article-sort-functions))) |
| 4899 | (gnus-message 7 "Sorting articles...done")))) |
| 4900 | |
| 4901 | ;; Written by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>. |
| 4902 | (defmacro gnus-thread-header (thread) |
| 4903 | "Return header of first article in THREAD. |
| 4904 | Note that THREAD must never, ever be anything else than a variable - |
| 4905 | using some other form will lead to serious barfage." |
| 4906 | (or (symbolp thread) (signal 'wrong-type-argument '(symbolp thread))) |
| 4907 | ;; (8% speedup to gnus-summary-prepare, just for fun :-) |
| 4908 | (list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207" |
| 4909 | (vector thread) 2)) |
| 4910 | |
| 4911 | (defsubst gnus-article-sort-by-number (h1 h2) |
| 4912 | "Sort articles by article number." |
| 4913 | (< (mail-header-number h1) |
| 4914 | (mail-header-number h2))) |
| 4915 | |
| 4916 | (defun gnus-thread-sort-by-number (h1 h2) |
| 4917 | "Sort threads by root article number." |
| 4918 | (gnus-article-sort-by-number |
| 4919 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 4920 | |
| 4921 | (defsubst gnus-article-sort-by-random (h1 h2) |
| 4922 | "Sort articles randomly." |
| 4923 | (zerop (random 2))) |
| 4924 | |
| 4925 | (defun gnus-thread-sort-by-random (h1 h2) |
| 4926 | "Sort threads randomly." |
| 4927 | (gnus-article-sort-by-random |
| 4928 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 4929 | |
| 4930 | (defsubst gnus-article-sort-by-lines (h1 h2) |
| 4931 | "Sort articles by article Lines header." |
| 4932 | (< (mail-header-lines h1) |
| 4933 | (mail-header-lines h2))) |
| 4934 | |
| 4935 | (defun gnus-thread-sort-by-lines (h1 h2) |
| 4936 | "Sort threads by root article Lines header." |
| 4937 | (gnus-article-sort-by-lines |
| 4938 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 4939 | |
| 4940 | (defsubst gnus-article-sort-by-chars (h1 h2) |
| 4941 | "Sort articles by octet length." |
| 4942 | (< (mail-header-chars h1) |
| 4943 | (mail-header-chars h2))) |
| 4944 | |
| 4945 | (defun gnus-thread-sort-by-chars (h1 h2) |
| 4946 | "Sort threads by root article octet length." |
| 4947 | (gnus-article-sort-by-chars |
| 4948 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 4949 | |
| 4950 | (defsubst gnus-article-sort-by-author (h1 h2) |
| 4951 | "Sort articles by root author." |
| 4952 | (gnus-string< |
| 4953 | (let ((extract (funcall |
| 4954 | gnus-extract-address-components |
| 4955 | (mail-header-from h1)))) |
| 4956 | (or (car extract) (cadr extract) "")) |
| 4957 | (let ((extract (funcall |
| 4958 | gnus-extract-address-components |
| 4959 | (mail-header-from h2)))) |
| 4960 | (or (car extract) (cadr extract) "")))) |
| 4961 | |
| 4962 | (defun gnus-thread-sort-by-author (h1 h2) |
| 4963 | "Sort threads by root author." |
| 4964 | (gnus-article-sort-by-author |
| 4965 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 4966 | |
| 4967 | (defsubst gnus-article-sort-by-recipient (h1 h2) |
| 4968 | "Sort articles by recipient." |
| 4969 | (gnus-string< |
| 4970 | (let ((extract (funcall |
| 4971 | gnus-extract-address-components |
| 4972 | (or (cdr (assq 'To (mail-header-extra h1))) "")))) |
| 4973 | (or (car extract) (cadr extract))) |
| 4974 | (let ((extract (funcall |
| 4975 | gnus-extract-address-components |
| 4976 | (or (cdr (assq 'To (mail-header-extra h2))) "")))) |
| 4977 | (or (car extract) (cadr extract))))) |
| 4978 | |
| 4979 | (defun gnus-thread-sort-by-recipient (h1 h2) |
| 4980 | "Sort threads by root recipient." |
| 4981 | (gnus-article-sort-by-recipient |
| 4982 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 4983 | |
| 4984 | (defsubst gnus-article-sort-by-subject (h1 h2) |
| 4985 | "Sort articles by root subject." |
| 4986 | (gnus-string< |
| 4987 | (downcase (gnus-simplify-subject-re (mail-header-subject h1))) |
| 4988 | (downcase (gnus-simplify-subject-re (mail-header-subject h2))))) |
| 4989 | |
| 4990 | (defun gnus-thread-sort-by-subject (h1 h2) |
| 4991 | "Sort threads by root subject." |
| 4992 | (gnus-article-sort-by-subject |
| 4993 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 4994 | |
| 4995 | (defsubst gnus-article-sort-by-date (h1 h2) |
| 4996 | "Sort articles by root article date." |
| 4997 | (time-less-p |
| 4998 | (gnus-date-get-time (mail-header-date h1)) |
| 4999 | (gnus-date-get-time (mail-header-date h2)))) |
| 5000 | |
| 5001 | (defun gnus-thread-sort-by-date (h1 h2) |
| 5002 | "Sort threads by root article date." |
| 5003 | (gnus-article-sort-by-date |
| 5004 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 5005 | |
| 5006 | (defsubst gnus-article-sort-by-score (h1 h2) |
| 5007 | "Sort articles by root article score. |
| 5008 | Unscored articles will be counted as having a score of zero." |
| 5009 | (> (or (cdr (assq (mail-header-number h1) |
| 5010 | gnus-newsgroup-scored)) |
| 5011 | gnus-summary-default-score 0) |
| 5012 | (or (cdr (assq (mail-header-number h2) |
| 5013 | gnus-newsgroup-scored)) |
| 5014 | gnus-summary-default-score 0))) |
| 5015 | |
| 5016 | (defun gnus-thread-sort-by-score (h1 h2) |
| 5017 | "Sort threads by root article score." |
| 5018 | (gnus-article-sort-by-score |
| 5019 | (gnus-thread-header h1) (gnus-thread-header h2))) |
| 5020 | |
| 5021 | (defun gnus-thread-sort-by-total-score (h1 h2) |
| 5022 | "Sort threads by the sum of all scores in the thread. |
| 5023 | Unscored articles will be counted as having a score of zero." |
| 5024 | (> (gnus-thread-total-score h1) (gnus-thread-total-score h2))) |
| 5025 | |
| 5026 | (defun gnus-thread-total-score (thread) |
| 5027 | ;; This function find the total score of THREAD. |
| 5028 | (cond |
| 5029 | ((null thread) |
| 5030 | 0) |
| 5031 | ((consp thread) |
| 5032 | (if (stringp (car thread)) |
| 5033 | (apply gnus-thread-score-function 0 |
| 5034 | (mapcar 'gnus-thread-total-score-1 (cdr thread))) |
| 5035 | (gnus-thread-total-score-1 thread))) |
| 5036 | (t |
| 5037 | (gnus-thread-total-score-1 (list thread))))) |
| 5038 | |
| 5039 | (defun gnus-article-sort-by-most-recent-number (h1 h2) |
| 5040 | "Sort articles by number." |
| 5041 | (gnus-article-sort-by-number h1 h2)) |
| 5042 | |
| 5043 | (defun gnus-thread-sort-by-most-recent-number (h1 h2) |
| 5044 | "Sort threads such that the thread with the most recently arrived article comes first." |
| 5045 | (> (gnus-thread-highest-number h1) (gnus-thread-highest-number h2))) |
| 5046 | |
| 5047 | (defun gnus-thread-highest-number (thread) |
| 5048 | "Return the highest article number in THREAD." |
| 5049 | (apply 'max (mapcar (lambda (header) |
| 5050 | (mail-header-number header)) |
| 5051 | (message-flatten-list thread)))) |
| 5052 | |
| 5053 | (defun gnus-article-sort-by-most-recent-date (h1 h2) |
| 5054 | "Sort articles by number." |
| 5055 | (gnus-article-sort-by-date h1 h2)) |
| 5056 | |
| 5057 | (defun gnus-thread-sort-by-most-recent-date (h1 h2) |
| 5058 | "Sort threads such that the thread with the most recently dated article comes first." |
| 5059 | (> (gnus-thread-latest-date h1) (gnus-thread-latest-date h2))) |
| 5060 | |
| 5061 | ; Since this is called not only to sort the top-level threads, but |
| 5062 | ; also in recursive sorts to order the articles within a thread, each |
| 5063 | ; article will be processed many times. Thus it speeds things up |
| 5064 | ; quite a bit to use gnus-date-get-time, which caches the time value. |
| 5065 | (defun gnus-thread-latest-date (thread) |
| 5066 | "Return the highest article date in THREAD." |
| 5067 | (apply 'max |
| 5068 | (mapcar (lambda (header) (gnus-float-time |
| 5069 | (gnus-date-get-time |
| 5070 | (mail-header-date header)))) |
| 5071 | (message-flatten-list thread)))) |
| 5072 | |
| 5073 | (defun gnus-thread-total-score-1 (root) |
| 5074 | ;; This function find the total score of the thread below ROOT. |
| 5075 | (setq root (car root)) |
| 5076 | (apply gnus-thread-score-function |
| 5077 | (or (append |
| 5078 | (mapcar 'gnus-thread-total-score |
| 5079 | (cdr (gnus-id-to-thread (mail-header-id root)))) |
| 5080 | (when (> (mail-header-number root) 0) |
| 5081 | (list (or (cdr (assq (mail-header-number root) |
| 5082 | gnus-newsgroup-scored)) |
| 5083 | gnus-summary-default-score 0)))) |
| 5084 | (list gnus-summary-default-score) |
| 5085 | '(0)))) |
| 5086 | |
| 5087 | ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>. |
| 5088 | (defvar gnus-tmp-prev-subject nil) |
| 5089 | (defvar gnus-tmp-false-parent nil) |
| 5090 | (defvar gnus-tmp-root-expunged nil) |
| 5091 | (defvar gnus-tmp-dummy-line nil) |
| 5092 | |
| 5093 | (defun gnus-extra-header (type &optional header) |
| 5094 | "Return the extra header of TYPE." |
| 5095 | (or (cdr (assq type (mail-header-extra (or header gnus-tmp-header)))) |
| 5096 | "")) |
| 5097 | |
| 5098 | (defvar gnus-tmp-thread-tree-header-string "") |
| 5099 | |
| 5100 | (defcustom gnus-sum-thread-tree-root "> " |
| 5101 | "With %B spec, used for the root of a thread. |
| 5102 | If nil, use subject instead." |
| 5103 | :version "22.1" |
| 5104 | :type '(radio (const :format "%v " nil) string) |
| 5105 | :group 'gnus-thread) |
| 5106 | |
| 5107 | (defcustom gnus-sum-thread-tree-false-root "> " |
| 5108 | "With %B spec, used for a false root of a thread. |
| 5109 | If nil, use subject instead." |
| 5110 | :version "22.1" |
| 5111 | :type '(radio (const :format "%v " nil) string) |
| 5112 | :group 'gnus-thread) |
| 5113 | |
| 5114 | (defcustom gnus-sum-thread-tree-single-indent "" |
| 5115 | "With %B spec, used for a thread with just one message. |
| 5116 | If nil, use subject instead." |
| 5117 | :version "22.1" |
| 5118 | :type '(radio (const :format "%v " nil) string) |
| 5119 | :group 'gnus-thread) |
| 5120 | |
| 5121 | (defcustom gnus-sum-thread-tree-vertical "| " |
| 5122 | "With %B spec, used for drawing a vertical line." |
| 5123 | :version "22.1" |
| 5124 | :type 'string |
| 5125 | :group 'gnus-thread) |
| 5126 | |
| 5127 | (defcustom gnus-sum-thread-tree-indent " " |
| 5128 | "With %B spec, used for indenting." |
| 5129 | :version "22.1" |
| 5130 | :type 'string |
| 5131 | :group 'gnus-thread) |
| 5132 | |
| 5133 | (defcustom gnus-sum-thread-tree-leaf-with-other "+-> " |
| 5134 | "With %B spec, used for a leaf with brothers." |
| 5135 | :version "22.1" |
| 5136 | :type 'string |
| 5137 | :group 'gnus-thread) |
| 5138 | |
| 5139 | (defcustom gnus-sum-thread-tree-single-leaf "\\-> " |
| 5140 | "With %B spec, used for a leaf without brothers." |
| 5141 | :version "22.1" |
| 5142 | :type 'string |
| 5143 | :group 'gnus-thread) |
| 5144 | |
| 5145 | (defcustom gnus-summary-display-while-building nil |
| 5146 | "If non-nil, show and update the summary buffer as it's being built. |
| 5147 | If the value is t, update the buffer after every line is inserted. If |
| 5148 | the value is an integer (N), update the display every N lines." |
| 5149 | :version "22.1" |
| 5150 | :group 'gnus-thread |
| 5151 | :type '(choice (const :tag "off" nil) |
| 5152 | number |
| 5153 | (const :tag "frequently" t))) |
| 5154 | |
| 5155 | (defun gnus-summary-prepare-threads (threads) |
| 5156 | "Prepare summary buffer from THREADS and indentation LEVEL. |
| 5157 | THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])' |
| 5158 | or a straight list of headers." |
| 5159 | (gnus-message 7 "Generating summary...") |
| 5160 | |
| 5161 | (setq gnus-newsgroup-threads threads) |
| 5162 | (beginning-of-line) |
| 5163 | |
| 5164 | (let ((gnus-tmp-level 0) |
| 5165 | (default-score (or gnus-summary-default-score 0)) |
| 5166 | (gnus-visual-p (gnus-visual-p 'summary-highlight 'highlight)) |
| 5167 | (building-line-count gnus-summary-display-while-building) |
| 5168 | (building-count (integerp gnus-summary-display-while-building)) |
| 5169 | thread number subject stack state gnus-tmp-gathered beg-match |
| 5170 | new-roots gnus-tmp-new-adopts thread-end simp-subject |
| 5171 | gnus-tmp-header gnus-tmp-unread gnus-tmp-downloaded |
| 5172 | gnus-tmp-replied gnus-tmp-subject-or-nil |
| 5173 | gnus-tmp-dummy gnus-tmp-indentation gnus-tmp-lines gnus-tmp-score |
| 5174 | gnus-tmp-score-char gnus-tmp-from gnus-tmp-name |
| 5175 | gnus-tmp-number gnus-tmp-opening-bracket gnus-tmp-closing-bracket |
| 5176 | tree-stack) |
| 5177 | |
| 5178 | (setq gnus-tmp-prev-subject nil |
| 5179 | gnus-tmp-thread-tree-header-string "") |
| 5180 | |
| 5181 | (if (vectorp (car threads)) |
| 5182 | ;; If this is a straight (sic) list of headers, then a |
| 5183 | ;; threaded summary display isn't required, so we just create |
| 5184 | ;; an unthreaded one. |
| 5185 | (gnus-summary-prepare-unthreaded threads) |
| 5186 | |
| 5187 | ;; Do the threaded display. |
| 5188 | |
| 5189 | (if gnus-summary-display-while-building |
| 5190 | (switch-to-buffer (buffer-name))) |
| 5191 | (while (or threads stack gnus-tmp-new-adopts new-roots) |
| 5192 | |
| 5193 | (if (and (= gnus-tmp-level 0) |
| 5194 | (or (not stack) |
| 5195 | (= (caar stack) 0)) |
| 5196 | (not gnus-tmp-false-parent) |
| 5197 | (or gnus-tmp-new-adopts new-roots)) |
| 5198 | (if gnus-tmp-new-adopts |
| 5199 | (setq gnus-tmp-level (if gnus-tmp-root-expunged 0 1) |
| 5200 | thread (list (car gnus-tmp-new-adopts)) |
| 5201 | gnus-tmp-header (caar thread) |
| 5202 | gnus-tmp-new-adopts (cdr gnus-tmp-new-adopts)) |
| 5203 | (when new-roots |
| 5204 | (setq thread (list (car new-roots)) |
| 5205 | gnus-tmp-header (caar thread) |
| 5206 | new-roots (cdr new-roots)))) |
| 5207 | |
| 5208 | (if threads |
| 5209 | ;; If there are some threads, we do them before the |
| 5210 | ;; threads on the stack. |
| 5211 | (setq thread threads |
| 5212 | gnus-tmp-header (caar thread)) |
| 5213 | ;; There were no current threads, so we pop something off |
| 5214 | ;; the stack. |
| 5215 | (setq state (car stack) |
| 5216 | gnus-tmp-level (car state) |
| 5217 | tree-stack (cadr state) |
| 5218 | thread (caddr state) |
| 5219 | stack (cdr stack) |
| 5220 | gnus-tmp-header (caar thread)))) |
| 5221 | |
| 5222 | (setq gnus-tmp-false-parent nil) |
| 5223 | (setq gnus-tmp-root-expunged nil) |
| 5224 | (setq thread-end nil) |
| 5225 | |
| 5226 | (if (stringp gnus-tmp-header) |
| 5227 | ;; The header is a dummy root. |
| 5228 | (cond |
| 5229 | ((eq gnus-summary-make-false-root 'adopt) |
| 5230 | ;; We let the first article adopt the rest. |
| 5231 | (setq gnus-tmp-new-adopts (nconc gnus-tmp-new-adopts |
| 5232 | (cddar thread))) |
| 5233 | (setq gnus-tmp-gathered |
| 5234 | (nconc (mapcar |
| 5235 | (lambda (h) (mail-header-number (car h))) |
| 5236 | (cddar thread)) |
| 5237 | gnus-tmp-gathered)) |
| 5238 | (setq thread (cons (list (caar thread) |
| 5239 | (cadar thread)) |
| 5240 | (cdr thread))) |
| 5241 | (setq gnus-tmp-level -1 |
| 5242 | gnus-tmp-false-parent t)) |
| 5243 | ((eq gnus-summary-make-false-root 'empty) |
| 5244 | ;; We print adopted articles with empty subject fields. |
| 5245 | (setq gnus-tmp-gathered |
| 5246 | (nconc (mapcar |
| 5247 | (lambda (h) (mail-header-number (car h))) |
| 5248 | (cddar thread)) |
| 5249 | gnus-tmp-gathered)) |
| 5250 | (setq gnus-tmp-level -1)) |
| 5251 | ((eq gnus-summary-make-false-root 'dummy) |
| 5252 | ;; We remember that we probably want to output a dummy |
| 5253 | ;; root. |
| 5254 | (setq gnus-tmp-dummy-line gnus-tmp-header) |
| 5255 | (setq gnus-tmp-prev-subject gnus-tmp-header)) |
| 5256 | (t |
| 5257 | ;; We do not make a root for the gathered |
| 5258 | ;; sub-threads at all. |
| 5259 | (setq gnus-tmp-level -1))) |
| 5260 | |
| 5261 | (setq number (mail-header-number gnus-tmp-header) |
| 5262 | subject (mail-header-subject gnus-tmp-header) |
| 5263 | simp-subject (gnus-simplify-subject-fully subject)) |
| 5264 | |
| 5265 | (cond |
| 5266 | ;; If the thread has changed subject, we might want to make |
| 5267 | ;; this subthread into a root. |
| 5268 | ((and (null gnus-thread-ignore-subject) |
| 5269 | (not (zerop gnus-tmp-level)) |
| 5270 | gnus-tmp-prev-subject |
| 5271 | (not (string= gnus-tmp-prev-subject simp-subject))) |
| 5272 | (setq new-roots (nconc new-roots (list (car thread))) |
| 5273 | thread-end t |
| 5274 | gnus-tmp-header nil)) |
| 5275 | ;; If the article lies outside the current limit, |
| 5276 | ;; then we do not display it. |
| 5277 | ((not (memq number gnus-newsgroup-limit)) |
| 5278 | (setq gnus-tmp-gathered |
| 5279 | (nconc (mapcar |
| 5280 | (lambda (h) (mail-header-number (car h))) |
| 5281 | (cdar thread)) |
| 5282 | gnus-tmp-gathered)) |
| 5283 | (setq gnus-tmp-new-adopts (if (cdar thread) |
| 5284 | (append gnus-tmp-new-adopts |
| 5285 | (cdar thread)) |
| 5286 | gnus-tmp-new-adopts) |
| 5287 | thread-end t |
| 5288 | gnus-tmp-header nil) |
| 5289 | (when (zerop gnus-tmp-level) |
| 5290 | (setq gnus-tmp-root-expunged t))) |
| 5291 | ;; Perhaps this article is to be marked as read? |
| 5292 | ((and gnus-summary-mark-below |
| 5293 | (< (or (cdr (assq number gnus-newsgroup-scored)) |
| 5294 | default-score) |
| 5295 | gnus-summary-mark-below) |
| 5296 | ;; Don't touch sparse articles. |
| 5297 | (not (gnus-summary-article-sparse-p number)) |
| 5298 | (not (gnus-summary-article-ancient-p number))) |
| 5299 | (setq gnus-newsgroup-unreads |
| 5300 | (delq number gnus-newsgroup-unreads)) |
| 5301 | (if gnus-newsgroup-auto-expire |
| 5302 | (setq gnus-newsgroup-expirable |
| 5303 | (gnus-add-to-sorted-list |
| 5304 | gnus-newsgroup-expirable number)) |
| 5305 | (push (cons number gnus-low-score-mark) |
| 5306 | gnus-newsgroup-reads)))) |
| 5307 | |
| 5308 | (when gnus-tmp-header |
| 5309 | ;; We may have an old dummy line to output before this |
| 5310 | ;; article. |
| 5311 | (when (and gnus-tmp-dummy-line |
| 5312 | (gnus-subject-equal |
| 5313 | gnus-tmp-dummy-line |
| 5314 | (mail-header-subject gnus-tmp-header))) |
| 5315 | (gnus-summary-insert-dummy-line |
| 5316 | gnus-tmp-dummy-line (mail-header-number gnus-tmp-header)) |
| 5317 | (setq gnus-tmp-dummy-line nil)) |
| 5318 | |
| 5319 | ;; Compute the mark. |
| 5320 | (setq gnus-tmp-unread (gnus-article-mark number)) |
| 5321 | |
| 5322 | (push (gnus-data-make number gnus-tmp-unread (1+ (point)) |
| 5323 | gnus-tmp-header gnus-tmp-level) |
| 5324 | gnus-newsgroup-data) |
| 5325 | |
| 5326 | ;; Actually insert the line. |
| 5327 | (setq |
| 5328 | gnus-tmp-subject-or-nil |
| 5329 | (cond |
| 5330 | ((and gnus-thread-ignore-subject |
| 5331 | gnus-tmp-prev-subject |
| 5332 | (not (string= gnus-tmp-prev-subject simp-subject))) |
| 5333 | subject) |
| 5334 | ((zerop gnus-tmp-level) |
| 5335 | (if (and (eq gnus-summary-make-false-root 'empty) |
| 5336 | (memq number gnus-tmp-gathered) |
| 5337 | gnus-tmp-prev-subject |
| 5338 | (string= gnus-tmp-prev-subject simp-subject)) |
| 5339 | gnus-summary-same-subject |
| 5340 | subject)) |
| 5341 | (t gnus-summary-same-subject))) |
| 5342 | (if (and (eq gnus-summary-make-false-root 'adopt) |
| 5343 | (= gnus-tmp-level 1) |
| 5344 | (memq number gnus-tmp-gathered)) |
| 5345 | (setq gnus-tmp-opening-bracket ?\< |
| 5346 | gnus-tmp-closing-bracket ?\>) |
| 5347 | (setq gnus-tmp-opening-bracket ?\[ |
| 5348 | gnus-tmp-closing-bracket ?\])) |
| 5349 | (if (>= gnus-tmp-level (length gnus-thread-indent-array)) |
| 5350 | (gnus-make-thread-indent-array |
| 5351 | (max (* 2 (length gnus-thread-indent-array)) |
| 5352 | gnus-tmp-level))) |
| 5353 | (setq |
| 5354 | gnus-tmp-indentation |
| 5355 | (aref gnus-thread-indent-array gnus-tmp-level) |
| 5356 | gnus-tmp-lines (mail-header-lines gnus-tmp-header) |
| 5357 | gnus-tmp-score (or (cdr (assq number gnus-newsgroup-scored)) |
| 5358 | gnus-summary-default-score 0) |
| 5359 | gnus-tmp-score-char |
| 5360 | (if (or (null gnus-summary-default-score) |
| 5361 | (<= (abs (- gnus-tmp-score gnus-summary-default-score)) |
| 5362 | gnus-summary-zcore-fuzz)) |
| 5363 | ? ;Whitespace |
| 5364 | (if (< gnus-tmp-score gnus-summary-default-score) |
| 5365 | gnus-score-below-mark gnus-score-over-mark)) |
| 5366 | gnus-tmp-replied |
| 5367 | (cond ((memq number gnus-newsgroup-processable) |
| 5368 | gnus-process-mark) |
| 5369 | ((memq number gnus-newsgroup-cached) |
| 5370 | gnus-cached-mark) |
| 5371 | ((memq number gnus-newsgroup-replied) |
| 5372 | gnus-replied-mark) |
| 5373 | ((memq number gnus-newsgroup-forwarded) |
| 5374 | gnus-forwarded-mark) |
| 5375 | ((memq number gnus-newsgroup-saved) |
| 5376 | gnus-saved-mark) |
| 5377 | ((memq number gnus-newsgroup-unseen) |
| 5378 | gnus-unseen-mark) |
| 5379 | (t gnus-no-mark)) |
| 5380 | gnus-tmp-downloaded |
| 5381 | (cond ((memq number gnus-newsgroup-undownloaded) |
| 5382 | gnus-undownloaded-mark) |
| 5383 | (gnus-newsgroup-agentized |
| 5384 | gnus-downloaded-mark) |
| 5385 | (t |
| 5386 | gnus-no-mark)) |
| 5387 | gnus-tmp-from (mail-header-from gnus-tmp-header) |
| 5388 | gnus-tmp-name |
| 5389 | (cond |
| 5390 | ((string-match "<[^>]+> *$" gnus-tmp-from) |
| 5391 | (setq beg-match (match-beginning 0)) |
| 5392 | (or (and (string-match "^\".+\"" gnus-tmp-from) |
| 5393 | (substring gnus-tmp-from 1 (1- (match-end 0)))) |
| 5394 | (substring gnus-tmp-from 0 beg-match))) |
| 5395 | ((string-match "(.+)" gnus-tmp-from) |
| 5396 | (substring gnus-tmp-from |
| 5397 | (1+ (match-beginning 0)) (1- (match-end 0)))) |
| 5398 | (t gnus-tmp-from)) |
| 5399 | |
| 5400 | ;; Do the %B string |
| 5401 | gnus-tmp-thread-tree-header-string |
| 5402 | (cond |
| 5403 | ((not gnus-show-threads) "") |
| 5404 | ((zerop gnus-tmp-level) |
| 5405 | (cond ((cdar thread) |
| 5406 | (or gnus-sum-thread-tree-root subject)) |
| 5407 | (gnus-tmp-new-adopts |
| 5408 | (or gnus-sum-thread-tree-false-root subject)) |
| 5409 | (t |
| 5410 | (or gnus-sum-thread-tree-single-indent subject)))) |
| 5411 | (t |
| 5412 | (concat (apply 'concat |
| 5413 | (mapcar (lambda (item) |
| 5414 | (if (= item 1) |
| 5415 | gnus-sum-thread-tree-vertical |
| 5416 | gnus-sum-thread-tree-indent)) |
| 5417 | (cdr (reverse tree-stack)))) |
| 5418 | (if (nth 1 thread) |
| 5419 | gnus-sum-thread-tree-leaf-with-other |
| 5420 | gnus-sum-thread-tree-single-leaf))))) |
| 5421 | (when (string= gnus-tmp-name "") |
| 5422 | (setq gnus-tmp-name gnus-tmp-from)) |
| 5423 | (unless (numberp gnus-tmp-lines) |
| 5424 | (setq gnus-tmp-lines -1)) |
| 5425 | (if (= gnus-tmp-lines -1) |
| 5426 | (setq gnus-tmp-lines "?") |
| 5427 | (setq gnus-tmp-lines (number-to-string gnus-tmp-lines))) |
| 5428 | (gnus-put-text-property |
| 5429 | (point) |
| 5430 | (progn (eval gnus-summary-line-format-spec) (point)) |
| 5431 | 'gnus-number number) |
| 5432 | (when gnus-visual-p |
| 5433 | (forward-line -1) |
| 5434 | (gnus-summary-highlight-line) |
| 5435 | (when gnus-summary-update-hook |
| 5436 | (gnus-run-hooks 'gnus-summary-update-hook)) |
| 5437 | (forward-line 1)) |
| 5438 | |
| 5439 | (setq gnus-tmp-prev-subject simp-subject))) |
| 5440 | |
| 5441 | (when (nth 1 thread) |
| 5442 | (push (list (max 0 gnus-tmp-level) |
| 5443 | (copy-sequence tree-stack) |
| 5444 | (nthcdr 1 thread)) |
| 5445 | stack)) |
| 5446 | (push (if (nth 1 thread) 1 0) tree-stack) |
| 5447 | (incf gnus-tmp-level) |
| 5448 | (setq threads (if thread-end nil (cdar thread))) |
| 5449 | (if gnus-summary-display-while-building |
| 5450 | (if building-count |
| 5451 | (progn |
| 5452 | ;; use a set frequency |
| 5453 | (setq building-line-count (1- building-line-count)) |
| 5454 | (when (= building-line-count 0) |
| 5455 | (sit-for 0) |
| 5456 | (setq building-line-count |
| 5457 | gnus-summary-display-while-building))) |
| 5458 | ;; always |
| 5459 | (sit-for 0))) |
| 5460 | (unless threads |
| 5461 | (setq gnus-tmp-level 0))))) |
| 5462 | (gnus-message 7 "Generating summary...done")) |
| 5463 | |
| 5464 | (defun gnus-summary-prepare-unthreaded (headers) |
| 5465 | "Generate an unthreaded summary buffer based on HEADERS." |
| 5466 | (let (header number mark) |
| 5467 | |
| 5468 | (beginning-of-line) |
| 5469 | |
| 5470 | (while headers |
| 5471 | ;; We may have to root out some bad articles... |
| 5472 | (when (memq (setq number (mail-header-number |
| 5473 | (setq header (pop headers)))) |
| 5474 | gnus-newsgroup-limit) |
| 5475 | ;; Mark article as read when it has a low score. |
| 5476 | (when (and gnus-summary-mark-below |
| 5477 | (< (or (cdr (assq number gnus-newsgroup-scored)) |
| 5478 | gnus-summary-default-score 0) |
| 5479 | gnus-summary-mark-below) |
| 5480 | (not (gnus-summary-article-ancient-p number))) |
| 5481 | (setq gnus-newsgroup-unreads |
| 5482 | (delq number gnus-newsgroup-unreads)) |
| 5483 | (if gnus-newsgroup-auto-expire |
| 5484 | (push number gnus-newsgroup-expirable) |
| 5485 | (push (cons number gnus-low-score-mark) |
| 5486 | gnus-newsgroup-reads))) |
| 5487 | |
| 5488 | (setq mark (gnus-article-mark number)) |
| 5489 | (push (gnus-data-make number mark (1+ (point)) header 0) |
| 5490 | gnus-newsgroup-data) |
| 5491 | (gnus-summary-insert-line |
| 5492 | header 0 number |
| 5493 | (memq number gnus-newsgroup-undownloaded) |
| 5494 | mark (memq number gnus-newsgroup-replied) |
| 5495 | (memq number gnus-newsgroup-expirable) |
| 5496 | (mail-header-subject header) nil |
| 5497 | (cdr (assq number gnus-newsgroup-scored)) |
| 5498 | (memq number gnus-newsgroup-processable)))))) |
| 5499 | |
| 5500 | (declare-function gnus-parameter-list-identifier "gnus-art" (name) t) |
| 5501 | |
| 5502 | (defun gnus-group-get-list-identifiers (group) |
| 5503 | "Get list identifier regexp for GROUP." |
| 5504 | (or (gnus-parameter-list-identifier group) |
| 5505 | (if (consp gnus-list-identifiers) |
| 5506 | (mapconcat 'identity gnus-list-identifiers " *\\|") |
| 5507 | gnus-list-identifiers))) |
| 5508 | |
| 5509 | (defun gnus-summary-remove-list-identifiers () |
| 5510 | "Remove list identifiers in `gnus-list-identifiers' from articles in the current group." |
| 5511 | (let ((regexp (gnus-group-get-list-identifiers gnus-newsgroup-name)) |
| 5512 | changed subject) |
| 5513 | (when regexp |
| 5514 | (setq regexp (concat "^\\(?:R[Ee]: +\\)*\\(" regexp " *\\)")) |
| 5515 | (dolist (header gnus-newsgroup-headers) |
| 5516 | (setq subject (mail-header-subject header) |
| 5517 | changed nil) |
| 5518 | (while (string-match regexp subject) |
| 5519 | (setq subject |
| 5520 | (concat (substring subject 0 (match-beginning 1)) |
| 5521 | (substring subject (match-end 0))) |
| 5522 | changed t)) |
| 5523 | (when changed |
| 5524 | (when (string-match "^\\(\\(?:R[Ee]: +\\)+\\)R[Ee]: +" subject) |
| 5525 | (setq subject |
| 5526 | (concat (substring subject 0 (match-beginning 1)) |
| 5527 | (substring subject (match-end 1))))) |
| 5528 | (mail-header-set-subject header subject)))))) |
| 5529 | |
| 5530 | (defun gnus-fetch-headers (articles &optional limit force-new dependencies) |
| 5531 | "Fetch headers of ARTICLES." |
| 5532 | (let ((name (gnus-group-decoded-name gnus-newsgroup-name))) |
| 5533 | (gnus-message 7 "Fetching headers for %s..." name) |
| 5534 | (prog1 |
| 5535 | (if (eq 'nov |
| 5536 | (setq gnus-headers-retrieved-by |
| 5537 | (gnus-retrieve-headers |
| 5538 | articles gnus-newsgroup-name |
| 5539 | (or limit |
| 5540 | ;; We might want to fetch old headers, but |
| 5541 | ;; not if there is only 1 article. |
| 5542 | (and (or (and |
| 5543 | (not (eq gnus-fetch-old-headers 'some)) |
| 5544 | (not (numberp gnus-fetch-old-headers))) |
| 5545 | (> (length articles) 1)) |
| 5546 | gnus-fetch-old-headers))))) |
| 5547 | (gnus-get-newsgroup-headers-xover |
| 5548 | articles force-new dependencies gnus-newsgroup-name t) |
| 5549 | (gnus-get-newsgroup-headers dependencies force-new)) |
| 5550 | (gnus-message 7 "Fetching headers for %s...done" name)))) |
| 5551 | |
| 5552 | (defun gnus-select-newsgroup (group &optional read-all select-articles) |
| 5553 | "Select newsgroup GROUP. |
| 5554 | If READ-ALL is non-nil, all articles in the group are selected. |
| 5555 | If SELECT-ARTICLES, only select those articles from GROUP." |
| 5556 | (let* ((entry (gnus-group-entry group)) |
| 5557 | ;;!!! Dirty hack; should be removed. |
| 5558 | (gnus-summary-ignore-duplicates |
| 5559 | (if (eq (car (gnus-find-method-for-group group)) 'nnvirtual) |
| 5560 | t |
| 5561 | gnus-summary-ignore-duplicates)) |
| 5562 | (info (nth 2 entry)) |
| 5563 | charset articles fetched-articles cached) |
| 5564 | |
| 5565 | (unless (gnus-check-server |
| 5566 | (set (make-local-variable 'gnus-current-select-method) |
| 5567 | (gnus-find-method-for-group group))) |
| 5568 | (error "Couldn't open server")) |
| 5569 | (setq charset (gnus-group-name-charset gnus-current-select-method group)) |
| 5570 | |
| 5571 | (or (and entry (not (eq (car entry) t))) ; Either it's active... |
| 5572 | (gnus-activate-group group) ; Or we can activate it... |
| 5573 | (progn ; Or we bug out. |
| 5574 | (when (equal major-mode 'gnus-summary-mode) |
| 5575 | (gnus-kill-buffer (current-buffer))) |
| 5576 | (error |
| 5577 | "Couldn't activate group %s: %s" |
| 5578 | (mm-decode-coding-string group charset) |
| 5579 | (mm-decode-coding-string (gnus-status-message group) charset)))) |
| 5580 | |
| 5581 | (unless (gnus-request-group group t) |
| 5582 | (when (equal major-mode 'gnus-summary-mode) |
| 5583 | (gnus-kill-buffer (current-buffer))) |
| 5584 | (error "Couldn't request group %s: %s" |
| 5585 | (mm-decode-coding-string group charset) |
| 5586 | (mm-decode-coding-string (gnus-status-message group) charset))) |
| 5587 | |
| 5588 | (when (and gnus-agent |
| 5589 | (gnus-active group)) |
| 5590 | (gnus-agent-possibly-alter-active group (gnus-active group) info) |
| 5591 | |
| 5592 | (setq gnus-summary-use-undownloaded-faces |
| 5593 | (gnus-agent-find-parameter |
| 5594 | group |
| 5595 | 'agent-enable-undownloaded-faces))) |
| 5596 | |
| 5597 | (setq gnus-newsgroup-name group |
| 5598 | gnus-newsgroup-unselected nil |
| 5599 | gnus-newsgroup-unreads (gnus-list-of-unread-articles group)) |
| 5600 | |
| 5601 | (let ((display (gnus-group-find-parameter group 'display))) |
| 5602 | (setq gnus-newsgroup-display |
| 5603 | (cond |
| 5604 | ((not (zerop (or (car-safe read-all) 0))) |
| 5605 | ;; The user entered the group with C-u SPC/RET, let's show |
| 5606 | ;; all articles. |
| 5607 | 'gnus-not-ignore) |
| 5608 | ((eq display 'all) |
| 5609 | 'gnus-not-ignore) |
| 5610 | ((arrayp display) |
| 5611 | (gnus-summary-display-make-predicate (mapcar 'identity display))) |
| 5612 | ((numberp display) |
| 5613 | ;; The following is probably the "correct" solution, but |
| 5614 | ;; it makes Gnus fetch all headers and then limit the |
| 5615 | ;; articles (which is slow), so instead we hack the |
| 5616 | ;; select-articles parameter instead. -- Simon Josefsson |
| 5617 | ;; <jas@kth.se> |
| 5618 | ;; |
| 5619 | ;; (gnus-byte-compile |
| 5620 | ;; `(lambda () (> number ,(- (cdr (gnus-active group)) |
| 5621 | ;; display))))) |
| 5622 | (setq select-articles |
| 5623 | (gnus-uncompress-range |
| 5624 | (cons (let ((tmp (- (cdr (gnus-active group)) display))) |
| 5625 | (if (> tmp 0) |
| 5626 | tmp |
| 5627 | 1)) |
| 5628 | (cdr (gnus-active group))))) |
| 5629 | nil) |
| 5630 | (t |
| 5631 | nil)))) |
| 5632 | |
| 5633 | (gnus-summary-setup-default-charset) |
| 5634 | |
| 5635 | ;; Kludge to avoid having cached articles nixed out in virtual groups. |
| 5636 | (when (gnus-virtual-group-p group) |
| 5637 | (setq cached gnus-newsgroup-cached)) |
| 5638 | |
| 5639 | (setq gnus-newsgroup-unreads |
| 5640 | (gnus-sorted-ndifference |
| 5641 | (gnus-sorted-ndifference gnus-newsgroup-unreads |
| 5642 | gnus-newsgroup-marked) |
| 5643 | gnus-newsgroup-dormant)) |
| 5644 | |
| 5645 | (setq gnus-newsgroup-processable nil) |
| 5646 | |
| 5647 | (gnus-update-read-articles group gnus-newsgroup-unreads t) |
| 5648 | |
| 5649 | ;; Adjust and set lists of article marks. |
| 5650 | (when info |
| 5651 | (gnus-adjust-marked-articles info)) |
| 5652 | (if (setq articles select-articles) |
| 5653 | (setq gnus-newsgroup-unselected |
| 5654 | (gnus-sorted-difference gnus-newsgroup-unreads articles)) |
| 5655 | (setq articles (gnus-articles-to-read group read-all))) |
| 5656 | |
| 5657 | (cond |
| 5658 | ((null articles) |
| 5659 | ;;(gnus-message 3 "Couldn't select newsgroup -- no articles to display") |
| 5660 | 'quit) |
| 5661 | ((eq articles 0) nil) |
| 5662 | (t |
| 5663 | ;; Init the dependencies hash table. |
| 5664 | (setq gnus-newsgroup-dependencies |
| 5665 | (gnus-make-hashtable (length articles))) |
| 5666 | (if (gnus-buffer-live-p gnus-group-buffer) |
| 5667 | (gnus-set-global-variables) |
| 5668 | (set-default 'gnus-newsgroup-name gnus-newsgroup-name)) |
| 5669 | ;; Retrieve the headers and read them in. |
| 5670 | |
| 5671 | (setq gnus-newsgroup-headers (gnus-fetch-headers articles)) |
| 5672 | |
| 5673 | ;; Kludge to avoid having cached articles nixed out in virtual groups. |
| 5674 | (when cached |
| 5675 | (setq gnus-newsgroup-cached cached)) |
| 5676 | |
| 5677 | ;; Suppress duplicates? |
| 5678 | (when gnus-suppress-duplicates |
| 5679 | (gnus-dup-suppress-articles)) |
| 5680 | |
| 5681 | ;; Set the initial limit. |
| 5682 | (setq gnus-newsgroup-limit (copy-sequence articles)) |
| 5683 | ;; Remove canceled articles from the list of unread articles. |
| 5684 | (setq fetched-articles |
| 5685 | (mapcar (lambda (headers) (mail-header-number headers)) |
| 5686 | gnus-newsgroup-headers)) |
| 5687 | (setq gnus-newsgroup-articles fetched-articles) |
| 5688 | (setq gnus-newsgroup-unreads |
| 5689 | (gnus-sorted-nintersection |
| 5690 | gnus-newsgroup-unreads fetched-articles)) |
| 5691 | (gnus-compute-unseen-list) |
| 5692 | |
| 5693 | ;; Removed marked articles that do not exist. |
| 5694 | (gnus-update-missing-marks |
| 5695 | (gnus-sorted-difference articles fetched-articles)) |
| 5696 | ;; We might want to build some more threads first. |
| 5697 | (when (and gnus-fetch-old-headers |
| 5698 | (eq gnus-headers-retrieved-by 'nov)) |
| 5699 | (if (eq gnus-fetch-old-headers 'invisible) |
| 5700 | (gnus-build-all-threads) |
| 5701 | (gnus-build-old-threads))) |
| 5702 | ;; Let the Gnus agent mark articles as read. |
| 5703 | (when gnus-agent |
| 5704 | (gnus-agent-get-undownloaded-list)) |
| 5705 | ;; Remove list identifiers from subject |
| 5706 | (gnus-summary-remove-list-identifiers) |
| 5707 | ;; Check whether auto-expire is to be done in this group. |
| 5708 | (setq gnus-newsgroup-auto-expire |
| 5709 | (and (gnus-group-auto-expirable-p group) |
| 5710 | (not (gnus-group-read-only-p group)))) |
| 5711 | ;; Set up the article buffer now, if necessary. |
| 5712 | (unless (and gnus-single-article-buffer |
| 5713 | (equal gnus-article-buffer "*Article*")) |
| 5714 | (gnus-article-setup-buffer)) |
| 5715 | ;; First and last article in this newsgroup. |
| 5716 | (when gnus-newsgroup-headers |
| 5717 | (setq gnus-newsgroup-begin |
| 5718 | (mail-header-number (car gnus-newsgroup-headers)) |
| 5719 | gnus-newsgroup-end |
| 5720 | (mail-header-number |
| 5721 | (gnus-last-element gnus-newsgroup-headers)))) |
| 5722 | ;; GROUP is successfully selected. |
| 5723 | (or gnus-newsgroup-headers t))))) |
| 5724 | |
| 5725 | (defun gnus-compute-unseen-list () |
| 5726 | ;; The `seen' marks are treated specially. |
| 5727 | (if (not gnus-newsgroup-seen) |
| 5728 | (setq gnus-newsgroup-unseen gnus-newsgroup-articles) |
| 5729 | (setq gnus-newsgroup-unseen |
| 5730 | (gnus-inverse-list-range-intersection |
| 5731 | gnus-newsgroup-articles gnus-newsgroup-seen)))) |
| 5732 | |
| 5733 | (declare-function gnus-get-predicate "gnus-agent" (predicate)) |
| 5734 | |
| 5735 | (defun gnus-summary-display-make-predicate (display) |
| 5736 | (require 'gnus-agent) |
| 5737 | (when (= (length display) 1) |
| 5738 | (setq display (car display))) |
| 5739 | (unless gnus-summary-display-cache |
| 5740 | (dolist (elem (append '((unread . unread) |
| 5741 | (read . read) |
| 5742 | (unseen . unseen)) |
| 5743 | gnus-article-mark-lists)) |
| 5744 | (push (cons (cdr elem) |
| 5745 | (gnus-byte-compile ;Why bother? |
| 5746 | `(lambda () (gnus-article-marked-p ',(cdr elem))))) |
| 5747 | gnus-summary-display-cache))) |
| 5748 | (let ((gnus-category-predicate-alist gnus-summary-display-cache) |
| 5749 | (gnus-category-predicate-cache gnus-summary-display-cache)) |
| 5750 | (gnus-get-predicate display))) |
| 5751 | |
| 5752 | ;; Uses the dynamically bound `gnus-number' variable. |
| 5753 | (defvar gnus-number) |
| 5754 | (defun gnus-article-marked-p (type &optional article) |
| 5755 | (let ((article (or article gnus-number))) |
| 5756 | (cond |
| 5757 | ((eq type 'tick) |
| 5758 | (memq article gnus-newsgroup-marked)) |
| 5759 | ((eq type 'spam) |
| 5760 | (memq article gnus-newsgroup-spam-marked)) |
| 5761 | ((eq type 'unsend) |
| 5762 | (memq article gnus-newsgroup-unsendable)) |
| 5763 | ((eq type 'undownload) |
| 5764 | (memq article gnus-newsgroup-undownloaded)) |
| 5765 | ((eq type 'download) |
| 5766 | (memq article gnus-newsgroup-downloadable)) |
| 5767 | ((eq type 'unread) |
| 5768 | (memq article gnus-newsgroup-unreads)) |
| 5769 | ((eq type 'read) |
| 5770 | (memq article gnus-newsgroup-reads)) |
| 5771 | ((eq type 'dormant) |
| 5772 | (memq article gnus-newsgroup-dormant) ) |
| 5773 | ((eq type 'expire) |
| 5774 | (memq article gnus-newsgroup-expirable)) |
| 5775 | ((eq type 'reply) |
| 5776 | (memq article gnus-newsgroup-replied)) |
| 5777 | ((eq type 'killed) |
| 5778 | (memq article gnus-newsgroup-killed)) |
| 5779 | ((eq type 'bookmark) |
| 5780 | (assq article gnus-newsgroup-bookmarks)) |
| 5781 | ((eq type 'score) |
| 5782 | (assq article gnus-newsgroup-scored)) |
| 5783 | ((eq type 'save) |
| 5784 | (memq article gnus-newsgroup-saved)) |
| 5785 | ((eq type 'cache) |
| 5786 | (memq article gnus-newsgroup-cached)) |
| 5787 | ((eq type 'forward) |
| 5788 | (memq article gnus-newsgroup-forwarded)) |
| 5789 | ((eq type 'seen) |
| 5790 | (not (memq article gnus-newsgroup-unseen))) |
| 5791 | (t t)))) |
| 5792 | |
| 5793 | (defun gnus-articles-to-read (group &optional read-all) |
| 5794 | "Find out what articles the user wants to read." |
| 5795 | (let* ((only-read-p t) |
| 5796 | (articles |
| 5797 | (gnus-list-range-difference |
| 5798 | ;; Select all articles if `read-all' is non-nil, or if there |
| 5799 | ;; are no unread articles. |
| 5800 | (if (or read-all |
| 5801 | (and (zerop (length gnus-newsgroup-marked)) |
| 5802 | (zerop (length gnus-newsgroup-unreads))) |
| 5803 | ;; Fetch all if the predicate is non-nil. |
| 5804 | gnus-newsgroup-display) |
| 5805 | ;; We want to select the headers for all the articles in |
| 5806 | ;; the group, so we select either all the active |
| 5807 | ;; articles in the group, or (if that's nil), the |
| 5808 | ;; articles in the cache. |
| 5809 | (or |
| 5810 | (if gnus-newsgroup-maximum-articles |
| 5811 | (let ((active (gnus-active group))) |
| 5812 | (gnus-uncompress-range |
| 5813 | (cons (max (car active) |
| 5814 | (- (cdr active) |
| 5815 | gnus-newsgroup-maximum-articles |
| 5816 | -1)) |
| 5817 | (cdr active)))) |
| 5818 | (gnus-uncompress-range (gnus-active group))) |
| 5819 | (gnus-cache-articles-in-group group)) |
| 5820 | ;; Select only the "normal" subset of articles. |
| 5821 | (setq only-read-p nil) |
| 5822 | (gnus-sorted-nunion |
| 5823 | (gnus-sorted-union gnus-newsgroup-dormant gnus-newsgroup-marked) |
| 5824 | gnus-newsgroup-unreads)) |
| 5825 | (cdr (assq 'unexist (gnus-info-marks (gnus-get-info group)))))) |
| 5826 | (scored-list (gnus-killed-articles gnus-newsgroup-killed articles)) |
| 5827 | (scored (length scored-list)) |
| 5828 | (number (length articles)) |
| 5829 | (marked (+ (length gnus-newsgroup-marked) |
| 5830 | (length gnus-newsgroup-dormant))) |
| 5831 | (select |
| 5832 | (cond |
| 5833 | ((numberp read-all) |
| 5834 | read-all) |
| 5835 | ((numberp gnus-newsgroup-display) |
| 5836 | gnus-newsgroup-display) |
| 5837 | (t |
| 5838 | (condition-case () |
| 5839 | (cond |
| 5840 | ((and (or (<= scored marked) (= scored number)) |
| 5841 | (numberp gnus-large-newsgroup) |
| 5842 | (> number gnus-large-newsgroup)) |
| 5843 | (let* ((cursor-in-echo-area nil) |
| 5844 | (initial (gnus-parameter-large-newsgroup-initial |
| 5845 | gnus-newsgroup-name)) |
| 5846 | (default (if only-read-p |
| 5847 | (or initial gnus-large-newsgroup) |
| 5848 | number)) |
| 5849 | (input |
| 5850 | (read-string |
| 5851 | (if only-read-p |
| 5852 | (format |
| 5853 | "How many articles from %s (available %d, default %d): " |
| 5854 | (gnus-group-decoded-name |
| 5855 | (gnus-group-real-name gnus-newsgroup-name)) |
| 5856 | number default) |
| 5857 | (format |
| 5858 | "How many articles from %s (%d default): " |
| 5859 | (gnus-group-decoded-name |
| 5860 | (gnus-group-real-name gnus-newsgroup-name)) |
| 5861 | default)) |
| 5862 | nil |
| 5863 | nil |
| 5864 | (number-to-string default)))) |
| 5865 | (if (string-match "^[ \t]*$" input) number input))) |
| 5866 | ((and (> scored marked) (< scored number) |
| 5867 | (> (- scored number) 20)) |
| 5868 | (let ((input |
| 5869 | (read-string |
| 5870 | (format "%s %s (%d scored, %d total): " |
| 5871 | "How many articles from" |
| 5872 | (gnus-group-decoded-name |
| 5873 | (gnus-group-real-name gnus-newsgroup-name)) |
| 5874 | scored number)))) |
| 5875 | (if (string-match "^[ \t]*$" input) |
| 5876 | number input))) |
| 5877 | (t number)) |
| 5878 | (quit |
| 5879 | (message "Quit getting the articles to read") |
| 5880 | nil)))))) |
| 5881 | (setq select (if (stringp select) (string-to-number select) select)) |
| 5882 | (if (or (null select) (zerop select)) |
| 5883 | select |
| 5884 | (if (and (not (zerop scored)) (<= (abs select) scored)) |
| 5885 | (progn |
| 5886 | (setq articles (sort scored-list '<)) |
| 5887 | (setq number (length articles))) |
| 5888 | (setq articles (copy-sequence articles))) |
| 5889 | |
| 5890 | (when (< (abs select) number) |
| 5891 | (if (< select 0) |
| 5892 | ;; Select the N oldest articles. |
| 5893 | (setcdr (nthcdr (1- (abs select)) articles) nil) |
| 5894 | ;; Select the N most recent articles. |
| 5895 | (setq articles (nthcdr (- number select) articles)))) |
| 5896 | (setq gnus-newsgroup-unselected |
| 5897 | (gnus-sorted-difference gnus-newsgroup-unreads articles)) |
| 5898 | (when gnus-alter-articles-to-read-function |
| 5899 | (setq articles |
| 5900 | (sort |
| 5901 | (funcall gnus-alter-articles-to-read-function |
| 5902 | gnus-newsgroup-name articles) |
| 5903 | '<))) |
| 5904 | articles))) |
| 5905 | |
| 5906 | (defun gnus-killed-articles (killed articles) |
| 5907 | (let (out) |
| 5908 | (while articles |
| 5909 | (when (inline (gnus-member-of-range (car articles) killed)) |
| 5910 | (push (car articles) out)) |
| 5911 | (setq articles (cdr articles))) |
| 5912 | out)) |
| 5913 | |
| 5914 | (defun gnus-article-mark-to-type (mark) |
| 5915 | "Return the type of MARK." |
| 5916 | (or (cadr (assq mark gnus-article-special-mark-lists)) |
| 5917 | 'list)) |
| 5918 | |
| 5919 | (defun gnus-article-unpropagatable-p (mark) |
| 5920 | "Return whether MARK should be propagated to back end." |
| 5921 | (memq mark gnus-article-unpropagated-mark-lists)) |
| 5922 | |
| 5923 | (defun gnus-adjust-marked-articles (info) |
| 5924 | "Set all article lists and remove all marks that are no longer valid." |
| 5925 | (let* ((marked-lists (gnus-info-marks info)) |
| 5926 | (active (gnus-active (gnus-info-group info))) |
| 5927 | (min (car active)) |
| 5928 | (max (cdr active)) |
| 5929 | (types gnus-article-mark-lists) |
| 5930 | marks var articles article mark mark-type |
| 5931 | bgn end) |
| 5932 | ;; Hack to avoid adjusting marks for imap. |
| 5933 | (when (eq (car (gnus-find-method-for-group (gnus-info-group info))) |
| 5934 | 'nnimap) |
| 5935 | (setq min 1)) |
| 5936 | |
| 5937 | (dolist (marks marked-lists) |
| 5938 | (setq mark (car marks) |
| 5939 | mark-type (gnus-article-mark-to-type mark) |
| 5940 | var (intern (format "gnus-newsgroup-%s" (car (rassq mark types))))) |
| 5941 | ;; We set the variable according to the type of the marks list, |
| 5942 | ;; and then adjust the marks to a subset of the active articles. |
| 5943 | (cond |
| 5944 | ;; Adjust "simple" lists - compressed yet unsorted |
| 5945 | ((eq mark-type 'list) |
| 5946 | ;; Simultaneously uncompress and clip to active range |
| 5947 | ;; See gnus-uncompress-range for a description of possible marks |
| 5948 | (let (l lh) |
| 5949 | (if (not (cadr marks)) |
| 5950 | (set var nil) |
| 5951 | (setq articles (if (numberp (cddr marks)) |
| 5952 | (list (cdr marks)) |
| 5953 | (cdr marks)) |
| 5954 | lh (cons nil nil) |
| 5955 | l lh) |
| 5956 | |
| 5957 | (while (setq article (pop articles)) |
| 5958 | (cond ((consp article) |
| 5959 | (setq bgn (max (car article) min) |
| 5960 | end (min (cdr article) max)) |
| 5961 | (while (<= bgn end) |
| 5962 | (setq l (setcdr l (cons bgn nil)) |
| 5963 | bgn (1+ bgn)))) |
| 5964 | ((and (<= min article) |
| 5965 | (>= max article)) |
| 5966 | (setq l (setcdr l (cons article nil)))))) |
| 5967 | (set var (cdr lh))))) |
| 5968 | ;; Adjust assocs. |
| 5969 | ((eq mark-type 'tuple) |
| 5970 | (set var (setq articles (cdr marks))) |
| 5971 | (when (not (listp (cdr (symbol-value var)))) |
| 5972 | (set var (list (symbol-value var)))) |
| 5973 | (when (not (listp (cdr articles))) |
| 5974 | (setq articles (list articles))) |
| 5975 | (while articles |
| 5976 | (when (or (not (consp (setq article (pop articles)))) |
| 5977 | (< (car article) min) |
| 5978 | (> (car article) max)) |
| 5979 | (set var (delq article (symbol-value var)))))) |
| 5980 | ;; Adjust ranges (sloppily). |
| 5981 | ((eq mark-type 'range) |
| 5982 | (cond |
| 5983 | ((eq mark 'seen) |
| 5984 | ;; Fix the record for `seen' if it looks like (seen NUM1 . NUM2). |
| 5985 | ;; It should be (seen (NUM1 . NUM2)). |
| 5986 | (when (numberp (cddr marks)) |
| 5987 | (setcdr marks (list (cdr marks)))) |
| 5988 | (setq articles (cdr marks)) |
| 5989 | (while (and articles |
| 5990 | (or (and (consp (car articles)) |
| 5991 | (> min (cdar articles))) |
| 5992 | (and (numberp (car articles)) |
| 5993 | (> min (car articles))))) |
| 5994 | (pop articles)) |
| 5995 | (set var articles)) |
| 5996 | ((eq mark 'unexist) |
| 5997 | (set var (cdr marks))))))))) |
| 5998 | |
| 5999 | (defun gnus-update-missing-marks (missing) |
| 6000 | "Go through the list of MISSING articles and remove them from the mark lists." |
| 6001 | (when missing |
| 6002 | (let (var m) |
| 6003 | ;; Go through all types. |
| 6004 | (dolist (elem gnus-article-mark-lists) |
| 6005 | (when (eq (gnus-article-mark-to-type (cdr elem)) 'list) |
| 6006 | (setq var (intern (format "gnus-newsgroup-%s" (car elem)))) |
| 6007 | (when (symbol-value var) |
| 6008 | ;; This list has articles. So we delete all missing |
| 6009 | ;; articles from it. |
| 6010 | (setq m missing) |
| 6011 | (while m |
| 6012 | (set var (delq (pop m) (symbol-value var)))))))))) |
| 6013 | |
| 6014 | (defun gnus-update-marks () |
| 6015 | "Enter the various lists of marked articles into the newsgroup info list." |
| 6016 | (let ((types gnus-article-mark-lists) |
| 6017 | (info (gnus-get-info gnus-newsgroup-name)) |
| 6018 | type list newmarked symbol delta-marks) |
| 6019 | (when info |
| 6020 | ;; Add all marks lists to the list of marks lists. |
| 6021 | (while (setq type (pop types)) |
| 6022 | (setq list (symbol-value |
| 6023 | (setq symbol |
| 6024 | (intern (format "gnus-newsgroup-%s" (car type)))))) |
| 6025 | |
| 6026 | (when list |
| 6027 | ;; Get rid of the entries of the articles that have the |
| 6028 | ;; default score. |
| 6029 | (when (and (eq (cdr type) 'score) |
| 6030 | gnus-save-score |
| 6031 | list) |
| 6032 | (let* ((arts list) |
| 6033 | (prev (cons nil list)) |
| 6034 | (all prev)) |
| 6035 | (while arts |
| 6036 | (if (or (not (consp (car arts))) |
| 6037 | (= (cdar arts) gnus-summary-default-score)) |
| 6038 | (setcdr prev (cdr arts)) |
| 6039 | (setq prev arts)) |
| 6040 | (setq arts (cdr arts))) |
| 6041 | (setq list (cdr all))))) |
| 6042 | |
| 6043 | (when (eq (cdr type) 'seen) |
| 6044 | (setq list (gnus-range-add list gnus-newsgroup-unseen))) |
| 6045 | |
| 6046 | (when (eq (gnus-article-mark-to-type (cdr type)) 'list) |
| 6047 | (setq list (gnus-compress-sequence (set symbol (sort list '<)) t))) |
| 6048 | |
| 6049 | (when (and (gnus-check-backend-function |
| 6050 | 'request-set-mark gnus-newsgroup-name) |
| 6051 | (not (gnus-article-unpropagatable-p (cdr type)))) |
| 6052 | (let* ((old (cdr (assq (cdr type) (gnus-info-marks info)))) |
| 6053 | ;; Don't do anything about marks for articles we |
| 6054 | ;; didn't actually get any headers for. |
| 6055 | (del |
| 6056 | (gnus-list-range-intersection |
| 6057 | gnus-newsgroup-articles |
| 6058 | (gnus-remove-from-range (gnus-copy-sequence old) list))) |
| 6059 | (add |
| 6060 | (gnus-list-range-intersection |
| 6061 | gnus-newsgroup-articles |
| 6062 | (gnus-remove-from-range |
| 6063 | (gnus-copy-sequence list) old)))) |
| 6064 | (when add |
| 6065 | (push (list add 'add (list (cdr type))) delta-marks)) |
| 6066 | (when del |
| 6067 | ;; Don't delete marks from outside the active range. |
| 6068 | ;; This shouldn't happen, but is a sanity check. |
| 6069 | (setq del (gnus-sorted-range-intersection |
| 6070 | (gnus-active gnus-newsgroup-name) del)) |
| 6071 | (push (list del 'del (list (cdr type))) delta-marks)))) |
| 6072 | |
| 6073 | (when (or list |
| 6074 | (eq (cdr type) 'unexist)) |
| 6075 | (push (cons (cdr type) list) newmarked))) |
| 6076 | |
| 6077 | (when delta-marks |
| 6078 | (unless (gnus-check-group gnus-newsgroup-name) |
| 6079 | (error "Can't open server for %s" gnus-newsgroup-name)) |
| 6080 | (gnus-request-set-mark gnus-newsgroup-name delta-marks)) |
| 6081 | |
| 6082 | ;; Enter these new marks into the info of the group. |
| 6083 | (if (nthcdr 3 info) |
| 6084 | (setcar (nthcdr 3 info) newmarked) |
| 6085 | ;; Add the marks lists to the end of the info. |
| 6086 | (when newmarked |
| 6087 | (setcdr (nthcdr 2 info) (list newmarked)))) |
| 6088 | |
| 6089 | ;; Cut off the end of the info if there's nothing else there. |
| 6090 | (let ((i 5)) |
| 6091 | (while (and (> i 2) |
| 6092 | (not (nth i info))) |
| 6093 | (when (nthcdr (decf i) info) |
| 6094 | (setcdr (nthcdr i info) nil))))))) |
| 6095 | |
| 6096 | (defun gnus-set-mode-line (where) |
| 6097 | "Set the mode line of the article or summary buffers. |
| 6098 | If WHERE is `summary', the summary mode line format will be used." |
| 6099 | ;; Is this mode line one we keep updated? |
| 6100 | (when (and (memq where gnus-updated-mode-lines) |
| 6101 | (symbol-value |
| 6102 | (intern (format "gnus-%s-mode-line-format-spec" where)))) |
| 6103 | (let (mode-string) |
| 6104 | ;; We evaluate this in the summary buffer since these |
| 6105 | ;; variables are buffer-local to that buffer. |
| 6106 | (with-current-buffer gnus-summary-buffer |
| 6107 | ;; We bind all these variables that are used in the `eval' form |
| 6108 | ;; below. |
| 6109 | (let* ((mformat (symbol-value |
| 6110 | (intern |
| 6111 | (format "gnus-%s-mode-line-format-spec" where)))) |
| 6112 | (gnus-tmp-group-name (gnus-mode-string-quote |
| 6113 | (gnus-group-decoded-name |
| 6114 | gnus-newsgroup-name))) |
| 6115 | (gnus-tmp-article-number (or gnus-current-article 0)) |
| 6116 | (gnus-tmp-unread gnus-newsgroup-unreads) |
| 6117 | (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads)) |
| 6118 | (gnus-tmp-unselected (length gnus-newsgroup-unselected)) |
| 6119 | (gnus-tmp-unread-and-unselected |
| 6120 | (cond ((and (zerop gnus-tmp-unread-and-unticked) |
| 6121 | (zerop gnus-tmp-unselected)) |
| 6122 | "") |
| 6123 | ((zerop gnus-tmp-unselected) |
| 6124 | (format "{%d more}" gnus-tmp-unread-and-unticked)) |
| 6125 | (t (format "{%d(+%d) more}" |
| 6126 | gnus-tmp-unread-and-unticked |
| 6127 | gnus-tmp-unselected)))) |
| 6128 | (gnus-tmp-subject |
| 6129 | (if (and gnus-current-headers |
| 6130 | (vectorp gnus-current-headers)) |
| 6131 | (gnus-mode-string-quote |
| 6132 | (mail-header-subject gnus-current-headers)) |
| 6133 | "")) |
| 6134 | bufname-length max-len |
| 6135 | gnus-tmp-header) ;; passed as argument to any user-format-funcs |
| 6136 | (setq mode-string (eval mformat)) |
| 6137 | (setq bufname-length (if (string-match "%b" mode-string) |
| 6138 | (- (length |
| 6139 | (buffer-name |
| 6140 | (if (eq where 'summary) |
| 6141 | nil |
| 6142 | (get-buffer gnus-article-buffer)))) |
| 6143 | 2) |
| 6144 | 0)) |
| 6145 | (setq max-len (max 4 (if gnus-mode-non-string-length |
| 6146 | (- (window-width) |
| 6147 | gnus-mode-non-string-length |
| 6148 | bufname-length) |
| 6149 | (length mode-string)))) |
| 6150 | ;; We might have to chop a bit of the string off... |
| 6151 | (when (> (length mode-string) max-len) |
| 6152 | (setq mode-string |
| 6153 | (concat (truncate-string-to-width mode-string (- max-len 3)) |
| 6154 | "..."))))) |
| 6155 | ;; Update the mode line. |
| 6156 | (setq mode-line-buffer-identification |
| 6157 | (gnus-mode-line-buffer-identification (list mode-string))) |
| 6158 | (set-buffer-modified-p t)))) |
| 6159 | |
| 6160 | (defun gnus-create-xref-hashtb (from-newsgroup headers unreads) |
| 6161 | "Go through the HEADERS list and add all Xrefs to a hash table. |
| 6162 | The resulting hash table is returned, or nil if no Xrefs were found." |
| 6163 | (let* ((virtual (gnus-virtual-group-p from-newsgroup)) |
| 6164 | (prefix (if virtual "" (gnus-group-real-prefix from-newsgroup))) |
| 6165 | (xref-hashtb (gnus-make-hashtable)) |
| 6166 | start group entry number xrefs header) |
| 6167 | (while headers |
| 6168 | (setq header (pop headers)) |
| 6169 | (when (and (setq xrefs (mail-header-xref header)) |
| 6170 | (not (memq (setq number (mail-header-number header)) |
| 6171 | unreads))) |
| 6172 | (setq start 0) |
| 6173 | (while (string-match "\\([^ ]+\\)[:/]\\([0-9]+\\)" xrefs start) |
| 6174 | (setq start (match-end 0)) |
| 6175 | (setq group (if prefix |
| 6176 | (concat prefix (substring xrefs (match-beginning 1) |
| 6177 | (match-end 1))) |
| 6178 | (substring xrefs (match-beginning 1) (match-end 1)))) |
| 6179 | (setq number |
| 6180 | (string-to-number (substring xrefs (match-beginning 2) |
| 6181 | (match-end 2)))) |
| 6182 | (if (setq entry (gnus-gethash group xref-hashtb)) |
| 6183 | (setcdr entry (cons number (cdr entry))) |
| 6184 | (gnus-sethash group (cons number nil) xref-hashtb))))) |
| 6185 | (and start xref-hashtb))) |
| 6186 | |
| 6187 | (defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads) |
| 6188 | "Look through all the headers and mark the Xrefs as read." |
| 6189 | (let ((virtual (gnus-virtual-group-p from-newsgroup)) |
| 6190 | name info xref-hashtb idlist method nth4) |
| 6191 | (with-current-buffer gnus-group-buffer |
| 6192 | (when (setq xref-hashtb |
| 6193 | (gnus-create-xref-hashtb from-newsgroup headers unreads)) |
| 6194 | (mapatoms |
| 6195 | (lambda (group) |
| 6196 | (unless (string= from-newsgroup (setq name (symbol-name group))) |
| 6197 | (setq idlist (symbol-value group)) |
| 6198 | ;; Dead groups are not updated. |
| 6199 | (and (prog1 |
| 6200 | (setq info (gnus-get-info name)) |
| 6201 | (when (stringp (setq nth4 (gnus-info-method info))) |
| 6202 | (setq nth4 (gnus-server-to-method nth4)))) |
| 6203 | ;; Only do the xrefs if the group has the same |
| 6204 | ;; select method as the group we have just read. |
| 6205 | (or (gnus-methods-equal-p |
| 6206 | nth4 (gnus-find-method-for-group from-newsgroup)) |
| 6207 | virtual |
| 6208 | (equal nth4 (setq method (gnus-find-method-for-group |
| 6209 | from-newsgroup))) |
| 6210 | (and (equal (car nth4) (car method)) |
| 6211 | (equal (nth 1 nth4) (nth 1 method)))) |
| 6212 | gnus-use-cross-reference |
| 6213 | (or (not (eq gnus-use-cross-reference t)) |
| 6214 | virtual |
| 6215 | ;; Only do cross-references on subscribed |
| 6216 | ;; groups, if that is what is wanted. |
| 6217 | (<= (gnus-info-level info) gnus-level-subscribed)) |
| 6218 | (gnus-group-make-articles-read name idlist)))) |
| 6219 | xref-hashtb))))) |
| 6220 | |
| 6221 | (defun gnus-compute-read-articles (group articles) |
| 6222 | (let* ((entry (gnus-group-entry group)) |
| 6223 | (info (nth 2 entry)) |
| 6224 | (active (gnus-active group)) |
| 6225 | ninfo) |
| 6226 | (when entry |
| 6227 | ;; First peel off all invalid article numbers. |
| 6228 | (when active |
| 6229 | (let ((ids articles) |
| 6230 | id first) |
| 6231 | (while (setq id (pop ids)) |
| 6232 | (when (and first (> id (cdr active))) |
| 6233 | ;; We'll end up in this situation in one particular |
| 6234 | ;; obscure situation. If you re-scan a group and get |
| 6235 | ;; a new article that is cross-posted to a different |
| 6236 | ;; group that has not been re-scanned, you might get |
| 6237 | ;; crossposted article that has a higher number than |
| 6238 | ;; Gnus believes possible. So we re-activate this |
| 6239 | ;; group as well. This might mean doing the |
| 6240 | ;; crossposting thingy will *increase* the number |
| 6241 | ;; of articles in some groups. Tsk, tsk. |
| 6242 | (setq active (or (gnus-activate-group group) active))) |
| 6243 | (when (or (> id (cdr active)) |
| 6244 | (< id (car active))) |
| 6245 | (setq articles (delq id articles)))))) |
| 6246 | ;; If the read list is nil, we init it. |
| 6247 | (if (and active |
| 6248 | (null (gnus-info-read info)) |
| 6249 | (> (car active) 1)) |
| 6250 | (setq ninfo (cons 1 (1- (car active)))) |
| 6251 | (setq ninfo (gnus-info-read info))) |
| 6252 | ;; Then we add the read articles to the range. |
| 6253 | (gnus-add-to-range |
| 6254 | ninfo (setq articles (sort articles '<)))))) |
| 6255 | |
| 6256 | (defun gnus-group-make-articles-read (group articles) |
| 6257 | "Update the info of GROUP to say that ARTICLES are read." |
| 6258 | (let* ((num 0) |
| 6259 | (entry (gnus-group-entry group)) |
| 6260 | (info (nth 2 entry)) |
| 6261 | (active (gnus-active group)) |
| 6262 | (set-marks |
| 6263 | (gnus-method-option-p |
| 6264 | (gnus-find-method-for-group group) |
| 6265 | 'server-marks)) |
| 6266 | range) |
| 6267 | (if (not entry) |
| 6268 | ;; Group that Gnus doesn't know exists, but still allow the |
| 6269 | ;; backend to set marks. |
| 6270 | (when set-marks |
| 6271 | (gnus-request-set-mark |
| 6272 | group (list (list (gnus-compress-sequence (sort articles #'<)) |
| 6273 | 'add '(read))))) |
| 6274 | ;; Normal, subscribed groups. |
| 6275 | (setq range (gnus-compute-read-articles group articles)) |
| 6276 | (with-current-buffer gnus-group-buffer |
| 6277 | (gnus-undo-register |
| 6278 | `(progn |
| 6279 | (gnus-info-set-marks ',info ',(gnus-info-marks info) t) |
| 6280 | (gnus-info-set-read ',info ',(gnus-info-read info)) |
| 6281 | (gnus-get-unread-articles-in-group ',info (gnus-active ,group)) |
| 6282 | (when ,set-marks |
| 6283 | (gnus-request-set-mark |
| 6284 | ,group (list (list ',range 'del '(read))))) |
| 6285 | (gnus-group-update-group ,group t)))) |
| 6286 | ;; Add the read articles to the range. |
| 6287 | (gnus-info-set-read info range) |
| 6288 | (when set-marks |
| 6289 | (gnus-request-set-mark group (list (list range 'add '(read))))) |
| 6290 | ;; Then we have to re-compute how many unread |
| 6291 | ;; articles there are in this group. |
| 6292 | (when active |
| 6293 | (cond |
| 6294 | ((not range) |
| 6295 | (setq num (- (1+ (cdr active)) (car active)))) |
| 6296 | ((not (listp (cdr range))) |
| 6297 | (setq num (- (cdr active) (- (1+ (cdr range)) |
| 6298 | (car range))))) |
| 6299 | (t |
| 6300 | (while range |
| 6301 | (if (numberp (car range)) |
| 6302 | (setq num (1+ num)) |
| 6303 | (setq num (+ num (- (1+ (cdar range)) (caar range))))) |
| 6304 | (setq range (cdr range))) |
| 6305 | (setq num (- (cdr active) num)))) |
| 6306 | ;; Update the number of unread articles. |
| 6307 | (setcar entry num) |
| 6308 | ;; Update the group buffer. |
| 6309 | (unless (gnus-ephemeral-group-p group) |
| 6310 | (gnus-group-update-group group t)))))) |
| 6311 | |
| 6312 | (defun gnus-get-newsgroup-headers (&optional dependencies force-new) |
| 6313 | (let ((cur nntp-server-buffer) |
| 6314 | (dependencies |
| 6315 | (or dependencies |
| 6316 | (with-current-buffer gnus-summary-buffer |
| 6317 | gnus-newsgroup-dependencies))) |
| 6318 | headers id end ref number |
| 6319 | (mail-parse-charset gnus-newsgroup-charset) |
| 6320 | (mail-parse-ignored-charsets |
| 6321 | (save-current-buffer (condition-case nil |
| 6322 | (set-buffer gnus-summary-buffer) |
| 6323 | (error)) |
| 6324 | gnus-newsgroup-ignored-charsets))) |
| 6325 | (with-current-buffer nntp-server-buffer |
| 6326 | ;; Translate all TAB characters into SPACE characters. |
| 6327 | (subst-char-in-region (point-min) (point-max) ?\t ? t) |
| 6328 | (subst-char-in-region (point-min) (point-max) ?\r ? t) |
| 6329 | (ietf-drums-unfold-fws) |
| 6330 | (gnus-run-hooks 'gnus-parse-headers-hook) |
| 6331 | (let ((case-fold-search t) |
| 6332 | in-reply-to header p lines chars) |
| 6333 | (goto-char (point-min)) |
| 6334 | ;; Search to the beginning of the next header. Error messages |
| 6335 | ;; do not begin with 2 or 3. |
| 6336 | (while (re-search-forward "^[23][0-9]+ " nil t) |
| 6337 | (setq id nil |
| 6338 | ref nil) |
| 6339 | ;; This implementation of this function, with nine |
| 6340 | ;; search-forwards instead of the one re-search-forward and |
| 6341 | ;; a case (which basically was the old function) is actually |
| 6342 | ;; about twice as fast, even though it looks messier. You |
| 6343 | ;; can't have everything, I guess. Speed and elegance |
| 6344 | ;; doesn't always go hand in hand. |
| 6345 | (setq |
| 6346 | header |
| 6347 | (vector |
| 6348 | ;; Number. |
| 6349 | (prog1 |
| 6350 | (setq number (read cur)) |
| 6351 | (end-of-line) |
| 6352 | (setq p (point)) |
| 6353 | (narrow-to-region (point) |
| 6354 | (or (and (search-forward "\n.\n" nil t) |
| 6355 | (- (point) 2)) |
| 6356 | (point)))) |
| 6357 | ;; Subject. |
| 6358 | (progn |
| 6359 | (goto-char p) |
| 6360 | (if (search-forward "\nsubject:" nil t) |
| 6361 | (funcall gnus-decode-encoded-word-function |
| 6362 | (nnheader-header-value)) |
| 6363 | "(none)")) |
| 6364 | ;; From. |
| 6365 | (progn |
| 6366 | (goto-char p) |
| 6367 | (if (search-forward "\nfrom:" nil t) |
| 6368 | (funcall gnus-decode-encoded-address-function |
| 6369 | (nnheader-header-value)) |
| 6370 | "(nobody)")) |
| 6371 | ;; Date. |
| 6372 | (progn |
| 6373 | (goto-char p) |
| 6374 | (if (search-forward "\ndate:" nil t) |
| 6375 | (nnheader-header-value) "")) |
| 6376 | ;; Message-ID. |
| 6377 | (progn |
| 6378 | (goto-char p) |
| 6379 | (setq id (if (re-search-forward |
| 6380 | "^message-id: *\\(<[^\n\t> ]+>\\)" nil t) |
| 6381 | ;; We do it this way to make sure the Message-ID |
| 6382 | ;; is (somewhat) syntactically valid. |
| 6383 | (buffer-substring (match-beginning 1) |
| 6384 | (match-end 1)) |
| 6385 | ;; If there was no message-id, we just fake one |
| 6386 | ;; to make subsequent routines simpler. |
| 6387 | (nnheader-generate-fake-message-id number)))) |
| 6388 | ;; References. |
| 6389 | (progn |
| 6390 | (goto-char p) |
| 6391 | (if (search-forward "\nreferences:" nil t) |
| 6392 | (progn |
| 6393 | (setq end (point)) |
| 6394 | (prog1 |
| 6395 | (nnheader-header-value) |
| 6396 | (setq ref |
| 6397 | (buffer-substring |
| 6398 | (progn |
| 6399 | (end-of-line) |
| 6400 | (search-backward ">" end t) |
| 6401 | (1+ (point))) |
| 6402 | (progn |
| 6403 | (search-backward "<" end t) |
| 6404 | (point)))))) |
| 6405 | ;; Get the references from the in-reply-to header if there |
| 6406 | ;; were no references and the in-reply-to header looks |
| 6407 | ;; promising. |
| 6408 | (if (and (search-forward "\nin-reply-to:" nil t) |
| 6409 | (setq in-reply-to (nnheader-header-value)) |
| 6410 | (string-match "<[^>]+>" in-reply-to)) |
| 6411 | (let (ref2) |
| 6412 | (setq ref (substring in-reply-to (match-beginning 0) |
| 6413 | (match-end 0))) |
| 6414 | (while (string-match "<[^>]+>" in-reply-to (match-end 0)) |
| 6415 | (setq ref2 (substring in-reply-to (match-beginning 0) |
| 6416 | (match-end 0))) |
| 6417 | (when (> (length ref2) (length ref)) |
| 6418 | (setq ref ref2))) |
| 6419 | ref) |
| 6420 | (setq ref nil)))) |
| 6421 | ;; Chars. |
| 6422 | (progn |
| 6423 | (goto-char p) |
| 6424 | (if (search-forward "\nchars: " nil t) |
| 6425 | (if (numberp (setq chars (ignore-errors (read cur)))) |
| 6426 | chars -1) |
| 6427 | -1)) |
| 6428 | ;; Lines. |
| 6429 | (progn |
| 6430 | (goto-char p) |
| 6431 | (if (search-forward "\nlines: " nil t) |
| 6432 | (if (numberp (setq lines (ignore-errors (read cur)))) |
| 6433 | lines -1) |
| 6434 | -1)) |
| 6435 | ;; Xref. |
| 6436 | (progn |
| 6437 | (goto-char p) |
| 6438 | (and (search-forward "\nxref:" nil t) |
| 6439 | (nnheader-header-value))) |
| 6440 | ;; Extra. |
| 6441 | (when gnus-extra-headers |
| 6442 | (let ((extra gnus-extra-headers) |
| 6443 | out) |
| 6444 | (while extra |
| 6445 | (goto-char p) |
| 6446 | (when (search-forward |
| 6447 | (concat "\n" (symbol-name (car extra)) ":") nil t) |
| 6448 | (push (cons (car extra) (nnheader-header-value)) |
| 6449 | out)) |
| 6450 | (pop extra)) |
| 6451 | out)))) |
| 6452 | (when (equal id ref) |
| 6453 | (setq ref nil)) |
| 6454 | |
| 6455 | (when gnus-alter-header-function |
| 6456 | (funcall gnus-alter-header-function header) |
| 6457 | (setq id (mail-header-id header) |
| 6458 | ref (gnus-parent-id (mail-header-references header)))) |
| 6459 | |
| 6460 | (when (setq header |
| 6461 | (gnus-dependencies-add-header |
| 6462 | header dependencies force-new)) |
| 6463 | (push header headers)) |
| 6464 | (goto-char (point-max)) |
| 6465 | (widen)) |
| 6466 | (nreverse headers))))) |
| 6467 | |
| 6468 | ;; Goes through the xover lines and returns a list of vectors |
| 6469 | (defun gnus-get-newsgroup-headers-xover (sequence &optional |
| 6470 | force-new dependencies |
| 6471 | group also-fetch-heads) |
| 6472 | "Parse the news overview data in the server buffer. |
| 6473 | Return a list of headers that match SEQUENCE (see |
| 6474 | `nntp-retrieve-headers')." |
| 6475 | ;; Get the Xref when the users reads the articles since most/some |
| 6476 | ;; NNTP servers do not include Xrefs when using XOVER. |
| 6477 | (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs)) |
| 6478 | (let ((mail-parse-charset gnus-newsgroup-charset) |
| 6479 | (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets) |
| 6480 | (cur nntp-server-buffer) |
| 6481 | (dependencies (or dependencies gnus-newsgroup-dependencies)) |
| 6482 | (allp (cond |
| 6483 | ((eq gnus-read-all-available-headers t) |
| 6484 | t) |
| 6485 | ((and (stringp gnus-read-all-available-headers) |
| 6486 | group) |
| 6487 | (string-match gnus-read-all-available-headers group)) |
| 6488 | (t |
| 6489 | nil))) |
| 6490 | number headers header) |
| 6491 | (with-current-buffer nntp-server-buffer |
| 6492 | (subst-char-in-region (point-min) (point-max) ?\r ? t) |
| 6493 | ;; Allow the user to mangle the headers before parsing them. |
| 6494 | (gnus-run-hooks 'gnus-parse-headers-hook) |
| 6495 | (goto-char (point-min)) |
| 6496 | (gnus-parse-without-error |
| 6497 | (while (and (or sequence allp) |
| 6498 | (not (eobp))) |
| 6499 | (setq number (read cur)) |
| 6500 | (when (not allp) |
| 6501 | (while (and sequence |
| 6502 | (< (car sequence) number)) |
| 6503 | (setq sequence (cdr sequence)))) |
| 6504 | (when (and (or allp |
| 6505 | (and sequence |
| 6506 | (eq number (car sequence)))) |
| 6507 | (progn |
| 6508 | (setq sequence (cdr sequence)) |
| 6509 | (setq header (inline |
| 6510 | (gnus-nov-parse-line |
| 6511 | number dependencies force-new))))) |
| 6512 | (push header headers)) |
| 6513 | (forward-line 1))) |
| 6514 | ;; A common bug in inn is that if you have posted an article and |
| 6515 | ;; then retrieves the active file, it will answer correctly -- |
| 6516 | ;; the new article is included. However, a NOV entry for the |
| 6517 | ;; article may not have been generated yet, so this may fail. |
| 6518 | ;; We work around this problem by retrieving the last few |
| 6519 | ;; headers using HEAD. |
| 6520 | (if (or (not also-fetch-heads) |
| 6521 | (not sequence)) |
| 6522 | ;; We (probably) got all the headers. |
| 6523 | (nreverse headers) |
| 6524 | (let ((gnus-nov-is-evil t)) |
| 6525 | (nconc |
| 6526 | (nreverse headers) |
| 6527 | (when (eq (gnus-retrieve-headers sequence group) 'headers) |
| 6528 | (gnus-get-newsgroup-headers)))))))) |
| 6529 | |
| 6530 | (defun gnus-article-get-xrefs () |
| 6531 | "Fill in the Xref value in `gnus-current-headers', if necessary. |
| 6532 | This is meant to be called in `gnus-article-internal-prepare-hook'." |
| 6533 | (let ((headers (with-current-buffer gnus-summary-buffer |
| 6534 | gnus-current-headers))) |
| 6535 | (or (not gnus-use-cross-reference) |
| 6536 | (not headers) |
| 6537 | (and (mail-header-xref headers) |
| 6538 | (not (string= (mail-header-xref headers) ""))) |
| 6539 | (let ((case-fold-search t) |
| 6540 | xref) |
| 6541 | (save-restriction |
| 6542 | (nnheader-narrow-to-headers) |
| 6543 | (goto-char (point-min)) |
| 6544 | (when (or (and (not (eobp)) |
| 6545 | (eq (downcase (char-after)) ?x) |
| 6546 | (looking-at "Xref:")) |
| 6547 | (search-forward "\nXref:" nil t)) |
| 6548 | (goto-char (1+ (match-end 0))) |
| 6549 | (setq xref (buffer-substring (point) (point-at-eol))) |
| 6550 | (mail-header-set-xref headers xref))))))) |
| 6551 | |
| 6552 | (defun gnus-summary-insert-subject (id &optional old-header use-old-header) |
| 6553 | "Find article ID and insert the summary line for that article. |
| 6554 | OLD-HEADER can either be a header or a line number to insert |
| 6555 | the subject line on. |
| 6556 | If USE-OLD-HEADER is non-nil, then OLD-HEADER should be a header, |
| 6557 | and OLD-HEADER will be used when the summary line is inserted, |
| 6558 | too, instead of trying to fetch new headers." |
| 6559 | (let* ((line (and (numberp old-header) old-header)) |
| 6560 | (old-header (and (vectorp old-header) old-header)) |
| 6561 | (header (cond ((and old-header use-old-header) |
| 6562 | old-header) |
| 6563 | ((and (numberp id) |
| 6564 | (gnus-number-to-header id)) |
| 6565 | (gnus-number-to-header id)) |
| 6566 | (t |
| 6567 | (gnus-read-header id)))) |
| 6568 | (number (and (numberp id) id)) |
| 6569 | d) |
| 6570 | (when header |
| 6571 | ;; Rebuild the thread that this article is part of and go to the |
| 6572 | ;; article we have fetched. |
| 6573 | (when (and (not gnus-show-threads) |
| 6574 | old-header) |
| 6575 | (when (and number |
| 6576 | (setq d (gnus-data-find (mail-header-number old-header)))) |
| 6577 | (goto-char (gnus-data-pos d)) |
| 6578 | (gnus-data-remove |
| 6579 | number |
| 6580 | (- (point-at-bol) |
| 6581 | (prog1 |
| 6582 | (1+ (point-at-eol)) |
| 6583 | (gnus-delete-line)))))) |
| 6584 | ;; Remove list identifiers from subject. |
| 6585 | (let ((gnus-newsgroup-headers (list header))) |
| 6586 | (gnus-summary-remove-list-identifiers)) |
| 6587 | (when old-header |
| 6588 | (mail-header-set-number header (mail-header-number old-header))) |
| 6589 | (setq gnus-newsgroup-sparse |
| 6590 | (delq (setq number (mail-header-number header)) |
| 6591 | gnus-newsgroup-sparse)) |
| 6592 | (setq gnus-newsgroup-ancient (delq number gnus-newsgroup-ancient)) |
| 6593 | (push number gnus-newsgroup-limit) |
| 6594 | (gnus-rebuild-thread (mail-header-id header) line) |
| 6595 | (gnus-summary-goto-subject number nil t)) |
| 6596 | (when (and (numberp number) |
| 6597 | (> number 0)) |
| 6598 | ;; We have to update the boundaries even if we can't fetch the |
| 6599 | ;; article if ID is a number -- so that the next `P' or `N' |
| 6600 | ;; command will fetch the previous (or next) article even |
| 6601 | ;; if the one we tried to fetch this time has been canceled. |
| 6602 | (unless (and gnus-newsgroup-end (< number gnus-newsgroup-end)) |
| 6603 | (setq gnus-newsgroup-end number)) |
| 6604 | (unless (and gnus-newsgroup-begin (> number gnus-newsgroup-begin)) |
| 6605 | (setq gnus-newsgroup-begin number)) |
| 6606 | (setq gnus-newsgroup-unselected |
| 6607 | (delq number gnus-newsgroup-unselected))) |
| 6608 | ;; Report back a success? |
| 6609 | (and header (mail-header-number header)))) |
| 6610 | |
| 6611 | ;;; Process/prefix in the summary buffer |
| 6612 | |
| 6613 | (defun gnus-summary-work-articles (n) |
| 6614 | "Return a list of articles to be worked upon. |
| 6615 | The prefix argument, the list of process marked articles, and the |
| 6616 | current article will be taken into consideration." |
| 6617 | (with-current-buffer gnus-summary-buffer |
| 6618 | (cond |
| 6619 | (n |
| 6620 | ;; A numerical prefix has been given. |
| 6621 | (setq n (prefix-numeric-value n)) |
| 6622 | (let ((backward (< n 0)) |
| 6623 | (n (abs (prefix-numeric-value n))) |
| 6624 | articles article) |
| 6625 | (save-excursion |
| 6626 | (while |
| 6627 | (and (> n 0) |
| 6628 | (push (setq article (gnus-summary-article-number)) |
| 6629 | articles) |
| 6630 | (if backward |
| 6631 | (gnus-summary-find-prev nil article) |
| 6632 | (gnus-summary-find-next nil article))) |
| 6633 | (decf n))) |
| 6634 | (nreverse articles))) |
| 6635 | ((and (gnus-region-active-p) (mark)) |
| 6636 | (message "region active") |
| 6637 | ;; Work on the region between point and mark. |
| 6638 | (let ((max (max (point) (mark))) |
| 6639 | articles article) |
| 6640 | (save-excursion |
| 6641 | (goto-char (min (point) (mark))) |
| 6642 | (while |
| 6643 | (and |
| 6644 | (push (setq article (gnus-summary-article-number)) articles) |
| 6645 | (gnus-summary-find-next nil article) |
| 6646 | (< (point) max))) |
| 6647 | (nreverse articles)))) |
| 6648 | (gnus-newsgroup-processable |
| 6649 | ;; There are process-marked articles present. |
| 6650 | ;; Save current state. |
| 6651 | (gnus-summary-save-process-mark) |
| 6652 | ;; Return the list. |
| 6653 | (reverse gnus-newsgroup-processable)) |
| 6654 | (t |
| 6655 | ;; Just return the current article. |
| 6656 | (list (gnus-summary-article-number)))))) |
| 6657 | |
| 6658 | (defmacro gnus-summary-iterate (arg &rest forms) |
| 6659 | "Iterate over the process/prefixed articles and do FORMS. |
| 6660 | ARG is the interactive prefix given to the command. FORMS will be |
| 6661 | executed with point over the summary line of the articles." |
| 6662 | (let ((articles (make-symbol "gnus-summary-iterate-articles"))) |
| 6663 | `(let ((,articles (gnus-summary-work-articles ,arg))) |
| 6664 | (while ,articles |
| 6665 | (gnus-summary-goto-subject (car ,articles)) |
| 6666 | ,@forms |
| 6667 | (pop ,articles))))) |
| 6668 | |
| 6669 | (put 'gnus-summary-iterate 'lisp-indent-function 1) |
| 6670 | (put 'gnus-summary-iterate 'edebug-form-spec '(form body)) |
| 6671 | |
| 6672 | (defun gnus-summary-save-process-mark () |
| 6673 | "Push the current set of process marked articles on the stack." |
| 6674 | (interactive) |
| 6675 | (push (copy-sequence gnus-newsgroup-processable) |
| 6676 | gnus-newsgroup-process-stack)) |
| 6677 | |
| 6678 | (defun gnus-summary-kill-process-mark () |
| 6679 | "Push the current set of process marked articles on the stack and unmark." |
| 6680 | (interactive) |
| 6681 | (gnus-summary-save-process-mark) |
| 6682 | (gnus-summary-unmark-all-processable)) |
| 6683 | |
| 6684 | (defun gnus-summary-yank-process-mark () |
| 6685 | "Pop the last process mark state off the stack and restore it." |
| 6686 | (interactive) |
| 6687 | (unless gnus-newsgroup-process-stack |
| 6688 | (error "Empty mark stack")) |
| 6689 | (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack))) |
| 6690 | |
| 6691 | (defun gnus-summary-process-mark-set (set) |
| 6692 | "Make SET into the current process marked articles." |
| 6693 | (gnus-summary-unmark-all-processable) |
| 6694 | (mapc 'gnus-summary-set-process-mark set)) |
| 6695 | |
| 6696 | ;;; Searching and stuff |
| 6697 | |
| 6698 | (defun gnus-summary-search-group (&optional backward use-level) |
| 6699 | "Search for next unread newsgroup. |
| 6700 | If optional argument BACKWARD is non-nil, search backward instead." |
| 6701 | (with-current-buffer gnus-group-buffer |
| 6702 | (when (gnus-group-search-forward |
| 6703 | backward nil (if use-level (gnus-group-group-level) nil)) |
| 6704 | (gnus-group-group-name)))) |
| 6705 | |
| 6706 | (defun gnus-summary-best-group (&optional exclude-group) |
| 6707 | "Find the name of the best unread group. |
| 6708 | If EXCLUDE-GROUP, do not go to this group." |
| 6709 | (with-current-buffer gnus-group-buffer |
| 6710 | (save-excursion |
| 6711 | (gnus-group-best-unread-group exclude-group)))) |
| 6712 | |
| 6713 | (defun gnus-summary-find-next (&optional unread article backward) |
| 6714 | (if backward |
| 6715 | (gnus-summary-find-prev unread article) |
| 6716 | (let* ((dummy (gnus-summary-article-intangible-p)) |
| 6717 | (article (or article (gnus-summary-article-number))) |
| 6718 | (data (gnus-data-find-list article)) |
| 6719 | result) |
| 6720 | (when (and (not dummy) |
| 6721 | (or (not gnus-summary-check-current) |
| 6722 | (not unread) |
| 6723 | (not (gnus-data-unread-p (car data))))) |
| 6724 | (setq data (cdr data))) |
| 6725 | (when (setq result |
| 6726 | (if unread |
| 6727 | (progn |
| 6728 | (while data |
| 6729 | (unless (memq (gnus-data-number (car data)) |
| 6730 | (cond |
| 6731 | ((eq gnus-auto-goto-ignores |
| 6732 | 'always-undownloaded) |
| 6733 | gnus-newsgroup-undownloaded) |
| 6734 | (gnus-plugged |
| 6735 | nil) |
| 6736 | ((eq gnus-auto-goto-ignores |
| 6737 | 'unfetched) |
| 6738 | gnus-newsgroup-unfetched) |
| 6739 | ((eq gnus-auto-goto-ignores |
| 6740 | 'undownloaded) |
| 6741 | gnus-newsgroup-undownloaded))) |
| 6742 | (when (gnus-data-unread-p (car data)) |
| 6743 | (setq result (car data) |
| 6744 | data nil))) |
| 6745 | (setq data (cdr data))) |
| 6746 | result) |
| 6747 | (car data))) |
| 6748 | (goto-char (gnus-data-pos result)) |
| 6749 | (gnus-data-number result))))) |
| 6750 | |
| 6751 | (defun gnus-summary-find-prev (&optional unread article) |
| 6752 | (let* ((eobp (eobp)) |
| 6753 | (article (or article (gnus-summary-article-number))) |
| 6754 | (data (gnus-data-find-list article (gnus-data-list 'rev))) |
| 6755 | result) |
| 6756 | (when (and (not eobp) |
| 6757 | (or (not gnus-summary-check-current) |
| 6758 | (not unread) |
| 6759 | (not (gnus-data-unread-p (car data))))) |
| 6760 | (setq data (cdr data))) |
| 6761 | (when (setq result |
| 6762 | (if unread |
| 6763 | (progn |
| 6764 | (while data |
| 6765 | (unless (memq (gnus-data-number (car data)) |
| 6766 | (cond |
| 6767 | ((eq gnus-auto-goto-ignores |
| 6768 | 'always-undownloaded) |
| 6769 | gnus-newsgroup-undownloaded) |
| 6770 | (gnus-plugged |
| 6771 | nil) |
| 6772 | ((eq gnus-auto-goto-ignores |
| 6773 | 'unfetched) |
| 6774 | gnus-newsgroup-unfetched) |
| 6775 | ((eq gnus-auto-goto-ignores |
| 6776 | 'undownloaded) |
| 6777 | gnus-newsgroup-undownloaded))) |
| 6778 | (when (gnus-data-unread-p (car data)) |
| 6779 | (setq result (car data) |
| 6780 | data nil))) |
| 6781 | (setq data (cdr data))) |
| 6782 | result) |
| 6783 | (car data))) |
| 6784 | (goto-char (gnus-data-pos result)) |
| 6785 | (gnus-data-number result)))) |
| 6786 | |
| 6787 | (defun gnus-summary-find-subject (subject &optional unread backward article) |
| 6788 | (let* ((simp-subject (gnus-simplify-subject-fully subject)) |
| 6789 | (article (or article (gnus-summary-article-number))) |
| 6790 | (articles (gnus-data-list backward)) |
| 6791 | (arts (gnus-data-find-list article articles)) |
| 6792 | result) |
| 6793 | (when (or (not gnus-summary-check-current) |
| 6794 | (not unread) |
| 6795 | (not (gnus-data-unread-p (car arts)))) |
| 6796 | (setq arts (cdr arts))) |
| 6797 | (while arts |
| 6798 | (and (or (not unread) |
| 6799 | (gnus-data-unread-p (car arts))) |
| 6800 | (vectorp (gnus-data-header (car arts))) |
| 6801 | (gnus-subject-equal |
| 6802 | simp-subject (mail-header-subject (gnus-data-header (car arts))) t) |
| 6803 | (setq result (car arts) |
| 6804 | arts nil)) |
| 6805 | (setq arts (cdr arts))) |
| 6806 | (and result |
| 6807 | (goto-char (gnus-data-pos result)) |
| 6808 | (gnus-data-number result)))) |
| 6809 | |
| 6810 | (defun gnus-summary-search-forward (&optional unread subject backward) |
| 6811 | "Search forward for an article. |
| 6812 | If UNREAD, look for unread articles. If SUBJECT, look for |
| 6813 | articles with that subject. If BACKWARD, search backward instead." |
| 6814 | (cond (subject (gnus-summary-find-subject subject unread backward)) |
| 6815 | (backward (gnus-summary-find-prev unread)) |
| 6816 | (t (gnus-summary-find-next unread)))) |
| 6817 | |
| 6818 | (defun gnus-recenter (&optional n) |
| 6819 | "Center point in window and redisplay frame. |
| 6820 | Also do horizontal recentering." |
| 6821 | (interactive "P") |
| 6822 | (when (and gnus-auto-center-summary |
| 6823 | (not (eq gnus-auto-center-summary 'vertical))) |
| 6824 | (gnus-horizontal-recenter)) |
| 6825 | (if (fboundp 'recenter-top-bottom) |
| 6826 | (recenter-top-bottom n) |
| 6827 | (recenter n))) |
| 6828 | |
| 6829 | (put 'gnus-recenter 'isearch-scroll t) |
| 6830 | |
| 6831 | (defun gnus-forward-line-ignore-invisible (n) |
| 6832 | "Move N lines forward (backward if N is negative). |
| 6833 | Like forward-line, but skip over (and don't count) invisible lines." |
| 6834 | (let (done) |
| 6835 | (while (and (> n 0) (not done)) |
| 6836 | ;; If the following character is currently invisible, |
| 6837 | ;; skip all characters with that same `invisible' property value. |
| 6838 | (while (gnus-invisible-p (point)) |
| 6839 | (goto-char (gnus-next-char-property-change (point)))) |
| 6840 | (forward-line 1) |
| 6841 | (if (eobp) |
| 6842 | (setq done t) |
| 6843 | (setq n (1- n)))) |
| 6844 | (while (and (< n 0) (not done)) |
| 6845 | (forward-line -1) |
| 6846 | (if (bobp) (setq done t) |
| 6847 | (setq n (1+ n)) |
| 6848 | (while (and (not (bobp)) (gnus-invisible-p (1- (point)))) |
| 6849 | (goto-char (gnus-previous-char-property-change (point)))))))) |
| 6850 | |
| 6851 | (defun gnus-summary-recenter () |
| 6852 | "Center point in the summary window. |
| 6853 | If `gnus-auto-center-summary' is nil, or the article buffer isn't |
| 6854 | displayed, no centering will be performed." |
| 6855 | ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle). |
| 6856 | ;; Recenter only when requested. Suggested by popovich@park.cs.columbia.edu. |
| 6857 | (interactive) |
| 6858 | ;; The user has to want it. |
| 6859 | (when gnus-auto-center-summary |
| 6860 | (let* ((top (cond ((< (window-height) 4) 0) |
| 6861 | ((< (window-height) 7) 1) |
| 6862 | (t (if (numberp gnus-auto-center-summary) |
| 6863 | gnus-auto-center-summary |
| 6864 | (/ (1- (window-height)) 2))))) |
| 6865 | (height (1- (window-height))) |
| 6866 | (bottom (save-excursion |
| 6867 | (goto-char (point-max)) |
| 6868 | (gnus-forward-line-ignore-invisible (- height)) |
| 6869 | (point))) |
| 6870 | (window (get-buffer-window (current-buffer)))) |
| 6871 | (when (get-buffer-window gnus-article-buffer) |
| 6872 | ;; Only do recentering when the article buffer is displayed, |
| 6873 | ;; Set the window start to either `bottom', which is the biggest |
| 6874 | ;; possible valid number, or the second line from the top, |
| 6875 | ;; whichever is the least. |
| 6876 | (let ((top-pos (save-excursion |
| 6877 | (gnus-forward-line-ignore-invisible (- top)) |
| 6878 | (point)))) |
| 6879 | (if (> bottom top-pos) |
| 6880 | ;; Keep the second line from the top visible |
| 6881 | (set-window-start window top-pos) |
| 6882 | ;; Try to keep the bottom line visible; if it's partially |
| 6883 | ;; obscured, either scroll one more line to make it fully |
| 6884 | ;; visible, or revert to using TOP-POS. |
| 6885 | (save-excursion |
| 6886 | (goto-char (point-max)) |
| 6887 | (gnus-forward-line-ignore-invisible -1) |
| 6888 | (let ((last-line-start (point))) |
| 6889 | (goto-char bottom) |
| 6890 | (set-window-start window (point) t) |
| 6891 | (when (not (pos-visible-in-window-p last-line-start window)) |
| 6892 | (gnus-forward-line-ignore-invisible 1) |
| 6893 | (set-window-start window (min (point) top-pos) t))))))) |
| 6894 | ;; Do horizontal recentering while we're at it. |
| 6895 | (when (and (get-buffer-window (current-buffer) t) |
| 6896 | (not (eq gnus-auto-center-summary 'vertical))) |
| 6897 | (let ((selected (selected-window))) |
| 6898 | (select-window (get-buffer-window (current-buffer) t)) |
| 6899 | (gnus-summary-position-point) |
| 6900 | (gnus-horizontal-recenter) |
| 6901 | (select-window selected)))))) |
| 6902 | |
| 6903 | (defun gnus-summary-jump-to-group (newsgroup) |
| 6904 | "Move point to NEWSGROUP in group mode buffer." |
| 6905 | ;; Keep update point of group mode buffer if visible. |
| 6906 | (if (eq (current-buffer) (get-buffer gnus-group-buffer)) |
| 6907 | (save-window-excursion |
| 6908 | ;; Take care of tree window mode. |
| 6909 | (when (get-buffer-window gnus-group-buffer) |
| 6910 | (pop-to-buffer gnus-group-buffer)) |
| 6911 | (gnus-group-jump-to-group newsgroup)) |
| 6912 | (save-excursion |
| 6913 | ;; Take care of tree window mode. |
| 6914 | (if (get-buffer-window gnus-group-buffer 0) |
| 6915 | (pop-to-buffer gnus-group-buffer) |
| 6916 | (set-buffer gnus-group-buffer)) |
| 6917 | (gnus-group-jump-to-group newsgroup)))) |
| 6918 | |
| 6919 | ;; This function returns a list of article numbers based on the |
| 6920 | ;; difference between the ranges of read articles in this group and |
| 6921 | ;; the range of active articles. |
| 6922 | (defun gnus-list-of-unread-articles (group) |
| 6923 | (let* ((read (gnus-info-read (gnus-get-info group))) |
| 6924 | (active (or (gnus-active group) (gnus-activate-group group))) |
| 6925 | (last (or (cdr active) |
| 6926 | (error "Group %s couldn't be activated " group))) |
| 6927 | (bottom (if gnus-newsgroup-maximum-articles |
| 6928 | (max (car active) |
| 6929 | (- last gnus-newsgroup-maximum-articles -1)) |
| 6930 | (car active))) |
| 6931 | first nlast unread) |
| 6932 | ;; If none are read, then all are unread. |
| 6933 | (if (not read) |
| 6934 | (setq first bottom) |
| 6935 | ;; If the range of read articles is a single range, then the |
| 6936 | ;; first unread article is the article after the last read |
| 6937 | ;; article. Sounds logical, doesn't it? |
| 6938 | (if (and (not (listp (cdr read))) |
| 6939 | (or (< (car read) bottom) |
| 6940 | (progn (setq read (list read)) |
| 6941 | nil))) |
| 6942 | (setq first (max bottom (1+ (cdr read)))) |
| 6943 | ;; `read' is a list of ranges. |
| 6944 | (when (/= (setq nlast (or (and (numberp (car read)) (car read)) |
| 6945 | (caar read))) |
| 6946 | 1) |
| 6947 | (setq first bottom)) |
| 6948 | (while read |
| 6949 | (when first |
| 6950 | (while (< first nlast) |
| 6951 | (setq unread (cons first unread) |
| 6952 | first (1+ first)))) |
| 6953 | (setq first (1+ (if (atom (car read)) (car read) (cdar read)))) |
| 6954 | (setq nlast (if (atom (cadr read)) (cadr read) (caadr read))) |
| 6955 | (setq read (cdr read))))) |
| 6956 | ;; And add the last unread articles. |
| 6957 | (while (<= first last) |
| 6958 | (setq unread (cons first unread) |
| 6959 | first (1+ first))) |
| 6960 | ;; Return the list of unread articles. |
| 6961 | (delq 0 (nreverse unread)))) |
| 6962 | |
| 6963 | (defun gnus-list-of-read-articles (group) |
| 6964 | "Return a list of unread, unticked and non-dormant articles." |
| 6965 | (let* ((info (gnus-get-info group)) |
| 6966 | (marked (gnus-info-marks info)) |
| 6967 | (active (gnus-active group))) |
| 6968 | (and info active |
| 6969 | (gnus-list-range-difference |
| 6970 | (gnus-list-range-difference |
| 6971 | (gnus-sorted-complement |
| 6972 | (gnus-uncompress-range |
| 6973 | (if gnus-newsgroup-maximum-articles |
| 6974 | (cons (max (car active) |
| 6975 | (- (cdr active) |
| 6976 | gnus-newsgroup-maximum-articles |
| 6977 | -1)) |
| 6978 | (cdr active)) |
| 6979 | active)) |
| 6980 | (gnus-list-of-unread-articles group)) |
| 6981 | (cdr (assq 'dormant marked))) |
| 6982 | (cdr (assq 'tick marked)))))) |
| 6983 | |
| 6984 | ;; This function returns a sequence of article numbers based on the |
| 6985 | ;; difference between the ranges of read articles in this group and |
| 6986 | ;; the range of active articles. |
| 6987 | (defun gnus-sequence-of-unread-articles (group) |
| 6988 | (let* ((read (gnus-info-read (gnus-get-info group))) |
| 6989 | (active (or (gnus-active group) (gnus-activate-group group))) |
| 6990 | (last (cdr active)) |
| 6991 | (bottom (if gnus-newsgroup-maximum-articles |
| 6992 | (max (car active) |
| 6993 | (- last gnus-newsgroup-maximum-articles -1)) |
| 6994 | (car active))) |
| 6995 | first nlast unread) |
| 6996 | ;; If none are read, then all are unread. |
| 6997 | (if (not read) |
| 6998 | (setq first bottom) |
| 6999 | ;; If the range of read articles is a single range, then the |
| 7000 | ;; first unread article is the article after the last read |
| 7001 | ;; article. Sounds logical, doesn't it? |
| 7002 | (if (and (not (listp (cdr read))) |
| 7003 | (or (< (car read) bottom) |
| 7004 | (progn (setq read (list read)) |
| 7005 | nil))) |
| 7006 | (setq first (max bottom (1+ (cdr read)))) |
| 7007 | ;; `read' is a list of ranges. |
| 7008 | (when (/= (setq nlast (or (and (numberp (car read)) (car read)) |
| 7009 | (caar read))) |
| 7010 | 1) |
| 7011 | (setq first bottom)) |
| 7012 | (while read |
| 7013 | (when first |
| 7014 | (push (cons first nlast) unread)) |
| 7015 | (setq first (1+ (if (atom (car read)) (car read) (cdar read)))) |
| 7016 | (setq nlast (if (atom (cadr read)) (cadr read) (caadr read))) |
| 7017 | (setq read (cdr read))))) |
| 7018 | ;; And add the last unread articles. |
| 7019 | (cond ((not (and first last)) |
| 7020 | nil) |
| 7021 | ((< first last) |
| 7022 | (push (cons first last) unread)) |
| 7023 | ((= first last) |
| 7024 | (push first unread))) |
| 7025 | ;; Return the sequence of unread articles. |
| 7026 | (delq 0 (nreverse unread)))) |
| 7027 | |
| 7028 | ;; Various summary commands |
| 7029 | |
| 7030 | (defun gnus-summary-select-article-buffer () |
| 7031 | "Reconfigure windows to show the article buffer. |
| 7032 | If `gnus-widen-article-window' is set, show only the article |
| 7033 | buffer." |
| 7034 | (interactive) |
| 7035 | (if (not (gnus-buffer-live-p gnus-article-buffer)) |
| 7036 | (error "There is no article buffer for this summary buffer") |
| 7037 | (unless (get-buffer-window gnus-article-buffer) |
| 7038 | (gnus-summary-show-article)) |
| 7039 | (gnus-configure-windows |
| 7040 | (if gnus-widen-article-window |
| 7041 | 'only-article |
| 7042 | 'article) |
| 7043 | t) |
| 7044 | (select-window (get-buffer-window gnus-article-buffer)))) |
| 7045 | |
| 7046 | (defun gnus-summary-universal-argument (arg) |
| 7047 | "Perform any operation on all articles that are process/prefixed." |
| 7048 | (interactive "P") |
| 7049 | (let ((articles (gnus-summary-work-articles arg)) |
| 7050 | func article) |
| 7051 | (if (eq |
| 7052 | (setq |
| 7053 | func |
| 7054 | (key-binding |
| 7055 | (read-key-sequence |
| 7056 | (substitute-command-keys |
| 7057 | "\\<gnus-summary-mode-map>\\[gnus-summary-universal-argument]")))) |
| 7058 | 'undefined) |
| 7059 | (gnus-error 1 "Undefined key") |
| 7060 | (save-excursion |
| 7061 | (while articles |
| 7062 | (gnus-summary-goto-subject (setq article (pop articles))) |
| 7063 | (let (gnus-newsgroup-processable) |
| 7064 | (command-execute func)) |
| 7065 | (gnus-summary-remove-process-mark article))))) |
| 7066 | (gnus-summary-position-point)) |
| 7067 | |
| 7068 | (defun gnus-summary-toggle-truncation (&optional arg) |
| 7069 | "Toggle truncation of summary lines. |
| 7070 | With ARG, turn line truncation on if ARG is positive." |
| 7071 | (interactive "P") |
| 7072 | (setq truncate-lines |
| 7073 | (if (null arg) (not truncate-lines) |
| 7074 | (> (prefix-numeric-value arg) 0))) |
| 7075 | (redraw-display)) |
| 7076 | |
| 7077 | (defun gnus-summary-find-for-reselect () |
| 7078 | "Return the number of an article to stay on across a reselect. |
| 7079 | The current article is considered, then following articles, then previous |
| 7080 | articles. An article is sought which is not canceled and isn't a temporary |
| 7081 | insertion from another group. If there's no such then return a dummy 0." |
| 7082 | (let (found) |
| 7083 | (dolist (rev '(nil t)) |
| 7084 | (unless found ; don't demand the reverse list if we don't need it |
| 7085 | (let ((data (gnus-data-find-list |
| 7086 | (gnus-summary-article-number) (gnus-data-list rev)))) |
| 7087 | (while (and data (not found)) |
| 7088 | (if (and (< 0 (gnus-data-number (car data))) |
| 7089 | (not (eq gnus-canceled-mark (gnus-data-mark (car data))))) |
| 7090 | (setq found (gnus-data-number (car data)))) |
| 7091 | (setq data (cdr data)))))) |
| 7092 | (or found 0))) |
| 7093 | |
| 7094 | (defun gnus-summary-reselect-current-group (&optional all rescan) |
| 7095 | "Exit and then reselect the current newsgroup. |
| 7096 | The prefix argument ALL means to select all articles." |
| 7097 | (interactive "P") |
| 7098 | (when (gnus-ephemeral-group-p gnus-newsgroup-name) |
| 7099 | (error "Ephemeral groups can't be reselected")) |
| 7100 | (let ((current-subject (gnus-summary-find-for-reselect)) |
| 7101 | (group gnus-newsgroup-name)) |
| 7102 | (setq gnus-newsgroup-begin nil) |
| 7103 | (gnus-summary-exit nil 'leave-hidden) |
| 7104 | ;; We have to adjust the point of group mode buffer because |
| 7105 | ;; point was moved to the next unread newsgroup by exiting. |
| 7106 | (gnus-summary-jump-to-group group) |
| 7107 | (when rescan |
| 7108 | (save-excursion |
| 7109 | (gnus-group-get-new-news-this-group 1))) |
| 7110 | (gnus-group-read-group all t) |
| 7111 | (gnus-summary-goto-subject current-subject nil t))) |
| 7112 | |
| 7113 | (defun gnus-summary-rescan-group (&optional all) |
| 7114 | "Exit the newsgroup, ask for new articles, and select the newsgroup." |
| 7115 | (interactive "P") |
| 7116 | (let ((config gnus-current-window-configuration)) |
| 7117 | (gnus-summary-reselect-current-group all t) |
| 7118 | (gnus-configure-windows config) |
| 7119 | (when (eq config 'article) |
| 7120 | (gnus-summary-select-article)))) |
| 7121 | |
| 7122 | (defun gnus-summary-update-info (&optional non-destructive) |
| 7123 | (save-excursion |
| 7124 | (let ((group gnus-newsgroup-name)) |
| 7125 | (when group |
| 7126 | (when gnus-newsgroup-kill-headers |
| 7127 | (setq gnus-newsgroup-killed |
| 7128 | (gnus-compress-sequence |
| 7129 | (gnus-sorted-union |
| 7130 | (gnus-list-range-intersection |
| 7131 | gnus-newsgroup-unselected gnus-newsgroup-killed) |
| 7132 | gnus-newsgroup-unreads) |
| 7133 | t))) |
| 7134 | (unless (listp (cdr gnus-newsgroup-killed)) |
| 7135 | (setq gnus-newsgroup-killed (list gnus-newsgroup-killed))) |
| 7136 | (let ((headers gnus-newsgroup-headers) |
| 7137 | (ephemeral-p (gnus-ephemeral-group-p group)) |
| 7138 | info) |
| 7139 | (unless ephemeral-p |
| 7140 | (setq info (copy-sequence (gnus-get-info group)) |
| 7141 | info (delq (gnus-info-params info) info))) |
| 7142 | ;; Set the new ranges of read articles. |
| 7143 | (with-current-buffer gnus-group-buffer |
| 7144 | (gnus-undo-force-boundary)) |
| 7145 | (gnus-update-read-articles |
| 7146 | group (gnus-sorted-union |
| 7147 | gnus-newsgroup-unreads gnus-newsgroup-unselected)) |
| 7148 | ;; Set the current article marks. |
| 7149 | (let ((gnus-newsgroup-scored |
| 7150 | (if (and (not gnus-save-score) |
| 7151 | (not non-destructive)) |
| 7152 | nil |
| 7153 | gnus-newsgroup-scored))) |
| 7154 | (save-excursion |
| 7155 | (gnus-update-marks))) |
| 7156 | ;; Do the cross-ref thing. |
| 7157 | (when gnus-use-cross-reference |
| 7158 | (gnus-mark-xrefs-as-read group headers gnus-newsgroup-unreads)) |
| 7159 | ;; Do not switch windows but change the buffer to work. |
| 7160 | (set-buffer gnus-group-buffer) |
| 7161 | (unless ephemeral-p |
| 7162 | (gnus-group-update-group |
| 7163 | group nil |
| 7164 | (equal info |
| 7165 | (setq info (copy-sequence (gnus-get-info group)) |
| 7166 | info (delq (gnus-info-params info) info)))))))))) |
| 7167 | |
| 7168 | (defun gnus-summary-save-newsrc (&optional force) |
| 7169 | "Save the current number of read/marked articles in the dribble buffer. |
| 7170 | The dribble buffer will then be saved. |
| 7171 | If FORCE (the prefix), also save the .newsrc file(s)." |
| 7172 | (interactive "P") |
| 7173 | (gnus-summary-update-info t) |
| 7174 | (if force |
| 7175 | (gnus-save-newsrc-file) |
| 7176 | (gnus-dribble-save))) |
| 7177 | |
| 7178 | (declare-function gnus-cache-write-active "gnus-cache" (&optional force)) |
| 7179 | |
| 7180 | (defun gnus-summary-exit (&optional temporary leave-hidden) |
| 7181 | "Exit reading current newsgroup, and then return to group selection mode. |
| 7182 | `gnus-exit-group-hook' is called with no arguments if that value is non-nil." |
| 7183 | (interactive) |
| 7184 | (gnus-set-global-variables) |
| 7185 | (when (gnus-buffer-live-p gnus-article-buffer) |
| 7186 | (with-current-buffer gnus-article-buffer |
| 7187 | (mm-destroy-parts gnus-article-mime-handles) |
| 7188 | ;; Set it to nil for safety reason. |
| 7189 | (setq gnus-article-mime-handle-alist nil) |
| 7190 | (setq gnus-article-mime-handles nil))) |
| 7191 | (gnus-kill-save-kill-buffer) |
| 7192 | (gnus-async-halt-prefetch) |
| 7193 | (let* ((group gnus-newsgroup-name) |
| 7194 | (quit-config (gnus-group-quit-config gnus-newsgroup-name)) |
| 7195 | (gnus-group-is-exiting-p t) |
| 7196 | (article-buffer gnus-article-buffer) |
| 7197 | (original-article-buffer gnus-original-article-buffer) |
| 7198 | (mode major-mode) |
| 7199 | (group-point nil) |
| 7200 | (buf (current-buffer)) |
| 7201 | ;; `gnus-single-article-buffer' is nil buffer-locally in |
| 7202 | ;; ephemeral group of which summary buffer will be killed, |
| 7203 | ;; but the global value may be non-nil. |
| 7204 | (single-article-buffer gnus-single-article-buffer)) |
| 7205 | (unless quit-config |
| 7206 | ;; Do adaptive scoring, and possibly save score files. |
| 7207 | (when gnus-newsgroup-adaptive |
| 7208 | (gnus-score-adaptive)) |
| 7209 | (when gnus-use-scoring |
| 7210 | (gnus-score-save))) |
| 7211 | (gnus-run-hooks 'gnus-summary-prepare-exit-hook) |
| 7212 | (when gnus-use-cache |
| 7213 | (gnus-cache-possibly-remove-articles) |
| 7214 | (gnus-cache-save-buffers)) |
| 7215 | (gnus-async-prefetch-remove-group group) |
| 7216 | (when gnus-suppress-duplicates |
| 7217 | (gnus-dup-enter-articles)) |
| 7218 | (when gnus-use-trees |
| 7219 | (gnus-tree-close group)) |
| 7220 | (when gnus-use-cache |
| 7221 | (gnus-cache-write-active)) |
| 7222 | ;; Remove entries for this group. |
| 7223 | (nnmail-purge-split-history (gnus-group-real-name group)) |
| 7224 | ;; Make all changes in this group permanent. |
| 7225 | (unless quit-config |
| 7226 | (gnus-run-hooks 'gnus-exit-group-hook) |
| 7227 | (gnus-summary-update-info)) |
| 7228 | (gnus-close-group group) |
| 7229 | ;; Make sure where we were, and go to next newsgroup. |
| 7230 | (when (buffer-live-p (get-buffer gnus-group-buffer)) |
| 7231 | (set-buffer gnus-group-buffer)) |
| 7232 | (unless quit-config |
| 7233 | (gnus-group-jump-to-group group)) |
| 7234 | (gnus-run-hooks 'gnus-summary-exit-hook) |
| 7235 | (unless (or quit-config |
| 7236 | (not gnus-summary-next-group-on-exit) |
| 7237 | ;; If this group has disappeared from the summary |
| 7238 | ;; buffer, don't skip forwards. |
| 7239 | (not (string= group (gnus-group-group-name)))) |
| 7240 | (gnus-group-next-unread-group 1)) |
| 7241 | (setq group-point (point)) |
| 7242 | (if temporary |
| 7243 | nil ;Nothing to do. |
| 7244 | (set-buffer buf) |
| 7245 | (if (not gnus-kill-summary-on-exit) |
| 7246 | (progn |
| 7247 | (gnus-deaden-summary) |
| 7248 | (setq mode nil)) |
| 7249 | (when (get-buffer gnus-article-buffer) |
| 7250 | (bury-buffer gnus-article-buffer)) |
| 7251 | ;; Return to group mode buffer. |
| 7252 | (when (eq mode 'gnus-summary-mode) |
| 7253 | (gnus-kill-buffer buf))) |
| 7254 | |
| 7255 | (setq gnus-current-select-method gnus-select-method) |
| 7256 | (when (gnus-buffer-live-p gnus-group-buffer) |
| 7257 | (set-buffer gnus-group-buffer)) |
| 7258 | (if quit-config |
| 7259 | (gnus-handle-ephemeral-exit quit-config) |
| 7260 | (goto-char group-point) |
| 7261 | ;; If gnus-group-buffer is already displayed, make sure we also move |
| 7262 | ;; the cursor in the window that displays it. |
| 7263 | (let ((win (get-buffer-window (current-buffer) 0))) |
| 7264 | (if win (set-window-point win (point)))) |
| 7265 | (unless leave-hidden |
| 7266 | (gnus-configure-windows 'group 'force))) |
| 7267 | |
| 7268 | ;; If we have several article buffers, we kill them at exit. |
| 7269 | (unless single-article-buffer |
| 7270 | (when (gnus-buffer-live-p article-buffer) |
| 7271 | (with-current-buffer article-buffer |
| 7272 | ;; Don't kill sticky article buffers |
| 7273 | (unless (eq major-mode 'gnus-sticky-article-mode) |
| 7274 | (gnus-kill-buffer article-buffer) |
| 7275 | (setq gnus-article-current nil)))) |
| 7276 | (gnus-kill-buffer original-article-buffer)) |
| 7277 | |
| 7278 | ;; Clear the current group name. |
| 7279 | (unless quit-config |
| 7280 | (setq gnus-newsgroup-name nil))))) |
| 7281 | |
| 7282 | (declare-function gnus-article-stop-animations "gnus-art" ()) |
| 7283 | (declare-function gnus-stop-downloads "gnus-art" ()) |
| 7284 | |
| 7285 | (defalias 'gnus-summary-quit 'gnus-summary-exit-no-update) |
| 7286 | (defun gnus-summary-exit-no-update (&optional no-questions) |
| 7287 | "Quit reading current newsgroup without updating read article info." |
| 7288 | (interactive) |
| 7289 | (let* ((group gnus-newsgroup-name) |
| 7290 | (gnus-group-is-exiting-p t) |
| 7291 | (gnus-group-is-exiting-without-update-p t) |
| 7292 | (quit-config (gnus-group-quit-config group))) |
| 7293 | (when (or no-questions |
| 7294 | gnus-expert-user |
| 7295 | (gnus-y-or-n-p "Discard changes to this group and exit? ")) |
| 7296 | (gnus-async-halt-prefetch) |
| 7297 | (run-hooks 'gnus-summary-prepare-exit-hook) |
| 7298 | (when (gnus-buffer-live-p gnus-article-buffer) |
| 7299 | (with-current-buffer gnus-article-buffer |
| 7300 | (gnus-article-stop-animations) |
| 7301 | (gnus-stop-downloads) |
| 7302 | (mm-destroy-parts gnus-article-mime-handles) |
| 7303 | ;; Set it to nil for safety reason. |
| 7304 | (setq gnus-article-mime-handle-alist nil) |
| 7305 | (setq gnus-article-mime-handles nil))) |
| 7306 | ;; If we have several article buffers, we kill them at exit. |
| 7307 | (unless gnus-single-article-buffer |
| 7308 | (gnus-kill-buffer gnus-article-buffer) |
| 7309 | (gnus-kill-buffer gnus-original-article-buffer) |
| 7310 | (setq gnus-article-current nil)) |
| 7311 | ;; Return to the group buffer. |
| 7312 | (if (not gnus-kill-summary-on-exit) |
| 7313 | (progn |
| 7314 | (gnus-deaden-summary) |
| 7315 | (gnus-configure-windows 'group 'force)) |
| 7316 | (gnus-configure-windows 'group 'force) |
| 7317 | (gnus-close-group group) |
| 7318 | (gnus-kill-buffer gnus-summary-buffer)) |
| 7319 | (unless gnus-single-article-buffer |
| 7320 | (setq gnus-article-current nil)) |
| 7321 | (when gnus-use-trees |
| 7322 | (gnus-tree-close group)) |
| 7323 | (gnus-async-prefetch-remove-group group) |
| 7324 | (when (get-buffer gnus-article-buffer) |
| 7325 | (bury-buffer gnus-article-buffer)) |
| 7326 | ;; Clear the current group name. |
| 7327 | (setq gnus-newsgroup-name nil) |
| 7328 | (unless (gnus-ephemeral-group-p group) |
| 7329 | (gnus-group-update-group group nil t)) |
| 7330 | (when (equal (gnus-group-group-name) group) |
| 7331 | (gnus-group-next-unread-group 1)) |
| 7332 | (when quit-config |
| 7333 | (gnus-handle-ephemeral-exit quit-config))))) |
| 7334 | |
| 7335 | (defun gnus-handle-ephemeral-exit (quit-config) |
| 7336 | "Handle movement when leaving an ephemeral group. |
| 7337 | The state which existed when entering the ephemeral is reset." |
| 7338 | (if (not (buffer-live-p (car quit-config))) |
| 7339 | (when (gnus-buffer-live-p gnus-group-buffer) |
| 7340 | (gnus-configure-windows 'group 'force)) |
| 7341 | (set-buffer (car quit-config)) |
| 7342 | (unless (eq (cdr quit-config) 'group) |
| 7343 | (setq gnus-current-select-method |
| 7344 | (gnus-find-method-for-group gnus-newsgroup-name))) |
| 7345 | (cond ((eq major-mode 'gnus-summary-mode) |
| 7346 | (gnus-set-global-variables)) |
| 7347 | ((eq major-mode 'gnus-article-mode) |
| 7348 | (save-current-buffer |
| 7349 | ;; The `gnus-summary-buffer' variable may point |
| 7350 | ;; to the old summary buffer when using a single |
| 7351 | ;; article buffer. |
| 7352 | (unless (gnus-buffer-live-p gnus-summary-buffer) |
| 7353 | (set-buffer gnus-group-buffer)) |
| 7354 | (set-buffer gnus-summary-buffer) |
| 7355 | (gnus-set-global-variables)))) |
| 7356 | (if (or (eq (cdr quit-config) 'article) |
| 7357 | (eq (cdr quit-config) 'pick)) |
| 7358 | (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode)) |
| 7359 | (gnus-configure-windows 'pick 'force) |
| 7360 | (gnus-configure-windows (cdr quit-config) 'force)) |
| 7361 | (gnus-configure-windows (cdr quit-config) 'force)) |
| 7362 | (when (eq major-mode 'gnus-summary-mode) |
| 7363 | (if (memq gnus-auto-select-on-ephemeral-exit '(next-noselect |
| 7364 | next-unread-noselect)) |
| 7365 | (when (zerop (cond ((eq gnus-auto-select-on-ephemeral-exit |
| 7366 | 'next-noselect) |
| 7367 | (gnus-summary-next-subject 1 nil t)) |
| 7368 | ((eq gnus-auto-select-on-ephemeral-exit |
| 7369 | 'next-unread-noselect) |
| 7370 | (gnus-summary-next-subject 1 t t)))) |
| 7371 | ;; Hide the article buffer which displays the article different |
| 7372 | ;; from the one that the cursor points to in the summary buffer. |
| 7373 | (gnus-configure-windows 'summary 'force)) |
| 7374 | (cond ((eq gnus-auto-select-on-ephemeral-exit 'next) |
| 7375 | (gnus-summary-next-subject 1)) |
| 7376 | ((eq gnus-auto-select-on-ephemeral-exit 'next-unread) |
| 7377 | (gnus-summary-next-subject 1 t)))) |
| 7378 | (gnus-summary-recenter) |
| 7379 | (gnus-summary-position-point)))) |
| 7380 | |
| 7381 | ;;; Dead summaries. |
| 7382 | |
| 7383 | (defvar gnus-dead-summary-mode-map |
| 7384 | (let ((map (make-keymap))) |
| 7385 | (suppress-keymap map) |
| 7386 | (substitute-key-definition 'undefined 'gnus-summary-wake-up-the-dead map) |
| 7387 | (dolist (key '("\C-d" "\r" "\177" [delete])) |
| 7388 | (define-key map key 'gnus-summary-wake-up-the-dead)) |
| 7389 | (dolist (key '("q" "Q")) |
| 7390 | (define-key map key 'bury-buffer)) |
| 7391 | map)) |
| 7392 | |
| 7393 | (define-minor-mode gnus-dead-summary-mode |
| 7394 | "Minor mode for Gnus summary buffers." |
| 7395 | :lighter " Dead" :keymap gnus-dead-summary-mode-map |
| 7396 | (unless (derived-mode-p 'gnus-summary-mode) |
| 7397 | (setq gnus-dead-summary-mode nil))) |
| 7398 | |
| 7399 | (defun gnus-deaden-summary () |
| 7400 | "Make the current summary buffer into a dead summary buffer." |
| 7401 | ;; Kill any previous dead summary buffer. |
| 7402 | (when (and gnus-dead-summary |
| 7403 | (buffer-name gnus-dead-summary)) |
| 7404 | (with-current-buffer gnus-dead-summary |
| 7405 | (when gnus-dead-summary-mode |
| 7406 | (kill-buffer (current-buffer))))) |
| 7407 | ;; Make this the current dead summary. |
| 7408 | (setq gnus-dead-summary (current-buffer)) |
| 7409 | (gnus-dead-summary-mode 1) |
| 7410 | (let ((name (buffer-name))) |
| 7411 | (when (string-match "Summary" name) |
| 7412 | (rename-buffer |
| 7413 | (concat (substring name 0 (match-beginning 0)) "Dead " |
| 7414 | (substring name (match-beginning 0))) |
| 7415 | t) |
| 7416 | (bury-buffer)))) |
| 7417 | |
| 7418 | (defun gnus-kill-or-deaden-summary (buffer) |
| 7419 | "Kill or deaden the summary BUFFER." |
| 7420 | (save-excursion |
| 7421 | (when (and (buffer-name buffer) |
| 7422 | (not gnus-single-article-buffer)) |
| 7423 | (with-current-buffer buffer |
| 7424 | (gnus-kill-buffer gnus-article-buffer) |
| 7425 | (gnus-kill-buffer gnus-original-article-buffer))) |
| 7426 | (cond |
| 7427 | ;; Kill the buffer. |
| 7428 | (gnus-kill-summary-on-exit |
| 7429 | (when (and gnus-use-trees |
| 7430 | (gnus-buffer-exists-p buffer)) |
| 7431 | (with-current-buffer buffer |
| 7432 | (gnus-tree-close gnus-newsgroup-name))) |
| 7433 | (gnus-kill-buffer buffer)) |
| 7434 | ;; Deaden the buffer. |
| 7435 | ((gnus-buffer-exists-p buffer) |
| 7436 | (with-current-buffer buffer |
| 7437 | (gnus-deaden-summary)))))) |
| 7438 | |
| 7439 | (defun gnus-summary-wake-up-the-dead (&rest args) |
| 7440 | "Wake up the dead summary buffer." |
| 7441 | (interactive) |
| 7442 | (gnus-dead-summary-mode -1) |
| 7443 | (let ((name (buffer-name))) |
| 7444 | (when (string-match "Dead " name) |
| 7445 | (rename-buffer |
| 7446 | (concat (substring name 0 (match-beginning 0)) |
| 7447 | (substring name (match-end 0))) |
| 7448 | t))) |
| 7449 | (gnus-message 3 "This dead summary is now alive again")) |
| 7450 | |
| 7451 | ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>. |
| 7452 | (defun gnus-summary-describe-group (&optional force) |
| 7453 | "Describe the current newsgroup." |
| 7454 | (interactive "P") |
| 7455 | (gnus-group-describe-group force gnus-newsgroup-name)) |
| 7456 | |
| 7457 | (defun gnus-summary-describe-briefly () |
| 7458 | "Describe summary mode commands briefly." |
| 7459 | (interactive) |
| 7460 | (gnus-message 6 "%s" (substitute-command-keys "\\<gnus-summary-mode-map>\\[gnus-summary-next-page]:Select \\[gnus-summary-next-unread-article]:Forward \\[gnus-summary-prev-unread-article]:Backward \\[gnus-summary-exit]:Exit \\[gnus-info-find-node]:Run Info \\[gnus-summary-describe-briefly]:This help"))) |
| 7461 | |
| 7462 | ;; Walking around group mode buffer from summary mode. |
| 7463 | |
| 7464 | (defun gnus-summary-next-group (&optional no-article target-group backward) |
| 7465 | "Exit current newsgroup and then select next unread newsgroup. |
| 7466 | If prefix argument NO-ARTICLE is non-nil, no article is selected |
| 7467 | initially. If TARGET-GROUP, go to this group. If BACKWARD, go to |
| 7468 | previous group instead." |
| 7469 | (interactive "P") |
| 7470 | ;; Stop pre-fetching. |
| 7471 | (gnus-async-halt-prefetch) |
| 7472 | (let ((current-group gnus-newsgroup-name) |
| 7473 | (current-buffer (current-buffer)) |
| 7474 | entered) |
| 7475 | ;; First we semi-exit this group to update Xrefs and all variables. |
| 7476 | ;; We can't do a real exit, because the window conf must remain |
| 7477 | ;; the same in case the user is prompted for info, and we don't |
| 7478 | ;; want the window conf to change before that... |
| 7479 | (gnus-summary-exit t) |
| 7480 | (while (not entered) |
| 7481 | ;; Then we find what group we are supposed to enter. |
| 7482 | (set-buffer gnus-group-buffer) |
| 7483 | (gnus-group-jump-to-group current-group) |
| 7484 | (setq target-group |
| 7485 | (or target-group |
| 7486 | (if (eq gnus-keep-same-level 'best) |
| 7487 | (gnus-summary-best-group gnus-newsgroup-name) |
| 7488 | (gnus-summary-search-group backward gnus-keep-same-level)))) |
| 7489 | (if (not target-group) |
| 7490 | ;; There are no further groups, so we return to the group |
| 7491 | ;; buffer. |
| 7492 | (progn |
| 7493 | (gnus-message 5 "Returning to the group buffer") |
| 7494 | (setq entered t) |
| 7495 | (when (gnus-buffer-live-p current-buffer) |
| 7496 | (set-buffer current-buffer) |
| 7497 | (gnus-summary-exit)) |
| 7498 | (gnus-run-hooks 'gnus-group-no-more-groups-hook)) |
| 7499 | ;; We try to enter the target group. |
| 7500 | (gnus-group-jump-to-group target-group) |
| 7501 | (let ((unreads (gnus-group-group-unread))) |
| 7502 | (if (and (or (eq t unreads) |
| 7503 | (and unreads (not (zerop unreads)))) |
| 7504 | (gnus-summary-read-group |
| 7505 | target-group nil no-article |
| 7506 | (and (buffer-name current-buffer) current-buffer) |
| 7507 | nil backward)) |
| 7508 | (setq entered t) |
| 7509 | (setq current-group target-group |
| 7510 | target-group nil))))))) |
| 7511 | |
| 7512 | (defun gnus-summary-prev-group (&optional no-article) |
| 7513 | "Exit current newsgroup and then select previous unread newsgroup. |
| 7514 | If prefix argument NO-ARTICLE is non-nil, no article is selected initially." |
| 7515 | (interactive "P") |
| 7516 | (gnus-summary-next-group no-article nil t)) |
| 7517 | |
| 7518 | ;; Walking around summary lines. |
| 7519 | |
| 7520 | (defun gnus-summary-first-subject (&optional unread undownloaded unseen) |
| 7521 | "Go to the first subject satisfying any non-nil constraint. |
| 7522 | If UNREAD is non-nil, the article should be unread. |
| 7523 | If UNDOWNLOADED is non-nil, the article should be undownloaded. |
| 7524 | If UNSEEN is non-nil, the article should be unseen as well as unread. |
| 7525 | Returns the article selected or nil if there are no matching articles." |
| 7526 | (interactive "P") |
| 7527 | (cond |
| 7528 | ;; Empty summary. |
| 7529 | ((null gnus-newsgroup-data) |
| 7530 | (gnus-message 3 "No articles in the group") |
| 7531 | nil) |
| 7532 | ;; Pick the first article. |
| 7533 | ((not (or unread undownloaded unseen)) |
| 7534 | (goto-char (gnus-data-pos (car gnus-newsgroup-data))) |
| 7535 | (gnus-data-number (car gnus-newsgroup-data))) |
| 7536 | ;; Find the first unread article. |
| 7537 | (t |
| 7538 | (let ((data gnus-newsgroup-data)) |
| 7539 | (while (and data |
| 7540 | (let ((num (gnus-data-number (car data)))) |
| 7541 | (or (memq num gnus-newsgroup-unfetched) |
| 7542 | (not (or (and unread |
| 7543 | (memq num gnus-newsgroup-unreads)) |
| 7544 | (and undownloaded |
| 7545 | (memq num gnus-newsgroup-undownloaded)) |
| 7546 | (and unseen |
| 7547 | (memq num gnus-newsgroup-unseen) |
| 7548 | (memq num gnus-newsgroup-unreads))))))) |
| 7549 | (setq data (cdr data))) |
| 7550 | (prog1 |
| 7551 | (if data |
| 7552 | (progn |
| 7553 | (goto-char (gnus-data-pos (car data))) |
| 7554 | (gnus-data-number (car data))) |
| 7555 | (gnus-message 3 "No more%s articles" |
| 7556 | (let* ((r (when unread " unread")) |
| 7557 | (d (when undownloaded " undownloaded")) |
| 7558 | (s (when unseen " unseen")) |
| 7559 | (l (delq nil (list r d s)))) |
| 7560 | (cond ((= 3 (length l)) |
| 7561 | (concat r "," d ", or" s)) |
| 7562 | ((= 2 (length l)) |
| 7563 | (concat (car l) ", or" (cadr l))) |
| 7564 | ((= 1 (length l)) |
| 7565 | (car l)) |
| 7566 | (t |
| 7567 | "")))) |
| 7568 | nil |
| 7569 | ) |
| 7570 | (gnus-summary-position-point)))))) |
| 7571 | |
| 7572 | (defun gnus-summary-next-subject (n &optional unread dont-display) |
| 7573 | "Go to next N'th summary line. |
| 7574 | If N is negative, go to the previous N'th subject line. |
| 7575 | If UNREAD is non-nil, only unread articles are selected. |
| 7576 | The difference between N and the actual number of steps taken is |
| 7577 | returned." |
| 7578 | (interactive "p") |
| 7579 | (let ((backward (< n 0)) |
| 7580 | (n (abs n))) |
| 7581 | (while (and (> n 0) |
| 7582 | (if backward |
| 7583 | (gnus-summary-find-prev unread) |
| 7584 | (gnus-summary-find-next unread))) |
| 7585 | (unless (zerop (setq n (1- n))) |
| 7586 | (gnus-summary-show-thread))) |
| 7587 | (when (/= 0 n) |
| 7588 | (gnus-message 7 "No more%s articles" |
| 7589 | (if unread " unread" ""))) |
| 7590 | (unless dont-display |
| 7591 | (gnus-summary-recenter) |
| 7592 | (gnus-summary-position-point)) |
| 7593 | n)) |
| 7594 | |
| 7595 | (defun gnus-summary-next-unread-subject (n) |
| 7596 | "Go to next N'th unread summary line." |
| 7597 | (interactive "p") |
| 7598 | (gnus-summary-next-subject n t)) |
| 7599 | |
| 7600 | (defun gnus-summary-prev-subject (n &optional unread) |
| 7601 | "Go to previous N'th summary line. |
| 7602 | If optional argument UNREAD is non-nil, only unread article is selected." |
| 7603 | (interactive "p") |
| 7604 | (gnus-summary-next-subject (- n) unread)) |
| 7605 | |
| 7606 | (defun gnus-summary-prev-unread-subject (n) |
| 7607 | "Go to previous N'th unread summary line." |
| 7608 | (interactive "p") |
| 7609 | (gnus-summary-next-subject (- n) t)) |
| 7610 | |
| 7611 | (defun gnus-summary-goto-subjects (articles) |
| 7612 | "Insert the subject header for ARTICLES in the current buffer." |
| 7613 | (save-excursion |
| 7614 | (dolist (article articles) |
| 7615 | (gnus-summary-goto-subject article t))) |
| 7616 | (gnus-summary-limit (append articles gnus-newsgroup-limit)) |
| 7617 | (gnus-summary-position-point)) |
| 7618 | |
| 7619 | (defun gnus-summary-goto-subject (article &optional force silent) |
| 7620 | "Go to the subject line of ARTICLE. |
| 7621 | If FORCE, also allow jumping to articles not currently shown." |
| 7622 | (interactive "nArticle number: ") |
| 7623 | (unless (numberp article) |
| 7624 | (error "Article %s is not a number" article)) |
| 7625 | (let ((b (point)) |
| 7626 | (data (gnus-data-find article))) |
| 7627 | ;; We read in the article if we have to. |
| 7628 | (and (not data) |
| 7629 | force |
| 7630 | (gnus-summary-insert-subject |
| 7631 | article |
| 7632 | (if (or (numberp force) (vectorp force)) force) |
| 7633 | t) |
| 7634 | (setq data (gnus-data-find article))) |
| 7635 | (goto-char b) |
| 7636 | (if (not data) |
| 7637 | (progn |
| 7638 | (unless silent |
| 7639 | (gnus-message 3 "Can't find article %d" article)) |
| 7640 | nil) |
| 7641 | (let ((pt (gnus-data-pos data))) |
| 7642 | (goto-char pt) |
| 7643 | (gnus-summary-set-article-display-arrow pt)) |
| 7644 | (gnus-summary-position-point) |
| 7645 | article))) |
| 7646 | |
| 7647 | ;; Walking around summary lines with displaying articles. |
| 7648 | |
| 7649 | (defun gnus-summary-expand-window (&optional arg) |
| 7650 | "Make the summary buffer take up the entire Emacs frame. |
| 7651 | Given a prefix, will force an `article' buffer configuration." |
| 7652 | (interactive "P") |
| 7653 | (if arg |
| 7654 | (gnus-configure-windows 'article 'force) |
| 7655 | (gnus-configure-windows 'summary 'force))) |
| 7656 | |
| 7657 | (defun gnus-summary-display-article (article &optional all-header) |
| 7658 | "Display ARTICLE in article buffer." |
| 7659 | (unless (and (gnus-buffer-live-p gnus-article-buffer) |
| 7660 | (with-current-buffer gnus-article-buffer |
| 7661 | (eq major-mode 'gnus-article-mode))) |
| 7662 | (gnus-article-setup-buffer)) |
| 7663 | (gnus-set-global-variables) |
| 7664 | (with-current-buffer gnus-article-buffer |
| 7665 | (setq gnus-article-charset gnus-newsgroup-charset) |
| 7666 | (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets) |
| 7667 | (mm-enable-multibyte)) |
| 7668 | (if (null article) |
| 7669 | nil |
| 7670 | (prog1 |
| 7671 | (if gnus-summary-display-article-function |
| 7672 | (funcall gnus-summary-display-article-function article all-header) |
| 7673 | (gnus-article-prepare article all-header)) |
| 7674 | (gnus-run-hooks 'gnus-select-article-hook) |
| 7675 | (when (and gnus-current-article |
| 7676 | (not (zerop gnus-current-article))) |
| 7677 | (gnus-summary-goto-subject gnus-current-article)) |
| 7678 | (gnus-summary-recenter) |
| 7679 | (when (and gnus-use-trees gnus-show-threads) |
| 7680 | (gnus-possibly-generate-tree article) |
| 7681 | (gnus-highlight-selected-tree article)) |
| 7682 | ;; Successfully display article. |
| 7683 | (gnus-article-set-window-start |
| 7684 | (cdr (assq article gnus-newsgroup-bookmarks)))))) |
| 7685 | |
| 7686 | (defun gnus-summary-select-article (&optional all-headers force pseudo article) |
| 7687 | "Select the current article. |
| 7688 | If ALL-HEADERS is non-nil, show all header fields. If FORCE is |
| 7689 | non-nil, the article will be re-fetched even if it already present in |
| 7690 | the article buffer. If PSEUDO is non-nil, pseudo-articles will also |
| 7691 | be displayed." |
| 7692 | ;; Make sure we are in the summary buffer to work around bbdb bug. |
| 7693 | (unless (eq major-mode 'gnus-summary-mode) |
| 7694 | (set-buffer gnus-summary-buffer)) |
| 7695 | (let ((article (or article (gnus-summary-article-number))) |
| 7696 | (all-headers (not (not all-headers))) ;Must be t or nil. |
| 7697 | gnus-summary-display-article-function) |
| 7698 | (and (not pseudo) |
| 7699 | (gnus-summary-article-pseudo-p article) |
| 7700 | (error "This is a pseudo-article")) |
| 7701 | (with-current-buffer gnus-summary-buffer |
| 7702 | (if (or (and gnus-single-article-buffer |
| 7703 | (or (null gnus-current-article) |
| 7704 | (null gnus-article-current) |
| 7705 | (null (get-buffer gnus-article-buffer)) |
| 7706 | (not (eq article (cdr gnus-article-current))) |
| 7707 | (not (equal (car gnus-article-current) |
| 7708 | gnus-newsgroup-name)) |
| 7709 | (not (get-buffer gnus-original-article-buffer)))) |
| 7710 | (and (not gnus-single-article-buffer) |
| 7711 | (or (null gnus-current-article) |
| 7712 | (not (get-buffer gnus-original-article-buffer)) |
| 7713 | (not (eq gnus-current-article article)))) |
| 7714 | force) |
| 7715 | ;; The requested article is different from the current article. |
| 7716 | (progn |
| 7717 | (gnus-summary-display-article article all-headers) |
| 7718 | (when (gnus-buffer-live-p gnus-article-buffer) |
| 7719 | (with-current-buffer gnus-article-buffer |
| 7720 | (if (not gnus-article-decoded-p) ;; a local variable |
| 7721 | (mm-disable-multibyte)))) |
| 7722 | (gnus-article-set-window-start |
| 7723 | (cdr (assq article gnus-newsgroup-bookmarks))) |
| 7724 | article) |
| 7725 | 'old)))) |
| 7726 | |
| 7727 | (defun gnus-summary-force-verify-and-decrypt () |
| 7728 | "Display buttons for signed/encrypted parts and verify/decrypt them." |
| 7729 | (interactive) |
| 7730 | (let ((mm-verify-option 'known) |
| 7731 | (mm-decrypt-option 'known) |
| 7732 | (gnus-article-emulate-mime t) |
| 7733 | (gnus-buttonized-mime-types (append (list "multipart/signed" |
| 7734 | "multipart/encrypted") |
| 7735 | gnus-buttonized-mime-types))) |
| 7736 | (gnus-summary-select-article nil 'force))) |
| 7737 | |
| 7738 | (defun gnus-summary-next-article (&optional unread subject backward push) |
| 7739 | "Select the next article. |
| 7740 | If UNREAD, only unread articles are selected. |
| 7741 | If SUBJECT, only articles with SUBJECT are selected. |
| 7742 | If BACKWARD, the previous article is selected instead of the next." |
| 7743 | (interactive "P") |
| 7744 | ;; Make sure we are in the summary buffer. |
| 7745 | (unless (eq major-mode 'gnus-summary-mode) |
| 7746 | (set-buffer gnus-summary-buffer)) |
| 7747 | (cond |
| 7748 | ;; Is there such an article? |
| 7749 | ((and (gnus-summary-search-forward unread subject backward) |
| 7750 | (or (gnus-summary-display-article (gnus-summary-article-number)) |
| 7751 | (eq (gnus-summary-article-mark) gnus-canceled-mark))) |
| 7752 | (gnus-summary-position-point)) |
| 7753 | ;; If not, we try the first unread, if that is wanted. |
| 7754 | ((and subject |
| 7755 | gnus-auto-select-same |
| 7756 | (gnus-summary-first-unread-article)) |
| 7757 | (gnus-summary-position-point) |
| 7758 | (gnus-message 6 "Wrapped")) |
| 7759 | ;; Try to get next/previous article not displayed in this group. |
| 7760 | ((and gnus-auto-extend-newsgroup |
| 7761 | (not unread) (not subject)) |
| 7762 | (gnus-summary-goto-article |
| 7763 | (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end)) |
| 7764 | nil (count-lines (point-min) (point)))) |
| 7765 | ;; Go to next/previous group. |
| 7766 | (t |
| 7767 | (unless (gnus-ephemeral-group-p gnus-newsgroup-name) |
| 7768 | (gnus-summary-jump-to-group gnus-newsgroup-name)) |
| 7769 | (let ((cmd (if (featurep 'xemacs) |
| 7770 | last-command-char |
| 7771 | last-command-event)) |
| 7772 | (point |
| 7773 | (with-current-buffer gnus-group-buffer |
| 7774 | (point))) |
| 7775 | (current-summary (current-buffer)) |
| 7776 | (group |
| 7777 | (if (eq gnus-keep-same-level 'best) |
| 7778 | (gnus-summary-best-group gnus-newsgroup-name) |
| 7779 | (gnus-summary-search-group backward gnus-keep-same-level)))) |
| 7780 | ;; Select next unread newsgroup automagically. |
| 7781 | (cond |
| 7782 | ((or (not gnus-auto-select-next) |
| 7783 | (not cmd)) |
| 7784 | (gnus-message 7 "No more%s articles" (if unread " unread" ""))) |
| 7785 | ((or (eq gnus-auto-select-next 'quietly) |
| 7786 | (and (eq gnus-auto-select-next 'slightly-quietly) |
| 7787 | push) |
| 7788 | (and (eq gnus-auto-select-next 'almost-quietly) |
| 7789 | (gnus-summary-last-article-p))) |
| 7790 | ;; Select quietly. |
| 7791 | (if (gnus-ephemeral-group-p gnus-newsgroup-name) |
| 7792 | (gnus-summary-exit) |
| 7793 | (gnus-message 7 "No more%s articles (%s)..." |
| 7794 | (if unread " unread" "") |
| 7795 | (if group (concat "selecting " group) |
| 7796 | "exiting")) |
| 7797 | (gnus-summary-next-group nil group backward))) |
| 7798 | (t |
| 7799 | (when (gnus-key-press-event-p last-input-event) |
| 7800 | ;; Somehow or other, we may now have selected a different |
| 7801 | ;; window. Make point go back to the summary buffer. |
| 7802 | (when (eq current-summary (current-buffer)) |
| 7803 | ;; FIXME: This burps when get-buffer-window returns nil. |
| 7804 | (select-window (get-buffer-window current-summary 0))) |
| 7805 | (gnus-summary-walk-group-buffer |
| 7806 | gnus-newsgroup-name cmd unread backward point)))))))) |
| 7807 | |
| 7808 | (defun gnus-summary-walk-group-buffer (from-group cmd unread backward start) |
| 7809 | (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1)) |
| 7810 | (?\C-p (gnus-group-prev-unread-group 1)))) |
| 7811 | (cursor-in-echo-area t) |
| 7812 | keve key group ended prompt) |
| 7813 | (with-current-buffer gnus-group-buffer |
| 7814 | (goto-char start) |
| 7815 | (setq group |
| 7816 | (if (eq gnus-keep-same-level 'best) |
| 7817 | (gnus-summary-best-group gnus-newsgroup-name) |
| 7818 | (gnus-summary-search-group backward gnus-keep-same-level)))) |
| 7819 | (while (not ended) |
| 7820 | (setq prompt |
| 7821 | (format |
| 7822 | "No more%s articles%s " (if unread " unread" "") |
| 7823 | (if (and group |
| 7824 | (not (gnus-ephemeral-group-p gnus-newsgroup-name))) |
| 7825 | (format " (Type %s for %s [%s])" |
| 7826 | (single-key-description cmd) |
| 7827 | (gnus-group-decoded-name group) |
| 7828 | (gnus-group-unread group)) |
| 7829 | (format " (Type %s to exit %s)" |
| 7830 | (single-key-description cmd) |
| 7831 | (gnus-group-decoded-name gnus-newsgroup-name))))) |
| 7832 | ;; Confirm auto selection. |
| 7833 | (setq key (car (setq keve (gnus-read-event-char prompt))) |
| 7834 | ended t) |
| 7835 | (cond |
| 7836 | ((assq key keystrokes) |
| 7837 | (let ((obuf (current-buffer))) |
| 7838 | (switch-to-buffer gnus-group-buffer) |
| 7839 | (when group |
| 7840 | (gnus-group-jump-to-group group)) |
| 7841 | (eval (cadr (assq key keystrokes))) |
| 7842 | (setq group (gnus-group-group-name)) |
| 7843 | (switch-to-buffer obuf)) |
| 7844 | (setq ended nil)) |
| 7845 | ((equal key cmd) |
| 7846 | (if (or (not group) |
| 7847 | (gnus-ephemeral-group-p gnus-newsgroup-name)) |
| 7848 | (gnus-summary-exit) |
| 7849 | (gnus-summary-next-group nil group backward))) |
| 7850 | (t |
| 7851 | (push (cdr keve) unread-command-events)))))) |
| 7852 | |
| 7853 | (defun gnus-summary-next-unread-article () |
| 7854 | "Select unread article after current one." |
| 7855 | (interactive) |
| 7856 | (gnus-summary-next-article |
| 7857 | (or (not (eq gnus-summary-goto-unread 'never)) |
| 7858 | (gnus-summary-last-article-p (gnus-summary-article-number))) |
| 7859 | (and gnus-auto-select-same |
| 7860 | (gnus-summary-article-subject)))) |
| 7861 | |
| 7862 | (defun gnus-summary-prev-article (&optional unread subject) |
| 7863 | "Select the article before the current one. |
| 7864 | If UNREAD is non-nil, only unread articles are selected." |
| 7865 | (interactive "P") |
| 7866 | (gnus-summary-next-article unread subject t)) |
| 7867 | |
| 7868 | (defun gnus-summary-prev-unread-article () |
| 7869 | "Select unread article before current one." |
| 7870 | (interactive) |
| 7871 | (gnus-summary-prev-article |
| 7872 | (or (not (eq gnus-summary-goto-unread 'never)) |
| 7873 | (gnus-summary-first-article-p (gnus-summary-article-number))) |
| 7874 | (and gnus-auto-select-same |
| 7875 | (gnus-summary-article-subject)))) |
| 7876 | |
| 7877 | (declare-function gnus-article-only-boring-p "gnus-art" ()) |
| 7878 | |
| 7879 | (defun gnus-summary-next-page (&optional lines circular stop) |
| 7880 | "Show next page of the selected article. |
| 7881 | If at the end of the current article, select the next article. |
| 7882 | LINES says how many lines should be scrolled up. |
| 7883 | |
| 7884 | If CIRCULAR is non-nil, go to the start of the article instead of |
| 7885 | selecting the next article when reaching the end of the current |
| 7886 | article. |
| 7887 | |
| 7888 | If STOP is non-nil, just stop when reaching the end of the message. |
| 7889 | |
| 7890 | Also see the variable `gnus-article-skip-boring'." |
| 7891 | (interactive "P") |
| 7892 | (gnus-set-global-variables) |
| 7893 | (let ((article (gnus-summary-article-number)) |
| 7894 | (article-window (get-buffer-window gnus-article-buffer t)) |
| 7895 | endp) |
| 7896 | ;; If the buffer is empty, we have no article. |
| 7897 | (unless article |
| 7898 | (error "No article to select")) |
| 7899 | (gnus-configure-windows 'article) |
| 7900 | (if (eq (cdr (assq article gnus-newsgroup-reads)) gnus-canceled-mark) |
| 7901 | (if (and (eq gnus-summary-goto-unread 'never) |
| 7902 | (not (gnus-summary-last-article-p article))) |
| 7903 | (gnus-summary-next-article) |
| 7904 | (gnus-summary-next-unread-article)) |
| 7905 | (if (or (null gnus-current-article) |
| 7906 | (null gnus-article-current) |
| 7907 | (/= article (cdr gnus-article-current)) |
| 7908 | (not (equal (car gnus-article-current) gnus-newsgroup-name))) |
| 7909 | ;; Selected subject is different from current article's. |
| 7910 | (gnus-summary-display-article article) |
| 7911 | (when article-window |
| 7912 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 7913 | (setq endp (or (gnus-article-next-page lines) |
| 7914 | (gnus-article-only-boring-p)))) |
| 7915 | (when endp |
| 7916 | (cond ((or stop gnus-summary-stop-at-end-of-message) |
| 7917 | (gnus-message 3 "End of message")) |
| 7918 | (circular |
| 7919 | (gnus-summary-beginning-of-article)) |
| 7920 | (lines |
| 7921 | (gnus-message 3 "End of message")) |
| 7922 | ((null lines) |
| 7923 | (if (and (eq gnus-summary-goto-unread 'never) |
| 7924 | (not (gnus-summary-last-article-p article))) |
| 7925 | (gnus-summary-next-article) |
| 7926 | (gnus-summary-next-unread-article)))))))) |
| 7927 | (gnus-summary-recenter) |
| 7928 | (gnus-summary-position-point))) |
| 7929 | |
| 7930 | (defun gnus-summary-prev-page (&optional lines move) |
| 7931 | "Show previous page of selected article. |
| 7932 | Argument LINES specifies lines to be scrolled down. |
| 7933 | If MOVE, move to the previous unread article if point is at |
| 7934 | the beginning of the buffer." |
| 7935 | (interactive "P") |
| 7936 | (let ((article (gnus-summary-article-number)) |
| 7937 | (article-window (get-buffer-window gnus-article-buffer t)) |
| 7938 | endp) |
| 7939 | (gnus-configure-windows 'article) |
| 7940 | (if (or (null gnus-current-article) |
| 7941 | (null gnus-article-current) |
| 7942 | (/= article (cdr gnus-article-current)) |
| 7943 | (not (equal (car gnus-article-current) gnus-newsgroup-name))) |
| 7944 | ;; Selected subject is different from current article's. |
| 7945 | (gnus-summary-display-article article) |
| 7946 | (gnus-summary-recenter) |
| 7947 | (when article-window |
| 7948 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 7949 | (setq endp (gnus-article-prev-page lines))) |
| 7950 | (when (and move endp) |
| 7951 | (cond (lines |
| 7952 | (gnus-message 3 "Beginning of message")) |
| 7953 | ((null lines) |
| 7954 | (if (and (eq gnus-summary-goto-unread 'never) |
| 7955 | (not (gnus-summary-first-article-p article))) |
| 7956 | (gnus-summary-prev-article) |
| 7957 | (gnus-summary-prev-unread-article)))))))) |
| 7958 | (gnus-summary-position-point)) |
| 7959 | |
| 7960 | (defun gnus-summary-prev-page-or-article (&optional lines) |
| 7961 | "Show previous page of selected article. |
| 7962 | Argument LINES specifies lines to be scrolled down. |
| 7963 | If at the beginning of the article, go to the next article." |
| 7964 | (interactive "P") |
| 7965 | (gnus-summary-prev-page lines t)) |
| 7966 | |
| 7967 | (defun gnus-summary-scroll-up (lines) |
| 7968 | "Scroll up (or down) one line current article. |
| 7969 | Argument LINES specifies lines to be scrolled up (or down if negative). |
| 7970 | If no article is selected, then the current article will be selected first." |
| 7971 | (interactive "p") |
| 7972 | (gnus-configure-windows 'article) |
| 7973 | (gnus-summary-show-thread) |
| 7974 | (when (eq (gnus-summary-select-article nil nil 'pseudo) 'old) |
| 7975 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 7976 | (cond ((> lines 0) |
| 7977 | (when (gnus-article-next-page lines) |
| 7978 | (gnus-message 3 "End of message"))) |
| 7979 | ((< lines 0) |
| 7980 | (gnus-article-prev-page (- lines)))))) |
| 7981 | (gnus-summary-recenter) |
| 7982 | (gnus-summary-position-point)) |
| 7983 | |
| 7984 | (defun gnus-summary-scroll-down (lines) |
| 7985 | "Scroll down (or up) one line current article. |
| 7986 | Argument LINES specifies lines to be scrolled down (or up if negative). |
| 7987 | If no article is selected, then the current article will be selected first." |
| 7988 | (interactive "p") |
| 7989 | (gnus-summary-scroll-up (- lines))) |
| 7990 | |
| 7991 | (defun gnus-summary-next-same-subject () |
| 7992 | "Select next article which has the same subject as current one." |
| 7993 | (interactive) |
| 7994 | (gnus-summary-next-article nil (gnus-summary-article-subject))) |
| 7995 | |
| 7996 | (defun gnus-summary-prev-same-subject () |
| 7997 | "Select previous article which has the same subject as current one." |
| 7998 | (interactive) |
| 7999 | (gnus-summary-prev-article nil (gnus-summary-article-subject))) |
| 8000 | |
| 8001 | (defun gnus-summary-next-unread-same-subject () |
| 8002 | "Select next unread article which has the same subject as current one." |
| 8003 | (interactive) |
| 8004 | (gnus-summary-next-article t (gnus-summary-article-subject))) |
| 8005 | |
| 8006 | (defun gnus-summary-prev-unread-same-subject () |
| 8007 | "Select previous unread article which has the same subject as current one." |
| 8008 | (interactive) |
| 8009 | (gnus-summary-prev-article t (gnus-summary-article-subject))) |
| 8010 | |
| 8011 | (defun gnus-summary-first-unread-article () |
| 8012 | "Select the first unread article. |
| 8013 | Return nil if there are no unread articles." |
| 8014 | (interactive) |
| 8015 | (prog1 |
| 8016 | (when (gnus-summary-first-subject t) |
| 8017 | (gnus-summary-show-thread) |
| 8018 | (gnus-summary-first-subject t) |
| 8019 | (gnus-summary-display-article (gnus-summary-article-number))) |
| 8020 | (gnus-summary-position-point))) |
| 8021 | |
| 8022 | (defun gnus-summary-first-unread-subject () |
| 8023 | "Place the point on the subject line of the first unread article. |
| 8024 | Return nil if there are no unread articles." |
| 8025 | (interactive) |
| 8026 | (prog1 |
| 8027 | (when (gnus-summary-first-subject t) |
| 8028 | (gnus-summary-show-thread) |
| 8029 | (gnus-summary-first-subject t)) |
| 8030 | (gnus-summary-position-point))) |
| 8031 | |
| 8032 | (defun gnus-summary-first-unseen-subject () |
| 8033 | "Place the point on the subject line of the first unseen article. |
| 8034 | Return nil if there are no unseen articles." |
| 8035 | (interactive) |
| 8036 | (prog1 |
| 8037 | (when (gnus-summary-first-subject nil nil t) |
| 8038 | (gnus-summary-show-thread) |
| 8039 | (gnus-summary-first-subject nil nil t)) |
| 8040 | (gnus-summary-position-point))) |
| 8041 | |
| 8042 | (defun gnus-summary-first-unseen-or-unread-subject () |
| 8043 | "Place the point on the subject line of the first unseen and unread article. |
| 8044 | If all article have been seen, on the subject line of the first unread |
| 8045 | article." |
| 8046 | (interactive) |
| 8047 | (prog1 |
| 8048 | (unless (when (gnus-summary-first-subject nil nil t) |
| 8049 | (gnus-summary-show-thread) |
| 8050 | (gnus-summary-first-subject nil nil t)) |
| 8051 | (when (gnus-summary-first-subject t) |
| 8052 | (gnus-summary-show-thread) |
| 8053 | (gnus-summary-first-subject t))) |
| 8054 | (gnus-summary-position-point))) |
| 8055 | |
| 8056 | (defun gnus-summary-first-article () |
| 8057 | "Select the first article. |
| 8058 | Return nil if there are no articles." |
| 8059 | (interactive) |
| 8060 | (prog1 |
| 8061 | (when (gnus-summary-first-subject) |
| 8062 | (gnus-summary-show-thread) |
| 8063 | (gnus-summary-first-subject) |
| 8064 | (gnus-summary-display-article (gnus-summary-article-number))) |
| 8065 | (gnus-summary-position-point))) |
| 8066 | |
| 8067 | (defun gnus-summary-best-unread-article (&optional arg) |
| 8068 | "Select the unread article with the highest score. |
| 8069 | If given a prefix argument, select the next unread article that has a |
| 8070 | score higher than the default score." |
| 8071 | (interactive "P") |
| 8072 | (let ((article (if arg |
| 8073 | (gnus-summary-better-unread-subject) |
| 8074 | (gnus-summary-best-unread-subject)))) |
| 8075 | (if article |
| 8076 | (gnus-summary-goto-article article) |
| 8077 | (error "No unread articles")))) |
| 8078 | |
| 8079 | (defun gnus-summary-best-unread-subject () |
| 8080 | "Select the unread subject with the highest score." |
| 8081 | (interactive) |
| 8082 | (let ((best -1000000) |
| 8083 | (data gnus-newsgroup-data) |
| 8084 | article score) |
| 8085 | (while data |
| 8086 | (and (gnus-data-unread-p (car data)) |
| 8087 | (> (setq score |
| 8088 | (gnus-summary-article-score (gnus-data-number (car data)))) |
| 8089 | best) |
| 8090 | (setq best score |
| 8091 | article (gnus-data-number (car data)))) |
| 8092 | (setq data (cdr data))) |
| 8093 | (when article |
| 8094 | (gnus-summary-goto-subject article)) |
| 8095 | (gnus-summary-position-point) |
| 8096 | article)) |
| 8097 | |
| 8098 | (defun gnus-summary-better-unread-subject () |
| 8099 | "Select the first unread subject that has a score over the default score." |
| 8100 | (interactive) |
| 8101 | (let ((data gnus-newsgroup-data) |
| 8102 | article score) |
| 8103 | (while (and (setq article (gnus-data-number (car data))) |
| 8104 | (or (gnus-data-read-p (car data)) |
| 8105 | (not (> (gnus-summary-article-score article) |
| 8106 | gnus-summary-default-score)))) |
| 8107 | (setq data (cdr data))) |
| 8108 | (when article |
| 8109 | (gnus-summary-goto-subject article)) |
| 8110 | (gnus-summary-position-point) |
| 8111 | article)) |
| 8112 | |
| 8113 | (defun gnus-summary-last-subject () |
| 8114 | "Go to the last displayed subject line in the group." |
| 8115 | (let ((article (gnus-data-number (car (gnus-data-list t))))) |
| 8116 | (when article |
| 8117 | (gnus-summary-goto-subject article)))) |
| 8118 | |
| 8119 | (defun gnus-summary-goto-article (article &optional all-headers force) |
| 8120 | "Fetch ARTICLE (article number or Message-ID) and display it if it exists. |
| 8121 | If ALL-HEADERS is non-nil, no header lines are hidden. |
| 8122 | If FORCE, go to the article even if it isn't displayed. If FORCE |
| 8123 | is a number, it is the line the article is to be displayed on." |
| 8124 | (interactive |
| 8125 | (list |
| 8126 | (gnus-completing-read |
| 8127 | "Article number or Message-ID" |
| 8128 | (mapcar 'int-to-string gnus-newsgroup-limit)) |
| 8129 | current-prefix-arg |
| 8130 | t)) |
| 8131 | (prog1 |
| 8132 | (if (and (stringp article) |
| 8133 | (string-match "@\\|%40" article)) |
| 8134 | (gnus-summary-refer-article article) |
| 8135 | (when (stringp article) |
| 8136 | (setq article (string-to-number article))) |
| 8137 | (if (gnus-summary-goto-subject article force) |
| 8138 | (gnus-summary-display-article article all-headers) |
| 8139 | (gnus-message 4 "Couldn't go to article %s" article) nil)) |
| 8140 | (gnus-summary-position-point))) |
| 8141 | |
| 8142 | (defun gnus-summary-goto-last-article () |
| 8143 | "Go to the previously read article." |
| 8144 | (interactive) |
| 8145 | (prog1 |
| 8146 | (when gnus-last-article |
| 8147 | (gnus-summary-goto-article gnus-last-article nil t)) |
| 8148 | (gnus-summary-position-point))) |
| 8149 | |
| 8150 | (defun gnus-summary-pop-article (number) |
| 8151 | "Pop one article off the history and go to the previous. |
| 8152 | NUMBER articles will be popped off." |
| 8153 | (interactive "p") |
| 8154 | (let (to) |
| 8155 | (setq gnus-newsgroup-history |
| 8156 | (cdr (setq to (nthcdr number gnus-newsgroup-history)))) |
| 8157 | (if to |
| 8158 | (gnus-summary-goto-article (car to) nil t) |
| 8159 | (error "Article history empty"))) |
| 8160 | (gnus-summary-position-point)) |
| 8161 | |
| 8162 | ;; Summary commands and functions for limiting the summary buffer. |
| 8163 | |
| 8164 | (defun gnus-summary-limit-to-articles (n) |
| 8165 | "Limit the summary buffer to the next N articles. |
| 8166 | If not given a prefix, use the process marked articles instead." |
| 8167 | (interactive "P") |
| 8168 | (prog1 |
| 8169 | (let ((articles (gnus-summary-work-articles n))) |
| 8170 | (setq gnus-newsgroup-processable nil) |
| 8171 | (gnus-summary-limit articles)) |
| 8172 | (gnus-summary-position-point))) |
| 8173 | |
| 8174 | (defun gnus-summary-pop-limit (&optional total) |
| 8175 | "Restore the previous limit. |
| 8176 | If given a prefix, remove all limits." |
| 8177 | (interactive "P") |
| 8178 | (when total |
| 8179 | (setq gnus-newsgroup-limits |
| 8180 | (list (mapcar (lambda (h) (mail-header-number h)) |
| 8181 | gnus-newsgroup-headers)))) |
| 8182 | (unless gnus-newsgroup-limits |
| 8183 | (error "No limit to pop")) |
| 8184 | (prog1 |
| 8185 | (gnus-summary-limit nil 'pop) |
| 8186 | (gnus-summary-position-point))) |
| 8187 | |
| 8188 | (defun gnus-summary-limit-to-subject (subject &optional header not-matching) |
| 8189 | "Limit the summary buffer to articles that have subjects that match a regexp. |
| 8190 | If NOT-MATCHING, excluding articles that have subjects that match a regexp." |
| 8191 | (interactive |
| 8192 | (list (read-string (if current-prefix-arg |
| 8193 | "Exclude subject (regexp): " |
| 8194 | "Limit to subject (regexp): ")) |
| 8195 | nil current-prefix-arg)) |
| 8196 | (unless header |
| 8197 | (setq header "subject")) |
| 8198 | (when (not (equal "" subject)) |
| 8199 | (prog1 |
| 8200 | (let ((articles (gnus-summary-find-matching |
| 8201 | (or header "subject") subject 'all nil nil |
| 8202 | not-matching))) |
| 8203 | (unless articles |
| 8204 | (error "Found no matches for \"%s\"" subject)) |
| 8205 | (gnus-summary-limit articles)) |
| 8206 | (gnus-summary-position-point)))) |
| 8207 | |
| 8208 | (defun gnus-summary-limit-to-author (from &optional not-matching) |
| 8209 | "Limit the summary buffer to articles that have authors that match a regexp. |
| 8210 | If NOT-MATCHING, excluding articles that have authors that match a regexp." |
| 8211 | (interactive |
| 8212 | (list (let* ((header (gnus-summary-article-header)) |
| 8213 | (default (and header (car (mail-header-parse-address |
| 8214 | (mail-header-from header)))))) |
| 8215 | (read-string (concat (if current-prefix-arg |
| 8216 | "Exclude author (regexp" |
| 8217 | "Limit to author (regexp") |
| 8218 | (if default |
| 8219 | (concat ", default \"" default "\"): ") |
| 8220 | "): ")) |
| 8221 | nil nil |
| 8222 | default)) |
| 8223 | current-prefix-arg)) |
| 8224 | (gnus-summary-limit-to-subject from "from" not-matching)) |
| 8225 | |
| 8226 | (defun gnus-summary-limit-to-recipient (recipient &optional not-matching) |
| 8227 | "Limit the summary buffer to articles with the given RECIPIENT. |
| 8228 | |
| 8229 | If NOT-MATCHING, exclude RECIPIENT. |
| 8230 | |
| 8231 | To and Cc headers are checked. You need to include them in |
| 8232 | `nnmail-extra-headers'." |
| 8233 | ;; Unlike `rmail-summary-by-recipients', doesn't include From. |
| 8234 | (interactive |
| 8235 | (list (read-string (format "%s recipient (regexp): " |
| 8236 | (if current-prefix-arg "Exclude" "Limit to"))) |
| 8237 | current-prefix-arg)) |
| 8238 | (when (not (equal "" recipient)) |
| 8239 | (prog1 (let* ((to |
| 8240 | (if (memq 'To nnmail-extra-headers) |
| 8241 | (gnus-summary-find-matching |
| 8242 | (cons 'extra 'To) recipient 'all nil nil |
| 8243 | not-matching) |
| 8244 | (gnus-message |
| 8245 | 1 "`To' isn't present in `nnmail-extra-headers'") |
| 8246 | (sit-for 1) |
| 8247 | nil)) |
| 8248 | (cc |
| 8249 | (if (memq 'Cc nnmail-extra-headers) |
| 8250 | (gnus-summary-find-matching |
| 8251 | (cons 'extra 'Cc) recipient 'all nil nil |
| 8252 | not-matching) |
| 8253 | (gnus-message |
| 8254 | 1 "`Cc' isn't present in `nnmail-extra-headers'") |
| 8255 | (sit-for 1) |
| 8256 | nil)) |
| 8257 | (articles |
| 8258 | (if not-matching |
| 8259 | ;; We need the numbers that are in both lists: |
| 8260 | (mapcar (lambda (a) |
| 8261 | (and (memq a to) a)) |
| 8262 | cc) |
| 8263 | (nconc to cc)))) |
| 8264 | (unless articles |
| 8265 | (error "Found no matches for \"%s\"" recipient)) |
| 8266 | (gnus-summary-limit articles)) |
| 8267 | (gnus-summary-position-point)))) |
| 8268 | |
| 8269 | (defun gnus-summary-limit-to-address (address &optional not-matching) |
| 8270 | "Limit the summary buffer to articles with the given ADDRESS. |
| 8271 | |
| 8272 | If NOT-MATCHING, exclude ADDRESS. |
| 8273 | |
| 8274 | To, Cc and From headers are checked. You need to include `To' and `Cc' |
| 8275 | in `nnmail-extra-headers'." |
| 8276 | (interactive |
| 8277 | (list (read-string (format "%s address (regexp): " |
| 8278 | (if current-prefix-arg "Exclude" "Limit to"))) |
| 8279 | current-prefix-arg)) |
| 8280 | (when (not (equal "" address)) |
| 8281 | (prog1 (let* ((to |
| 8282 | (if (memq 'To nnmail-extra-headers) |
| 8283 | (gnus-summary-find-matching |
| 8284 | (cons 'extra 'To) address 'all nil nil |
| 8285 | not-matching) |
| 8286 | (gnus-message |
| 8287 | 1 "`To' isn't present in `nnmail-extra-headers'") |
| 8288 | (sit-for 1) |
| 8289 | t)) |
| 8290 | (cc |
| 8291 | (if (memq 'Cc nnmail-extra-headers) |
| 8292 | (gnus-summary-find-matching |
| 8293 | (cons 'extra 'Cc) address 'all nil nil |
| 8294 | not-matching) |
| 8295 | (gnus-message |
| 8296 | 1 "`Cc' isn't present in `nnmail-extra-headers'") |
| 8297 | (sit-for 1) |
| 8298 | t)) |
| 8299 | (from |
| 8300 | (gnus-summary-find-matching "from" address |
| 8301 | 'all nil nil not-matching)) |
| 8302 | (articles |
| 8303 | (if not-matching |
| 8304 | ;; We need the numbers that are in all lists: |
| 8305 | (if (eq cc t) |
| 8306 | (if (eq to t) |
| 8307 | from |
| 8308 | (mapcar (lambda (a) (car (memq a from))) to)) |
| 8309 | (if (eq to t) |
| 8310 | (mapcar (lambda (a) (car (memq a from))) cc) |
| 8311 | (mapcar (lambda (a) (car (memq a from))) |
| 8312 | (mapcar (lambda (a) (car (memq a to))) |
| 8313 | cc)))) |
| 8314 | (nconc (if (eq to t) nil to) |
| 8315 | (if (eq cc t) nil cc) |
| 8316 | from)))) |
| 8317 | (unless articles |
| 8318 | (error "Found no matches for \"%s\"" address)) |
| 8319 | (gnus-summary-limit articles)) |
| 8320 | (gnus-summary-position-point)))) |
| 8321 | |
| 8322 | (defun gnus-summary-limit-strange-charsets-predicate (header) |
| 8323 | (when (fboundp 'char-charset) |
| 8324 | (let ((string (concat (mail-header-subject header) |
| 8325 | (mail-header-from header))) |
| 8326 | charset found) |
| 8327 | (dotimes (i (1- (length string))) |
| 8328 | (setq charset (format "%s" (char-charset (aref string (1+ i))))) |
| 8329 | (when (string-match "unicode\\|big\\|japanese" charset) |
| 8330 | (setq found t))) |
| 8331 | found))) |
| 8332 | |
| 8333 | (defun gnus-summary-limit-to-predicate (predicate) |
| 8334 | "Limit to articles where PREDICATE returns non-nil. |
| 8335 | PREDICATE will be called with the header structures of the |
| 8336 | articles." |
| 8337 | (let ((articles nil) |
| 8338 | (case-fold-search t)) |
| 8339 | (dolist (header gnus-newsgroup-headers) |
| 8340 | (when (funcall predicate header) |
| 8341 | (push (mail-header-number header) articles))) |
| 8342 | (gnus-summary-limit (nreverse articles)))) |
| 8343 | |
| 8344 | (defun gnus-summary-limit-to-age (age &optional younger-p) |
| 8345 | "Limit the summary buffer to articles that are older than (or equal) AGE days. |
| 8346 | If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to |
| 8347 | articles that are younger than AGE days." |
| 8348 | (interactive |
| 8349 | (let ((younger current-prefix-arg) |
| 8350 | (days-got nil) |
| 8351 | days) |
| 8352 | (while (not days-got) |
| 8353 | (setq days (if younger |
| 8354 | (read-string "Limit to articles younger than (in days, older when negative): ") |
| 8355 | (read-string |
| 8356 | "Limit to articles older than (in days, younger when negative): "))) |
| 8357 | (when (> (length days) 0) |
| 8358 | (setq days (read days))) |
| 8359 | (if (numberp days) |
| 8360 | (progn |
| 8361 | (setq days-got t) |
| 8362 | (when (< days 0) |
| 8363 | (setq younger (not younger)) |
| 8364 | (setq days (* days -1)))) |
| 8365 | (message "Please enter a number.") |
| 8366 | (sleep-for 1))) |
| 8367 | (list days younger))) |
| 8368 | (prog1 |
| 8369 | (let ((data gnus-newsgroup-data) |
| 8370 | (cutoff (days-to-time age)) |
| 8371 | articles d date is-younger) |
| 8372 | (while (setq d (pop data)) |
| 8373 | (when (and (vectorp (gnus-data-header d)) |
| 8374 | (setq date (mail-header-date (gnus-data-header d)))) |
| 8375 | (setq is-younger (time-less-p |
| 8376 | (time-since (gnus-date-get-time date)) |
| 8377 | cutoff)) |
| 8378 | (when (if younger-p |
| 8379 | is-younger |
| 8380 | (not is-younger)) |
| 8381 | (push (gnus-data-number d) articles)))) |
| 8382 | (gnus-summary-limit (nreverse articles))) |
| 8383 | (gnus-summary-position-point))) |
| 8384 | |
| 8385 | (defun gnus-summary-limit-to-extra (header regexp &optional not-matching) |
| 8386 | "Limit the summary buffer to articles that match an 'extra' header." |
| 8387 | (interactive |
| 8388 | (let ((header |
| 8389 | (intern |
| 8390 | (gnus-completing-read |
| 8391 | (if current-prefix-arg |
| 8392 | "Exclude extra header" |
| 8393 | "Limit extra header") |
| 8394 | (mapcar 'symbol-name gnus-extra-headers) |
| 8395 | t nil nil |
| 8396 | (symbol-name (car gnus-extra-headers)))))) |
| 8397 | (list header |
| 8398 | (read-string (format "%s header %s (regexp): " |
| 8399 | (if current-prefix-arg "Exclude" "Limit to") |
| 8400 | header)) |
| 8401 | current-prefix-arg))) |
| 8402 | (when (not (equal "" regexp)) |
| 8403 | (prog1 |
| 8404 | (let ((articles (gnus-summary-find-matching |
| 8405 | (cons 'extra header) regexp 'all nil nil |
| 8406 | not-matching))) |
| 8407 | (unless articles |
| 8408 | (error "Found no matches for \"%s\"" regexp)) |
| 8409 | (gnus-summary-limit articles)) |
| 8410 | (gnus-summary-position-point)))) |
| 8411 | |
| 8412 | (defun gnus-summary-limit-to-display-predicate () |
| 8413 | "Limit the summary buffer to the predicated in the `display' group parameter." |
| 8414 | (interactive) |
| 8415 | (unless gnus-newsgroup-display |
| 8416 | (error "There is no `display' group parameter")) |
| 8417 | (let (articles) |
| 8418 | (dolist (gnus-number gnus-newsgroup-articles) |
| 8419 | (when (funcall gnus-newsgroup-display) |
| 8420 | (push gnus-number articles))) |
| 8421 | (gnus-summary-limit articles)) |
| 8422 | (gnus-summary-position-point)) |
| 8423 | |
| 8424 | (defun gnus-summary-limit-to-unread (&optional all) |
| 8425 | "Limit the summary buffer to articles that are not marked as read. |
| 8426 | If ALL is non-nil, limit strictly to unread articles." |
| 8427 | (interactive "P") |
| 8428 | (if all |
| 8429 | (gnus-summary-limit-to-marks (char-to-string gnus-unread-mark)) |
| 8430 | (gnus-summary-limit-to-marks |
| 8431 | ;; Concat all the marks that say that an article is read and have |
| 8432 | ;; those removed. |
| 8433 | (list gnus-del-mark gnus-read-mark gnus-ancient-mark |
| 8434 | gnus-killed-mark gnus-spam-mark gnus-kill-file-mark |
| 8435 | gnus-low-score-mark gnus-expirable-mark |
| 8436 | gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark |
| 8437 | gnus-duplicate-mark) |
| 8438 | 'reverse))) |
| 8439 | |
| 8440 | (defun gnus-summary-limit-to-headers (match &optional reverse) |
| 8441 | "Limit the summary buffer to articles that have headers that match MATCH. |
| 8442 | If REVERSE (the prefix), limit to articles that don't match." |
| 8443 | (interactive "sMatch headers (regexp): \nP") |
| 8444 | (gnus-summary-limit-to-bodies match reverse t)) |
| 8445 | |
| 8446 | (declare-function article-goto-body "gnus-art" ()) |
| 8447 | |
| 8448 | (defun gnus-summary-limit-to-bodies (match &optional reverse headersp) |
| 8449 | "Limit the summary buffer to articles that have bodies that match MATCH. |
| 8450 | If REVERSE (the prefix), limit to articles that don't match." |
| 8451 | (interactive "sMatch body (regexp): \nP") |
| 8452 | (let ((articles nil) |
| 8453 | (gnus-select-article-hook nil) ;Disable hook. |
| 8454 | (gnus-article-prepare-hook nil) |
| 8455 | (gnus-use-article-prefetch nil) |
| 8456 | (gnus-keep-backlog nil) |
| 8457 | (gnus-break-pages nil) |
| 8458 | (gnus-summary-display-arrow nil) |
| 8459 | (gnus-updated-mode-lines nil) |
| 8460 | (gnus-auto-center-summary nil) |
| 8461 | (gnus-display-mime-function nil)) |
| 8462 | (dolist (data gnus-newsgroup-data) |
| 8463 | (let (gnus-mark-article-hook) |
| 8464 | (gnus-summary-select-article t t nil (gnus-data-number data))) |
| 8465 | (with-current-buffer gnus-article-buffer |
| 8466 | (article-goto-body) |
| 8467 | (let* ((case-fold-search t) |
| 8468 | (found (if headersp |
| 8469 | (re-search-backward match nil t) |
| 8470 | (re-search-forward match nil t)))) |
| 8471 | (when (or (and found |
| 8472 | (not reverse)) |
| 8473 | (and (not found) |
| 8474 | reverse)) |
| 8475 | (push (gnus-data-number data) articles))))) |
| 8476 | (if (not articles) |
| 8477 | (message "No messages matched") |
| 8478 | (gnus-summary-limit articles))) |
| 8479 | (gnus-summary-position-point)) |
| 8480 | |
| 8481 | (defun gnus-summary-limit-to-singletons (&optional threadsp) |
| 8482 | "Limit the summary buffer to articles that aren't part on any thread. |
| 8483 | If THREADSP (the prefix), limit to articles that are in threads." |
| 8484 | (interactive "P") |
| 8485 | (let ((articles nil) |
| 8486 | thread-articles |
| 8487 | threads) |
| 8488 | (dolist (thread gnus-newsgroup-threads) |
| 8489 | (if (stringp (car thread)) |
| 8490 | (dolist (thread (cdr thread)) |
| 8491 | (push thread threads)) |
| 8492 | (push thread threads))) |
| 8493 | (dolist (thread threads) |
| 8494 | (setq thread-articles (gnus-articles-in-thread thread)) |
| 8495 | (when (or (and threadsp |
| 8496 | (> (length thread-articles) 1)) |
| 8497 | (and (not threadsp) |
| 8498 | (= (length thread-articles) 1))) |
| 8499 | (setq articles (nconc thread-articles articles)))) |
| 8500 | (if (not articles) |
| 8501 | (message "No messages matched") |
| 8502 | (gnus-summary-limit articles)) |
| 8503 | (gnus-summary-position-point))) |
| 8504 | |
| 8505 | (defun gnus-summary-limit-to-replied (&optional unreplied) |
| 8506 | "Limit the summary buffer to replied articles. |
| 8507 | If UNREPLIED (the prefix), limit to unreplied articles." |
| 8508 | (interactive "P") |
| 8509 | (if unreplied |
| 8510 | (gnus-summary-limit |
| 8511 | (gnus-set-difference gnus-newsgroup-articles |
| 8512 | gnus-newsgroup-replied)) |
| 8513 | (gnus-summary-limit gnus-newsgroup-replied)) |
| 8514 | (gnus-summary-position-point)) |
| 8515 | |
| 8516 | (defun gnus-summary-limit-exclude-marks (marks &optional reverse) |
| 8517 | "Exclude articles that are marked with MARKS (e.g. \"DK\"). |
| 8518 | If REVERSE, limit the summary buffer to articles that are marked |
| 8519 | with MARKS. MARKS can either be a string of marks or a list of marks. |
| 8520 | Returns how many articles were removed." |
| 8521 | (interactive "sMarks: ") |
| 8522 | (gnus-summary-limit-to-marks marks t)) |
| 8523 | |
| 8524 | (defun gnus-summary-limit-to-marks (marks &optional reverse) |
| 8525 | "Limit the summary buffer to articles that are marked with MARKS (e.g. \"DK\"). |
| 8526 | If REVERSE (the prefix), limit the summary buffer to articles that are |
| 8527 | not marked with MARKS. MARKS can either be a string of marks or a |
| 8528 | list of marks. |
| 8529 | Returns how many articles were removed." |
| 8530 | (interactive "sMarks: \nP") |
| 8531 | (prog1 |
| 8532 | (let ((data gnus-newsgroup-data) |
| 8533 | (marks (if (listp marks) marks |
| 8534 | (append marks nil))) ; Transform to list. |
| 8535 | articles) |
| 8536 | (while data |
| 8537 | (when (if reverse (not (memq (gnus-data-mark (car data)) marks)) |
| 8538 | (memq (gnus-data-mark (car data)) marks)) |
| 8539 | (push (gnus-data-number (car data)) articles)) |
| 8540 | (setq data (cdr data))) |
| 8541 | (gnus-summary-limit articles)) |
| 8542 | (gnus-summary-position-point))) |
| 8543 | |
| 8544 | (defun gnus-summary-limit-to-score (score) |
| 8545 | "Limit to articles with score at or above SCORE." |
| 8546 | (interactive "NLimit to articles with score of at least: ") |
| 8547 | (let ((data gnus-newsgroup-data) |
| 8548 | articles) |
| 8549 | (while data |
| 8550 | (when (>= (gnus-summary-article-score (gnus-data-number (car data))) |
| 8551 | score) |
| 8552 | (push (gnus-data-number (car data)) articles)) |
| 8553 | (setq data (cdr data))) |
| 8554 | (prog1 |
| 8555 | (gnus-summary-limit articles) |
| 8556 | (gnus-summary-position-point)))) |
| 8557 | |
| 8558 | (defun gnus-summary-limit-to-unseen () |
| 8559 | "Limit to unseen articles." |
| 8560 | (interactive) |
| 8561 | (prog1 |
| 8562 | (gnus-summary-limit gnus-newsgroup-unseen) |
| 8563 | (gnus-summary-position-point))) |
| 8564 | |
| 8565 | (defun gnus-summary-limit-include-thread (id) |
| 8566 | "Display all the hidden articles that is in the thread with ID in it. |
| 8567 | When called interactively, ID is the Message-ID of the current |
| 8568 | article." |
| 8569 | (interactive (list (mail-header-id (gnus-summary-article-header)))) |
| 8570 | (let ((articles (gnus-articles-in-thread |
| 8571 | (gnus-id-to-thread (gnus-root-id id)))) |
| 8572 | ;;we REALLY want the whole thread---this prevents cut-threads |
| 8573 | ;;from removing the thread we want to include. |
| 8574 | (gnus-fetch-old-headers nil) |
| 8575 | (gnus-build-sparse-threads nil)) |
| 8576 | (prog1 |
| 8577 | (gnus-summary-limit (nconc articles gnus-newsgroup-limit)) |
| 8578 | (gnus-summary-limit-include-matching-articles |
| 8579 | "subject" |
| 8580 | (regexp-quote (gnus-simplify-subject-re |
| 8581 | (mail-header-subject (gnus-id-to-header id))))) |
| 8582 | (gnus-summary-position-point)))) |
| 8583 | |
| 8584 | (defun gnus-summary-limit-include-matching-articles (header regexp) |
| 8585 | "Display all the hidden articles that have HEADERs that match REGEXP." |
| 8586 | (interactive (list (read-string "Match on header: ") |
| 8587 | (read-string "Regexp: "))) |
| 8588 | (let ((articles (gnus-find-matching-articles header regexp))) |
| 8589 | (prog1 |
| 8590 | (gnus-summary-limit (nconc articles gnus-newsgroup-limit)) |
| 8591 | (gnus-summary-position-point)))) |
| 8592 | |
| 8593 | (defun gnus-summary-insert-dormant-articles () |
| 8594 | "Insert all the dormant articles for this group into the current buffer." |
| 8595 | (interactive) |
| 8596 | (let ((gnus-verbose (max 6 gnus-verbose))) |
| 8597 | (if (not gnus-newsgroup-dormant) |
| 8598 | (gnus-message 3 "No dormant articles for this group") |
| 8599 | (gnus-summary-goto-subjects gnus-newsgroup-dormant)))) |
| 8600 | |
| 8601 | (defun gnus-summary-insert-ticked-articles () |
| 8602 | "Insert ticked articles for this group into the current buffer." |
| 8603 | (interactive) |
| 8604 | (let ((gnus-verbose (max 6 gnus-verbose))) |
| 8605 | (if (not gnus-newsgroup-marked) |
| 8606 | (gnus-message 3 "No ticked articles for this group") |
| 8607 | (gnus-summary-goto-subjects gnus-newsgroup-marked)))) |
| 8608 | |
| 8609 | (defun gnus-summary-limit-include-dormant () |
| 8610 | "Display all the hidden articles that are marked as dormant. |
| 8611 | Note that this command only works on a subset of the articles currently |
| 8612 | fetched for this group." |
| 8613 | (interactive) |
| 8614 | (unless gnus-newsgroup-dormant |
| 8615 | (error "There are no dormant articles in this group")) |
| 8616 | (prog1 |
| 8617 | (gnus-summary-limit (append gnus-newsgroup-dormant gnus-newsgroup-limit)) |
| 8618 | (gnus-summary-position-point))) |
| 8619 | |
| 8620 | (defun gnus-summary-include-articles (articles) |
| 8621 | "Fetch the headers for ARTICLES and then display the summary lines." |
| 8622 | (let ((gnus-inhibit-demon t) |
| 8623 | (gnus-agent nil) |
| 8624 | (gnus-read-all-available-headers t)) |
| 8625 | (setq gnus-newsgroup-headers |
| 8626 | (gnus-merge |
| 8627 | 'list gnus-newsgroup-headers |
| 8628 | (gnus-fetch-headers articles nil t) |
| 8629 | 'gnus-article-sort-by-number)) |
| 8630 | (setq gnus-newsgroup-articles |
| 8631 | (gnus-sorted-nunion gnus-newsgroup-articles articles)) |
| 8632 | (gnus-summary-limit (append articles gnus-newsgroup-limit)))) |
| 8633 | |
| 8634 | (defun gnus-summary-limit-exclude-dormant () |
| 8635 | "Hide all dormant articles." |
| 8636 | (interactive) |
| 8637 | (prog1 |
| 8638 | (gnus-summary-limit-to-marks (list gnus-dormant-mark) 'reverse) |
| 8639 | (gnus-summary-position-point))) |
| 8640 | |
| 8641 | (defun gnus-summary-limit-exclude-childless-dormant () |
| 8642 | "Hide all dormant articles that have no children." |
| 8643 | (interactive) |
| 8644 | (let ((data (gnus-data-list t)) |
| 8645 | articles d children) |
| 8646 | ;; Find all articles that are either not dormant or have |
| 8647 | ;; children. |
| 8648 | (while (setq d (pop data)) |
| 8649 | (when (or (not (= (gnus-data-mark d) gnus-dormant-mark)) |
| 8650 | (and (setq children |
| 8651 | (gnus-article-children (gnus-data-number d))) |
| 8652 | (let (found) |
| 8653 | (while children |
| 8654 | (when (memq (car children) articles) |
| 8655 | (setq children nil |
| 8656 | found t)) |
| 8657 | (pop children)) |
| 8658 | found))) |
| 8659 | (push (gnus-data-number d) articles))) |
| 8660 | ;; Do the limiting. |
| 8661 | (prog1 |
| 8662 | (gnus-summary-limit articles) |
| 8663 | (gnus-summary-position-point)))) |
| 8664 | |
| 8665 | (defun gnus-summary-limit-mark-excluded-as-read (&optional all) |
| 8666 | "Mark all unread excluded articles as read. |
| 8667 | If ALL, mark even excluded ticked and dormants as read." |
| 8668 | (interactive "P") |
| 8669 | (setq gnus-newsgroup-limit (sort gnus-newsgroup-limit '<)) |
| 8670 | (let ((articles (gnus-sorted-ndifference |
| 8671 | (sort |
| 8672 | (mapcar (lambda (h) (mail-header-number h)) |
| 8673 | gnus-newsgroup-headers) |
| 8674 | '<) |
| 8675 | gnus-newsgroup-limit)) |
| 8676 | article) |
| 8677 | (setq gnus-newsgroup-unreads |
| 8678 | (gnus-sorted-intersection gnus-newsgroup-unreads |
| 8679 | gnus-newsgroup-limit)) |
| 8680 | (if all |
| 8681 | (setq gnus-newsgroup-dormant nil |
| 8682 | gnus-newsgroup-marked nil |
| 8683 | gnus-newsgroup-reads |
| 8684 | (nconc |
| 8685 | (mapcar (lambda (n) (cons n gnus-catchup-mark)) articles) |
| 8686 | gnus-newsgroup-reads)) |
| 8687 | (while (setq article (pop articles)) |
| 8688 | (unless (or (memq article gnus-newsgroup-dormant) |
| 8689 | (memq article gnus-newsgroup-marked)) |
| 8690 | (push (cons article gnus-catchup-mark) gnus-newsgroup-reads)))))) |
| 8691 | |
| 8692 | (defun gnus-summary-limit (articles &optional pop) |
| 8693 | (if pop |
| 8694 | ;; We pop the previous limit off the stack and use that. |
| 8695 | (setq articles (car gnus-newsgroup-limits) |
| 8696 | gnus-newsgroup-limits (cdr gnus-newsgroup-limits)) |
| 8697 | ;; We use the new limit, so we push the old limit on the stack. |
| 8698 | (push gnus-newsgroup-limit gnus-newsgroup-limits)) |
| 8699 | ;; Set the limit. |
| 8700 | (setq gnus-newsgroup-limit articles) |
| 8701 | (let ((total (length gnus-newsgroup-data)) |
| 8702 | (data (gnus-data-find-list (gnus-summary-article-number))) |
| 8703 | (gnus-summary-mark-below nil) ; Inhibit this. |
| 8704 | found) |
| 8705 | ;; This will do all the work of generating the new summary buffer |
| 8706 | ;; according to the new limit. |
| 8707 | (gnus-summary-prepare) |
| 8708 | ;; Hide any threads, possibly. |
| 8709 | (gnus-summary-maybe-hide-threads) |
| 8710 | ;; Try to return to the article you were at, or one in the |
| 8711 | ;; neighborhood. |
| 8712 | (when data |
| 8713 | ;; We try to find some article after the current one. |
| 8714 | (while data |
| 8715 | (when (gnus-summary-goto-subject (gnus-data-number (car data)) nil t) |
| 8716 | (setq data nil |
| 8717 | found t)) |
| 8718 | (setq data (cdr data)))) |
| 8719 | (unless found |
| 8720 | ;; If there is no data, that means that we were after the last |
| 8721 | ;; article. The same goes when we can't find any articles |
| 8722 | ;; after the current one. |
| 8723 | (goto-char (point-max)) |
| 8724 | (gnus-summary-find-prev)) |
| 8725 | (gnus-set-mode-line 'summary) |
| 8726 | ;; We return how many articles were removed from the summary |
| 8727 | ;; buffer as a result of the new limit. |
| 8728 | (- total (length gnus-newsgroup-data)))) |
| 8729 | |
| 8730 | (defsubst gnus-invisible-cut-children (threads) |
| 8731 | (let ((num 0)) |
| 8732 | (while threads |
| 8733 | (when (memq (mail-header-number (caar threads)) gnus-newsgroup-limit) |
| 8734 | (incf num)) |
| 8735 | (pop threads)) |
| 8736 | (< num 2))) |
| 8737 | |
| 8738 | (defsubst gnus-cut-thread (thread) |
| 8739 | "Go forwards in the thread until we find an article that we want to display." |
| 8740 | (when (or (eq gnus-fetch-old-headers 'some) |
| 8741 | (eq gnus-fetch-old-headers 'invisible) |
| 8742 | (numberp gnus-fetch-old-headers) |
| 8743 | (eq gnus-build-sparse-threads 'some) |
| 8744 | (eq gnus-build-sparse-threads 'more)) |
| 8745 | ;; Deal with old-fetched headers and sparse threads. |
| 8746 | (while (and |
| 8747 | thread |
| 8748 | (or |
| 8749 | (gnus-summary-article-sparse-p (mail-header-number (car thread))) |
| 8750 | (gnus-summary-article-ancient-p |
| 8751 | (mail-header-number (car thread)))) |
| 8752 | (if (or (<= (length (cdr thread)) 1) |
| 8753 | (eq gnus-fetch-old-headers 'invisible)) |
| 8754 | (setq gnus-newsgroup-limit |
| 8755 | (delq (mail-header-number (car thread)) |
| 8756 | gnus-newsgroup-limit) |
| 8757 | thread (cadr thread)) |
| 8758 | (when (gnus-invisible-cut-children (cdr thread)) |
| 8759 | (let ((th (cdr thread))) |
| 8760 | (while th |
| 8761 | (if (memq (mail-header-number (caar th)) |
| 8762 | gnus-newsgroup-limit) |
| 8763 | (setq thread (car th) |
| 8764 | th nil) |
| 8765 | (setq th (cdr th)))))))))) |
| 8766 | thread) |
| 8767 | |
| 8768 | (defun gnus-cut-threads (threads) |
| 8769 | "Cut off all uninteresting articles from the beginning of THREADS." |
| 8770 | (when (or (eq gnus-fetch-old-headers 'some) |
| 8771 | (eq gnus-fetch-old-headers 'invisible) |
| 8772 | (numberp gnus-fetch-old-headers) |
| 8773 | (eq gnus-build-sparse-threads 'some) |
| 8774 | (eq gnus-build-sparse-threads 'more)) |
| 8775 | (let ((th threads)) |
| 8776 | (while th |
| 8777 | (setcar th (gnus-cut-thread (car th))) |
| 8778 | (setq th (cdr th))))) |
| 8779 | ;; Remove nixed out threads. |
| 8780 | (delq nil threads)) |
| 8781 | |
| 8782 | (defun gnus-summary-initial-limit (&optional show-if-empty) |
| 8783 | "Figure out what the initial limit is supposed to be on group entry. |
| 8784 | This entails weeding out unwanted dormants, low-scored articles, |
| 8785 | fetch-old-headers verbiage, and so on." |
| 8786 | ;; Most groups have nothing to remove. |
| 8787 | (unless (or gnus-inhibit-limiting |
| 8788 | (and (null gnus-newsgroup-dormant) |
| 8789 | (eq gnus-newsgroup-display 'gnus-not-ignore) |
| 8790 | (not (eq gnus-fetch-old-headers 'some)) |
| 8791 | (not (numberp gnus-fetch-old-headers)) |
| 8792 | (not (eq gnus-fetch-old-headers 'invisible)) |
| 8793 | (null gnus-summary-expunge-below) |
| 8794 | (not (eq gnus-build-sparse-threads 'some)) |
| 8795 | (not (eq gnus-build-sparse-threads 'more)) |
| 8796 | (null gnus-thread-expunge-below))) |
| 8797 | (push gnus-newsgroup-limit gnus-newsgroup-limits) |
| 8798 | (setq gnus-newsgroup-limit nil) |
| 8799 | (mapatoms |
| 8800 | (lambda (node) |
| 8801 | (unless (car (symbol-value node)) |
| 8802 | ;; These threads have no parents -- they are roots. |
| 8803 | (let ((nodes (cdr (symbol-value node))) |
| 8804 | thread) |
| 8805 | (while nodes |
| 8806 | (if (and gnus-thread-expunge-below |
| 8807 | (< (gnus-thread-total-score (car nodes)) |
| 8808 | gnus-thread-expunge-below)) |
| 8809 | (gnus-expunge-thread (pop nodes)) |
| 8810 | (setq thread (pop nodes)) |
| 8811 | (gnus-summary-limit-children thread)))))) |
| 8812 | gnus-newsgroup-dependencies) |
| 8813 | ;; If this limitation resulted in an empty group, we might |
| 8814 | ;; pop the previous limit and use it instead. |
| 8815 | (when (and (not gnus-newsgroup-limit) |
| 8816 | show-if-empty) |
| 8817 | (setq gnus-newsgroup-limit (pop gnus-newsgroup-limits))) |
| 8818 | gnus-newsgroup-limit)) |
| 8819 | |
| 8820 | (defun gnus-summary-limit-children (thread) |
| 8821 | "Return 1 if this subthread is visible and 0 if it is not." |
| 8822 | ;; First we get the number of visible children to this thread. This |
| 8823 | ;; is done by recursing down the thread using this function, so this |
| 8824 | ;; will really go down to a leaf article first, before slowly |
| 8825 | ;; working its way up towards the root. |
| 8826 | (when thread |
| 8827 | (let* ((max-lisp-eval-depth (max 5000 max-lisp-eval-depth)) |
| 8828 | (children |
| 8829 | (if (cdr thread) |
| 8830 | (apply '+ (mapcar 'gnus-summary-limit-children |
| 8831 | (cdr thread))) |
| 8832 | 0)) |
| 8833 | (number (mail-header-number (car thread))) |
| 8834 | score) |
| 8835 | (if (and |
| 8836 | (not (memq number gnus-newsgroup-marked)) |
| 8837 | (or |
| 8838 | ;; If this article is dormant and has absolutely no visible |
| 8839 | ;; children, then this article isn't visible. |
| 8840 | (and (memq number gnus-newsgroup-dormant) |
| 8841 | (zerop children)) |
| 8842 | ;; If this is "fetch-old-headered" and there is no |
| 8843 | ;; visible children, then we don't want this article. |
| 8844 | (and (or (eq gnus-fetch-old-headers 'some) |
| 8845 | (numberp gnus-fetch-old-headers)) |
| 8846 | (gnus-summary-article-ancient-p number) |
| 8847 | (zerop children)) |
| 8848 | ;; If this is "fetch-old-headered" and `invisible', then |
| 8849 | ;; we don't want this article. |
| 8850 | (and (eq gnus-fetch-old-headers 'invisible) |
| 8851 | (gnus-summary-article-ancient-p number)) |
| 8852 | ;; If this is a sparsely inserted article with no children, |
| 8853 | ;; we don't want it. |
| 8854 | (and (eq gnus-build-sparse-threads 'some) |
| 8855 | (gnus-summary-article-sparse-p number) |
| 8856 | (zerop children)) |
| 8857 | ;; If we use expunging, and this article is really |
| 8858 | ;; low-scored, then we don't want this article. |
| 8859 | (when (and gnus-summary-expunge-below |
| 8860 | (< (setq score |
| 8861 | (or (cdr (assq number gnus-newsgroup-scored)) |
| 8862 | gnus-summary-default-score)) |
| 8863 | gnus-summary-expunge-below)) |
| 8864 | ;; We increase the expunge-tally here, but that has |
| 8865 | ;; nothing to do with the limits, really. |
| 8866 | (incf gnus-newsgroup-expunged-tally) |
| 8867 | ;; We also mark as read here, if that's wanted. |
| 8868 | (when (and gnus-summary-mark-below |
| 8869 | (< score gnus-summary-mark-below)) |
| 8870 | (setq gnus-newsgroup-unreads |
| 8871 | (delq number gnus-newsgroup-unreads)) |
| 8872 | (if gnus-newsgroup-auto-expire |
| 8873 | (push number gnus-newsgroup-expirable) |
| 8874 | (push (cons number gnus-low-score-mark) |
| 8875 | gnus-newsgroup-reads))) |
| 8876 | t) |
| 8877 | ;; Do the `display' group parameter. |
| 8878 | (and gnus-newsgroup-display |
| 8879 | (let ((gnus-number number)) |
| 8880 | (not (funcall gnus-newsgroup-display)))))) |
| 8881 | ;; Nope, invisible article. |
| 8882 | 0 |
| 8883 | ;; Ok, this article is to be visible, so we add it to the limit |
| 8884 | ;; and return 1. |
| 8885 | (push number gnus-newsgroup-limit) |
| 8886 | 1)))) |
| 8887 | |
| 8888 | (defun gnus-expunge-thread (thread) |
| 8889 | "Mark all articles in THREAD as read." |
| 8890 | (let* ((number (mail-header-number (car thread)))) |
| 8891 | (incf gnus-newsgroup-expunged-tally) |
| 8892 | ;; We also mark as read here, if that's wanted. |
| 8893 | (setq gnus-newsgroup-unreads |
| 8894 | (delq number gnus-newsgroup-unreads)) |
| 8895 | (if gnus-newsgroup-auto-expire |
| 8896 | (push number gnus-newsgroup-expirable) |
| 8897 | (push (cons number gnus-low-score-mark) |
| 8898 | gnus-newsgroup-reads))) |
| 8899 | ;; Go recursively through all subthreads. |
| 8900 | (mapcar 'gnus-expunge-thread (cdr thread))) |
| 8901 | |
| 8902 | ;; Summary article oriented commands |
| 8903 | |
| 8904 | (defun gnus-summary-refer-parent-article (n) |
| 8905 | "Refer parent article N times. |
| 8906 | If N is negative, go to ancestor -N instead. |
| 8907 | The difference between N and the number of articles fetched is returned." |
| 8908 | (interactive "p") |
| 8909 | (let ((skip 1) |
| 8910 | error header ref) |
| 8911 | (when (not (natnump n)) |
| 8912 | (setq skip (abs n) |
| 8913 | n 1)) |
| 8914 | (while (and (> n 0) |
| 8915 | (not error)) |
| 8916 | (setq header (gnus-summary-article-header)) |
| 8917 | (if (and (eq (mail-header-number header) |
| 8918 | (cdr gnus-article-current)) |
| 8919 | (equal gnus-newsgroup-name |
| 8920 | (car gnus-article-current))) |
| 8921 | ;; If we try to find the parent of the currently |
| 8922 | ;; displayed article, then we take a look at the actual |
| 8923 | ;; References header, since this is slightly more |
| 8924 | ;; reliable than the References field we got from the |
| 8925 | ;; server. |
| 8926 | (with-current-buffer gnus-original-article-buffer |
| 8927 | (nnheader-narrow-to-headers) |
| 8928 | (unless (setq ref (message-fetch-field "references")) |
| 8929 | (when (setq ref (message-fetch-field "in-reply-to")) |
| 8930 | (setq ref (gnus-extract-message-id-from-in-reply-to ref)))) |
| 8931 | (widen)) |
| 8932 | (setq ref |
| 8933 | ;; It's not the current article, so we take a bet on |
| 8934 | ;; the value we got from the server. |
| 8935 | (mail-header-references header))) |
| 8936 | (if (and ref |
| 8937 | (not (equal ref ""))) |
| 8938 | (unless (gnus-summary-refer-article (gnus-parent-id ref skip)) |
| 8939 | (gnus-message 1 "Couldn't find parent")) |
| 8940 | (gnus-message 1 "No references in article %d" |
| 8941 | (gnus-summary-article-number)) |
| 8942 | (setq error t)) |
| 8943 | (decf n)) |
| 8944 | (gnus-summary-position-point) |
| 8945 | n)) |
| 8946 | |
| 8947 | (defun gnus-summary-refer-references () |
| 8948 | "Fetch all articles mentioned in the References header. |
| 8949 | Return the number of articles fetched." |
| 8950 | (interactive) |
| 8951 | (let ((ref (mail-header-references (gnus-summary-article-header))) |
| 8952 | (current (gnus-summary-article-number)) |
| 8953 | (n 0)) |
| 8954 | (if (or (not ref) |
| 8955 | (equal ref "")) |
| 8956 | (error "No References in the current article") |
| 8957 | ;; For each Message-ID in the References header... |
| 8958 | (while (string-match "<[^>]*>" ref) |
| 8959 | (incf n) |
| 8960 | ;; ... fetch that article. |
| 8961 | (gnus-summary-refer-article |
| 8962 | (prog1 (match-string 0 ref) |
| 8963 | (setq ref (substring ref (match-end 0)))))) |
| 8964 | (gnus-summary-goto-subject current) |
| 8965 | (gnus-summary-position-point) |
| 8966 | n))) |
| 8967 | |
| 8968 | (defun gnus-delete-duplicate-headers (headers) |
| 8969 | ;; First remove leading duplicates. |
| 8970 | (while (and (> (length headers) 1) |
| 8971 | (= (mail-header-number (car headers)) |
| 8972 | (mail-header-number (cadr headers)))) |
| 8973 | (pop headers)) |
| 8974 | ;; Then the rest. |
| 8975 | (let ((result headers)) |
| 8976 | (while (> (length headers) 1) |
| 8977 | (if (= (mail-header-number (car headers)) |
| 8978 | (mail-header-number (cadr headers))) |
| 8979 | (setcdr headers (cddr headers)) |
| 8980 | (pop headers))) |
| 8981 | result)) |
| 8982 | |
| 8983 | (defun gnus-summary-refer-thread (&optional limit) |
| 8984 | "Fetch all articles in the current thread. For backends that |
| 8985 | know how to search for threads (currently only 'nnimap) a |
| 8986 | non-numeric prefix arg will use nnir to search the entire |
| 8987 | server; without a prefix arg only the current group is |
| 8988 | searched. If the variable `gnus-refer-thread-use-nnir' is |
| 8989 | non-nil the prefix arg has the reverse meaning. If no |
| 8990 | backend-specific 'request-thread function is available fetch |
| 8991 | LIMIT (the numerical prefix) old headers. If LIMIT is |
| 8992 | non-numeric or nil fetch the number specified by the |
| 8993 | `gnus-refer-thread-limit' variable." |
| 8994 | (interactive "P") |
| 8995 | (gnus-warp-to-article) |
| 8996 | (let* ((header (gnus-summary-article-header)) |
| 8997 | (id (mail-header-id header)) |
| 8998 | (gnus-inhibit-demon t) |
| 8999 | (gnus-summary-ignore-duplicates t) |
| 9000 | (gnus-read-all-available-headers t) |
| 9001 | (gnus-refer-thread-use-nnir |
| 9002 | (if (and (not (null limit)) (listp limit)) |
| 9003 | (not gnus-refer-thread-use-nnir) gnus-refer-thread-use-nnir)) |
| 9004 | (new-headers |
| 9005 | (if (gnus-check-backend-function |
| 9006 | 'request-thread gnus-newsgroup-name) |
| 9007 | (gnus-request-thread header gnus-newsgroup-name) |
| 9008 | (let* ((limit (if (numberp limit) (prefix-numeric-value limit) |
| 9009 | gnus-refer-thread-limit)) |
| 9010 | (last (if (numberp limit) |
| 9011 | (min (+ (mail-header-number header) |
| 9012 | limit) |
| 9013 | gnus-newsgroup-highest) |
| 9014 | gnus-newsgroup-highest)) |
| 9015 | (subject (gnus-simplify-subject |
| 9016 | (mail-header-subject header))) |
| 9017 | (refs (split-string (or (mail-header-references header) |
| 9018 | ""))) |
| 9019 | (gnus-parse-headers-hook |
| 9020 | `(lambda () (goto-char (point-min)) |
| 9021 | (keep-lines |
| 9022 | (regexp-opt ',(append refs (list id subject))))))) |
| 9023 | (gnus-fetch-headers (list last) (if (numberp limit) |
| 9024 | (* 2 limit) limit) t)))) |
| 9025 | article-ids) |
| 9026 | (when (listp new-headers) |
| 9027 | (dolist (header new-headers) |
| 9028 | (push (mail-header-number header) article-ids) |
| 9029 | (when (member (mail-header-number header) gnus-newsgroup-unselected) |
| 9030 | (push (mail-header-number header) gnus-newsgroup-unreads) |
| 9031 | (setq gnus-newsgroup-unselected |
| 9032 | (delete (mail-header-number header) |
| 9033 | gnus-newsgroup-unselected)))) |
| 9034 | (setq gnus-newsgroup-headers |
| 9035 | (gnus-delete-duplicate-headers |
| 9036 | (gnus-merge |
| 9037 | 'list gnus-newsgroup-headers new-headers |
| 9038 | 'gnus-article-sort-by-number))) |
| 9039 | (setq gnus-newsgroup-articles |
| 9040 | (gnus-sorted-nunion gnus-newsgroup-articles (nreverse article-ids))) |
| 9041 | (gnus-summary-limit-include-thread id))) |
| 9042 | (gnus-summary-show-thread)) |
| 9043 | |
| 9044 | (defun gnus-summary-refer-article (message-id) |
| 9045 | "Fetch an article specified by MESSAGE-ID." |
| 9046 | (interactive "sMessage-ID: ") |
| 9047 | (gnus-warp-to-article) |
| 9048 | (when (and (stringp message-id) |
| 9049 | (not (zerop (length message-id)))) |
| 9050 | (setq message-id (gnus-replace-in-string message-id " " "")) |
| 9051 | ;; Construct the correct Message-ID if necessary. |
| 9052 | ;; Suggested by tale@pawl.rpi.edu. |
| 9053 | (unless (string-match "^<" message-id) |
| 9054 | (setq message-id (concat "<" message-id))) |
| 9055 | (unless (string-match ">$" message-id) |
| 9056 | (setq message-id (concat message-id ">"))) |
| 9057 | ;; People often post MIDs from URLs, so unhex it: |
| 9058 | (unless (string-match "@" message-id) |
| 9059 | (setq message-id (gnus-url-unhex-string message-id))) |
| 9060 | (let* ((header (gnus-id-to-header message-id)) |
| 9061 | (sparse (and header |
| 9062 | (gnus-summary-article-sparse-p |
| 9063 | (mail-header-number header)) |
| 9064 | (memq (mail-header-number header) |
| 9065 | gnus-newsgroup-limit))) |
| 9066 | number) |
| 9067 | (cond |
| 9068 | ;; If the article is present in the buffer we just go to it. |
| 9069 | ((and header |
| 9070 | (or (not (gnus-summary-article-sparse-p |
| 9071 | (mail-header-number header))) |
| 9072 | sparse)) |
| 9073 | (prog1 |
| 9074 | (gnus-summary-goto-article |
| 9075 | (mail-header-number header) nil t) |
| 9076 | (when sparse |
| 9077 | (gnus-summary-update-article (mail-header-number header))))) |
| 9078 | (t |
| 9079 | ;; We fetch the article. |
| 9080 | (catch 'found |
| 9081 | (dolist (gnus-override-method (gnus-refer-article-methods)) |
| 9082 | (when (and (gnus-check-server gnus-override-method) |
| 9083 | ;; Fetch the header, |
| 9084 | (setq number (gnus-summary-insert-subject message-id))) |
| 9085 | ;; and display the article. |
| 9086 | (gnus-summary-select-article nil nil nil number) |
| 9087 | (throw 'found t))) |
| 9088 | (gnus-message 3 "Couldn't fetch article %s" message-id))))))) |
| 9089 | |
| 9090 | (defun gnus-refer-article-methods () |
| 9091 | "Return a list of referable methods." |
| 9092 | (cond |
| 9093 | ;; No method, so we default to current and native. |
| 9094 | ((null gnus-refer-article-method) |
| 9095 | (list gnus-current-select-method gnus-select-method)) |
| 9096 | ;; Current. |
| 9097 | ((eq 'current gnus-refer-article-method) |
| 9098 | (list gnus-current-select-method)) |
| 9099 | ;; List of select methods. |
| 9100 | ((not (and (symbolp (car gnus-refer-article-method)) |
| 9101 | (assq (car gnus-refer-article-method) nnoo-definition-alist))) |
| 9102 | (let (out) |
| 9103 | (dolist (method gnus-refer-article-method) |
| 9104 | (push (if (eq 'current method) |
| 9105 | gnus-current-select-method |
| 9106 | (if (eq 'nnir (car method)) |
| 9107 | (list |
| 9108 | 'nnir |
| 9109 | (or (cadr method) |
| 9110 | (gnus-method-to-server gnus-current-select-method))) |
| 9111 | method)) |
| 9112 | out)) |
| 9113 | (nreverse out))) |
| 9114 | ;; One single select method. |
| 9115 | (t |
| 9116 | (list gnus-refer-article-method)))) |
| 9117 | |
| 9118 | (defun gnus-summary-edit-parameters () |
| 9119 | "Edit the group parameters of the current group." |
| 9120 | (interactive) |
| 9121 | (gnus-group-edit-group gnus-newsgroup-name 'params)) |
| 9122 | |
| 9123 | (defun gnus-summary-customize-parameters () |
| 9124 | "Customize the group parameters of the current group." |
| 9125 | (interactive) |
| 9126 | (gnus-group-customize gnus-newsgroup-name)) |
| 9127 | |
| 9128 | (defun gnus-summary-enter-digest-group (&optional force) |
| 9129 | "Enter an nndoc group based on the current article. |
| 9130 | If FORCE, force a digest interpretation. If not, try to guess |
| 9131 | what the document format is. |
| 9132 | |
| 9133 | To control what happens when you exit the group, see the |
| 9134 | `gnus-auto-select-on-ephemeral-exit' variable." |
| 9135 | (interactive "P") |
| 9136 | (let ((conf gnus-current-window-configuration)) |
| 9137 | (save-window-excursion |
| 9138 | (save-excursion |
| 9139 | (let (gnus-article-prepare-hook |
| 9140 | gnus-display-mime-function |
| 9141 | gnus-break-pages) |
| 9142 | (gnus-summary-select-article)))) |
| 9143 | (setq gnus-current-window-configuration conf) |
| 9144 | (let* ((name (format "%s-%d" |
| 9145 | (gnus-group-prefixed-name |
| 9146 | gnus-newsgroup-name (list 'nndoc "")) |
| 9147 | (with-current-buffer gnus-summary-buffer |
| 9148 | gnus-current-article))) |
| 9149 | (ogroup gnus-newsgroup-name) |
| 9150 | (params (append (gnus-info-params (gnus-get-info ogroup)) |
| 9151 | (list (cons 'to-group ogroup)) |
| 9152 | (list (cons 'parent-group ogroup)) |
| 9153 | (list (cons 'save-article-group ogroup)))) |
| 9154 | (case-fold-search t) |
| 9155 | (buf (current-buffer)) |
| 9156 | dig to-address charset) |
| 9157 | (with-current-buffer gnus-original-article-buffer |
| 9158 | ;; Have the digest group inherit the main mail address of |
| 9159 | ;; the parent article. |
| 9160 | (when (setq to-address (or (gnus-fetch-field "reply-to") |
| 9161 | (gnus-fetch-field "from"))) |
| 9162 | (setq params |
| 9163 | (append |
| 9164 | (list (cons 'to-address |
| 9165 | (funcall gnus-decode-encoded-address-function |
| 9166 | to-address)))))) |
| 9167 | (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*")) |
| 9168 | (insert-buffer-substring gnus-original-article-buffer) |
| 9169 | (narrow-to-region |
| 9170 | (goto-char (point-min)) |
| 9171 | (or (search-forward "\n\n" nil t) (point))) |
| 9172 | ;; Remove lines that may lead nndoc to misinterpret the |
| 9173 | ;; document type. |
| 9174 | (goto-char (point-min)) |
| 9175 | (delete-matching-lines "^Path:\\|^From ") |
| 9176 | ;; Parse charset, and decode content transfer encoding. |
| 9177 | (setq charset (mail-content-type-get |
| 9178 | (mail-header-parse-content-type |
| 9179 | (or (gnus-fetch-field "content-type") "")) |
| 9180 | 'charset)) |
| 9181 | (let ((encoding (gnus-fetch-field "content-transfer-encoding"))) |
| 9182 | (when encoding |
| 9183 | (message-remove-header "content-transfer-encoding") |
| 9184 | (goto-char (point-max)) |
| 9185 | (widen) |
| 9186 | (narrow-to-region (point) (point-max)) |
| 9187 | (mm-decode-content-transfer-encoding |
| 9188 | (intern (downcase (mail-header-strip encoding)))))) |
| 9189 | (widen)) |
| 9190 | (unwind-protect |
| 9191 | (if (let ((gnus-newsgroup-ephemeral-charset |
| 9192 | (if charset |
| 9193 | (intern (downcase (gnus-strip-whitespace charset))) |
| 9194 | gnus-newsgroup-charset)) |
| 9195 | (gnus-newsgroup-ephemeral-ignored-charsets |
| 9196 | gnus-newsgroup-ignored-charsets)) |
| 9197 | (gnus-group-read-ephemeral-group |
| 9198 | name `(nndoc ,name (nndoc-address ,(get-buffer dig)) |
| 9199 | (nndoc-article-type |
| 9200 | ,(if force 'mbox 'guess))) |
| 9201 | t nil nil nil |
| 9202 | `((adapt-file . ,(gnus-score-file-name gnus-newsgroup-name |
| 9203 | "ADAPT"))))) |
| 9204 | ;; Make all postings to this group go to the parent group. |
| 9205 | (nconc (gnus-info-params (gnus-get-info name)) |
| 9206 | params) |
| 9207 | ;; Couldn't select this doc group. |
| 9208 | (switch-to-buffer buf) |
| 9209 | (gnus-set-global-variables) |
| 9210 | (gnus-configure-windows 'summary) |
| 9211 | (gnus-message 3 "Article couldn't be entered?")) |
| 9212 | (kill-buffer dig))))) |
| 9213 | |
| 9214 | (defun gnus-summary-read-document (n) |
| 9215 | "Open a new group based on the current article(s). |
| 9216 | This will allow you to read digests and other similar |
| 9217 | documents as newsgroups. |
| 9218 | Obeys the standard process/prefix convention." |
| 9219 | (interactive "P") |
| 9220 | (let* ((ogroup gnus-newsgroup-name) |
| 9221 | (params (append (gnus-info-params (gnus-get-info ogroup)) |
| 9222 | (list (cons 'to-group ogroup)))) |
| 9223 | group egroup groups vgroup) |
| 9224 | (dolist (article (gnus-summary-work-articles n)) |
| 9225 | (setq group (format "%s-%d" gnus-newsgroup-name article)) |
| 9226 | (gnus-summary-remove-process-mark article) |
| 9227 | (when (gnus-summary-display-article article) |
| 9228 | (save-excursion ;;What for? |
| 9229 | (with-temp-buffer |
| 9230 | (insert-buffer-substring gnus-original-article-buffer) |
| 9231 | ;; Remove some headers that may lead nndoc to make |
| 9232 | ;; the wrong guess. |
| 9233 | (message-narrow-to-head) |
| 9234 | (goto-char (point-min)) |
| 9235 | (delete-matching-lines "^Path:\\|^From ") |
| 9236 | (widen) |
| 9237 | (if (setq egroup |
| 9238 | (gnus-group-read-ephemeral-group |
| 9239 | group `(nndoc ,group (nndoc-address ,(current-buffer)) |
| 9240 | (nndoc-article-type guess)) |
| 9241 | t nil t)) |
| 9242 | (progn |
| 9243 | ;; Make all postings to this group go to the parent group. |
| 9244 | (nconc (gnus-info-params (gnus-get-info egroup)) |
| 9245 | params) |
| 9246 | (push egroup groups)) |
| 9247 | ;; Couldn't select this doc group. |
| 9248 | (gnus-error 3 "Article couldn't be entered")))))) |
| 9249 | ;; Now we have selected all the documents. |
| 9250 | (cond |
| 9251 | ((not groups) |
| 9252 | (error "None of the articles could be interpreted as documents")) |
| 9253 | ((gnus-group-read-ephemeral-group |
| 9254 | (setq vgroup (format |
| 9255 | "nnvirtual:%s-%s" gnus-newsgroup-name |
| 9256 | (format-time-string "%Y%m%dT%H%M%S" (current-time)))) |
| 9257 | `(nnvirtual ,vgroup (nnvirtual-component-groups ,groups)) |
| 9258 | t |
| 9259 | (cons (current-buffer) 'summary))) |
| 9260 | (t |
| 9261 | (error "Couldn't select virtual nndoc group"))))) |
| 9262 | |
| 9263 | (defun gnus-summary-widget-forward (arg) |
| 9264 | "Move point to the next field or button in the article. |
| 9265 | With optional ARG, move across that many fields." |
| 9266 | (interactive "p") |
| 9267 | (gnus-summary-select-article) |
| 9268 | (gnus-configure-windows 'article) |
| 9269 | (select-window (gnus-get-buffer-window gnus-article-buffer)) |
| 9270 | (widget-forward arg)) |
| 9271 | |
| 9272 | (defun gnus-summary-widget-backward (arg) |
| 9273 | "Move point to the previous field or button in the article. |
| 9274 | With optional ARG, move across that many fields." |
| 9275 | (interactive "p") |
| 9276 | (gnus-summary-select-article) |
| 9277 | (gnus-configure-windows 'article) |
| 9278 | (select-window (gnus-get-buffer-window gnus-article-buffer)) |
| 9279 | (unless (widget-at (point)) |
| 9280 | (goto-char (point-max))) |
| 9281 | (widget-backward arg)) |
| 9282 | |
| 9283 | (defun gnus-summary-isearch-article (&optional regexp-p) |
| 9284 | "Do incremental search forward on the current article. |
| 9285 | If REGEXP-P (the prefix) is non-nil, do regexp isearch." |
| 9286 | (interactive "P") |
| 9287 | (gnus-summary-select-article) |
| 9288 | (gnus-configure-windows 'article) |
| 9289 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9290 | (save-restriction |
| 9291 | (widen) |
| 9292 | (isearch-forward regexp-p)))) |
| 9293 | |
| 9294 | (defun gnus-summary-repeat-search-article-forward () |
| 9295 | "Repeat the previous search forwards." |
| 9296 | (interactive) |
| 9297 | (unless gnus-last-search-regexp |
| 9298 | (error "No previous search")) |
| 9299 | (gnus-summary-search-article-forward gnus-last-search-regexp)) |
| 9300 | |
| 9301 | (defun gnus-summary-repeat-search-article-backward () |
| 9302 | "Repeat the previous search backwards." |
| 9303 | (interactive) |
| 9304 | (unless gnus-last-search-regexp |
| 9305 | (error "No previous search")) |
| 9306 | (gnus-summary-search-article-forward gnus-last-search-regexp t)) |
| 9307 | |
| 9308 | (defun gnus-summary-search-article-forward (regexp &optional backward) |
| 9309 | "Search for an article containing REGEXP forward. |
| 9310 | If BACKWARD, search backward instead." |
| 9311 | (interactive |
| 9312 | (list (read-string |
| 9313 | (format "Search article %s (regexp%s): " |
| 9314 | (if current-prefix-arg "backward" "forward") |
| 9315 | (if gnus-last-search-regexp |
| 9316 | (concat ", default " gnus-last-search-regexp) |
| 9317 | ""))) |
| 9318 | current-prefix-arg)) |
| 9319 | (if (string-equal regexp "") |
| 9320 | (setq regexp (or gnus-last-search-regexp "")) |
| 9321 | (setq gnus-last-search-regexp regexp) |
| 9322 | (setq gnus-article-before-search gnus-current-article)) |
| 9323 | ;; Intentionally set gnus-last-article. |
| 9324 | (setq gnus-last-article gnus-article-before-search) |
| 9325 | (let ((gnus-last-article gnus-last-article)) |
| 9326 | (if (gnus-summary-search-article regexp backward) |
| 9327 | (gnus-summary-show-thread) |
| 9328 | (signal 'search-failed (list regexp))))) |
| 9329 | |
| 9330 | (defun gnus-summary-search-article-backward (regexp) |
| 9331 | "Search for an article containing REGEXP backward." |
| 9332 | (interactive |
| 9333 | (list (read-string |
| 9334 | (format "Search article backward (regexp%s): " |
| 9335 | (if gnus-last-search-regexp |
| 9336 | (concat ", default " gnus-last-search-regexp) |
| 9337 | ""))))) |
| 9338 | (gnus-summary-search-article-forward regexp 'backward)) |
| 9339 | |
| 9340 | (defun gnus-summary-search-article (regexp &optional backward) |
| 9341 | "Search for an article containing REGEXP. |
| 9342 | Optional argument BACKWARD means do search for backward. |
| 9343 | `gnus-select-article-hook' is not called during the search." |
| 9344 | ;; We have to require this here to make sure that the following |
| 9345 | ;; dynamic binding isn't shadowed by autoloading. |
| 9346 | (require 'gnus-async) |
| 9347 | (require 'gnus-art) |
| 9348 | (let ((gnus-select-article-hook nil) ;Disable hook. |
| 9349 | (gnus-article-prepare-hook nil) |
| 9350 | (gnus-mark-article-hook nil) ;Inhibit marking as read. |
| 9351 | (gnus-use-article-prefetch nil) |
| 9352 | (gnus-xmas-force-redisplay nil) ;Inhibit XEmacs redisplay. |
| 9353 | (gnus-use-trees nil) ;Inhibit updating tree buffer. |
| 9354 | (gnus-visual nil) |
| 9355 | (gnus-keep-backlog nil) |
| 9356 | (gnus-break-pages nil) |
| 9357 | (gnus-summary-display-arrow nil) |
| 9358 | (gnus-updated-mode-lines nil) |
| 9359 | (gnus-auto-center-summary nil) |
| 9360 | (sum (current-buffer)) |
| 9361 | (gnus-display-mime-function nil) |
| 9362 | (found nil) |
| 9363 | point) |
| 9364 | (gnus-save-hidden-threads |
| 9365 | (gnus-summary-select-article) |
| 9366 | (set-buffer gnus-article-buffer) |
| 9367 | (goto-char (window-point (get-buffer-window (current-buffer)))) |
| 9368 | (when backward |
| 9369 | (forward-line -1)) |
| 9370 | (while (not found) |
| 9371 | (gnus-message 7 "Searching article: %d..." (cdr gnus-article-current)) |
| 9372 | (if (if backward |
| 9373 | (re-search-backward regexp nil t) |
| 9374 | (re-search-forward regexp nil t)) |
| 9375 | ;; We found the regexp. |
| 9376 | (progn |
| 9377 | (setq found 'found) |
| 9378 | (beginning-of-line) |
| 9379 | (set-window-start |
| 9380 | (get-buffer-window (current-buffer)) |
| 9381 | (point)) |
| 9382 | (forward-line 1) |
| 9383 | (set-window-point |
| 9384 | (get-buffer-window (current-buffer)) |
| 9385 | (point)) |
| 9386 | (set-buffer sum) |
| 9387 | (setq point (point))) |
| 9388 | ;; We didn't find it, so we go to the next article. |
| 9389 | (set-buffer sum) |
| 9390 | (setq found 'not) |
| 9391 | (while (eq found 'not) |
| 9392 | (if (not (if backward (gnus-summary-find-prev) |
| 9393 | (gnus-summary-find-next))) |
| 9394 | ;; No more articles. |
| 9395 | (setq found t) |
| 9396 | ;; Select the next article and adjust point. |
| 9397 | (unless (gnus-summary-article-sparse-p |
| 9398 | (gnus-summary-article-number)) |
| 9399 | (setq found nil) |
| 9400 | (gnus-summary-select-article) |
| 9401 | (set-buffer gnus-article-buffer) |
| 9402 | (widen) |
| 9403 | (goto-char (if backward (point-max) (point-min)))))))) |
| 9404 | (gnus-message 7 "")) |
| 9405 | ;; Return whether we found the regexp. |
| 9406 | (when (eq found 'found) |
| 9407 | (goto-char point) |
| 9408 | (gnus-summary-show-thread) |
| 9409 | (gnus-summary-goto-subject gnus-current-article) |
| 9410 | (gnus-summary-position-point) |
| 9411 | t))) |
| 9412 | |
| 9413 | (defun gnus-find-matching-articles (header regexp) |
| 9414 | "Return a list of all articles that match REGEXP on HEADER. |
| 9415 | This search includes all articles in the current group that Gnus has |
| 9416 | fetched headers for, whether they are displayed or not." |
| 9417 | (let ((articles nil) |
| 9418 | ;; Can't eta-reduce because it's a macro. |
| 9419 | (func `(lambda (h) (,(intern (concat "mail-header-" header)) h))) |
| 9420 | (case-fold-search t)) |
| 9421 | (dolist (header gnus-newsgroup-headers) |
| 9422 | (when (string-match regexp (funcall func header)) |
| 9423 | (push (mail-header-number header) articles))) |
| 9424 | (nreverse articles))) |
| 9425 | |
| 9426 | (defun gnus-summary-find-matching (header regexp &optional backward unread |
| 9427 | not-case-fold not-matching) |
| 9428 | "Return a list of all articles that match REGEXP on HEADER. |
| 9429 | The search stars on the current article and goes forwards unless |
| 9430 | BACKWARD is non-nil. If BACKWARD is `all', do all articles. |
| 9431 | If UNREAD is non-nil, only unread articles will |
| 9432 | be taken into consideration. If NOT-CASE-FOLD, case won't be folded |
| 9433 | in the comparisons. If NOT-MATCHING, return a list of all articles that |
| 9434 | not match REGEXP on HEADER." |
| 9435 | (let ((case-fold-search (not not-case-fold)) |
| 9436 | articles d func) |
| 9437 | (if (consp header) |
| 9438 | (if (eq (car header) 'extra) |
| 9439 | (setq func |
| 9440 | `(lambda (h) |
| 9441 | (or (cdr (assq ',(cdr header) (mail-header-extra h))) |
| 9442 | ""))) |
| 9443 | (error "%s is an invalid header" header)) |
| 9444 | (unless (fboundp (intern (concat "mail-header-" header))) |
| 9445 | (error "%s is not a valid header" header)) |
| 9446 | (setq func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))) |
| 9447 | (dolist (d (if (eq backward 'all) |
| 9448 | gnus-newsgroup-data |
| 9449 | (gnus-data-find-list |
| 9450 | (gnus-summary-article-number) |
| 9451 | (gnus-data-list backward)))) |
| 9452 | (when (and (or (not unread) ; We want all articles... |
| 9453 | (gnus-data-unread-p d)) ; Or just unreads. |
| 9454 | (vectorp (gnus-data-header d)) ; It's not a pseudo. |
| 9455 | (if not-matching |
| 9456 | (not (string-match |
| 9457 | regexp |
| 9458 | (funcall func (gnus-data-header d)))) |
| 9459 | (string-match regexp |
| 9460 | (funcall func (gnus-data-header d))))) |
| 9461 | (push (gnus-data-number d) articles))) ; Success! |
| 9462 | (nreverse articles))) |
| 9463 | |
| 9464 | (defun gnus-summary-execute-command (header regexp command &optional backward) |
| 9465 | "Search forward for an article whose HEADER matches REGEXP and execute COMMAND. |
| 9466 | If HEADER is an empty string (or nil), the match is done on the entire |
| 9467 | article. If BACKWARD (the prefix) is non-nil, search backward instead." |
| 9468 | (interactive |
| 9469 | (list (let ((completion-ignore-case t)) |
| 9470 | (gnus-completing-read |
| 9471 | "Header name" |
| 9472 | (mapcar 'symbol-name |
| 9473 | (append |
| 9474 | '(Number Subject From Lines Date |
| 9475 | Message-ID Xref References Body) |
| 9476 | gnus-extra-headers)) |
| 9477 | 'require-match)) |
| 9478 | (read-string "Regexp: ") |
| 9479 | (read-key-sequence "Command: ") |
| 9480 | current-prefix-arg)) |
| 9481 | (when (equal header "Body") |
| 9482 | (setq header "")) |
| 9483 | ;; Hidden thread subtrees must be searched as well. |
| 9484 | (gnus-summary-show-all-threads) |
| 9485 | ;; We don't want to change current point nor window configuration. |
| 9486 | (save-excursion |
| 9487 | (save-window-excursion |
| 9488 | (let (gnus-visual |
| 9489 | gnus-treat-strip-trailing-blank-lines |
| 9490 | gnus-treat-strip-leading-blank-lines |
| 9491 | gnus-treat-strip-multiple-blank-lines |
| 9492 | gnus-treat-hide-boring-headers |
| 9493 | gnus-treat-fold-newsgroups |
| 9494 | gnus-article-prepare-hook) |
| 9495 | (gnus-message 6 "Executing %s..." (key-description command)) |
| 9496 | ;; We'd like to execute COMMAND interactively so as to give arguments. |
| 9497 | (gnus-execute header regexp |
| 9498 | `(call-interactively ',(key-binding command)) |
| 9499 | backward) |
| 9500 | (gnus-message 6 "Executing %s...done" (key-description command)))))) |
| 9501 | |
| 9502 | (defun gnus-summary-beginning-of-article () |
| 9503 | "Scroll the article back to the beginning." |
| 9504 | (interactive) |
| 9505 | (gnus-summary-select-article) |
| 9506 | (gnus-configure-windows 'article) |
| 9507 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9508 | (widen) |
| 9509 | (goto-char (point-min)) |
| 9510 | (when gnus-break-pages |
| 9511 | (gnus-narrow-to-page)))) |
| 9512 | |
| 9513 | (defun gnus-summary-end-of-article () |
| 9514 | "Scroll to the end of the article." |
| 9515 | (interactive) |
| 9516 | (gnus-summary-select-article) |
| 9517 | (gnus-configure-windows 'article) |
| 9518 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9519 | (widen) |
| 9520 | (goto-char (point-max)) |
| 9521 | (recenter -3) |
| 9522 | (when gnus-break-pages |
| 9523 | (gnus-narrow-to-page)))) |
| 9524 | |
| 9525 | (defun gnus-summary-print-truncate-and-quote (string &optional len) |
| 9526 | "Truncate to LEN and quote all \"(\"'s in STRING." |
| 9527 | (gnus-replace-in-string (if (and len (> (length string) len)) |
| 9528 | (substring string 0 len) |
| 9529 | string) |
| 9530 | "[()]" "\\\\\\&")) |
| 9531 | |
| 9532 | (defun gnus-summary-print-article (&optional filename n) |
| 9533 | "Generate and print a PostScript image of the process-marked (mail) articles. |
| 9534 | |
| 9535 | If used interactively, print the current article if none are |
| 9536 | process-marked. With prefix arg, prompt the user for the name of the |
| 9537 | file to save in. |
| 9538 | |
| 9539 | When used from Lisp, accept two optional args FILENAME and N. N means |
| 9540 | to print the next N articles. If N is negative, print the N previous |
| 9541 | articles. If N is nil and articles have been marked with the process |
| 9542 | mark, print these instead. |
| 9543 | |
| 9544 | If the optional first argument FILENAME is nil, send the image to the |
| 9545 | printer. If FILENAME is a string, save the PostScript image in a file with |
| 9546 | that name. If FILENAME is a number, prompt the user for the name of the file |
| 9547 | to save in." |
| 9548 | (interactive (list (ps-print-preprint current-prefix-arg))) |
| 9549 | (dolist (article (gnus-summary-work-articles n)) |
| 9550 | (gnus-summary-select-article nil nil 'pseudo article) |
| 9551 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9552 | (gnus-print-buffer)) |
| 9553 | (gnus-summary-remove-process-mark article)) |
| 9554 | (ps-despool filename)) |
| 9555 | |
| 9556 | (defun gnus-print-buffer () |
| 9557 | (let ((ps-left-header |
| 9558 | (list |
| 9559 | (concat "(" |
| 9560 | (gnus-summary-print-truncate-and-quote |
| 9561 | (mail-header-subject gnus-current-headers) |
| 9562 | 66) ")") |
| 9563 | (concat "(" |
| 9564 | (gnus-summary-print-truncate-and-quote |
| 9565 | (mail-header-from gnus-current-headers) |
| 9566 | 45) ")"))) |
| 9567 | (ps-right-header |
| 9568 | (list |
| 9569 | "/pagenumberstring load" |
| 9570 | (concat "(" |
| 9571 | (mail-header-date gnus-current-headers) ")")))) |
| 9572 | (gnus-run-hooks 'gnus-ps-print-hook) |
| 9573 | (save-excursion |
| 9574 | (if ps-print-color-p |
| 9575 | (ps-spool-buffer-with-faces) |
| 9576 | (ps-spool-buffer))))) |
| 9577 | |
| 9578 | (declare-function gnus-flush-original-article-buffer "gnus-art" ()) |
| 9579 | |
| 9580 | (defun gnus-summary-show-complete-article () |
| 9581 | "Show a complete version of the current article. |
| 9582 | This is only useful if you're looking at a partial version of the |
| 9583 | article currently." |
| 9584 | (interactive) |
| 9585 | (let ((gnus-keep-backlog nil) |
| 9586 | (gnus-use-cache nil) |
| 9587 | (gnus-agent nil) |
| 9588 | (variable (intern |
| 9589 | (format "%s-fetch-partial-articles" |
| 9590 | (car (gnus-find-method-for-group |
| 9591 | gnus-newsgroup-name))) |
| 9592 | obarray)) |
| 9593 | old-val) |
| 9594 | (unwind-protect |
| 9595 | (progn |
| 9596 | (setq old-val (symbol-value variable)) |
| 9597 | (set variable nil) |
| 9598 | (gnus-flush-original-article-buffer) |
| 9599 | (gnus-summary-show-article)) |
| 9600 | (set variable old-val)))) |
| 9601 | |
| 9602 | (defun gnus-summary-show-article (&optional arg) |
| 9603 | "Force redisplaying of the current article. |
| 9604 | If ARG (the prefix) is a number, show the article with the charset |
| 9605 | defined in `gnus-summary-show-article-charset-alist', or the charset |
| 9606 | input. |
| 9607 | If ARG (the prefix) is non-nil and not a number, show the article, |
| 9608 | but without running any of the article treatment functions |
| 9609 | article. Normally, the keystroke is `C-u g'. When using `C-u |
| 9610 | C-u g', show the raw article." |
| 9611 | (interactive "P") |
| 9612 | (cond |
| 9613 | ((numberp arg) |
| 9614 | (gnus-summary-show-article t) |
| 9615 | (let ((gnus-newsgroup-charset |
| 9616 | (or (cdr (assq arg gnus-summary-show-article-charset-alist)) |
| 9617 | (mm-read-coding-system |
| 9618 | "View as charset: " ;; actually it is coding system. |
| 9619 | (with-current-buffer gnus-article-buffer |
| 9620 | (mm-detect-coding-region (point) (point-max)))))) |
| 9621 | (gnus-newsgroup-ignored-charsets 'gnus-all)) |
| 9622 | (gnus-summary-select-article nil 'force) |
| 9623 | (let ((deps gnus-newsgroup-dependencies) |
| 9624 | head header lines) |
| 9625 | (with-current-buffer gnus-original-article-buffer |
| 9626 | (save-restriction |
| 9627 | (message-narrow-to-head) |
| 9628 | (setq head (buffer-string)) |
| 9629 | (goto-char (point-min)) |
| 9630 | (unless (re-search-forward "^lines:[ \t]\\([0-9]+\\)" nil t) |
| 9631 | (goto-char (point-max)) |
| 9632 | (widen) |
| 9633 | (setq lines (1- (count-lines (point) (point-max)))))) |
| 9634 | (with-temp-buffer |
| 9635 | (insert (format "211 %d Article retrieved.\n" |
| 9636 | (cdr gnus-article-current))) |
| 9637 | (insert head) |
| 9638 | (if lines (insert (format "Lines: %d\n" lines))) |
| 9639 | (insert ".\n") |
| 9640 | (let ((nntp-server-buffer (current-buffer))) |
| 9641 | (setq header (car (gnus-get-newsgroup-headers deps t)))))) |
| 9642 | (gnus-data-set-header |
| 9643 | (gnus-data-find (cdr gnus-article-current)) |
| 9644 | header) |
| 9645 | (gnus-summary-update-article-line |
| 9646 | (cdr gnus-article-current) header) |
| 9647 | (when (gnus-summary-goto-subject (cdr gnus-article-current) nil t) |
| 9648 | (gnus-summary-update-secondary-mark (cdr gnus-article-current)))))) |
| 9649 | ((not arg) |
| 9650 | ;; Select the article the normal way. |
| 9651 | (if (eq mm-text-html-renderer 'shr) |
| 9652 | (progn |
| 9653 | (require 'shr) |
| 9654 | (let ((shr-ignore-cache t)) |
| 9655 | (gnus-summary-select-article nil 'force))) |
| 9656 | (gnus-summary-select-article nil 'force))) |
| 9657 | ((equal arg '(16)) |
| 9658 | ;; C-u C-u g |
| 9659 | (let ((gnus-inhibit-article-treatments t)) |
| 9660 | (gnus-summary-select-article nil 'force))) |
| 9661 | (t |
| 9662 | ;; We have to require this here to make sure that the following |
| 9663 | ;; dynamic binding isn't shadowed by autoloading. |
| 9664 | (require 'gnus-async) |
| 9665 | (require 'gnus-art) |
| 9666 | ;; Bind the article treatment functions to nil. |
| 9667 | (let ((gnus-have-all-headers t) |
| 9668 | gnus-article-prepare-hook |
| 9669 | gnus-article-decode-hook |
| 9670 | gnus-display-mime-function |
| 9671 | gnus-break-pages) |
| 9672 | ;; Destroy any MIME parts. |
| 9673 | (when (gnus-buffer-live-p gnus-article-buffer) |
| 9674 | (with-current-buffer gnus-article-buffer |
| 9675 | (gnus-article-stop-animations) |
| 9676 | (gnus-stop-downloads) |
| 9677 | (mm-destroy-parts gnus-article-mime-handles) |
| 9678 | ;; Set it to nil for safety reason. |
| 9679 | (setq gnus-article-mime-handle-alist nil) |
| 9680 | (setq gnus-article-mime-handles nil))) |
| 9681 | (gnus-summary-select-article nil 'force)))) |
| 9682 | (gnus-summary-goto-subject gnus-current-article) |
| 9683 | (gnus-summary-position-point)) |
| 9684 | |
| 9685 | (defun gnus-summary-show-raw-article () |
| 9686 | "Show the raw article without any article massaging functions being run." |
| 9687 | (interactive) |
| 9688 | (gnus-summary-show-article t)) |
| 9689 | |
| 9690 | (defun gnus-summary-verbose-headers (&optional arg) |
| 9691 | "Toggle permanent full header display. |
| 9692 | If ARG is a positive number, turn header display on. |
| 9693 | If ARG is a negative number, turn header display off." |
| 9694 | (interactive "P") |
| 9695 | (setq gnus-show-all-headers |
| 9696 | (cond ((or (not (numberp arg)) |
| 9697 | (zerop arg)) |
| 9698 | (not gnus-show-all-headers)) |
| 9699 | ((natnump arg) |
| 9700 | t))) |
| 9701 | (gnus-summary-show-article)) |
| 9702 | |
| 9703 | (declare-function article-narrow-to-head "gnus-art" ()) |
| 9704 | (declare-function gnus-article-hidden-text-p "gnus-art" (type)) |
| 9705 | (declare-function gnus-delete-wash-type "gnus-art" (type)) |
| 9706 | |
| 9707 | (defun gnus-summary-toggle-header (&optional arg) |
| 9708 | "Show the headers if they are hidden, or hide them if they are shown. |
| 9709 | If ARG is a positive number, show the entire header. |
| 9710 | If ARG is a negative number, hide the unwanted header lines." |
| 9711 | (interactive "P") |
| 9712 | (let ((window (and (gnus-buffer-live-p gnus-article-buffer) |
| 9713 | (get-buffer-window gnus-article-buffer t)))) |
| 9714 | (with-current-buffer gnus-article-buffer |
| 9715 | (widen) |
| 9716 | (article-narrow-to-head) |
| 9717 | (let* ((inhibit-read-only t) |
| 9718 | (inhibit-point-motion-hooks t) |
| 9719 | (hidden (if (numberp arg) |
| 9720 | (>= arg 0) |
| 9721 | (or (not (looking-at "[^ \t\n]+:")) |
| 9722 | (gnus-article-hidden-text-p 'headers)))) |
| 9723 | s e) |
| 9724 | (delete-region (point-min) (point-max)) |
| 9725 | (with-current-buffer gnus-original-article-buffer |
| 9726 | (goto-char (setq s (point-min))) |
| 9727 | (setq e (if (search-forward "\n\n" nil t) |
| 9728 | (1- (point)) |
| 9729 | (point-max)))) |
| 9730 | (insert-buffer-substring gnus-original-article-buffer s e) |
| 9731 | (run-hooks 'gnus-article-decode-hook) |
| 9732 | (if hidden |
| 9733 | (let ((gnus-treat-hide-headers nil) |
| 9734 | (gnus-treat-hide-boring-headers nil)) |
| 9735 | (gnus-delete-wash-type 'headers) |
| 9736 | (gnus-treat-article 'head)) |
| 9737 | (gnus-treat-article 'head)) |
| 9738 | (widen) |
| 9739 | (if window |
| 9740 | (set-window-start window (goto-char (point-min)))) |
| 9741 | (if gnus-break-pages |
| 9742 | (gnus-narrow-to-page) |
| 9743 | (when (gnus-visual-p 'page-marker) |
| 9744 | (let ((inhibit-read-only t)) |
| 9745 | (gnus-remove-text-with-property 'gnus-prev) |
| 9746 | (gnus-remove-text-with-property 'gnus-next)))) |
| 9747 | (gnus-set-mode-line 'article))))) |
| 9748 | |
| 9749 | (defun gnus-summary-show-all-headers () |
| 9750 | "Make all header lines visible." |
| 9751 | (interactive) |
| 9752 | (gnus-summary-toggle-header 1)) |
| 9753 | |
| 9754 | (defun gnus-summary-caesar-message (&optional arg) |
| 9755 | "Caesar rotate the current article by 13. |
| 9756 | With a non-numerical prefix, also rotate headers. A numerical |
| 9757 | prefix specifies how many places to rotate each letter forward." |
| 9758 | (interactive "P") |
| 9759 | (gnus-summary-select-article) |
| 9760 | (let ((mail-header-separator "")) |
| 9761 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9762 | (save-restriction |
| 9763 | (widen) |
| 9764 | (let ((start (window-start)) |
| 9765 | (inhibit-read-only t)) |
| 9766 | (if (equal arg '(4)) |
| 9767 | (message-caesar-buffer-body nil t) |
| 9768 | (message-caesar-buffer-body arg)) |
| 9769 | (set-window-start (get-buffer-window (current-buffer)) start))))) |
| 9770 | ;; Create buttons and stuff... |
| 9771 | (gnus-treat-article nil)) |
| 9772 | |
| 9773 | (declare-function idna-to-unicode "ext:idna" (str)) |
| 9774 | |
| 9775 | (defun gnus-summary-idna-message (&optional arg) |
| 9776 | "Decode IDNA encoded domain names in the current articles. |
| 9777 | IDNA encoded domain names looks like `xn--bar'. If a string |
| 9778 | remain unencoded after running this function, it is likely an |
| 9779 | invalid IDNA string (`xn--bar' is invalid). |
| 9780 | |
| 9781 | You must have GNU Libidn (URL `http://www.gnu.org/software/libidn/') |
| 9782 | installed for this command to work." |
| 9783 | (interactive "P") |
| 9784 | (if (not (and (condition-case nil (require 'idna) |
| 9785 | (file-error)) |
| 9786 | (mm-coding-system-p 'utf-8) |
| 9787 | (executable-find (symbol-value 'idna-program)))) |
| 9788 | (gnus-message |
| 9789 | 5 "GNU Libidn not installed properly (`idn' or `idna.el' missing)") |
| 9790 | (gnus-summary-select-article) |
| 9791 | (let ((mail-header-separator "")) |
| 9792 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9793 | (save-restriction |
| 9794 | (widen) |
| 9795 | (let ((start (window-start)) |
| 9796 | buffer-read-only) |
| 9797 | (while (re-search-forward "\\(xn--[-0-9a-z]+\\)" nil t) |
| 9798 | (replace-match (idna-to-unicode (match-string 1)))) |
| 9799 | (set-window-start (get-buffer-window (current-buffer)) start))))))) |
| 9800 | |
| 9801 | (defun gnus-summary-morse-message (&optional arg) |
| 9802 | "Morse decode the current article." |
| 9803 | (interactive "P") |
| 9804 | (gnus-summary-select-article) |
| 9805 | (let ((mail-header-separator "")) |
| 9806 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9807 | (save-excursion |
| 9808 | (save-restriction |
| 9809 | (widen) |
| 9810 | (let ((pos (window-start)) |
| 9811 | (inhibit-read-only t)) |
| 9812 | (goto-char (point-min)) |
| 9813 | (when (message-goto-body) |
| 9814 | (gnus-narrow-to-body)) |
| 9815 | (goto-char (point-min)) |
| 9816 | (while (search-forward "·" (point-max) t) |
| 9817 | (replace-match ".")) |
| 9818 | (unmorse-region (point-min) (point-max)) |
| 9819 | (widen) |
| 9820 | (set-window-start (get-buffer-window (current-buffer)) pos))))))) |
| 9821 | |
| 9822 | (defun gnus-summary-stop-page-breaking () |
| 9823 | "Stop page breaking in the current article." |
| 9824 | (interactive) |
| 9825 | (gnus-summary-select-article) |
| 9826 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 9827 | (widen) |
| 9828 | (when (gnus-visual-p 'page-marker) |
| 9829 | (let ((inhibit-read-only t)) |
| 9830 | (gnus-remove-text-with-property 'gnus-prev) |
| 9831 | (gnus-remove-text-with-property 'gnus-next)) |
| 9832 | (setq gnus-page-broken nil)))) |
| 9833 | |
| 9834 | (defun gnus-summary-move-article (&optional n to-newsgroup |
| 9835 | select-method action) |
| 9836 | "Move the current article to a different newsgroup. |
| 9837 | If N is a positive number, move the N next articles. |
| 9838 | If N is a negative number, move the N previous articles. |
| 9839 | If N is nil and any articles have been marked with the process mark, |
| 9840 | move those articles instead. |
| 9841 | If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to. |
| 9842 | If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but |
| 9843 | re-spool using this method. |
| 9844 | |
| 9845 | When called interactively with TO-NEWSGROUP being nil, the value of |
| 9846 | the variable `gnus-move-split-methods' is used for finding a default |
| 9847 | for the target newsgroup. |
| 9848 | |
| 9849 | For this function to work, both the current newsgroup and the |
| 9850 | newsgroup that you want to move to have to support the `request-move' |
| 9851 | and `request-accept' functions. |
| 9852 | |
| 9853 | ACTION can be either `move' (the default), `crosspost' or `copy'." |
| 9854 | (interactive "P") |
| 9855 | (unless action |
| 9856 | (setq action 'move)) |
| 9857 | ;; Check whether the source group supports the required functions. |
| 9858 | (cond ((and (eq action 'move) |
| 9859 | (not (gnus-check-backend-function |
| 9860 | 'request-move-article gnus-newsgroup-name))) |
| 9861 | (error "The current group does not support article moving")) |
| 9862 | ((and (eq action 'crosspost) |
| 9863 | (not (gnus-check-backend-function |
| 9864 | 'request-replace-article gnus-newsgroup-name))) |
| 9865 | (error "The current group does not support article editing"))) |
| 9866 | (let ((articles (gnus-summary-work-articles n)) |
| 9867 | (prefix (if (gnus-check-backend-function |
| 9868 | 'request-move-article gnus-newsgroup-name) |
| 9869 | (funcall gnus-move-group-prefix-function |
| 9870 | gnus-newsgroup-name) |
| 9871 | "")) |
| 9872 | (names '((move "Move" "Moving") |
| 9873 | (copy "Copy" "Copying") |
| 9874 | (crosspost "Crosspost" "Crossposting"))) |
| 9875 | (copy-buf (save-excursion |
| 9876 | (nnheader-set-temp-buffer " *copy article*"))) |
| 9877 | art-group to-method new-xref article to-groups |
| 9878 | articles-to-update-marks encoded) |
| 9879 | (unless (assq action names) |
| 9880 | (error "Unknown action %s" action)) |
| 9881 | ;; Read the newsgroup name. |
| 9882 | (when (and (not to-newsgroup) |
| 9883 | (not select-method)) |
| 9884 | (if (and gnus-move-split-methods |
| 9885 | (not |
| 9886 | (and (memq gnus-current-article articles) |
| 9887 | (gnus-buffer-live-p gnus-original-article-buffer)))) |
| 9888 | ;; When `gnus-move-split-methods' is non-nil, we have to |
| 9889 | ;; select an article to give `gnus-read-move-group-name' an |
| 9890 | ;; opportunity to suggest an appropriate default. However, |
| 9891 | ;; we needn't render or mark the article. |
| 9892 | (let ((gnus-display-mime-function nil) |
| 9893 | (gnus-article-prepare-hook nil) |
| 9894 | (gnus-mark-article-hook nil)) |
| 9895 | (gnus-summary-select-article nil nil nil (car articles)))) |
| 9896 | (setq to-newsgroup (gnus-read-move-group-name |
| 9897 | (cadr (assq action names)) |
| 9898 | (symbol-value |
| 9899 | (intern (format "gnus-current-%s-group" action))) |
| 9900 | articles prefix) |
| 9901 | encoded to-newsgroup |
| 9902 | to-method (gnus-server-to-method (gnus-group-method to-newsgroup))) |
| 9903 | (set (intern (format "gnus-current-%s-group" action)) |
| 9904 | (mm-decode-coding-string |
| 9905 | to-newsgroup |
| 9906 | (gnus-group-name-charset to-method to-newsgroup)))) |
| 9907 | (unless to-method |
| 9908 | (setq to-method (or select-method |
| 9909 | (gnus-server-to-method |
| 9910 | (gnus-group-method to-newsgroup))))) |
| 9911 | (setq to-newsgroup |
| 9912 | (or encoded |
| 9913 | (and to-newsgroup |
| 9914 | (mm-encode-coding-string |
| 9915 | to-newsgroup |
| 9916 | (gnus-group-name-charset to-method to-newsgroup))))) |
| 9917 | ;; Check the method we are to move this article to... |
| 9918 | (unless (gnus-check-backend-function |
| 9919 | 'request-accept-article (car to-method)) |
| 9920 | (error "%s does not support article copying" (car to-method))) |
| 9921 | (unless (gnus-check-server to-method) |
| 9922 | (error "Can't open server %s" (car to-method))) |
| 9923 | (gnus-message 6 "%s to %s: %s..." |
| 9924 | (caddr (assq action names)) |
| 9925 | (or (car select-method) |
| 9926 | (gnus-group-decoded-name to-newsgroup)) |
| 9927 | articles) |
| 9928 | (while articles |
| 9929 | (setq article (pop articles)) |
| 9930 | ;; Set any marks that may have changed in the summary buffer. |
| 9931 | (when gnus-preserve-marks |
| 9932 | (gnus-summary-push-marks-to-backend article)) |
| 9933 | (setq |
| 9934 | art-group |
| 9935 | (cond |
| 9936 | ;; Move the article. |
| 9937 | ((eq action 'move) |
| 9938 | ;; Remove this article from future suppression. |
| 9939 | (gnus-dup-unsuppress-article article) |
| 9940 | (let* ((from-method (gnus-find-method-for-group |
| 9941 | gnus-newsgroup-name)) |
| 9942 | (to-method (or select-method |
| 9943 | (gnus-find-method-for-group to-newsgroup))) |
| 9944 | (move-is-internal (gnus-server-equal from-method to-method))) |
| 9945 | (gnus-request-move-article |
| 9946 | article ; Article to move |
| 9947 | gnus-newsgroup-name ; From newsgroup |
| 9948 | (nth 1 (gnus-find-method-for-group |
| 9949 | gnus-newsgroup-name)) ; Server |
| 9950 | (list 'gnus-request-accept-article |
| 9951 | to-newsgroup (list 'quote select-method) |
| 9952 | (not articles) t) ; Accept form |
| 9953 | (not articles) ; Only save nov last time |
| 9954 | (and move-is-internal |
| 9955 | to-newsgroup ; Not respooling |
| 9956 | ; Is this move internal? |
| 9957 | (gnus-group-real-name to-newsgroup))))) |
| 9958 | ;; Copy the article. |
| 9959 | ((eq action 'copy) |
| 9960 | (with-current-buffer copy-buf |
| 9961 | (when (gnus-request-article-this-buffer article |
| 9962 | gnus-newsgroup-name) |
| 9963 | (save-restriction |
| 9964 | (nnheader-narrow-to-headers) |
| 9965 | (dolist (hdr gnus-copy-article-ignored-headers) |
| 9966 | (message-remove-header hdr t))) |
| 9967 | (gnus-request-accept-article |
| 9968 | to-newsgroup select-method (not articles) t)))) |
| 9969 | ;; Crosspost the article. |
| 9970 | ((eq action 'crosspost) |
| 9971 | (let ((xref (message-tokenize-header |
| 9972 | (mail-header-xref (gnus-summary-article-header |
| 9973 | article)) |
| 9974 | " "))) |
| 9975 | (setq new-xref (concat (gnus-group-real-name gnus-newsgroup-name) |
| 9976 | ":" (number-to-string article))) |
| 9977 | (unless xref |
| 9978 | (setq xref (list (system-name)))) |
| 9979 | (setq new-xref |
| 9980 | (concat |
| 9981 | (mapconcat 'identity |
| 9982 | (delete "Xref:" (delete new-xref xref)) |
| 9983 | " ") |
| 9984 | " " new-xref)) |
| 9985 | (with-current-buffer copy-buf |
| 9986 | ;; First put the article in the destination group. |
| 9987 | (gnus-request-article-this-buffer article gnus-newsgroup-name) |
| 9988 | (when (consp (setq art-group |
| 9989 | (gnus-request-accept-article |
| 9990 | to-newsgroup select-method (not articles) |
| 9991 | t))) |
| 9992 | (setq new-xref (concat new-xref " " (car art-group) |
| 9993 | ":" |
| 9994 | (number-to-string (cdr art-group)))) |
| 9995 | ;; Now we have the new Xrefs header, so we insert |
| 9996 | ;; it and replace the new article. |
| 9997 | (nnheader-replace-header "Xref" new-xref) |
| 9998 | (gnus-request-replace-article |
| 9999 | (cdr art-group) to-newsgroup (current-buffer) t) |
| 10000 | art-group)))))) |
| 10001 | (cond |
| 10002 | ((not art-group) |
| 10003 | (gnus-message 1 "Couldn't %s article %s: %s" |
| 10004 | (cadr (assq action names)) article |
| 10005 | (nnheader-get-report (car to-method)))) |
| 10006 | ((eq art-group 'junk) |
| 10007 | (when (eq action 'move) |
| 10008 | (gnus-summary-mark-article article gnus-canceled-mark) |
| 10009 | (gnus-message 4 "Deleted article %s" article) |
| 10010 | ;; run the delete hook |
| 10011 | (run-hook-with-args 'gnus-summary-article-delete-hook |
| 10012 | action |
| 10013 | (gnus-data-header |
| 10014 | (assoc article (gnus-data-list nil))) |
| 10015 | gnus-newsgroup-name nil |
| 10016 | select-method))) |
| 10017 | (t |
| 10018 | (let* ((pto-group (gnus-group-prefixed-name |
| 10019 | (car art-group) to-method)) |
| 10020 | (info (gnus-get-info pto-group)) |
| 10021 | (to-group (gnus-info-group info)) |
| 10022 | to-marks) |
| 10023 | ;; Update the group that has been moved to. |
| 10024 | (when (and info |
| 10025 | (memq action '(move copy))) |
| 10026 | (unless (member to-group to-groups) |
| 10027 | (push to-group to-groups)) |
| 10028 | |
| 10029 | (when (and (not (memq article gnus-newsgroup-unreads)) |
| 10030 | (cdr art-group)) |
| 10031 | (push 'read to-marks) |
| 10032 | (gnus-info-set-read |
| 10033 | info (gnus-add-to-range (gnus-info-read info) |
| 10034 | (list (cdr art-group))))) |
| 10035 | |
| 10036 | ;; See whether the article is to be put in the cache. |
| 10037 | (let* ((expirable (gnus-group-auto-expirable-p to-group)) |
| 10038 | (marks (if expirable |
| 10039 | gnus-article-mark-lists |
| 10040 | (delete '(expirable . expire) |
| 10041 | (copy-sequence |
| 10042 | gnus-article-mark-lists)))) |
| 10043 | (to-article (cdr art-group))) |
| 10044 | |
| 10045 | ;; Enter the article into the cache in the new group, |
| 10046 | ;; if that is required. |
| 10047 | (when (and to-article |
| 10048 | gnus-use-cache) |
| 10049 | (gnus-cache-possibly-enter-article |
| 10050 | to-group to-article |
| 10051 | (memq article gnus-newsgroup-marked) |
| 10052 | (memq article gnus-newsgroup-dormant) |
| 10053 | (memq article gnus-newsgroup-unreads))) |
| 10054 | |
| 10055 | (when (and gnus-preserve-marks |
| 10056 | to-article) |
| 10057 | ;; Copy any marks over to the new group. |
| 10058 | (when (and (equal to-group gnus-newsgroup-name) |
| 10059 | (not (memq article gnus-newsgroup-unreads))) |
| 10060 | ;; Mark this article as read in this group. |
| 10061 | (push (cons to-article gnus-read-mark) |
| 10062 | gnus-newsgroup-reads) |
| 10063 | ;; Increase the active status of this group. |
| 10064 | (setcdr (gnus-active to-group) to-article) |
| 10065 | (setcdr gnus-newsgroup-active to-article)) |
| 10066 | |
| 10067 | (while marks |
| 10068 | (when (eq (gnus-article-mark-to-type (cdar marks)) 'list) |
| 10069 | (when (memq article (symbol-value |
| 10070 | (intern (format "gnus-newsgroup-%s" |
| 10071 | (caar marks))))) |
| 10072 | (push (cdar marks) to-marks) |
| 10073 | ;; If the other group is the same as this group, |
| 10074 | ;; then we have to add the mark to the list. |
| 10075 | (when (equal to-group gnus-newsgroup-name) |
| 10076 | (set (intern (format "gnus-newsgroup-%s" |
| 10077 | (caar marks))) |
| 10078 | (cons to-article |
| 10079 | (symbol-value |
| 10080 | (intern (format "gnus-newsgroup-%s" |
| 10081 | (caar marks))))))) |
| 10082 | ;; Copy the marks to other group. |
| 10083 | (gnus-add-marked-articles |
| 10084 | to-group (cdar marks) (list to-article) info))) |
| 10085 | (setq marks (cdr marks))) |
| 10086 | |
| 10087 | (when (and expirable |
| 10088 | gnus-mark-copied-or-moved-articles-as-expirable |
| 10089 | (not (memq 'expire to-marks))) |
| 10090 | ;; Mark this article as expirable. |
| 10091 | (push 'expire to-marks) |
| 10092 | (when (equal to-group gnus-newsgroup-name) |
| 10093 | (push to-article gnus-newsgroup-expirable)) |
| 10094 | ;; Copy the expirable mark to other group. |
| 10095 | (gnus-add-marked-articles |
| 10096 | to-group 'expire (list to-article) info)) |
| 10097 | |
| 10098 | (when (and to-marks |
| 10099 | (gnus-method-option-p |
| 10100 | (gnus-find-method-for-group to-group) |
| 10101 | 'server-marks)) |
| 10102 | (gnus-request-set-mark |
| 10103 | to-group (list (list (list to-article) 'add to-marks))))) |
| 10104 | |
| 10105 | (gnus-dribble-enter |
| 10106 | (concat "(gnus-group-set-info '" |
| 10107 | (gnus-prin1-to-string (gnus-get-info to-group)) |
| 10108 | ")") |
| 10109 | (concat "^(gnus-group-set-info '(\"" |
| 10110 | (regexp-quote to-group) "\"")))) |
| 10111 | |
| 10112 | ;; Update the Xref header in this article to point to |
| 10113 | ;; the new crossposted article we have just created. |
| 10114 | (when (eq action 'crosspost) |
| 10115 | (with-current-buffer copy-buf |
| 10116 | (gnus-request-article-this-buffer article gnus-newsgroup-name) |
| 10117 | (nnheader-replace-header "Xref" new-xref) |
| 10118 | (gnus-request-replace-article |
| 10119 | article gnus-newsgroup-name (current-buffer) t))) |
| 10120 | |
| 10121 | ;; run the move/copy/crosspost/respool hook |
| 10122 | (run-hook-with-args 'gnus-summary-article-move-hook |
| 10123 | action |
| 10124 | (gnus-data-header |
| 10125 | (assoc article (gnus-data-list nil))) |
| 10126 | gnus-newsgroup-name |
| 10127 | to-newsgroup |
| 10128 | select-method)) |
| 10129 | |
| 10130 | ;;;!!!Why is this necessary? |
| 10131 | (set-buffer gnus-summary-buffer) |
| 10132 | |
| 10133 | (when (eq action 'move) |
| 10134 | (save-excursion |
| 10135 | (gnus-summary-goto-subject article) |
| 10136 | (gnus-summary-mark-article article gnus-canceled-mark))))) |
| 10137 | (push article articles-to-update-marks)) |
| 10138 | |
| 10139 | (save-excursion |
| 10140 | (apply 'gnus-summary-remove-process-mark articles-to-update-marks)) |
| 10141 | ;; Re-activate all groups that have been moved to. |
| 10142 | (with-current-buffer gnus-group-buffer |
| 10143 | (let ((gnus-group-marked to-groups)) |
| 10144 | (gnus-group-get-new-news-this-group nil t))) |
| 10145 | |
| 10146 | (gnus-kill-buffer copy-buf) |
| 10147 | (gnus-summary-position-point) |
| 10148 | (gnus-set-mode-line 'summary))) |
| 10149 | |
| 10150 | (defun gnus-summary-push-marks-to-backend (article) |
| 10151 | (let ((set nil) |
| 10152 | (del nil) |
| 10153 | (marks gnus-article-mark-lists)) |
| 10154 | (unless (memq article gnus-newsgroup-unreads) |
| 10155 | (push 'read set)) |
| 10156 | (while marks |
| 10157 | (if (and (eq (gnus-article-mark-to-type (cdar marks)) 'list) |
| 10158 | (memq article (symbol-value |
| 10159 | (intern (format "gnus-newsgroup-%s" |
| 10160 | (caar marks)))))) |
| 10161 | (push (cdar marks) set) |
| 10162 | (push (cdar marks) del)) |
| 10163 | (pop marks)) |
| 10164 | (gnus-request-set-mark gnus-newsgroup-name `(((,article) set ,set) |
| 10165 | ((,article) del ,del))))) |
| 10166 | |
| 10167 | (defun gnus-summary-copy-article (&optional n to-newsgroup select-method) |
| 10168 | "Copy the current article to some other group. |
| 10169 | If TO-NEWSGROUP is string, do not prompt for a newsgroup to copy to. |
| 10170 | When called interactively, if TO-NEWSGROUP is nil, use the value of |
| 10171 | the variable `gnus-move-split-methods' for finding a default target |
| 10172 | newsgroup. |
| 10173 | If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but |
| 10174 | re-spool using this method." |
| 10175 | (interactive "P") |
| 10176 | (gnus-summary-move-article n to-newsgroup select-method 'copy)) |
| 10177 | |
| 10178 | (defun gnus-summary-crosspost-article (&optional n) |
| 10179 | "Crosspost the current article to some other group." |
| 10180 | (interactive "P") |
| 10181 | (gnus-summary-move-article n nil nil 'crosspost)) |
| 10182 | |
| 10183 | (defcustom gnus-summary-respool-default-method nil |
| 10184 | "Default method type for respooling an article. |
| 10185 | If nil, use to the current newsgroup method." |
| 10186 | :type 'symbol |
| 10187 | :group 'gnus-summary-mail) |
| 10188 | |
| 10189 | (defun gnus-summary-respool-article (&optional n method) |
| 10190 | "Respool the current article. |
| 10191 | The article will be squeezed through the mail spooling process again, |
| 10192 | which means that it will be put in some mail newsgroup or other |
| 10193 | depending on `nnmail-split-methods'. |
| 10194 | If N is a positive number, respool the N next articles. |
| 10195 | If N is a negative number, respool the N previous articles. |
| 10196 | If N is nil and any articles have been marked with the process mark, |
| 10197 | respool those articles instead. |
| 10198 | |
| 10199 | Respooling can be done both from mail groups and \"real\" newsgroups. |
| 10200 | In the former case, the articles in question will be moved from the |
| 10201 | current group into whatever groups they are destined to. In the |
| 10202 | latter case, they will be copied into the relevant groups." |
| 10203 | (interactive |
| 10204 | (list current-prefix-arg |
| 10205 | (let* ((methods (mapcar #'car (gnus-methods-using 'respool))) |
| 10206 | (methname |
| 10207 | (symbol-name (or gnus-summary-respool-default-method |
| 10208 | (car (gnus-find-method-for-group |
| 10209 | gnus-newsgroup-name))))) |
| 10210 | (method |
| 10211 | (gnus-completing-read |
| 10212 | "Backend to use when respooling" |
| 10213 | methods t nil 'gnus-mail-method-history methname)) |
| 10214 | ms) |
| 10215 | (cond |
| 10216 | ((zerop (length (setq ms (gnus-servers-using-backend |
| 10217 | (intern method))))) |
| 10218 | (list (intern method) "")) |
| 10219 | ((= 1 (length ms)) |
| 10220 | (car ms)) |
| 10221 | (t |
| 10222 | (let ((ms-alist (mapcar (lambda (m) (cons (cadr m) m)) ms))) |
| 10223 | (cdr (assoc (gnus-completing-read "Server name" ms-alist t) |
| 10224 | ms-alist)))))))) |
| 10225 | (unless method |
| 10226 | (error "No method given for respooling")) |
| 10227 | (if (assoc (symbol-name |
| 10228 | (car (gnus-find-method-for-group gnus-newsgroup-name))) |
| 10229 | (gnus-methods-using 'respool)) |
| 10230 | (gnus-summary-move-article n nil method) |
| 10231 | (gnus-summary-copy-article n nil method))) |
| 10232 | |
| 10233 | (defun gnus-summary-import-article (file &optional edit) |
| 10234 | "Import an arbitrary file into a mail newsgroup." |
| 10235 | (interactive "fImport file: \nP") |
| 10236 | (let ((group gnus-newsgroup-name) |
| 10237 | (now (current-time)) |
| 10238 | atts lines group-art) |
| 10239 | (unless (gnus-check-backend-function 'request-accept-article group) |
| 10240 | (error "%s does not support article importing" group)) |
| 10241 | (or (file-readable-p file) |
| 10242 | (not (file-regular-p file)) |
| 10243 | (error "Can't read %s" file)) |
| 10244 | (with-current-buffer (gnus-get-buffer-create " *import file*") |
| 10245 | (erase-buffer) |
| 10246 | (nnheader-insert-file-contents file) |
| 10247 | (goto-char (point-min)) |
| 10248 | (if (nnheader-article-p) |
| 10249 | (save-restriction |
| 10250 | (goto-char (point-min)) |
| 10251 | (search-forward "\n\n" nil t) |
| 10252 | (narrow-to-region (point-min) (1- (point))) |
| 10253 | (goto-char (point-min)) |
| 10254 | (unless (re-search-forward "^date:" nil t) |
| 10255 | (goto-char (point-max)) |
| 10256 | (insert "Date: " (message-make-date (nth 5 atts)) "\n"))) |
| 10257 | ;; This doesn't look like an article, so we fudge some headers. |
| 10258 | (setq atts (file-attributes file) |
| 10259 | lines (count-lines (point-min) (point-max))) |
| 10260 | (insert "From: " (read-string "From: ") "\n" |
| 10261 | "Subject: " (read-string "Subject: ") "\n" |
| 10262 | "Date: " (message-make-date (nth 5 atts)) "\n" |
| 10263 | "Message-ID: " (message-make-message-id) "\n" |
| 10264 | "Lines: " (int-to-string lines) "\n" |
| 10265 | "Chars: " (int-to-string (nth 7 atts)) "\n\n")) |
| 10266 | (setq group-art (gnus-request-accept-article group nil t)) |
| 10267 | (kill-buffer (current-buffer))) |
| 10268 | (setq gnus-newsgroup-active (gnus-activate-group group)) |
| 10269 | (forward-line 1) |
| 10270 | (gnus-summary-goto-article (cdr group-art) nil t) |
| 10271 | (when edit |
| 10272 | (gnus-summary-edit-article)))) |
| 10273 | |
| 10274 | (defun gnus-summary-create-article () |
| 10275 | "Create an article in a mail newsgroup." |
| 10276 | (interactive) |
| 10277 | (let ((group gnus-newsgroup-name) |
| 10278 | (now (current-time)) |
| 10279 | group-art) |
| 10280 | (unless (gnus-check-backend-function 'request-accept-article group) |
| 10281 | (error "%s does not support article importing" group)) |
| 10282 | (with-current-buffer (gnus-get-buffer-create " *import file*") |
| 10283 | (erase-buffer) |
| 10284 | (goto-char (point-min)) |
| 10285 | ;; This doesn't look like an article, so we fudge some headers. |
| 10286 | (insert "From: " (read-string "From: ") "\n" |
| 10287 | "Subject: " (read-string "Subject: ") "\n" |
| 10288 | "Date: " (message-make-date now) "\n" |
| 10289 | "Message-ID: " (message-make-message-id) "\n") |
| 10290 | (setq group-art (gnus-request-accept-article group nil t)) |
| 10291 | (kill-buffer (current-buffer))) |
| 10292 | (setq gnus-newsgroup-active (gnus-activate-group group)) |
| 10293 | (forward-line 1) |
| 10294 | (gnus-summary-goto-article (cdr group-art) nil t) |
| 10295 | (gnus-summary-edit-article))) |
| 10296 | |
| 10297 | (defun gnus-summary-article-posted-p () |
| 10298 | "Say whether the current (mail) article is available from news as well. |
| 10299 | This will be the case if the article has both been mailed and posted." |
| 10300 | (interactive) |
| 10301 | (let ((id (mail-header-references (gnus-summary-article-header))) |
| 10302 | (gnus-override-method (car (gnus-refer-article-methods)))) |
| 10303 | (if (gnus-request-head id "") |
| 10304 | (gnus-message 2 "The current message was found on %s" |
| 10305 | gnus-override-method) |
| 10306 | (gnus-message 2 "The current message couldn't be found on %s" |
| 10307 | gnus-override-method) |
| 10308 | nil))) |
| 10309 | |
| 10310 | (defun gnus-summary-expire-articles (&optional now) |
| 10311 | "Expire all articles that are marked as expirable in the current group." |
| 10312 | (interactive) |
| 10313 | (when (and (not gnus-group-is-exiting-without-update-p) |
| 10314 | (gnus-check-backend-function |
| 10315 | 'request-expire-articles gnus-newsgroup-name)) |
| 10316 | ;; This backend supports expiry. |
| 10317 | (let* ((total (gnus-group-total-expirable-p gnus-newsgroup-name)) |
| 10318 | (expirable |
| 10319 | (gnus-list-range-difference |
| 10320 | (if total |
| 10321 | (progn |
| 10322 | ;; We need to update the info for |
| 10323 | ;; this group for `gnus-list-of-read-articles' |
| 10324 | ;; to give us the right answer. |
| 10325 | (gnus-run-hooks 'gnus-exit-group-hook) |
| 10326 | (gnus-summary-update-info) |
| 10327 | (gnus-list-of-read-articles gnus-newsgroup-name)) |
| 10328 | (setq gnus-newsgroup-expirable |
| 10329 | (sort gnus-newsgroup-expirable '<))) |
| 10330 | gnus-newsgroup-unexist)) |
| 10331 | (expiry-wait (if now 'immediate |
| 10332 | (gnus-group-find-parameter |
| 10333 | gnus-newsgroup-name 'expiry-wait))) |
| 10334 | (nnmail-expiry-target |
| 10335 | (or (gnus-group-find-parameter gnus-newsgroup-name 'expiry-target) |
| 10336 | nnmail-expiry-target)) |
| 10337 | es) |
| 10338 | (when expirable |
| 10339 | ;; There are expirable articles in this group, so we run them |
| 10340 | ;; through the expiry process. |
| 10341 | (gnus-message 6 "Expiring articles...") |
| 10342 | (when (gnus-check-group gnus-newsgroup-name) |
| 10343 | ;; The list of articles that weren't expired is returned. |
| 10344 | (save-excursion |
| 10345 | (if expiry-wait |
| 10346 | (let ((nnmail-expiry-wait-function nil) |
| 10347 | (nnmail-expiry-wait expiry-wait)) |
| 10348 | (setq es (gnus-request-expire-articles |
| 10349 | expirable gnus-newsgroup-name))) |
| 10350 | (setq es (gnus-request-expire-articles |
| 10351 | expirable gnus-newsgroup-name))) |
| 10352 | (unless total |
| 10353 | (setq gnus-newsgroup-expirable es)) |
| 10354 | ;; We go through the old list of expirable, and mark all |
| 10355 | ;; really expired articles as nonexistent. |
| 10356 | (unless (eq es expirable) ;If nothing was expired, we don't mark. |
| 10357 | (let ((gnus-use-cache nil)) |
| 10358 | (dolist (article expirable) |
| 10359 | (when (and (not (memq article es)) |
| 10360 | (gnus-data-find article)) |
| 10361 | (gnus-summary-mark-article article gnus-canceled-mark) |
| 10362 | (run-hook-with-args 'gnus-summary-article-expire-hook |
| 10363 | 'delete |
| 10364 | (gnus-data-header |
| 10365 | (assoc article (gnus-data-list nil))) |
| 10366 | gnus-newsgroup-name |
| 10367 | nil |
| 10368 | nil))))))) |
| 10369 | (gnus-message 6 "Expiring articles...done"))))) |
| 10370 | |
| 10371 | (defun gnus-summary-expire-articles-now () |
| 10372 | "Expunge all expirable articles in the current group. |
| 10373 | This means that *all* articles that are marked as expirable will be |
| 10374 | deleted forever, right now." |
| 10375 | (interactive) |
| 10376 | (or gnus-expert-user |
| 10377 | (gnus-yes-or-no-p |
| 10378 | "Are you really, really sure you want to delete all expirable messages? ") |
| 10379 | (error "Phew!")) |
| 10380 | (gnus-summary-expire-articles t)) |
| 10381 | |
| 10382 | ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>. |
| 10383 | (defun gnus-summary-delete-article (&optional n) |
| 10384 | "Delete the N next (mail) articles. |
| 10385 | This command actually deletes articles. This is not a marking |
| 10386 | command. The article will disappear forever from your life, never to |
| 10387 | return. |
| 10388 | |
| 10389 | If N is negative, delete backwards. |
| 10390 | If N is nil and articles have been marked with the process mark, |
| 10391 | delete these instead. |
| 10392 | |
| 10393 | If `gnus-novice-user' is non-nil you will be asked for |
| 10394 | confirmation before the articles are deleted." |
| 10395 | (interactive "P") |
| 10396 | (unless (gnus-check-backend-function 'request-expire-articles |
| 10397 | gnus-newsgroup-name) |
| 10398 | (error "The current newsgroup does not support article deletion")) |
| 10399 | (unless (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name)) |
| 10400 | (error "Couldn't open server")) |
| 10401 | ;; Compute the list of articles to delete. |
| 10402 | (let ((articles (sort (copy-sequence (gnus-summary-work-articles n)) '<)) |
| 10403 | (nnmail-expiry-target 'delete) |
| 10404 | not-deleted) |
| 10405 | (if (and gnus-novice-user |
| 10406 | (not (gnus-yes-or-no-p |
| 10407 | (format "Do you really want to delete %s forever? " |
| 10408 | (if (> (length articles) 1) |
| 10409 | (format "these %s articles" (length articles)) |
| 10410 | "this article"))))) |
| 10411 | () |
| 10412 | ;; Delete the articles. |
| 10413 | (setq not-deleted (gnus-request-expire-articles |
| 10414 | articles gnus-newsgroup-name 'force)) |
| 10415 | (save-excursion |
| 10416 | (while articles |
| 10417 | (gnus-summary-remove-process-mark (car articles)) |
| 10418 | ;; The backend might not have been able to delete the article |
| 10419 | ;; after all. |
| 10420 | (unless (memq (car articles) not-deleted) |
| 10421 | (gnus-summary-mark-article (car articles) gnus-canceled-mark) |
| 10422 | (let* ((article (car articles)) |
| 10423 | (ghead (gnus-data-header |
| 10424 | (assoc article (gnus-data-list nil))))) |
| 10425 | (run-hook-with-args 'gnus-summary-article-delete-hook |
| 10426 | 'delete ghead gnus-newsgroup-name nil |
| 10427 | nil))) |
| 10428 | (setq articles (cdr articles)))) |
| 10429 | (when not-deleted |
| 10430 | (gnus-message 4 "Couldn't delete articles %s" not-deleted))) |
| 10431 | (gnus-summary-position-point) |
| 10432 | (gnus-set-mode-line 'summary) |
| 10433 | not-deleted)) |
| 10434 | |
| 10435 | (defun gnus-summary-edit-article (&optional arg) |
| 10436 | "Edit the current article. |
| 10437 | This will have permanent effect only in mail groups. |
| 10438 | If ARG is nil, edit the decoded articles. |
| 10439 | If ARG is 1, edit the raw articles. |
| 10440 | If ARG is 2, edit the raw articles even in read-only groups. |
| 10441 | If ARG is 3, edit the articles with the current handles. |
| 10442 | Otherwise, allow editing of articles even in read-only |
| 10443 | groups." |
| 10444 | (interactive "P") |
| 10445 | (let (force raw current-handles) |
| 10446 | (cond |
| 10447 | ((null arg)) |
| 10448 | ((eq arg 1) |
| 10449 | (setq raw t)) |
| 10450 | ((eq arg 2) |
| 10451 | (setq raw t |
| 10452 | force t)) |
| 10453 | ((eq arg 3) |
| 10454 | (setq current-handles |
| 10455 | (and (gnus-buffer-live-p gnus-article-buffer) |
| 10456 | (with-current-buffer gnus-article-buffer |
| 10457 | (prog1 |
| 10458 | gnus-article-mime-handles |
| 10459 | (setq gnus-article-mime-handles nil)))))) |
| 10460 | (t |
| 10461 | (setq force t))) |
| 10462 | (when (and raw (not force) |
| 10463 | (member gnus-newsgroup-name '("nndraft:delayed" |
| 10464 | "nndraft:drafts" |
| 10465 | "nndraft:queue"))) |
| 10466 | (error "Can't edit the raw article in group %s" |
| 10467 | gnus-newsgroup-name)) |
| 10468 | (with-current-buffer gnus-summary-buffer |
| 10469 | (let ((mail-parse-charset gnus-newsgroup-charset) |
| 10470 | (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)) |
| 10471 | (gnus-set-global-variables) |
| 10472 | (when (and (not force) |
| 10473 | (gnus-group-read-only-p)) |
| 10474 | (error "The current newsgroup does not support article editing")) |
| 10475 | (gnus-summary-show-article t) |
| 10476 | (when (and (not raw) (gnus-buffer-live-p gnus-article-buffer)) |
| 10477 | (with-current-buffer gnus-article-buffer |
| 10478 | (mm-enable-multibyte))) |
| 10479 | (if (member gnus-newsgroup-name '("nndraft:delayed" "nndraft:drafts")) |
| 10480 | (setq raw t)) |
| 10481 | (gnus-article-edit-article |
| 10482 | (if raw 'ignore |
| 10483 | `(lambda () |
| 10484 | (let ((mbl mml-buffer-list)) |
| 10485 | (setq mml-buffer-list nil) |
| 10486 | (let ((rfc2047-quote-decoded-words-containing-tspecials t)) |
| 10487 | (mime-to-mml ,'current-handles)) |
| 10488 | (let ((mbl1 mml-buffer-list)) |
| 10489 | (setq mml-buffer-list mbl) |
| 10490 | (set (make-local-variable 'mml-buffer-list) mbl1)) |
| 10491 | (gnus-make-local-hook 'kill-buffer-hook) |
| 10492 | (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))) |
| 10493 | `(lambda (no-highlight) |
| 10494 | (let ((mail-parse-charset ',gnus-newsgroup-charset) |
| 10495 | (message-options message-options) |
| 10496 | (message-options-set-recipient) |
| 10497 | (mail-parse-ignored-charsets |
| 10498 | ',gnus-newsgroup-ignored-charsets) |
| 10499 | (rfc2047-header-encoding-alist |
| 10500 | ',(let ((charset (gnus-group-name-charset |
| 10501 | (gnus-find-method-for-group |
| 10502 | gnus-newsgroup-name) |
| 10503 | gnus-newsgroup-name))) |
| 10504 | (append (list (cons "Newsgroups" charset) |
| 10505 | (cons "Followup-To" charset) |
| 10506 | (cons "Xref" charset)) |
| 10507 | rfc2047-header-encoding-alist)))) |
| 10508 | ,(if (not raw) '(progn |
| 10509 | (mml-to-mime) |
| 10510 | (mml-destroy-buffers) |
| 10511 | (remove-hook 'kill-buffer-hook |
| 10512 | 'mml-destroy-buffers t) |
| 10513 | (kill-local-variable 'mml-buffer-list))) |
| 10514 | (gnus-summary-edit-article-done |
| 10515 | ,(or (mail-header-references gnus-current-headers) "") |
| 10516 | ,(gnus-group-read-only-p) |
| 10517 | ,gnus-summary-buffer no-highlight)))))))) |
| 10518 | |
| 10519 | (defalias 'gnus-summary-edit-article-postpone 'gnus-article-edit-exit) |
| 10520 | |
| 10521 | (defun gnus-summary-edit-article-done (&optional references read-only buffer |
| 10522 | no-highlight) |
| 10523 | "Make edits to the current article permanent." |
| 10524 | (interactive) |
| 10525 | (save-excursion |
| 10526 | ;; The buffer restriction contains the entire article if it exists. |
| 10527 | (when (article-goto-body) |
| 10528 | (let ((lines (count-lines (point) (point-max))) |
| 10529 | (length (- (point-max) (point))) |
| 10530 | (case-fold-search t) |
| 10531 | (body (copy-marker (point)))) |
| 10532 | (goto-char (point-min)) |
| 10533 | (when (re-search-forward "^content-length:[ \t]\\([0-9]+\\)" body t) |
| 10534 | (delete-region (match-beginning 1) (match-end 1)) |
| 10535 | (insert (number-to-string length))) |
| 10536 | (goto-char (point-min)) |
| 10537 | (when (re-search-forward |
| 10538 | "^x-content-length:[ \t]\\([0-9]+\\)" body t) |
| 10539 | (delete-region (match-beginning 1) (match-end 1)) |
| 10540 | (insert (number-to-string length))) |
| 10541 | (goto-char (point-min)) |
| 10542 | (when (re-search-forward "^lines:[ \t]\\([0-9]+\\)" body t) |
| 10543 | (delete-region (match-beginning 1) (match-end 1)) |
| 10544 | (insert (number-to-string lines)))))) |
| 10545 | ;; Replace the article. |
| 10546 | (let ((buf (current-buffer)) |
| 10547 | (article (cdr gnus-article-current)) |
| 10548 | replace-result) |
| 10549 | (with-temp-buffer |
| 10550 | (insert-buffer-substring buf) |
| 10551 | (if (and (not read-only) |
| 10552 | (not (setq replace-result |
| 10553 | (gnus-request-replace-article |
| 10554 | article (car gnus-article-current) |
| 10555 | (current-buffer) t)))) |
| 10556 | (error "Couldn't replace article") |
| 10557 | ;; If we got a number back, then that's the new article number |
| 10558 | ;; for this article. Otherwise, the article number didn't change. |
| 10559 | (when (numberp replace-result) |
| 10560 | (with-current-buffer gnus-summary-buffer |
| 10561 | (setq gnus-newsgroup-limit (delq article gnus-newsgroup-limit)) |
| 10562 | (gnus-summary-limit gnus-newsgroup-limit) |
| 10563 | (setq article replace-result) |
| 10564 | (gnus-summary-goto-subject article t))) |
| 10565 | ;; Update the summary buffer. |
| 10566 | (if (and references |
| 10567 | (equal (message-tokenize-header references " ") |
| 10568 | (message-tokenize-header |
| 10569 | (or (message-fetch-field "references") "") " "))) |
| 10570 | ;; We only have to update this line. |
| 10571 | (save-excursion |
| 10572 | (save-restriction |
| 10573 | (message-narrow-to-head) |
| 10574 | (let ((head (buffer-substring-no-properties |
| 10575 | (point-min) (point-max))) |
| 10576 | header) |
| 10577 | (with-temp-buffer |
| 10578 | (insert (format "211 %d Article retrieved.\n" article)) |
| 10579 | (insert head) |
| 10580 | (insert ".\n") |
| 10581 | (let ((nntp-server-buffer (current-buffer))) |
| 10582 | (setq header (car (gnus-get-newsgroup-headers nil t)))) |
| 10583 | (with-current-buffer gnus-summary-buffer |
| 10584 | (gnus-data-set-header (gnus-data-find article) header) |
| 10585 | (gnus-summary-update-article-line article header) |
| 10586 | (if (gnus-summary-goto-subject article nil t) |
| 10587 | (gnus-summary-update-secondary-mark article))))))) |
| 10588 | ;; Update threads. |
| 10589 | (set-buffer (or buffer gnus-summary-buffer)) |
| 10590 | (gnus-summary-update-article article) |
| 10591 | (if (gnus-summary-goto-subject article nil t) |
| 10592 | (gnus-summary-update-secondary-mark article))) |
| 10593 | ;; Prettify the article buffer again. |
| 10594 | (unless no-highlight |
| 10595 | (with-current-buffer gnus-article-buffer |
| 10596 | ;;!!! Fix this -- article should be rehighlighted. |
| 10597 | ;;(gnus-run-hooks 'gnus-article-display-hook) |
| 10598 | (set-buffer gnus-original-article-buffer) |
| 10599 | (gnus-request-article |
| 10600 | article (car gnus-article-current) (current-buffer)))) |
| 10601 | ;; Prettify the summary buffer line. |
| 10602 | (when (gnus-visual-p 'summary-highlight 'highlight) |
| 10603 | (gnus-run-hooks 'gnus-visual-mark-article-hook)))))) |
| 10604 | |
| 10605 | (defun gnus-summary-edit-wash (key) |
| 10606 | "Perform editing command KEY in the article buffer." |
| 10607 | (interactive |
| 10608 | (list |
| 10609 | (progn |
| 10610 | (message "%s" (concat (this-command-keys) "- ")) |
| 10611 | (read-char)))) |
| 10612 | (message "") |
| 10613 | (gnus-summary-edit-article) |
| 10614 | (execute-kbd-macro (concat (this-command-keys) key)) |
| 10615 | (gnus-article-edit-done)) |
| 10616 | |
| 10617 | ;;; Respooling |
| 10618 | |
| 10619 | (defun gnus-summary-respool-query (&optional silent trace) |
| 10620 | "Query where the respool algorithm would put this article." |
| 10621 | (interactive) |
| 10622 | (let (gnus-mark-article-hook) |
| 10623 | (gnus-summary-select-article) |
| 10624 | (with-current-buffer gnus-original-article-buffer |
| 10625 | (let ((groups (nnmail-article-group 'identity trace))) |
| 10626 | (unless silent |
| 10627 | (if groups |
| 10628 | (message "This message would go to %s" |
| 10629 | (mapconcat 'car groups ", ")) |
| 10630 | (message "This message would go to no groups")) |
| 10631 | groups))))) |
| 10632 | |
| 10633 | (defun gnus-summary-respool-trace () |
| 10634 | "Trace where the respool algorithm would put this article. |
| 10635 | Display a buffer showing all fancy splitting patterns which matched." |
| 10636 | (interactive) |
| 10637 | (gnus-summary-respool-query nil t)) |
| 10638 | |
| 10639 | ;; Summary marking commands. |
| 10640 | |
| 10641 | (defun gnus-summary-kill-same-subject-and-select (&optional unmark) |
| 10642 | "Mark articles which has the same subject as read, and then select the next. |
| 10643 | If UNMARK is positive, remove any kind of mark. |
| 10644 | If UNMARK is negative, tick articles." |
| 10645 | (interactive "P") |
| 10646 | (when unmark |
| 10647 | (setq unmark (prefix-numeric-value unmark))) |
| 10648 | (let ((count |
| 10649 | (gnus-summary-mark-same-subject |
| 10650 | (gnus-summary-article-subject) unmark))) |
| 10651 | ;; Select next unread article. If auto-select-same mode, should |
| 10652 | ;; select the first unread article. |
| 10653 | (gnus-summary-next-article t (and gnus-auto-select-same |
| 10654 | (gnus-summary-article-subject))) |
| 10655 | (gnus-message 7 "%d article%s marked as %s" |
| 10656 | count (if (= count 1) " is" "s are") |
| 10657 | (if unmark "unread" "read")))) |
| 10658 | |
| 10659 | (defun gnus-summary-kill-same-subject (&optional unmark) |
| 10660 | "Mark articles which has the same subject as read. |
| 10661 | If UNMARK is positive, remove any kind of mark. |
| 10662 | If UNMARK is negative, tick articles." |
| 10663 | (interactive "P") |
| 10664 | (when unmark |
| 10665 | (setq unmark (prefix-numeric-value unmark))) |
| 10666 | (let ((count |
| 10667 | (gnus-summary-mark-same-subject |
| 10668 | (gnus-summary-article-subject) unmark))) |
| 10669 | ;; If marked as read, go to next unread subject. |
| 10670 | (when (null unmark) |
| 10671 | ;; Go to next unread subject. |
| 10672 | (gnus-summary-next-subject 1 t)) |
| 10673 | (gnus-message 7 "%d articles are marked as %s" |
| 10674 | count (if unmark "unread" "read")))) |
| 10675 | |
| 10676 | (defun gnus-summary-mark-same-subject (subject &optional unmark) |
| 10677 | "Mark articles with same SUBJECT as read, and return marked number. |
| 10678 | If optional argument UNMARK is positive, remove any kinds of marks. |
| 10679 | If optional argument UNMARK is negative, mark articles as unread instead." |
| 10680 | (let ((count 1)) |
| 10681 | (save-excursion |
| 10682 | (cond |
| 10683 | ((null unmark) ; Mark as read. |
| 10684 | (while (and |
| 10685 | (progn |
| 10686 | (gnus-summary-mark-article-as-read gnus-killed-mark) |
| 10687 | (gnus-summary-show-thread) t) |
| 10688 | (gnus-summary-find-subject subject)) |
| 10689 | (setq count (1+ count)))) |
| 10690 | ((> unmark 0) ; Tick. |
| 10691 | (while (and |
| 10692 | (progn |
| 10693 | (gnus-summary-mark-article-as-unread gnus-ticked-mark) |
| 10694 | (gnus-summary-show-thread) t) |
| 10695 | (gnus-summary-find-subject subject)) |
| 10696 | (setq count (1+ count)))) |
| 10697 | (t ; Mark as unread. |
| 10698 | (while (and |
| 10699 | (progn |
| 10700 | (gnus-summary-mark-article-as-unread gnus-unread-mark) |
| 10701 | (gnus-summary-show-thread) t) |
| 10702 | (gnus-summary-find-subject subject)) |
| 10703 | (setq count (1+ count))))) |
| 10704 | (gnus-set-mode-line 'summary) |
| 10705 | ;; Return the number of marked articles. |
| 10706 | count))) |
| 10707 | |
| 10708 | (defun gnus-summary-mark-as-processable (n &optional unmark) |
| 10709 | "Set the process mark on the next N articles. |
| 10710 | If N is negative, mark backward instead. If UNMARK is non-nil, remove |
| 10711 | the process mark instead. The difference between N and the actual |
| 10712 | number of articles marked is returned." |
| 10713 | (interactive "P") |
| 10714 | (if (and (null n) (gnus-region-active-p)) |
| 10715 | (gnus-uu-mark-region (region-beginning) (region-end) unmark) |
| 10716 | (setq n (prefix-numeric-value n)) |
| 10717 | (let ((backward (< n 0)) |
| 10718 | (n (abs n))) |
| 10719 | (while (and |
| 10720 | (> n 0) |
| 10721 | (if unmark |
| 10722 | (gnus-summary-remove-process-mark |
| 10723 | (gnus-summary-article-number)) |
| 10724 | (gnus-summary-set-process-mark (gnus-summary-article-number))) |
| 10725 | (zerop (gnus-summary-next-subject (if backward -1 1) nil t))) |
| 10726 | (setq n (1- n))) |
| 10727 | (when (/= 0 n) |
| 10728 | (gnus-message 7 "No more articles")) |
| 10729 | (gnus-summary-recenter) |
| 10730 | (gnus-summary-position-point) |
| 10731 | n))) |
| 10732 | |
| 10733 | (defun gnus-summary-unmark-as-processable (n) |
| 10734 | "Remove the process mark from the next N articles. |
| 10735 | If N is negative, unmark backward instead. The difference between N and |
| 10736 | the actual number of articles unmarked is returned." |
| 10737 | (interactive "P") |
| 10738 | (gnus-summary-mark-as-processable n t)) |
| 10739 | |
| 10740 | (defun gnus-summary-unmark-all-processable () |
| 10741 | "Remove the process mark from all articles." |
| 10742 | (interactive) |
| 10743 | (save-excursion |
| 10744 | (while gnus-newsgroup-processable |
| 10745 | (gnus-summary-remove-process-mark (car gnus-newsgroup-processable)))) |
| 10746 | (gnus-summary-position-point)) |
| 10747 | |
| 10748 | (defun gnus-summary-add-mark (article type) |
| 10749 | "Mark ARTICLE with a mark of TYPE." |
| 10750 | (let ((vtype (car (assq type gnus-article-mark-lists))) |
| 10751 | var) |
| 10752 | (if (not vtype) |
| 10753 | (error "No such mark type: %s" type) |
| 10754 | (setq var (intern (format "gnus-newsgroup-%s" type))) |
| 10755 | (set var (cons article (symbol-value var))) |
| 10756 | (if (memq type '(processable cached replied forwarded recent saved)) |
| 10757 | (gnus-summary-update-secondary-mark article) |
| 10758 | ;;; !!! This is bogus. We should find out what primary |
| 10759 | ;;; !!! mark we want to set. |
| 10760 | (gnus-summary-update-mark gnus-del-mark 'unread))))) |
| 10761 | |
| 10762 | (defun gnus-summary-mark-as-expirable (n) |
| 10763 | "Mark N articles forward as expirable. |
| 10764 | If N is negative, mark backward instead. The difference between N and |
| 10765 | the actual number of articles marked is returned." |
| 10766 | (interactive "p") |
| 10767 | (gnus-summary-mark-forward n gnus-expirable-mark)) |
| 10768 | |
| 10769 | (defun gnus-summary-mark-as-spam (n) |
| 10770 | "Mark N articles forward as spam. |
| 10771 | If N is negative, mark backward instead. The difference between N and |
| 10772 | the actual number of articles marked is returned." |
| 10773 | (interactive "p") |
| 10774 | (gnus-summary-mark-forward n gnus-spam-mark)) |
| 10775 | |
| 10776 | (defun gnus-summary-mark-article-as-replied (article) |
| 10777 | "Mark ARTICLE as replied to and update the summary line. |
| 10778 | ARTICLE can also be a list of articles." |
| 10779 | (interactive (list (gnus-summary-article-number))) |
| 10780 | (let ((articles (if (listp article) article (list article)))) |
| 10781 | (dolist (article articles) |
| 10782 | (unless (numberp article) |
| 10783 | (error "%s is not a number" article)) |
| 10784 | (push article gnus-newsgroup-replied) |
| 10785 | (let ((inhibit-read-only t)) |
| 10786 | (when (gnus-summary-goto-subject article nil t) |
| 10787 | (gnus-summary-update-secondary-mark article)))))) |
| 10788 | |
| 10789 | (defun gnus-summary-mark-article-as-forwarded (article) |
| 10790 | "Mark ARTICLE as forwarded and update the summary line. |
| 10791 | ARTICLE can also be a list of articles." |
| 10792 | (let ((articles (if (listp article) article (list article)))) |
| 10793 | (dolist (article articles) |
| 10794 | (push article gnus-newsgroup-forwarded) |
| 10795 | (let ((inhibit-read-only t)) |
| 10796 | (when (gnus-summary-goto-subject article nil t) |
| 10797 | (gnus-summary-update-secondary-mark article)))))) |
| 10798 | |
| 10799 | (defun gnus-summary-set-bookmark (article) |
| 10800 | "Set a bookmark in current article." |
| 10801 | (interactive (list (gnus-summary-article-number))) |
| 10802 | (when (or (not (get-buffer gnus-article-buffer)) |
| 10803 | (not gnus-current-article) |
| 10804 | (not gnus-article-current) |
| 10805 | (not (equal gnus-newsgroup-name (car gnus-article-current)))) |
| 10806 | (error "No current article selected")) |
| 10807 | ;; Remove old bookmark, if one exists. |
| 10808 | (gnus-alist-pull article gnus-newsgroup-bookmarks) |
| 10809 | ;; Set the new bookmark, which is on the form |
| 10810 | ;; (article-number . line-number-in-body). |
| 10811 | (push |
| 10812 | (cons article |
| 10813 | (with-current-buffer gnus-article-buffer |
| 10814 | (count-lines |
| 10815 | (min (point) |
| 10816 | (save-excursion |
| 10817 | (article-goto-body) |
| 10818 | (point))) |
| 10819 | (point)))) |
| 10820 | gnus-newsgroup-bookmarks) |
| 10821 | (gnus-message 6 "A bookmark has been added to the current article.")) |
| 10822 | |
| 10823 | (defun gnus-summary-remove-bookmark (article) |
| 10824 | "Remove the bookmark from the current article." |
| 10825 | (interactive (list (gnus-summary-article-number))) |
| 10826 | ;; Remove old bookmark, if one exists. |
| 10827 | (if (not (assq article gnus-newsgroup-bookmarks)) |
| 10828 | (gnus-message 6 "No bookmark in current article.") |
| 10829 | (gnus-alist-pull article gnus-newsgroup-bookmarks) |
| 10830 | (gnus-message 6 "Removed bookmark."))) |
| 10831 | |
| 10832 | ;; Suggested by Daniel Quinlan <quinlan@best.com>. |
| 10833 | (defun gnus-summary-mark-as-dormant (n) |
| 10834 | "Mark N articles forward as dormant. |
| 10835 | If N is negative, mark backward instead. The difference between N and |
| 10836 | the actual number of articles marked is returned." |
| 10837 | (interactive "p") |
| 10838 | (gnus-summary-mark-forward n gnus-dormant-mark)) |
| 10839 | |
| 10840 | (defun gnus-summary-set-process-mark (article) |
| 10841 | "Set the process mark on ARTICLE and update the summary line." |
| 10842 | (setq gnus-newsgroup-processable |
| 10843 | (cons article |
| 10844 | (delq article gnus-newsgroup-processable))) |
| 10845 | (when (gnus-summary-goto-subject article) |
| 10846 | (gnus-summary-show-thread) |
| 10847 | (gnus-summary-goto-subject article) |
| 10848 | (gnus-summary-update-secondary-mark article))) |
| 10849 | |
| 10850 | (defun gnus-summary-remove-process-mark (&rest articles) |
| 10851 | "Remove the process mark from ARTICLES and update the summary line." |
| 10852 | (dolist (article articles) |
| 10853 | (setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable)) |
| 10854 | (when (gnus-summary-goto-subject article) |
| 10855 | (gnus-summary-show-thread) |
| 10856 | (gnus-summary-goto-subject article) |
| 10857 | (gnus-summary-update-secondary-mark article))) |
| 10858 | t) |
| 10859 | |
| 10860 | (defun gnus-summary-set-saved-mark (article) |
| 10861 | "Set the process mark on ARTICLE and update the summary line." |
| 10862 | (push article gnus-newsgroup-saved) |
| 10863 | (when (gnus-summary-goto-subject article) |
| 10864 | (gnus-summary-update-secondary-mark article))) |
| 10865 | |
| 10866 | (defun gnus-summary-mark-forward (n &optional mark no-expire) |
| 10867 | "Mark N articles as read forwards. |
| 10868 | If N is negative, mark backwards instead. Mark with MARK, ?r by default. |
| 10869 | The difference between N and the actual number of articles marked is |
| 10870 | returned. |
| 10871 | If NO-EXPIRE, auto-expiry will be inhibited." |
| 10872 | (interactive "p") |
| 10873 | (gnus-summary-show-thread) |
| 10874 | (let ((backward (< n 0)) |
| 10875 | (gnus-summary-goto-unread |
| 10876 | (and gnus-summary-goto-unread |
| 10877 | (not (eq gnus-summary-goto-unread 'never)) |
| 10878 | (not (memq mark (list gnus-unread-mark gnus-spam-mark |
| 10879 | gnus-ticked-mark gnus-dormant-mark))))) |
| 10880 | (n (abs n)) |
| 10881 | (mark (or mark gnus-del-mark))) |
| 10882 | (while (and (> n 0) |
| 10883 | (gnus-summary-mark-article nil mark no-expire) |
| 10884 | (zerop (gnus-summary-next-subject |
| 10885 | (if backward -1 1) |
| 10886 | (and gnus-summary-goto-unread |
| 10887 | (not (eq gnus-summary-goto-unread 'never))) |
| 10888 | t))) |
| 10889 | (setq n (1- n))) |
| 10890 | (when (/= 0 n) |
| 10891 | (gnus-message 7 "No more %sarticles" (if mark "" "unread "))) |
| 10892 | (gnus-summary-recenter) |
| 10893 | (gnus-summary-position-point) |
| 10894 | (gnus-set-mode-line 'summary) |
| 10895 | n)) |
| 10896 | |
| 10897 | (defun gnus-summary-mark-article-as-read (mark) |
| 10898 | "Mark the current article quickly as read with MARK." |
| 10899 | (let ((article (gnus-summary-article-number))) |
| 10900 | (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads)) |
| 10901 | (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked)) |
| 10902 | (setq gnus-newsgroup-spam-marked (delq article gnus-newsgroup-spam-marked)) |
| 10903 | (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant)) |
| 10904 | (push (cons article mark) gnus-newsgroup-reads) |
| 10905 | ;; Possibly remove from cache, if that is used. |
| 10906 | (when gnus-use-cache |
| 10907 | (gnus-cache-enter-remove-article article)) |
| 10908 | ;; Allow the backend to change the mark. |
| 10909 | (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark)) |
| 10910 | ;; Check for auto-expiry. |
| 10911 | (when (and gnus-newsgroup-auto-expire |
| 10912 | (memq mark gnus-auto-expirable-marks)) |
| 10913 | (setq mark gnus-expirable-mark) |
| 10914 | ;; Let the backend know about the mark change. |
| 10915 | (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark)) |
| 10916 | (push article gnus-newsgroup-expirable)) |
| 10917 | ;; Set the mark in the buffer. |
| 10918 | (gnus-summary-update-mark mark 'unread) |
| 10919 | t)) |
| 10920 | |
| 10921 | (defun gnus-summary-mark-article-as-unread (mark) |
| 10922 | "Mark the current article quickly as unread with MARK." |
| 10923 | (let* ((article (gnus-summary-article-number)) |
| 10924 | (old-mark (gnus-summary-article-mark article))) |
| 10925 | ;; Allow the backend to change the mark. |
| 10926 | (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark)) |
| 10927 | (if (eq mark old-mark) |
| 10928 | t |
| 10929 | (if (<= article 0) |
| 10930 | (progn |
| 10931 | (gnus-error 1 "Can't mark negative article numbers") |
| 10932 | nil) |
| 10933 | (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked)) |
| 10934 | (setq gnus-newsgroup-spam-marked |
| 10935 | (delq article gnus-newsgroup-spam-marked)) |
| 10936 | (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant)) |
| 10937 | (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable)) |
| 10938 | (setq gnus-newsgroup-reads (delq article gnus-newsgroup-reads)) |
| 10939 | (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads)) |
| 10940 | (cond ((= mark gnus-ticked-mark) |
| 10941 | (setq gnus-newsgroup-marked |
| 10942 | (gnus-add-to-sorted-list gnus-newsgroup-marked |
| 10943 | article))) |
| 10944 | ((= mark gnus-spam-mark) |
| 10945 | (setq gnus-newsgroup-spam-marked |
| 10946 | (gnus-add-to-sorted-list gnus-newsgroup-spam-marked |
| 10947 | article))) |
| 10948 | ((= mark gnus-dormant-mark) |
| 10949 | (setq gnus-newsgroup-dormant |
| 10950 | (gnus-add-to-sorted-list gnus-newsgroup-dormant |
| 10951 | article))) |
| 10952 | (t |
| 10953 | (setq gnus-newsgroup-unreads |
| 10954 | (gnus-add-to-sorted-list gnus-newsgroup-unreads |
| 10955 | article)))) |
| 10956 | (gnus-alist-pull article gnus-newsgroup-reads) |
| 10957 | |
| 10958 | ;; See whether the article is to be put in the cache. |
| 10959 | (and gnus-use-cache |
| 10960 | (vectorp (gnus-summary-article-header article)) |
| 10961 | (save-excursion |
| 10962 | (gnus-cache-possibly-enter-article |
| 10963 | gnus-newsgroup-name article |
| 10964 | (= mark gnus-ticked-mark) |
| 10965 | (= mark gnus-dormant-mark) (= mark gnus-unread-mark)))) |
| 10966 | |
| 10967 | ;; Fix the mark. |
| 10968 | (gnus-summary-update-mark mark 'unread) |
| 10969 | t)))) |
| 10970 | |
| 10971 | (defun gnus-summary-mark-article (&optional article mark no-expire) |
| 10972 | "Mark ARTICLE with MARK. MARK can be any character. |
| 10973 | Four MARK strings are reserved: `? ' (unread), `?!' (ticked), |
| 10974 | `??' (dormant) and `?E' (expirable). |
| 10975 | If MARK is nil, then the default character `?r' is used. |
| 10976 | If ARTICLE is nil, then the article on the current line will be |
| 10977 | marked. |
| 10978 | If NO-EXPIRE, auto-expiry will be inhibited." |
| 10979 | ;; The mark might be a string. |
| 10980 | (when (stringp mark) |
| 10981 | (setq mark (aref mark 0))) |
| 10982 | ;; If no mark is given, then we check auto-expiring. |
| 10983 | (when (null mark) |
| 10984 | (setq mark gnus-del-mark)) |
| 10985 | (when (and (not no-expire) |
| 10986 | gnus-newsgroup-auto-expire |
| 10987 | (memq mark gnus-auto-expirable-marks)) |
| 10988 | (setq mark gnus-expirable-mark)) |
| 10989 | (let ((article (or article (gnus-summary-article-number))) |
| 10990 | (old-mark (gnus-summary-article-mark article))) |
| 10991 | ;; Allow the backend to change the mark. |
| 10992 | (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark)) |
| 10993 | (if (eq mark old-mark) |
| 10994 | t |
| 10995 | (unless article |
| 10996 | (error "No article on current line")) |
| 10997 | (if (not (if (or (= mark gnus-unread-mark) |
| 10998 | (= mark gnus-ticked-mark) |
| 10999 | (= mark gnus-spam-mark) |
| 11000 | (= mark gnus-dormant-mark)) |
| 11001 | (gnus-mark-article-as-unread article mark) |
| 11002 | (gnus-mark-article-as-read article mark))) |
| 11003 | t |
| 11004 | ;; See whether the article is to be put in the cache. |
| 11005 | (and gnus-use-cache |
| 11006 | (not (= mark gnus-canceled-mark)) |
| 11007 | (vectorp (gnus-summary-article-header article)) |
| 11008 | (save-excursion |
| 11009 | (gnus-cache-possibly-enter-article |
| 11010 | gnus-newsgroup-name article |
| 11011 | (= mark gnus-ticked-mark) |
| 11012 | (= mark gnus-dormant-mark) (= mark gnus-unread-mark)))) |
| 11013 | |
| 11014 | (when (gnus-summary-goto-subject article nil t) |
| 11015 | (let ((inhibit-read-only t)) |
| 11016 | (gnus-summary-show-thread) |
| 11017 | ;; Fix the mark. |
| 11018 | (gnus-summary-update-mark mark 'unread) |
| 11019 | t)))))) |
| 11020 | |
| 11021 | (defun gnus-summary-update-secondary-mark (article) |
| 11022 | "Update the secondary (read, process, cache) mark." |
| 11023 | (gnus-summary-update-mark |
| 11024 | (cond ((memq article gnus-newsgroup-processable) |
| 11025 | gnus-process-mark) |
| 11026 | ((memq article gnus-newsgroup-cached) |
| 11027 | gnus-cached-mark) |
| 11028 | ((memq article gnus-newsgroup-replied) |
| 11029 | gnus-replied-mark) |
| 11030 | ((memq article gnus-newsgroup-forwarded) |
| 11031 | gnus-forwarded-mark) |
| 11032 | ((memq article gnus-newsgroup-saved) |
| 11033 | gnus-saved-mark) |
| 11034 | ((memq article gnus-newsgroup-unseen) |
| 11035 | gnus-unseen-mark) |
| 11036 | (t gnus-no-mark)) |
| 11037 | 'replied) |
| 11038 | (when (gnus-visual-p 'summary-highlight 'highlight) |
| 11039 | (gnus-summary-highlight-line) |
| 11040 | (gnus-run-hooks 'gnus-summary-update-hook)) |
| 11041 | t) |
| 11042 | |
| 11043 | (defun gnus-summary-update-download-mark (article) |
| 11044 | "Update the download mark." |
| 11045 | (gnus-summary-update-mark |
| 11046 | (cond ((memq article gnus-newsgroup-undownloaded) |
| 11047 | gnus-undownloaded-mark) |
| 11048 | (gnus-newsgroup-agentized |
| 11049 | gnus-downloaded-mark) |
| 11050 | (t |
| 11051 | gnus-no-mark)) |
| 11052 | 'download) |
| 11053 | (gnus-summary-update-line t) |
| 11054 | t) |
| 11055 | |
| 11056 | (defun gnus-summary-update-mark (mark type) |
| 11057 | (let ((forward (cdr (assq type gnus-summary-mark-positions))) |
| 11058 | (inhibit-read-only t)) |
| 11059 | (re-search-backward "[\n\r]" (point-at-bol) 'move-to-limit) |
| 11060 | (when forward |
| 11061 | (when (looking-at "\r") |
| 11062 | (incf forward)) |
| 11063 | (when (<= (+ forward (point)) (point-max)) |
| 11064 | ;; Go to the right position on the line. |
| 11065 | (goto-char (+ forward (point))) |
| 11066 | ;; Replace the old mark with the new mark. |
| 11067 | (let ((to-insert |
| 11068 | (mm-subst-char-in-string |
| 11069 | (char-after) mark |
| 11070 | (buffer-substring (point) (1+ (point)))))) |
| 11071 | (delete-region (point) (1+ (point))) |
| 11072 | (insert to-insert)) |
| 11073 | ;; Optionally update the marks by some user rule. |
| 11074 | (when (eq type 'unread) |
| 11075 | (gnus-data-set-mark |
| 11076 | (gnus-data-find (gnus-summary-article-number)) mark) |
| 11077 | (gnus-summary-update-line (eq mark gnus-unread-mark))))))) |
| 11078 | |
| 11079 | (defun gnus-mark-article-as-read (article &optional mark) |
| 11080 | "Enter ARTICLE in the pertinent lists and remove it from others." |
| 11081 | ;; Make the article expirable. |
| 11082 | (let ((mark (or mark gnus-del-mark))) |
| 11083 | (setq gnus-newsgroup-expirable |
| 11084 | (if (= mark gnus-expirable-mark) |
| 11085 | (gnus-add-to-sorted-list gnus-newsgroup-expirable article) |
| 11086 | (delq article gnus-newsgroup-expirable))) |
| 11087 | ;; Remove from unread and marked lists. |
| 11088 | (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads)) |
| 11089 | (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked)) |
| 11090 | (setq gnus-newsgroup-spam-marked (delq article gnus-newsgroup-spam-marked)) |
| 11091 | (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant)) |
| 11092 | (push (cons article mark) gnus-newsgroup-reads) |
| 11093 | ;; Possibly remove from cache, if that is used. |
| 11094 | (when gnus-use-cache |
| 11095 | (gnus-cache-enter-remove-article article)) |
| 11096 | t)) |
| 11097 | |
| 11098 | (defun gnus-mark-article-as-unread (article &optional mark) |
| 11099 | "Enter ARTICLE in the pertinent lists and remove it from others." |
| 11100 | (let ((mark (or mark gnus-ticked-mark))) |
| 11101 | (if (<= article 0) |
| 11102 | (progn |
| 11103 | (gnus-error 1 "Can't mark negative article numbers") |
| 11104 | nil) |
| 11105 | (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked) |
| 11106 | gnus-newsgroup-spam-marked (delq article gnus-newsgroup-spam-marked) |
| 11107 | gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant) |
| 11108 | gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable) |
| 11109 | gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads)) |
| 11110 | |
| 11111 | ;; Unsuppress duplicates? |
| 11112 | (when gnus-suppress-duplicates |
| 11113 | (gnus-dup-unsuppress-article article)) |
| 11114 | |
| 11115 | (cond ((= mark gnus-ticked-mark) |
| 11116 | (setq gnus-newsgroup-marked |
| 11117 | (gnus-add-to-sorted-list gnus-newsgroup-marked article))) |
| 11118 | ((= mark gnus-spam-mark) |
| 11119 | (setq gnus-newsgroup-spam-marked |
| 11120 | (gnus-add-to-sorted-list gnus-newsgroup-spam-marked |
| 11121 | article))) |
| 11122 | ((= mark gnus-dormant-mark) |
| 11123 | (setq gnus-newsgroup-dormant |
| 11124 | (gnus-add-to-sorted-list gnus-newsgroup-dormant article))) |
| 11125 | (t |
| 11126 | (setq gnus-newsgroup-unreads |
| 11127 | (gnus-add-to-sorted-list gnus-newsgroup-unreads article)))) |
| 11128 | (gnus-alist-pull article gnus-newsgroup-reads) |
| 11129 | t))) |
| 11130 | |
| 11131 | (defun gnus-summary-tick-article-forward (n) |
| 11132 | "Tick N articles forwards. |
| 11133 | If N is negative, tick backwards instead. |
| 11134 | The difference between N and the number of articles ticked is returned." |
| 11135 | (interactive "p") |
| 11136 | (gnus-summary-mark-forward n gnus-ticked-mark)) |
| 11137 | |
| 11138 | (defun gnus-summary-tick-article-backward (n) |
| 11139 | "Tick N articles backwards. |
| 11140 | The difference between N and the number of articles ticked is returned." |
| 11141 | (interactive "p") |
| 11142 | (gnus-summary-mark-forward (- n) gnus-ticked-mark)) |
| 11143 | |
| 11144 | (defun gnus-summary-tick-article (&optional article clear-mark) |
| 11145 | "Mark current article as unread. |
| 11146 | Optional 1st argument ARTICLE specifies article number to be marked as unread. |
| 11147 | Optional 2nd argument CLEAR-MARK remove any kinds of mark." |
| 11148 | (interactive) |
| 11149 | (gnus-summary-mark-article article (if clear-mark gnus-unread-mark |
| 11150 | gnus-ticked-mark))) |
| 11151 | |
| 11152 | (defun gnus-summary-mark-as-read-forward (n) |
| 11153 | "Mark N articles as read forwards. |
| 11154 | If N is negative, mark backwards instead. |
| 11155 | The difference between N and the actual number of articles marked is |
| 11156 | returned." |
| 11157 | (interactive "p") |
| 11158 | (gnus-summary-mark-forward n gnus-del-mark gnus-inhibit-user-auto-expire)) |
| 11159 | |
| 11160 | (defun gnus-summary-mark-as-read-backward (n) |
| 11161 | "Mark the N articles as read backwards. |
| 11162 | The difference between N and the actual number of articles marked is |
| 11163 | returned." |
| 11164 | (interactive "p") |
| 11165 | (gnus-summary-mark-forward |
| 11166 | (- n) gnus-del-mark gnus-inhibit-user-auto-expire)) |
| 11167 | |
| 11168 | (defun gnus-summary-mark-as-read (&optional article mark) |
| 11169 | "Mark current article as read. |
| 11170 | ARTICLE specifies the article to be marked as read. |
| 11171 | MARK specifies a string to be inserted at the beginning of the line." |
| 11172 | (gnus-summary-mark-article article mark)) |
| 11173 | |
| 11174 | (defun gnus-summary-clear-mark-forward (n) |
| 11175 | "Clear marks from N articles forward. |
| 11176 | If N is negative, clear backward instead. |
| 11177 | The difference between N and the number of marks cleared is returned." |
| 11178 | (interactive "p") |
| 11179 | (gnus-summary-mark-forward n gnus-unread-mark)) |
| 11180 | |
| 11181 | (defun gnus-summary-clear-mark-backward (n) |
| 11182 | "Clear marks from N articles backward. |
| 11183 | The difference between N and the number of marks cleared is returned." |
| 11184 | (interactive "p") |
| 11185 | (gnus-summary-mark-forward (- n) gnus-unread-mark)) |
| 11186 | |
| 11187 | (defun gnus-summary-mark-unread-as-read () |
| 11188 | "Intended to be used by `gnus-mark-article-hook'." |
| 11189 | (when (memq gnus-current-article gnus-newsgroup-unreads) |
| 11190 | (gnus-summary-mark-article gnus-current-article gnus-read-mark))) |
| 11191 | |
| 11192 | (defun gnus-summary-mark-read-and-unread-as-read (&optional new-mark) |
| 11193 | "Intended to be used by `gnus-mark-article-hook'." |
| 11194 | (let ((mark (gnus-summary-article-mark))) |
| 11195 | (when (or (gnus-unread-mark-p mark) |
| 11196 | (gnus-read-mark-p mark)) |
| 11197 | (gnus-summary-mark-article gnus-current-article |
| 11198 | (or new-mark gnus-read-mark))))) |
| 11199 | |
| 11200 | (defun gnus-summary-mark-current-read-and-unread-as-read (&optional new-mark) |
| 11201 | "Intended to be used by `gnus-mark-article-hook'." |
| 11202 | (let ((mark (gnus-summary-article-mark))) |
| 11203 | (when (or (gnus-unread-mark-p mark) |
| 11204 | (gnus-read-mark-p mark)) |
| 11205 | (gnus-summary-mark-article (gnus-summary-article-number) |
| 11206 | (or new-mark gnus-read-mark))))) |
| 11207 | |
| 11208 | (defun gnus-summary-mark-unread-as-ticked () |
| 11209 | "Intended to be used by `gnus-mark-article-hook'." |
| 11210 | (when (memq gnus-current-article gnus-newsgroup-unreads) |
| 11211 | (gnus-summary-mark-article gnus-current-article gnus-ticked-mark))) |
| 11212 | |
| 11213 | (defun gnus-summary-mark-region-as-read (point mark all) |
| 11214 | "Mark all unread articles between point and mark as read. |
| 11215 | If given a prefix, mark all articles between point and mark as read, |
| 11216 | even ticked and dormant ones." |
| 11217 | (interactive "r\nP") |
| 11218 | (save-excursion |
| 11219 | (let (article) |
| 11220 | (goto-char point) |
| 11221 | (beginning-of-line) |
| 11222 | (while (and |
| 11223 | (< (point) mark) |
| 11224 | (progn |
| 11225 | (when (or all |
| 11226 | (memq (setq article (gnus-summary-article-number)) |
| 11227 | gnus-newsgroup-unreads)) |
| 11228 | (gnus-summary-mark-article article gnus-del-mark)) |
| 11229 | t) |
| 11230 | (gnus-summary-find-next)))))) |
| 11231 | |
| 11232 | (defun gnus-summary-mark-below (score mark) |
| 11233 | "Mark articles with score less than SCORE with MARK." |
| 11234 | (interactive "P\ncMark: ") |
| 11235 | (setq score (if score |
| 11236 | (prefix-numeric-value score) |
| 11237 | (or gnus-summary-default-score 0))) |
| 11238 | (with-current-buffer gnus-summary-buffer |
| 11239 | (goto-char (point-min)) |
| 11240 | (while |
| 11241 | (progn |
| 11242 | (and (< (gnus-summary-article-score) score) |
| 11243 | (gnus-summary-mark-article nil mark)) |
| 11244 | (gnus-summary-find-next))))) |
| 11245 | |
| 11246 | (defun gnus-summary-kill-below (&optional score) |
| 11247 | "Mark articles with score below SCORE as read." |
| 11248 | (interactive "P") |
| 11249 | (gnus-summary-mark-below score gnus-killed-mark)) |
| 11250 | |
| 11251 | (defun gnus-summary-clear-above (&optional score) |
| 11252 | "Clear all marks from articles with score above SCORE." |
| 11253 | (interactive "P") |
| 11254 | (gnus-summary-mark-above score gnus-unread-mark)) |
| 11255 | |
| 11256 | (defun gnus-summary-tick-above (&optional score) |
| 11257 | "Tick all articles with score above SCORE." |
| 11258 | (interactive "P") |
| 11259 | (gnus-summary-mark-above score gnus-ticked-mark)) |
| 11260 | |
| 11261 | (defun gnus-summary-mark-above (score mark) |
| 11262 | "Mark articles with score over SCORE with MARK." |
| 11263 | (interactive "P\ncMark: ") |
| 11264 | (setq score (if score |
| 11265 | (prefix-numeric-value score) |
| 11266 | (or gnus-summary-default-score 0))) |
| 11267 | (with-current-buffer gnus-summary-buffer |
| 11268 | (goto-char (point-min)) |
| 11269 | (while (and (progn |
| 11270 | (when (> (gnus-summary-article-score) score) |
| 11271 | (gnus-summary-mark-article nil mark)) |
| 11272 | t) |
| 11273 | (gnus-summary-find-next))))) |
| 11274 | |
| 11275 | ;; Suggested by Daniel Quinlan <quinlan@best.com>. |
| 11276 | (defalias 'gnus-summary-show-all-expunged 'gnus-summary-limit-include-expunged) |
| 11277 | (defun gnus-summary-limit-include-expunged (&optional no-error) |
| 11278 | "Display all the hidden articles that were expunged for low scores." |
| 11279 | (interactive) |
| 11280 | (let ((inhibit-read-only t)) |
| 11281 | (let ((scored gnus-newsgroup-scored) |
| 11282 | headers h) |
| 11283 | (while scored |
| 11284 | (unless (gnus-summary-article-header (caar scored)) |
| 11285 | (and (setq h (gnus-number-to-header (caar scored))) |
| 11286 | (< (cdar scored) gnus-summary-expunge-below) |
| 11287 | (push h headers))) |
| 11288 | (setq scored (cdr scored))) |
| 11289 | (if (not headers) |
| 11290 | (when (not no-error) |
| 11291 | (error "No expunged articles hidden")) |
| 11292 | (goto-char (point-min)) |
| 11293 | (push gnus-newsgroup-limit gnus-newsgroup-limits) |
| 11294 | (setq gnus-newsgroup-limit (copy-sequence gnus-newsgroup-limit)) |
| 11295 | (dolist (x headers) |
| 11296 | (push (mail-header-number x) gnus-newsgroup-limit)) |
| 11297 | (gnus-summary-prepare-unthreaded (nreverse headers)) |
| 11298 | (goto-char (point-min)) |
| 11299 | (gnus-summary-position-point) |
| 11300 | t)))) |
| 11301 | |
| 11302 | (defun gnus-summary-catchup (&optional all quietly to-here not-mark reverse) |
| 11303 | "Mark all unread articles in this newsgroup as read. |
| 11304 | If prefix argument ALL is non-nil, ticked and dormant articles will |
| 11305 | also be marked as read. |
| 11306 | If QUIETLY is non-nil, no questions will be asked. |
| 11307 | |
| 11308 | If TO-HERE is non-nil, it should be a point in the buffer. All |
| 11309 | articles before (after, if REVERSE is set) this point will be marked |
| 11310 | as read. |
| 11311 | |
| 11312 | Note that this function will only catch up the unread article |
| 11313 | in the current summary buffer limitation. |
| 11314 | |
| 11315 | The number of articles marked as read is returned." |
| 11316 | (interactive "P") |
| 11317 | (prog1 |
| 11318 | (save-excursion |
| 11319 | (when (or quietly |
| 11320 | (not gnus-interactive-catchup) ;Without confirmation? |
| 11321 | gnus-expert-user |
| 11322 | (gnus-y-or-n-p |
| 11323 | (if all |
| 11324 | "Mark absolutely all articles as read? " |
| 11325 | "Mark all unread articles as read? "))) |
| 11326 | (if (and not-mark |
| 11327 | (not gnus-newsgroup-adaptive) |
| 11328 | (not gnus-newsgroup-auto-expire) |
| 11329 | (not gnus-suppress-duplicates) |
| 11330 | (or (not gnus-use-cache) |
| 11331 | (eq gnus-use-cache 'passive))) |
| 11332 | (progn |
| 11333 | (when all |
| 11334 | (setq gnus-newsgroup-marked nil |
| 11335 | gnus-newsgroup-spam-marked nil |
| 11336 | gnus-newsgroup-dormant nil)) |
| 11337 | (setq gnus-newsgroup-unreads |
| 11338 | (gnus-sorted-nunion |
| 11339 | (gnus-sorted-intersection gnus-newsgroup-unreads |
| 11340 | gnus-newsgroup-downloadable) |
| 11341 | (gnus-sorted-difference gnus-newsgroup-unfetched |
| 11342 | gnus-newsgroup-cached)))) |
| 11343 | ;; We actually mark all articles as canceled, which we |
| 11344 | ;; have to do when using auto-expiry or adaptive scoring. |
| 11345 | (gnus-summary-show-all-threads) |
| 11346 | (if (and to-here reverse) |
| 11347 | (progn |
| 11348 | (goto-char to-here) |
| 11349 | (gnus-summary-mark-current-read-and-unread-as-read |
| 11350 | gnus-catchup-mark) |
| 11351 | (while (gnus-summary-find-next (not all)) |
| 11352 | (gnus-summary-mark-article-as-read gnus-catchup-mark))) |
| 11353 | (when (gnus-summary-first-subject (not all)) |
| 11354 | (while (and |
| 11355 | (if to-here (< (point) to-here) t) |
| 11356 | (gnus-summary-mark-article-as-read gnus-catchup-mark) |
| 11357 | (gnus-summary-find-next (not all)))))) |
| 11358 | (gnus-set-mode-line 'summary)) |
| 11359 | t)) |
| 11360 | (gnus-summary-position-point))) |
| 11361 | |
| 11362 | (defun gnus-summary-catchup-to-here (&optional all) |
| 11363 | "Mark all unticked articles before the current one as read. |
| 11364 | If ALL is non-nil, also mark ticked and dormant articles as read." |
| 11365 | (interactive "P") |
| 11366 | (save-excursion |
| 11367 | (gnus-save-hidden-threads |
| 11368 | (let ((beg (point))) |
| 11369 | ;; We check that there are unread articles. |
| 11370 | (when (or all (gnus-summary-find-prev)) |
| 11371 | (gnus-summary-catchup all t beg))))) |
| 11372 | (gnus-summary-position-point)) |
| 11373 | |
| 11374 | (defun gnus-summary-catchup-from-here (&optional all) |
| 11375 | "Mark all unticked articles after (and including) the current one as read. |
| 11376 | If ALL is non-nil, also mark ticked and dormant articles as read." |
| 11377 | (interactive "P") |
| 11378 | (save-excursion |
| 11379 | (gnus-save-hidden-threads |
| 11380 | (let ((beg (point))) |
| 11381 | ;; We check that there are unread articles. |
| 11382 | (when (or all (gnus-summary-find-next)) |
| 11383 | (gnus-summary-catchup all t beg nil t))))) |
| 11384 | (gnus-summary-position-point)) |
| 11385 | |
| 11386 | (defun gnus-summary-catchup-all (&optional quietly) |
| 11387 | "Mark all articles in this newsgroup as read. |
| 11388 | This command is dangerous. Normally, you want \\[gnus-summary-catchup] |
| 11389 | instead, which marks only unread articles as read." |
| 11390 | (interactive "P") |
| 11391 | (gnus-summary-catchup t quietly)) |
| 11392 | |
| 11393 | (defun gnus-summary-catchup-and-exit (&optional all quietly) |
| 11394 | "Mark all unread articles in this group as read, then exit. |
| 11395 | If prefix argument ALL is non-nil, all articles are marked as read. |
| 11396 | If QUIETLY is non-nil, no questions will be asked." |
| 11397 | (interactive "P") |
| 11398 | (when (gnus-summary-catchup all quietly nil 'fast) |
| 11399 | ;; Select next newsgroup or exit. |
| 11400 | (if (and (not (gnus-group-quit-config gnus-newsgroup-name)) |
| 11401 | (eq gnus-auto-select-next 'quietly)) |
| 11402 | (gnus-summary-next-group nil) |
| 11403 | (gnus-summary-exit)))) |
| 11404 | |
| 11405 | (defun gnus-summary-catchup-all-and-exit (&optional quietly) |
| 11406 | "Mark all articles in this newsgroup as read, and then exit. |
| 11407 | This command is dangerous. Normally, you want \\[gnus-summary-catchup-and-exit] |
| 11408 | instead, which marks only unread articles as read." |
| 11409 | (interactive "P") |
| 11410 | (gnus-summary-catchup-and-exit t quietly)) |
| 11411 | |
| 11412 | (defun gnus-summary-catchup-and-goto-next-group (&optional all) |
| 11413 | "Mark all articles in this group as read and select the next group. |
| 11414 | If given a prefix, mark all articles, unread as well as ticked, as |
| 11415 | read." |
| 11416 | (interactive "P") |
| 11417 | (save-excursion |
| 11418 | (gnus-summary-catchup all)) |
| 11419 | (gnus-summary-next-group)) |
| 11420 | |
| 11421 | (defun gnus-summary-catchup-and-goto-prev-group (&optional all) |
| 11422 | "Mark all articles in this group as read and select the previous group. |
| 11423 | If given a prefix, mark all articles, unread as well as ticked, as |
| 11424 | read." |
| 11425 | (interactive "P") |
| 11426 | (save-excursion |
| 11427 | (gnus-summary-catchup all)) |
| 11428 | (gnus-summary-next-group nil nil t)) |
| 11429 | |
| 11430 | ;;; |
| 11431 | ;;; with article |
| 11432 | ;;; |
| 11433 | |
| 11434 | (defmacro gnus-with-article (article &rest forms) |
| 11435 | "Select ARTICLE and perform FORMS in the original article buffer. |
| 11436 | Then replace the article with the result." |
| 11437 | `(progn |
| 11438 | ;; We don't want the article to be marked as read. |
| 11439 | (let (gnus-mark-article-hook) |
| 11440 | (gnus-summary-select-article t t nil ,article)) |
| 11441 | (set-buffer gnus-original-article-buffer) |
| 11442 | ,@forms |
| 11443 | (if (not (gnus-check-backend-function |
| 11444 | 'request-replace-article (car gnus-article-current))) |
| 11445 | (gnus-message 5 "Read-only group; not replacing") |
| 11446 | (unless (gnus-request-replace-article |
| 11447 | ,article (car gnus-article-current) |
| 11448 | (current-buffer) t) |
| 11449 | (error "Couldn't replace article"))) |
| 11450 | ;; The cache and backlog have to be flushed somewhat. |
| 11451 | (when gnus-keep-backlog |
| 11452 | (gnus-backlog-remove-article |
| 11453 | (car gnus-article-current) (cdr gnus-article-current))) |
| 11454 | (when gnus-use-cache |
| 11455 | (gnus-cache-update-article |
| 11456 | (car gnus-article-current) (cdr gnus-article-current))))) |
| 11457 | |
| 11458 | (put 'gnus-with-article 'lisp-indent-function 1) |
| 11459 | (put 'gnus-with-article 'edebug-form-spec '(form body)) |
| 11460 | |
| 11461 | ;; Thread-based commands. |
| 11462 | |
| 11463 | (defun gnus-summary-articles-in-thread (&optional article) |
| 11464 | "Return a list of all articles in the current thread. |
| 11465 | If ARTICLE is non-nil, return all articles in the thread that starts |
| 11466 | with that article." |
| 11467 | (let* ((article (or article (gnus-summary-article-number))) |
| 11468 | (data (gnus-data-find-list article)) |
| 11469 | (top-level (gnus-data-level (car data))) |
| 11470 | (top-subject |
| 11471 | (cond ((null gnus-thread-operation-ignore-subject) |
| 11472 | (gnus-simplify-subject-re |
| 11473 | (mail-header-subject (gnus-data-header (car data))))) |
| 11474 | ((eq gnus-thread-operation-ignore-subject 'fuzzy) |
| 11475 | (gnus-simplify-subject-fuzzy |
| 11476 | (mail-header-subject (gnus-data-header (car data))))) |
| 11477 | (t nil))) |
| 11478 | (end-point (save-excursion |
| 11479 | (goto-char (gnus-data-pos (car data))) |
| 11480 | (if (gnus-summary-go-to-next-thread) |
| 11481 | (point) (point-max)))) |
| 11482 | articles) |
| 11483 | (while (and data |
| 11484 | (< (gnus-data-pos (car data)) end-point)) |
| 11485 | (when (or (not top-subject) |
| 11486 | (string= top-subject |
| 11487 | (if (eq gnus-thread-operation-ignore-subject 'fuzzy) |
| 11488 | (gnus-simplify-subject-fuzzy |
| 11489 | (mail-header-subject |
| 11490 | (gnus-data-header (car data)))) |
| 11491 | (gnus-simplify-subject-re |
| 11492 | (mail-header-subject |
| 11493 | (gnus-data-header (car data))))))) |
| 11494 | (push (gnus-data-number (car data)) articles)) |
| 11495 | (unless (and (setq data (cdr data)) |
| 11496 | (> (gnus-data-level (car data)) top-level)) |
| 11497 | (setq data nil))) |
| 11498 | ;; Return the list of articles. |
| 11499 | (nreverse articles))) |
| 11500 | |
| 11501 | (defun gnus-summary-rethread-current () |
| 11502 | "Rethread the thread the current article is part of." |
| 11503 | (interactive) |
| 11504 | (let* ((gnus-show-threads t) |
| 11505 | (article (gnus-summary-article-number)) |
| 11506 | (id (mail-header-id (gnus-summary-article-header))) |
| 11507 | (gnus-newsgroup-threads (list (gnus-id-to-thread (gnus-root-id id))))) |
| 11508 | (unless id |
| 11509 | (error "No article on the current line")) |
| 11510 | (gnus-rebuild-thread id) |
| 11511 | (gnus-summary-goto-subject article))) |
| 11512 | |
| 11513 | (defun gnus-summary-reparent-thread () |
| 11514 | "Make the current article child of the marked (or previous) article. |
| 11515 | |
| 11516 | Note that the re-threading will only work if `gnus-thread-ignore-subject' |
| 11517 | is non-nil or the Subject: of both articles are the same." |
| 11518 | (interactive) |
| 11519 | (unless (not (gnus-group-read-only-p)) |
| 11520 | (error "The current newsgroup does not support article editing")) |
| 11521 | (unless (<= (length gnus-newsgroup-processable) 1) |
| 11522 | (error "No more than one article may be marked")) |
| 11523 | (let ((child (gnus-summary-article-number)) |
| 11524 | ;; First grab the marked article, otherwise one line up. |
| 11525 | (parent (if (not (null gnus-newsgroup-processable)) |
| 11526 | (car gnus-newsgroup-processable) |
| 11527 | (save-excursion |
| 11528 | (if (eq (forward-line -1) 0) |
| 11529 | (gnus-summary-article-number) |
| 11530 | (error "Beginning of summary buffer")))))) |
| 11531 | (gnus-summary-reparent-children parent (list child)))) |
| 11532 | |
| 11533 | (defun gnus-summary-reparent-children (parent children) |
| 11534 | "Make PARENT the parent of CHILDREN. |
| 11535 | When called interactively, PARENT is the current article and CHILDREN |
| 11536 | are the process-marked articles." |
| 11537 | (interactive |
| 11538 | (list (gnus-summary-article-number) |
| 11539 | (gnus-summary-work-articles nil))) |
| 11540 | (dolist (child children) |
| 11541 | (save-window-excursion |
| 11542 | (let ((gnus-article-buffer " *reparent*")) |
| 11543 | (unless (not (eq parent child)) |
| 11544 | (error "An article may not be self-referential")) |
| 11545 | (let ((message-id (mail-header-id |
| 11546 | (gnus-summary-article-header parent)))) |
| 11547 | (unless (and message-id (not (equal message-id ""))) |
| 11548 | (error "No message-id in desired parent")) |
| 11549 | (gnus-with-article child |
| 11550 | (save-restriction |
| 11551 | (goto-char (point-min)) |
| 11552 | (message-narrow-to-head) |
| 11553 | (if (re-search-forward "^References: " nil t) |
| 11554 | (progn |
| 11555 | (re-search-forward "^[^ \t]" nil t) |
| 11556 | (forward-line -1) |
| 11557 | (end-of-line) |
| 11558 | (insert " " message-id)) |
| 11559 | (insert "References: " message-id "\n")))) |
| 11560 | (set-buffer gnus-summary-buffer) |
| 11561 | (gnus-summary-unmark-all-processable) |
| 11562 | (gnus-summary-update-article child) |
| 11563 | (when (gnus-summary-goto-subject (cdr gnus-article-current) nil t) |
| 11564 | (gnus-summary-update-secondary-mark (cdr gnus-article-current))) |
| 11565 | (gnus-summary-rethread-current) |
| 11566 | (gnus-message 3 "Article %d is now the child of article %d" |
| 11567 | child parent)))))) |
| 11568 | |
| 11569 | (defun gnus-summary-toggle-threads (&optional arg) |
| 11570 | "Toggle showing conversation threads. |
| 11571 | If ARG is positive number, turn showing conversation threads on." |
| 11572 | (interactive "P") |
| 11573 | (let ((current (or (gnus-summary-article-number) gnus-newsgroup-end))) |
| 11574 | (setq gnus-show-threads |
| 11575 | (if (null arg) (not gnus-show-threads) |
| 11576 | (> (prefix-numeric-value arg) 0))) |
| 11577 | (gnus-summary-prepare) |
| 11578 | (gnus-summary-goto-subject current) |
| 11579 | (gnus-message 6 "Threading is now %s" (if gnus-show-threads "on" "off")) |
| 11580 | (gnus-summary-position-point))) |
| 11581 | |
| 11582 | (eval-and-compile |
| 11583 | (if (fboundp 'remove-overlays) |
| 11584 | (defalias 'gnus-remove-overlays 'remove-overlays) |
| 11585 | (defun gnus-remove-overlays (beg end name val) |
| 11586 | "Clear BEG and END of overlays whose property NAME has value VAL. |
| 11587 | For compatibility with XEmacs." |
| 11588 | (dolist (ov (gnus-overlays-in beg end)) |
| 11589 | (when (eq (gnus-overlay-get ov name) val) |
| 11590 | (gnus-delete-overlay ov)))))) |
| 11591 | |
| 11592 | (defun gnus-summary-show-all-threads () |
| 11593 | "Show all threads." |
| 11594 | (interactive) |
| 11595 | (gnus-remove-overlays (point-min) (point-max) 'invisible 'gnus-sum) |
| 11596 | (gnus-summary-position-point)) |
| 11597 | |
| 11598 | (defsubst gnus-summary--inv (p) |
| 11599 | (and (eq (get-char-property p 'invisible) 'gnus-sum) p)) |
| 11600 | |
| 11601 | (defun gnus-summary-show-thread () |
| 11602 | "Show thread subtrees. |
| 11603 | Returns nil if no thread was there to be shown." |
| 11604 | (interactive) |
| 11605 | (let* ((orig (point)) |
| 11606 | (end (point-at-eol)) |
| 11607 | (end (or (gnus-summary--inv end) (gnus-summary--inv (1- end)))) |
| 11608 | ;; Leave point at bol |
| 11609 | (beg (progn (beginning-of-line) (if (bobp) (point) (1- (point))))) |
| 11610 | (eoi (when end |
| 11611 | (if (fboundp 'next-single-char-property-change) |
| 11612 | ;; Note: XEmacs version of n-s-c-p-c may return nil |
| 11613 | (or (next-single-char-property-change end 'invisible) |
| 11614 | (point-max)) |
| 11615 | (while (progn |
| 11616 | (end-of-line 2) |
| 11617 | (and (not (eobp)) |
| 11618 | (eq (get-char-property (point) 'invisible) |
| 11619 | 'gnus-sum)))) |
| 11620 | (point))))) |
| 11621 | (when eoi |
| 11622 | (gnus-remove-overlays beg eoi 'invisible 'gnus-sum) |
| 11623 | (goto-char orig) |
| 11624 | (gnus-summary-position-point) |
| 11625 | eoi))) |
| 11626 | |
| 11627 | (defun gnus-summary-maybe-hide-threads () |
| 11628 | "If requested, hide the threads that should be hidden." |
| 11629 | (when (and gnus-show-threads |
| 11630 | gnus-thread-hide-subtree) |
| 11631 | (gnus-summary-hide-all-threads |
| 11632 | (if (or (consp gnus-thread-hide-subtree) |
| 11633 | (functionp gnus-thread-hide-subtree)) |
| 11634 | (gnus-make-predicate gnus-thread-hide-subtree) |
| 11635 | nil)))) |
| 11636 | |
| 11637 | ;;; Hiding predicates. |
| 11638 | |
| 11639 | (defun gnus-article-unread-p (header) |
| 11640 | (memq (mail-header-number header) gnus-newsgroup-unreads)) |
| 11641 | |
| 11642 | (defun gnus-article-unseen-p (header) |
| 11643 | (memq (mail-header-number header) gnus-newsgroup-unseen)) |
| 11644 | |
| 11645 | (defun gnus-map-articles (predicate articles) |
| 11646 | "Map PREDICATE over ARTICLES and return non-nil if any predicate is non-nil." |
| 11647 | (apply 'gnus-or (mapcar predicate |
| 11648 | (mapcar (lambda (number) |
| 11649 | (gnus-summary-article-header number)) |
| 11650 | articles)))) |
| 11651 | |
| 11652 | (defun gnus-summary-hide-all-threads (&optional predicate) |
| 11653 | "Hide all thread subtrees. |
| 11654 | If PREDICATE is supplied, threads that satisfy this predicate |
| 11655 | will not be hidden." |
| 11656 | (interactive) |
| 11657 | (save-excursion |
| 11658 | (goto-char (point-min)) |
| 11659 | (let ((end nil) |
| 11660 | (count 0)) |
| 11661 | (while (not end) |
| 11662 | (incf count) |
| 11663 | (when (zerop (mod count 1000)) |
| 11664 | (message "Hiding all threads... %d" count)) |
| 11665 | (when (or (not predicate) |
| 11666 | (gnus-map-articles |
| 11667 | predicate (gnus-summary-article-children))) |
| 11668 | (gnus-summary-hide-thread)) |
| 11669 | (setq end (not (zerop (gnus-summary-next-thread 1 t))))))) |
| 11670 | (gnus-summary-position-point)) |
| 11671 | |
| 11672 | (defun gnus-summary-hide-thread () |
| 11673 | "Hide thread subtrees. |
| 11674 | If PREDICATE is supplied, threads that satisfy this predicate |
| 11675 | will not be hidden. |
| 11676 | Returns nil if no threads were there to be hidden." |
| 11677 | (interactive) |
| 11678 | (beginning-of-line) |
| 11679 | (let ((start (point)) |
| 11680 | (starteol (line-end-position)) |
| 11681 | (article (gnus-summary-article-number))) |
| 11682 | ;; Go forward until either the buffer ends or the subthread ends. |
| 11683 | (when (and (not (eobp)) |
| 11684 | (or (zerop (gnus-summary-next-thread 1 t)) |
| 11685 | (goto-char (point-max)))) |
| 11686 | (if (and (> (point) start) |
| 11687 | ;; FIXME: this should actually search for a non-invisible \n. |
| 11688 | (search-backward "\n" start t)) |
| 11689 | (progn |
| 11690 | (when (> (point) starteol) |
| 11691 | (gnus-remove-overlays starteol (point) 'invisible 'gnus-sum) |
| 11692 | (let ((ol (gnus-make-overlay starteol (point) nil t nil))) |
| 11693 | (gnus-overlay-put ol 'invisible 'gnus-sum) |
| 11694 | (gnus-overlay-put ol 'evaporate t))) |
| 11695 | (gnus-summary-goto-subject article) |
| 11696 | (when (> start (point)) |
| 11697 | (message "Hiding the thread moved us backwards, aborting!") |
| 11698 | (goto-char (point-max)))) |
| 11699 | (goto-char start) |
| 11700 | nil)))) |
| 11701 | |
| 11702 | (defun gnus-summary-go-to-next-thread (&optional previous) |
| 11703 | "Go to the same level (or less) next thread. |
| 11704 | If PREVIOUS is non-nil, go to previous thread instead. |
| 11705 | Return the article number moved to, or nil if moving was impossible." |
| 11706 | (let ((level (gnus-summary-thread-level)) |
| 11707 | (way (if previous -1 1)) |
| 11708 | (beg (point))) |
| 11709 | (forward-line way) |
| 11710 | (while (and (not (eobp)) |
| 11711 | (< level (gnus-summary-thread-level))) |
| 11712 | (forward-line way)) |
| 11713 | (if (eobp) |
| 11714 | (progn |
| 11715 | (goto-char beg) |
| 11716 | nil) |
| 11717 | (setq beg (point)) |
| 11718 | (prog1 |
| 11719 | (gnus-summary-article-number) |
| 11720 | (goto-char beg))))) |
| 11721 | |
| 11722 | (defun gnus-summary-next-thread (n &optional silent) |
| 11723 | "Go to the same level next N'th thread. |
| 11724 | If N is negative, search backward instead. |
| 11725 | Returns the difference between N and the number of skips actually |
| 11726 | done. |
| 11727 | |
| 11728 | If SILENT, don't output messages." |
| 11729 | (interactive "p") |
| 11730 | (let ((backward (< n 0)) |
| 11731 | (n (abs n))) |
| 11732 | (while (and (> n 0) |
| 11733 | (gnus-summary-go-to-next-thread backward)) |
| 11734 | (decf n)) |
| 11735 | (unless silent |
| 11736 | (gnus-summary-position-point)) |
| 11737 | (when (and (not silent) (/= 0 n)) |
| 11738 | (gnus-message 7 "No more threads")) |
| 11739 | n)) |
| 11740 | |
| 11741 | (defun gnus-summary-prev-thread (n) |
| 11742 | "Go to the same level previous N'th thread. |
| 11743 | Returns the difference between N and the number of skips actually |
| 11744 | done." |
| 11745 | (interactive "p") |
| 11746 | (gnus-summary-next-thread (- n))) |
| 11747 | |
| 11748 | (defun gnus-summary-go-down-thread () |
| 11749 | "Go down one level in the current thread." |
| 11750 | (let ((children (gnus-summary-article-children))) |
| 11751 | (when children |
| 11752 | (gnus-summary-goto-subject (car children))))) |
| 11753 | |
| 11754 | (defun gnus-summary-go-up-thread () |
| 11755 | "Go up one level in the current thread." |
| 11756 | (let ((parent (gnus-summary-article-parent))) |
| 11757 | (when parent |
| 11758 | (gnus-summary-goto-subject parent)))) |
| 11759 | |
| 11760 | (defun gnus-summary-down-thread (n) |
| 11761 | "Go down thread N steps. |
| 11762 | If N is negative, go up instead. |
| 11763 | Returns the difference between N and how many steps down that were |
| 11764 | taken." |
| 11765 | (interactive "p") |
| 11766 | (let ((up (< n 0)) |
| 11767 | (n (abs n))) |
| 11768 | (while (and (> n 0) |
| 11769 | (if up (gnus-summary-go-up-thread) |
| 11770 | (gnus-summary-go-down-thread))) |
| 11771 | (setq n (1- n))) |
| 11772 | (gnus-summary-position-point) |
| 11773 | (when (/= 0 n) |
| 11774 | (gnus-message 7 "Can't go further")) |
| 11775 | n)) |
| 11776 | |
| 11777 | (defun gnus-summary-up-thread (n) |
| 11778 | "Go up thread N steps. |
| 11779 | If N is negative, go down instead. |
| 11780 | Returns the difference between N and how many steps down that were |
| 11781 | taken." |
| 11782 | (interactive "p") |
| 11783 | (gnus-summary-down-thread (- n))) |
| 11784 | |
| 11785 | (defun gnus-summary-top-thread () |
| 11786 | "Go to the top of the thread." |
| 11787 | (interactive) |
| 11788 | (while (gnus-summary-go-up-thread)) |
| 11789 | (gnus-summary-article-number)) |
| 11790 | |
| 11791 | (defun gnus-summary-expire-thread () |
| 11792 | "Mark articles under current thread as expired." |
| 11793 | (interactive) |
| 11794 | (gnus-summary-kill-thread 0)) |
| 11795 | |
| 11796 | (defun gnus-summary-kill-thread (&optional unmark) |
| 11797 | "Mark articles under current thread as read. |
| 11798 | If the prefix argument is positive, remove any kinds of marks. |
| 11799 | If the prefix argument is zero, mark thread as expired. |
| 11800 | If the prefix argument is negative, tick articles instead." |
| 11801 | (interactive "P") |
| 11802 | (when unmark |
| 11803 | (setq unmark (prefix-numeric-value unmark))) |
| 11804 | (let ((articles (gnus-summary-articles-in-thread)) |
| 11805 | (hide (or (null unmark) (= unmark 0)))) |
| 11806 | (save-excursion |
| 11807 | ;; Expand the thread. |
| 11808 | (gnus-summary-show-thread) |
| 11809 | ;; Mark all the articles. |
| 11810 | (while articles |
| 11811 | (gnus-summary-goto-subject (car articles)) |
| 11812 | (cond ((null unmark) |
| 11813 | (gnus-summary-mark-article-as-read gnus-killed-mark)) |
| 11814 | ((> unmark 0) |
| 11815 | (gnus-summary-mark-article-as-unread gnus-unread-mark)) |
| 11816 | ((= unmark 0) |
| 11817 | (gnus-summary-mark-article nil gnus-expirable-mark)) |
| 11818 | (t |
| 11819 | (gnus-summary-mark-article-as-unread gnus-ticked-mark))) |
| 11820 | (setq articles (cdr articles)))) |
| 11821 | ;; Hide killed subtrees when hide is true. |
| 11822 | (and hide |
| 11823 | gnus-thread-hide-killed |
| 11824 | (gnus-summary-hide-thread)) |
| 11825 | ;; If hide is t, go to next unread subject. |
| 11826 | (when hide |
| 11827 | ;; Go to next unread subject. |
| 11828 | (gnus-summary-next-subject 1 t))) |
| 11829 | (gnus-set-mode-line 'summary)) |
| 11830 | |
| 11831 | ;; Summary sorting commands |
| 11832 | |
| 11833 | (defun gnus-summary-sort-by-number (&optional reverse) |
| 11834 | "Sort the summary buffer by article number. |
| 11835 | Argument REVERSE means reverse order." |
| 11836 | (interactive "P") |
| 11837 | (gnus-summary-sort 'number reverse)) |
| 11838 | |
| 11839 | (defun gnus-summary-sort-by-most-recent-number (&optional reverse) |
| 11840 | "Sort the summary buffer by most recent article number. |
| 11841 | Argument REVERSE means reverse order." |
| 11842 | (interactive "P") |
| 11843 | (gnus-summary-sort 'most-recent-number reverse)) |
| 11844 | |
| 11845 | (defun gnus-summary-sort-by-random (&optional reverse) |
| 11846 | "Randomize the order in the summary buffer. |
| 11847 | Argument REVERSE means to randomize in reverse order." |
| 11848 | (interactive "P") |
| 11849 | (gnus-summary-sort 'random reverse)) |
| 11850 | |
| 11851 | (defun gnus-summary-sort-by-author (&optional reverse) |
| 11852 | "Sort the summary buffer by author name alphabetically. |
| 11853 | If `case-fold-search' is non-nil, case of letters is ignored. |
| 11854 | Argument REVERSE means reverse order." |
| 11855 | (interactive "P") |
| 11856 | (gnus-summary-sort 'author reverse)) |
| 11857 | |
| 11858 | (defun gnus-summary-sort-by-recipient (&optional reverse) |
| 11859 | "Sort the summary buffer by recipient name alphabetically. |
| 11860 | If `case-fold-search' is non-nil, case of letters is ignored. |
| 11861 | Argument REVERSE means reverse order." |
| 11862 | (interactive "P") |
| 11863 | (gnus-summary-sort 'recipient reverse)) |
| 11864 | |
| 11865 | (defun gnus-summary-sort-by-subject (&optional reverse) |
| 11866 | "Sort the summary buffer by subject alphabetically. `Re:'s are ignored. |
| 11867 | If `case-fold-search' is non-nil, case of letters is ignored. |
| 11868 | Argument REVERSE means reverse order." |
| 11869 | (interactive "P") |
| 11870 | (gnus-summary-sort 'subject reverse)) |
| 11871 | |
| 11872 | (defun gnus-summary-sort-by-date (&optional reverse) |
| 11873 | "Sort the summary buffer by date. |
| 11874 | Argument REVERSE means reverse order." |
| 11875 | (interactive "P") |
| 11876 | (gnus-summary-sort 'date reverse)) |
| 11877 | |
| 11878 | (defun gnus-summary-sort-by-most-recent-date (&optional reverse) |
| 11879 | "Sort the summary buffer by most recent date. |
| 11880 | Argument REVERSE means reverse order." |
| 11881 | (interactive "P") |
| 11882 | (gnus-summary-sort 'most-recent-date reverse)) |
| 11883 | |
| 11884 | (defun gnus-summary-sort-by-score (&optional reverse) |
| 11885 | "Sort the summary buffer by score. |
| 11886 | Argument REVERSE means reverse order." |
| 11887 | (interactive "P") |
| 11888 | (gnus-summary-sort 'score reverse)) |
| 11889 | |
| 11890 | (defun gnus-summary-sort-by-lines (&optional reverse) |
| 11891 | "Sort the summary buffer by the number of lines. |
| 11892 | Argument REVERSE means reverse order." |
| 11893 | (interactive "P") |
| 11894 | (gnus-summary-sort 'lines reverse)) |
| 11895 | |
| 11896 | (defun gnus-summary-sort-by-chars (&optional reverse) |
| 11897 | "Sort the summary buffer by article length. |
| 11898 | Argument REVERSE means reverse order." |
| 11899 | (interactive "P") |
| 11900 | (gnus-summary-sort 'chars reverse)) |
| 11901 | |
| 11902 | (defun gnus-summary-sort-by-original (&optional reverse) |
| 11903 | "Sort the summary buffer using the default sorting method. |
| 11904 | Argument REVERSE means reverse order." |
| 11905 | (interactive "P") |
| 11906 | (let* ((inhibit-read-only t) |
| 11907 | (gnus-summary-prepare-hook nil)) |
| 11908 | ;; We do the sorting by regenerating the threads. |
| 11909 | (gnus-summary-prepare) |
| 11910 | ;; Hide subthreads if needed. |
| 11911 | (gnus-summary-maybe-hide-threads))) |
| 11912 | |
| 11913 | (defun gnus-summary-sort (predicate reverse) |
| 11914 | "Sort summary buffer by PREDICATE. REVERSE means reverse order." |
| 11915 | (let* ((thread (intern (format "gnus-thread-sort-by-%s" predicate))) |
| 11916 | (article (intern (format "gnus-article-sort-by-%s" predicate))) |
| 11917 | (gnus-thread-sort-functions |
| 11918 | (if (not reverse) |
| 11919 | thread |
| 11920 | `(lambda (t1 t2) |
| 11921 | (,thread t2 t1)))) |
| 11922 | (gnus-sort-gathered-threads-function |
| 11923 | gnus-thread-sort-functions) |
| 11924 | (gnus-article-sort-functions |
| 11925 | (if (not reverse) |
| 11926 | article |
| 11927 | `(lambda (t1 t2) |
| 11928 | (,article t2 t1)))) |
| 11929 | (inhibit-read-only t) |
| 11930 | (gnus-summary-prepare-hook nil)) |
| 11931 | ;; We do the sorting by regenerating the threads. |
| 11932 | (gnus-summary-prepare) |
| 11933 | ;; Hide subthreads if needed. |
| 11934 | (gnus-summary-maybe-hide-threads))) |
| 11935 | |
| 11936 | ;; Summary saving commands. |
| 11937 | |
| 11938 | (defun gnus-summary-save-article (&optional n not-saved) |
| 11939 | "Save the current article using the default saver function. |
| 11940 | If N is a positive number, save the N next articles. |
| 11941 | If N is a negative number, save the N previous articles. |
| 11942 | If N is nil and any articles have been marked with the process mark, |
| 11943 | save those articles instead. |
| 11944 | The variable `gnus-default-article-saver' specifies the saver function. |
| 11945 | |
| 11946 | If the optional second argument NOT-SAVED is non-nil, articles saved |
| 11947 | will not be marked as saved." |
| 11948 | (interactive "P") |
| 11949 | (require 'gnus-art) |
| 11950 | (let* ((articles (gnus-summary-work-articles n)) |
| 11951 | (save-buffer (save-excursion |
| 11952 | (nnheader-set-temp-buffer " *Gnus Save*"))) |
| 11953 | (num (length articles)) |
| 11954 | ;; Whether to save decoded articles or raw articles. |
| 11955 | (decode (when gnus-article-save-coding-system |
| 11956 | (get gnus-default-article-saver :decode))) |
| 11957 | ;; When saving many articles in a single file, use the other |
| 11958 | ;; function to save articles other than the first one. |
| 11959 | (saver2 (get gnus-default-article-saver :function)) |
| 11960 | (gnus-prompt-before-saving (if saver2 |
| 11961 | t |
| 11962 | gnus-prompt-before-saving)) |
| 11963 | (gnus-default-article-saver gnus-default-article-saver) |
| 11964 | header file) |
| 11965 | (dolist (article articles) |
| 11966 | (setq header (gnus-summary-article-header article)) |
| 11967 | (if (not (vectorp header)) |
| 11968 | ;; This is a pseudo-article. |
| 11969 | (if (assq 'name header) |
| 11970 | (gnus-copy-file (cdr (assq 'name header))) |
| 11971 | (gnus-message 1 "Article %d is unsavable" article)) |
| 11972 | ;; This is a real article. |
| 11973 | (save-window-excursion |
| 11974 | (gnus-summary-select-article decode decode nil article) |
| 11975 | (gnus-summary-goto-subject article)) |
| 11976 | (with-current-buffer save-buffer |
| 11977 | (erase-buffer) |
| 11978 | (insert-buffer-substring (if decode |
| 11979 | gnus-article-buffer |
| 11980 | gnus-original-article-buffer))) |
| 11981 | (setq file (gnus-article-save save-buffer file num)) |
| 11982 | (gnus-summary-remove-process-mark article) |
| 11983 | (unless not-saved |
| 11984 | (gnus-summary-set-saved-mark article))) |
| 11985 | (when saver2 |
| 11986 | (setq gnus-default-article-saver saver2 |
| 11987 | saver2 nil))) |
| 11988 | (gnus-kill-buffer save-buffer) |
| 11989 | (gnus-summary-position-point) |
| 11990 | (gnus-set-mode-line 'summary) |
| 11991 | n)) |
| 11992 | |
| 11993 | (declare-function gnus-summary-save-in-pipe "gnus-art" (&optional command raw)) |
| 11994 | |
| 11995 | (defun gnus-summary-pipe-output (&optional n sym) |
| 11996 | "Pipe the current article to a subprocess. |
| 11997 | If N is a positive number, pipe the N next articles. |
| 11998 | If N is a negative number, pipe the N previous articles. |
| 11999 | If N is nil and any articles have been marked with the process mark, |
| 12000 | pipe those articles instead. |
| 12001 | The default command to which articles are piped is specified by the |
| 12002 | variable `gnus-summary-pipe-output-default-command'; if it is nil, you |
| 12003 | will be prompted for the command. |
| 12004 | |
| 12005 | The properties `:decode' and `:headers' that are put to the function |
| 12006 | symbol `gnus-summary-save-in-pipe' control whether this function |
| 12007 | decodes articles and what headers to keep (see the doc string for the |
| 12008 | `gnus-default-article-saver' variable). If SYM (the symbolic prefix) |
| 12009 | is neither omitted nor the symbol `r', force including all headers |
| 12010 | regardless of the `:headers' property. If it is the symbol `r', |
| 12011 | articles that are not decoded and include all headers will be piped |
| 12012 | no matter what the properties `:decode' and `:headers' are." |
| 12013 | (interactive (gnus-interactive "P\ny")) |
| 12014 | (require 'gnus-art) |
| 12015 | (let* ((articles (gnus-summary-work-articles n)) |
| 12016 | (result-buffer "*Shell Command Output*") |
| 12017 | (all-headers (not (memq sym '(nil r)))) |
| 12018 | (gnus-save-all-headers (or all-headers gnus-save-all-headers)) |
| 12019 | (raw (eq sym 'r)) |
| 12020 | (headers (get 'gnus-summary-save-in-pipe :headers)) |
| 12021 | command result) |
| 12022 | (unless (numberp (car articles)) |
| 12023 | (error "No article to pipe")) |
| 12024 | (setq command (gnus-read-shell-command |
| 12025 | (concat "Shell command on " |
| 12026 | (if (cdr articles) |
| 12027 | (format "these %d articles" (length articles)) |
| 12028 | "this article") |
| 12029 | ": ") |
| 12030 | gnus-summary-pipe-output-default-command)) |
| 12031 | (when (string-equal command "") |
| 12032 | (error "A command is required")) |
| 12033 | (when all-headers |
| 12034 | (put 'gnus-summary-save-in-pipe :headers nil)) |
| 12035 | (unwind-protect |
| 12036 | (while articles |
| 12037 | (gnus-summary-goto-subject (pop articles)) |
| 12038 | (save-window-excursion (gnus-summary-save-in-pipe command raw)) |
| 12039 | (when (and (get-buffer result-buffer) |
| 12040 | (not (zerop (buffer-size (get-buffer result-buffer))))) |
| 12041 | (setq result (concat result (with-current-buffer result-buffer |
| 12042 | (buffer-string)))))) |
| 12043 | (put 'gnus-summary-save-in-pipe :headers headers)) |
| 12044 | (unless (zerop (length result)) |
| 12045 | (if (with-current-buffer (get-buffer-create result-buffer) |
| 12046 | (erase-buffer) |
| 12047 | (insert result) |
| 12048 | (prog1 |
| 12049 | (and (= (count-lines (point-min) (point)) 1) |
| 12050 | (progn |
| 12051 | (end-of-line 0) |
| 12052 | (<= (current-column) |
| 12053 | (window-width (minibuffer-window))))) |
| 12054 | (goto-char (point-min)))) |
| 12055 | (message "%s" (substring result 0 -1)) |
| 12056 | (message nil) |
| 12057 | (gnus-configure-windows 'pipe))))) |
| 12058 | |
| 12059 | (defun gnus-summary-save-article-mail (&optional arg) |
| 12060 | "Append the current article to a Unix mail box file. |
| 12061 | If N is a positive number, save the N next articles. |
| 12062 | If N is a negative number, save the N previous articles. |
| 12063 | If N is nil and any articles have been marked with the process mark, |
| 12064 | save those articles instead." |
| 12065 | (interactive "P") |
| 12066 | (require 'gnus-art) |
| 12067 | (let ((gnus-default-article-saver 'gnus-summary-save-in-mail)) |
| 12068 | (gnus-summary-save-article arg))) |
| 12069 | |
| 12070 | (defun gnus-summary-save-article-rmail (&optional arg) |
| 12071 | "Append the current article to an rmail file. |
| 12072 | If N is a positive number, save the N next articles. |
| 12073 | If N is a negative number, save the N previous articles. |
| 12074 | If N is nil and any articles have been marked with the process mark, |
| 12075 | save those articles instead." |
| 12076 | (interactive "P") |
| 12077 | (require 'gnus-art) |
| 12078 | (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail)) |
| 12079 | (gnus-summary-save-article arg))) |
| 12080 | |
| 12081 | (defun gnus-summary-save-article-file (&optional arg) |
| 12082 | "Append the current article to a file. |
| 12083 | If N is a positive number, save the N next articles. |
| 12084 | If N is a negative number, save the N previous articles. |
| 12085 | If N is nil and any articles have been marked with the process mark, |
| 12086 | save those articles instead." |
| 12087 | (interactive "P") |
| 12088 | (require 'gnus-art) |
| 12089 | (let ((gnus-default-article-saver 'gnus-summary-save-in-file)) |
| 12090 | (gnus-summary-save-article arg))) |
| 12091 | |
| 12092 | (defun gnus-summary-write-article-file (&optional arg) |
| 12093 | "Write the current article to a file, deleting the previous file. |
| 12094 | If N is a positive number, save the N next articles. |
| 12095 | If N is a negative number, save the N previous articles. |
| 12096 | If N is nil and any articles have been marked with the process mark, |
| 12097 | save those articles instead." |
| 12098 | (interactive "P") |
| 12099 | (require 'gnus-art) |
| 12100 | (let ((gnus-default-article-saver 'gnus-summary-write-to-file)) |
| 12101 | (gnus-summary-save-article arg))) |
| 12102 | |
| 12103 | (defun gnus-summary-save-article-body-file (&optional arg) |
| 12104 | "Append the current article body to a file. |
| 12105 | If N is a positive number, save the N next articles. |
| 12106 | If N is a negative number, save the N previous articles. |
| 12107 | If N is nil and any articles have been marked with the process mark, |
| 12108 | save those articles instead." |
| 12109 | (interactive "P") |
| 12110 | (require 'gnus-art) |
| 12111 | (let ((gnus-default-article-saver 'gnus-summary-save-body-in-file)) |
| 12112 | (gnus-summary-save-article arg))) |
| 12113 | |
| 12114 | (defun gnus-summary-write-article-body-file (&optional arg) |
| 12115 | "Write the current article body to a file, deleting the previous file. |
| 12116 | If N is a positive number, save the N next articles. |
| 12117 | If N is a negative number, save the N previous articles. |
| 12118 | If N is nil and any articles have been marked with the process mark, |
| 12119 | save those articles instead." |
| 12120 | (interactive "P") |
| 12121 | (require 'gnus-art) |
| 12122 | (let ((gnus-default-article-saver 'gnus-summary-write-body-to-file)) |
| 12123 | (gnus-summary-save-article arg))) |
| 12124 | |
| 12125 | (defun gnus-summary-muttprint (&optional arg) |
| 12126 | "Print the current article using Muttprint. |
| 12127 | If N is a positive number, save the N next articles. |
| 12128 | If N is a negative number, save the N previous articles. |
| 12129 | If N is nil and any articles have been marked with the process mark, |
| 12130 | save those articles instead." |
| 12131 | (interactive "P") |
| 12132 | (require 'gnus-art) |
| 12133 | (let ((gnus-default-article-saver 'gnus-summary-pipe-to-muttprint)) |
| 12134 | (gnus-summary-save-article arg t))) |
| 12135 | |
| 12136 | (defun gnus-summary-pipe-message (program) |
| 12137 | "Pipe the current article through PROGRAM." |
| 12138 | (interactive "sProgram: ") |
| 12139 | (gnus-summary-select-article) |
| 12140 | (let ((mail-header-separator "")) |
| 12141 | (gnus-eval-in-buffer-window gnus-article-buffer |
| 12142 | (save-restriction |
| 12143 | (widen) |
| 12144 | (let ((start (window-start)) |
| 12145 | (inhibit-read-only t)) |
| 12146 | (message-pipe-buffer-body program) |
| 12147 | (set-window-start (get-buffer-window (current-buffer)) start)))))) |
| 12148 | |
| 12149 | (defun gnus-get-split-value (methods) |
| 12150 | "Return a value based on the split METHODS." |
| 12151 | (let (split-name method result match) |
| 12152 | (when methods |
| 12153 | (with-current-buffer gnus-original-article-buffer |
| 12154 | (save-restriction |
| 12155 | (nnheader-narrow-to-headers) |
| 12156 | (while (and methods (not split-name)) |
| 12157 | (goto-char (point-min)) |
| 12158 | (setq method (pop methods)) |
| 12159 | (setq match (car method)) |
| 12160 | (when (cond |
| 12161 | ((stringp match) |
| 12162 | ;; Regular expression. |
| 12163 | (ignore-errors |
| 12164 | (re-search-forward match nil t))) |
| 12165 | ((functionp match) |
| 12166 | ;; Function. |
| 12167 | (save-restriction |
| 12168 | (widen) |
| 12169 | (setq result (funcall match gnus-newsgroup-name)))) |
| 12170 | ((consp match) |
| 12171 | ;; Form. |
| 12172 | (save-restriction |
| 12173 | (widen) |
| 12174 | (setq result (eval match))))) |
| 12175 | (setq split-name (cdr method)) |
| 12176 | (cond ((stringp result) |
| 12177 | (push (expand-file-name |
| 12178 | result gnus-article-save-directory) |
| 12179 | split-name)) |
| 12180 | ((consp result) |
| 12181 | (setq split-name (append result split-name))))))))) |
| 12182 | (nreverse split-name))) |
| 12183 | |
| 12184 | (defun gnus-valid-move-group-p (group) |
| 12185 | (and (symbolp group) |
| 12186 | (boundp group) |
| 12187 | (symbol-name group) |
| 12188 | (symbol-value group) |
| 12189 | (gnus-get-function (gnus-find-method-for-group |
| 12190 | (symbol-name group)) 'request-accept-article t))) |
| 12191 | |
| 12192 | (defun gnus-read-move-group-name (prompt default articles prefix) |
| 12193 | "Read a group name." |
| 12194 | (let* ((split-name (gnus-get-split-value gnus-move-split-methods)) |
| 12195 | (minibuffer-confirm-incomplete nil) ; XEmacs |
| 12196 | (prom |
| 12197 | (format "%s %s to" |
| 12198 | prompt |
| 12199 | (if (> (length articles) 1) |
| 12200 | (format "these %d articles" (length articles)) |
| 12201 | "this article"))) |
| 12202 | (to-newsgroup |
| 12203 | (cond |
| 12204 | ((null split-name) |
| 12205 | (gnus-group-completing-read |
| 12206 | prom |
| 12207 | (gnus-remove-if-not 'gnus-valid-move-group-p gnus-active-hashtb t) |
| 12208 | nil prefix nil default)) |
| 12209 | ((= 1 (length split-name)) |
| 12210 | (gnus-group-completing-read |
| 12211 | prom |
| 12212 | (gnus-remove-if-not 'gnus-valid-move-group-p gnus-active-hashtb t) |
| 12213 | nil prefix 'gnus-group-history (car split-name))) |
| 12214 | (t |
| 12215 | (gnus-completing-read |
| 12216 | prom (nreverse split-name) nil nil 'gnus-group-history)))) |
| 12217 | (to-method (gnus-server-to-method (gnus-group-method to-newsgroup))) |
| 12218 | encoded) |
| 12219 | (when to-newsgroup |
| 12220 | (if (or (string= to-newsgroup "") |
| 12221 | (string= to-newsgroup prefix)) |
| 12222 | (setq to-newsgroup default)) |
| 12223 | (unless to-newsgroup |
| 12224 | (error "No group name entered")) |
| 12225 | (setq encoded (mm-encode-coding-string |
| 12226 | to-newsgroup |
| 12227 | (gnus-group-name-charset to-method to-newsgroup))) |
| 12228 | (or (gnus-active encoded) |
| 12229 | (gnus-activate-group encoded nil nil to-method) |
| 12230 | (if (gnus-y-or-n-p (format "No such group: %s. Create it? " |
| 12231 | to-newsgroup)) |
| 12232 | (or (and (gnus-request-create-group encoded to-method) |
| 12233 | (gnus-activate-group encoded nil nil to-method) |
| 12234 | (gnus-subscribe-group encoded)) |
| 12235 | (error "Couldn't create group %s" to-newsgroup))) |
| 12236 | (error "No such group: %s" to-newsgroup)) |
| 12237 | encoded))) |
| 12238 | |
| 12239 | (defvar gnus-summary-save-parts-counter) |
| 12240 | (declare-function mm-uu-dissect "mm-uu" (&optional noheader mime-type)) |
| 12241 | |
| 12242 | (defun gnus-summary-save-parts (type dir n &optional reverse) |
| 12243 | "Save parts matching TYPE to DIR. |
| 12244 | If REVERSE, save parts that do not match TYPE." |
| 12245 | (interactive |
| 12246 | (list (read-string "Save parts of type: " |
| 12247 | (or (car gnus-summary-save-parts-type-history) |
| 12248 | gnus-summary-save-parts-default-mime) |
| 12249 | 'gnus-summary-save-parts-type-history) |
| 12250 | (setq gnus-summary-save-parts-last-directory |
| 12251 | (read-directory-name "Save to directory: " |
| 12252 | gnus-summary-save-parts-last-directory |
| 12253 | nil t)) |
| 12254 | current-prefix-arg)) |
| 12255 | (gnus-summary-iterate n |
| 12256 | (let ((gnus-display-mime-function nil) |
| 12257 | gnus-article-prepare-hook |
| 12258 | gnus-article-decode-hook |
| 12259 | gnus-display-mime-function |
| 12260 | gnus-break-pages |
| 12261 | (gnus-inhibit-treatment t)) |
| 12262 | (gnus-summary-select-article)) |
| 12263 | (with-current-buffer gnus-article-buffer |
| 12264 | (let ((handles (or gnus-article-mime-handles |
| 12265 | (mm-dissect-buffer nil gnus-article-loose-mime) |
| 12266 | (and gnus-article-emulate-mime |
| 12267 | (mm-uu-dissect)))) |
| 12268 | (gnus-summary-save-parts-counter 1)) |
| 12269 | (when handles |
| 12270 | (gnus-summary-save-parts-1 type dir handles reverse) |
| 12271 | (unless gnus-article-mime-handles ;; Don't destroy this case. |
| 12272 | (mm-destroy-parts handles))))))) |
| 12273 | |
| 12274 | (defun gnus-summary-save-parts-1 (type dir handle reverse) |
| 12275 | (if (stringp (car handle)) |
| 12276 | (mapcar (lambda (h) (gnus-summary-save-parts-1 type dir h reverse)) |
| 12277 | (cdr handle)) |
| 12278 | (when (if reverse |
| 12279 | (not (string-match type (mm-handle-media-type handle))) |
| 12280 | (string-match type (mm-handle-media-type handle))) |
| 12281 | (let ((file (expand-file-name |
| 12282 | (gnus-map-function |
| 12283 | mm-file-name-rewrite-functions |
| 12284 | (file-name-nondirectory |
| 12285 | (or |
| 12286 | (mm-handle-filename handle) |
| 12287 | (format "%s.%d.%d" gnus-newsgroup-name |
| 12288 | (cdr gnus-article-current) |
| 12289 | gnus-summary-save-parts-counter)))) |
| 12290 | dir))) |
| 12291 | (incf gnus-summary-save-parts-counter) |
| 12292 | (unless (file-exists-p file) |
| 12293 | (mm-save-part-to-file handle file)))))) |
| 12294 | |
| 12295 | ;; Summary extract commands |
| 12296 | |
| 12297 | (defun gnus-summary-insert-pseudos (pslist &optional not-view) |
| 12298 | (let ((inhibit-read-only t) |
| 12299 | (article (gnus-summary-article-number)) |
| 12300 | after-article b e) |
| 12301 | (unless (gnus-summary-goto-subject article) |
| 12302 | (error "No such article: %d" article)) |
| 12303 | (gnus-summary-position-point) |
| 12304 | ;; If all commands are to be bunched up on one line, we collect |
| 12305 | ;; them here. |
| 12306 | (unless gnus-view-pseudos-separately |
| 12307 | (let ((ps (setq pslist (sort pslist 'gnus-pseudos<))) |
| 12308 | files action) |
| 12309 | (while ps |
| 12310 | (setq action (cdr (assq 'action (car ps)))) |
| 12311 | (setq files (list (cdr (assq 'name (car ps))))) |
| 12312 | (while (and ps (cdr ps) |
| 12313 | (string= (or action "1") |
| 12314 | (or (cdr (assq 'action (cadr ps))) "2"))) |
| 12315 | (push (cdr (assq 'name (cadr ps))) files) |
| 12316 | (setcdr ps (cddr ps))) |
| 12317 | (when files |
| 12318 | (when (not (string-match "%s" action)) |
| 12319 | (push " " files)) |
| 12320 | (push " " files) |
| 12321 | (when (assq 'execute (car ps)) |
| 12322 | (setcdr (assq 'execute (car ps)) |
| 12323 | (funcall (if (string-match "%s" action) |
| 12324 | 'format 'concat) |
| 12325 | action |
| 12326 | (mapconcat |
| 12327 | (lambda (f) |
| 12328 | (if (equal f " ") |
| 12329 | f |
| 12330 | (shell-quote-argument f))) |
| 12331 | files " "))))) |
| 12332 | (setq ps (cdr ps))))) |
| 12333 | (if (and gnus-view-pseudos (not not-view)) |
| 12334 | (while pslist |
| 12335 | (when (assq 'execute (car pslist)) |
| 12336 | (gnus-execute-command (cdr (assq 'execute (car pslist))) |
| 12337 | (eq gnus-view-pseudos 'not-confirm))) |
| 12338 | (setq pslist (cdr pslist))) |
| 12339 | (save-excursion |
| 12340 | (while pslist |
| 12341 | (setq after-article (or (cdr (assq 'article (car pslist))) |
| 12342 | (gnus-summary-article-number))) |
| 12343 | (gnus-summary-goto-subject after-article) |
| 12344 | (forward-line 1) |
| 12345 | (setq b (point)) |
| 12346 | (insert " " (file-name-nondirectory |
| 12347 | (cdr (assq 'name (car pslist)))) |
| 12348 | ": " (or (cdr (assq 'execute (car pslist))) "") "\n") |
| 12349 | (setq e (point)) |
| 12350 | (forward-line -1) ; back to `b' |
| 12351 | (gnus-add-text-properties |
| 12352 | b (1- e) (list 'gnus-number gnus-reffed-article-number |
| 12353 | gnus-mouse-face-prop gnus-mouse-face)) |
| 12354 | (gnus-data-enter |
| 12355 | after-article gnus-reffed-article-number |
| 12356 | gnus-unread-mark b (car pslist) 0 (- e b)) |
| 12357 | (setq gnus-newsgroup-unreads |
| 12358 | (gnus-add-to-sorted-list gnus-newsgroup-unreads |
| 12359 | gnus-reffed-article-number)) |
| 12360 | (setq gnus-reffed-article-number (1- gnus-reffed-article-number)) |
| 12361 | (setq pslist (cdr pslist))))))) |
| 12362 | |
| 12363 | (defun gnus-pseudos< (p1 p2) |
| 12364 | (let ((c1 (cdr (assq 'action p1))) |
| 12365 | (c2 (cdr (assq 'action p2)))) |
| 12366 | (and c1 c2 (string< c1 c2)))) |
| 12367 | |
| 12368 | (defun gnus-request-pseudo-article (props) |
| 12369 | (cond ((assq 'execute props) |
| 12370 | (gnus-execute-command (cdr (assq 'execute props))))) |
| 12371 | (let ((gnus-current-article (gnus-summary-article-number))) |
| 12372 | (gnus-run-hooks 'gnus-mark-article-hook))) |
| 12373 | |
| 12374 | (defun gnus-execute-command (command &optional automatic) |
| 12375 | (save-excursion |
| 12376 | (gnus-article-setup-buffer) |
| 12377 | (set-buffer gnus-article-buffer) |
| 12378 | (setq buffer-read-only nil) |
| 12379 | (let ((command (if automatic command |
| 12380 | (read-string "Command: " (cons command 0))))) |
| 12381 | (erase-buffer) |
| 12382 | (insert "$ " command "\n\n") |
| 12383 | (if gnus-view-pseudo-asynchronously |
| 12384 | (start-process "gnus-execute" (current-buffer) shell-file-name |
| 12385 | shell-command-switch command) |
| 12386 | (call-process shell-file-name nil t nil |
| 12387 | shell-command-switch command))))) |
| 12388 | |
| 12389 | ;; Summary kill commands. |
| 12390 | |
| 12391 | (defun gnus-summary-edit-global-kill (article) |
| 12392 | "Edit the \"global\" kill file." |
| 12393 | (interactive (list (gnus-summary-article-number))) |
| 12394 | (gnus-group-edit-global-kill article)) |
| 12395 | |
| 12396 | (defun gnus-summary-edit-local-kill () |
| 12397 | "Edit a local kill file applied to the current newsgroup." |
| 12398 | (interactive) |
| 12399 | (setq gnus-current-headers (gnus-summary-article-header)) |
| 12400 | (gnus-group-edit-local-kill |
| 12401 | (gnus-summary-article-number) gnus-newsgroup-name)) |
| 12402 | |
| 12403 | ;;; Header reading. |
| 12404 | |
| 12405 | (defun gnus-read-header (id &optional header) |
| 12406 | "Read the headers of article ID and enter them into the Gnus system." |
| 12407 | (let ((group gnus-newsgroup-name) |
| 12408 | (gnus-override-method |
| 12409 | (or |
| 12410 | gnus-override-method |
| 12411 | (and (gnus-news-group-p gnus-newsgroup-name) |
| 12412 | (car (gnus-refer-article-methods))))) |
| 12413 | where) |
| 12414 | ;; First we check to see whether the header in question is already |
| 12415 | ;; fetched. |
| 12416 | (if (stringp id) |
| 12417 | ;; This is a Message-ID. |
| 12418 | (setq header (or header (gnus-id-to-header id))) |
| 12419 | ;; This is an article number. |
| 12420 | (setq header (or header (gnus-summary-article-header id)))) |
| 12421 | (if (and header |
| 12422 | (not (gnus-summary-article-sparse-p (mail-header-number header)))) |
| 12423 | ;; We have found the header. |
| 12424 | header |
| 12425 | ;; We have to really fetch the header to this article. |
| 12426 | (with-current-buffer nntp-server-buffer |
| 12427 | (when (setq where (gnus-request-head id group)) |
| 12428 | (nnheader-fold-continuation-lines) |
| 12429 | (goto-char (point-max)) |
| 12430 | (insert ".\n") |
| 12431 | (goto-char (point-min)) |
| 12432 | (insert "211 ") |
| 12433 | (princ (cond |
| 12434 | ((numberp id) id) |
| 12435 | ((cdr where) (cdr where)) |
| 12436 | (header (mail-header-number header)) |
| 12437 | (t gnus-reffed-article-number)) |
| 12438 | (current-buffer)) |
| 12439 | (insert " Article retrieved.\n")) |
| 12440 | (if (or (not where) |
| 12441 | (not (setq header (car (gnus-get-newsgroup-headers nil t))))) |
| 12442 | () ; Malformed head. |
| 12443 | (unless (gnus-summary-article-sparse-p (mail-header-number header)) |
| 12444 | (when (and (bound-and-true-p gnus-registry-enabled) |
| 12445 | (not (gnus-ephemeral-group-p (car where)))) |
| 12446 | (gnus-registry-handle-action |
| 12447 | (mail-header-id header) nil |
| 12448 | (gnus-group-prefixed-name |
| 12449 | (car where) |
| 12450 | (or gnus-override-method (gnus-find-method-for-group group))) |
| 12451 | (mail-header-subject header) |
| 12452 | (mail-header-from header))) |
| 12453 | (when (and (stringp id) |
| 12454 | (or |
| 12455 | (not (string= (gnus-group-real-name group) |
| 12456 | (car where))) |
| 12457 | (not (gnus-server-equal gnus-override-method |
| 12458 | (gnus-group-method group))))) |
| 12459 | ;; If we fetched by Message-ID and the article came from |
| 12460 | ;; a different group (or server), we fudge some bogus |
| 12461 | ;; article numbers for this article. |
| 12462 | (mail-header-set-number header gnus-reffed-article-number)) |
| 12463 | (with-current-buffer gnus-summary-buffer |
| 12464 | (decf gnus-reffed-article-number) |
| 12465 | (gnus-remove-header (mail-header-number header)) |
| 12466 | (push header gnus-newsgroup-headers) |
| 12467 | (setq gnus-current-headers header) |
| 12468 | (push (mail-header-number header) gnus-newsgroup-limit))) |
| 12469 | header))))) |
| 12470 | |
| 12471 | (defun gnus-remove-header (number) |
| 12472 | "Remove header NUMBER from `gnus-newsgroup-headers'." |
| 12473 | (if (and gnus-newsgroup-headers |
| 12474 | (= number (mail-header-number (car gnus-newsgroup-headers)))) |
| 12475 | (pop gnus-newsgroup-headers) |
| 12476 | (let ((headers gnus-newsgroup-headers)) |
| 12477 | (while (and (cdr headers) |
| 12478 | (not (= number (mail-header-number (cadr headers))))) |
| 12479 | (pop headers)) |
| 12480 | (when (cdr headers) |
| 12481 | (setcdr headers (cddr headers)))))) |
| 12482 | |
| 12483 | ;;; |
| 12484 | ;;; summary highlights |
| 12485 | ;;; |
| 12486 | |
| 12487 | (defun gnus-highlight-selected-summary () |
| 12488 | "Highlight selected article in summary buffer." |
| 12489 | ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>. |
| 12490 | (when gnus-summary-selected-face |
| 12491 | (save-excursion |
| 12492 | (let* ((beg (point-at-bol)) |
| 12493 | (end (point-at-eol)) |
| 12494 | ;; Fix by Mike Dugan <dugan@bucrf16.bu.edu>. |
| 12495 | (from (if (get-text-property beg gnus-mouse-face-prop) |
| 12496 | beg |
| 12497 | (or (next-single-property-change |
| 12498 | beg gnus-mouse-face-prop nil end) |
| 12499 | beg))) |
| 12500 | (to |
| 12501 | (if (= from end) |
| 12502 | (- from 2) |
| 12503 | (or (next-single-property-change |
| 12504 | from gnus-mouse-face-prop nil end) |
| 12505 | end)))) |
| 12506 | ;; If no mouse-face prop on line we will have to = from = end, |
| 12507 | ;; so we highlight the entire line instead. |
| 12508 | (when (= (+ to 2) from) |
| 12509 | (setq from beg) |
| 12510 | (setq to end)) |
| 12511 | (if gnus-newsgroup-selected-overlay |
| 12512 | ;; Move old overlay. |
| 12513 | (gnus-move-overlay |
| 12514 | gnus-newsgroup-selected-overlay from to (current-buffer)) |
| 12515 | ;; Create new overlay. |
| 12516 | (gnus-overlay-put |
| 12517 | (setq gnus-newsgroup-selected-overlay (gnus-make-overlay from to)) |
| 12518 | 'face gnus-summary-selected-face)))))) |
| 12519 | |
| 12520 | (defvar gnus-summary-highlight-line-cached nil) |
| 12521 | (defvar gnus-summary-highlight-line-trigger nil) |
| 12522 | |
| 12523 | (defun gnus-summary-highlight-line-0 () |
| 12524 | (if (and (eq gnus-summary-highlight-line-trigger |
| 12525 | gnus-summary-highlight) |
| 12526 | gnus-summary-highlight-line-cached) |
| 12527 | gnus-summary-highlight-line-cached |
| 12528 | (setq gnus-summary-highlight-line-trigger gnus-summary-highlight |
| 12529 | gnus-summary-highlight-line-cached |
| 12530 | (let* ((cond (list 'cond)) |
| 12531 | (c cond) |
| 12532 | (list gnus-summary-highlight)) |
| 12533 | (while list |
| 12534 | (setcdr c (cons (list (caar list) (list 'quote (cdar list))) |
| 12535 | nil)) |
| 12536 | (setq c (cdr c) |
| 12537 | list (cdr list))) |
| 12538 | (gnus-byte-compile (list 'lambda nil cond)))))) |
| 12539 | |
| 12540 | (defun gnus-summary-highlight-line () |
| 12541 | "Highlight current line according to `gnus-summary-highlight'." |
| 12542 | (let* ((beg (point-at-bol)) |
| 12543 | (article (or (gnus-summary-article-number) gnus-current-article)) |
| 12544 | (score (or (cdr (assq article |
| 12545 | gnus-newsgroup-scored)) |
| 12546 | gnus-summary-default-score 0)) |
| 12547 | (mark (or (gnus-summary-article-mark) gnus-unread-mark)) |
| 12548 | (inhibit-read-only t) |
| 12549 | (default gnus-summary-default-score) |
| 12550 | (default-high gnus-summary-default-high-score) |
| 12551 | (default-low gnus-summary-default-low-score) |
| 12552 | (uncached (and gnus-summary-use-undownloaded-faces |
| 12553 | (memq article gnus-newsgroup-undownloaded) |
| 12554 | (not (memq article gnus-newsgroup-cached))))) |
| 12555 | (let ((face (funcall (gnus-summary-highlight-line-0)))) |
| 12556 | (unless (eq face (gnus-get-text-property-excluding-characters-with-faces beg 'face)) |
| 12557 | (gnus-put-text-property-excluding-characters-with-faces |
| 12558 | beg (point-at-eol) 'face |
| 12559 | (setq face (if (boundp face) (symbol-value face) face))) |
| 12560 | (when gnus-summary-highlight-line-function |
| 12561 | (funcall gnus-summary-highlight-line-function article face)))))) |
| 12562 | |
| 12563 | (defun gnus-update-read-articles (group unread &optional compute) |
| 12564 | "Update the list of read articles in GROUP. |
| 12565 | UNREAD is a sorted list." |
| 12566 | (let ((active (or gnus-newsgroup-active (gnus-active group))) |
| 12567 | (info (gnus-get-info group)) |
| 12568 | (prev 1) |
| 12569 | read) |
| 12570 | (if (or (not info) (not active)) |
| 12571 | ;; There is no info on this group if it was, in fact, |
| 12572 | ;; killed. Gnus stores no information on killed groups, so |
| 12573 | ;; there's nothing to be done. |
| 12574 | ;; One could store the information somewhere temporarily, |
| 12575 | ;; perhaps... Hmmm... |
| 12576 | () |
| 12577 | ;; Remove any negative articles numbers. |
| 12578 | (while (and unread (< (car unread) 0)) |
| 12579 | (setq unread (cdr unread))) |
| 12580 | ;; Remove any expired article numbers |
| 12581 | (while (and unread (< (car unread) (car active))) |
| 12582 | (setq unread (cdr unread))) |
| 12583 | ;; Compute the ranges of read articles by looking at the list of |
| 12584 | ;; unread articles. |
| 12585 | (while unread |
| 12586 | (when (/= (car unread) prev) |
| 12587 | (push (if (= prev (1- (car unread))) prev |
| 12588 | (cons prev (1- (car unread)))) |
| 12589 | read)) |
| 12590 | (setq prev (1+ (car unread))) |
| 12591 | (setq unread (cdr unread))) |
| 12592 | (when (<= prev (cdr active)) |
| 12593 | (push (cons prev (cdr active)) read)) |
| 12594 | (setq read (if (> (length read) 1) (nreverse read) read)) |
| 12595 | (if compute |
| 12596 | read |
| 12597 | (save-excursion |
| 12598 | (let (setmarkundo) |
| 12599 | ;; Propagate the read marks to the backend. |
| 12600 | (when (and (gnus-method-option-p |
| 12601 | (gnus-find-method-for-group group) |
| 12602 | 'server-marks) |
| 12603 | (gnus-check-backend-function 'request-set-mark group)) |
| 12604 | (let ((del (gnus-remove-from-range (gnus-info-read info) read)) |
| 12605 | (add (gnus-remove-from-range read (gnus-info-read info)))) |
| 12606 | (when (or add del) |
| 12607 | (unless (gnus-check-group group) |
| 12608 | (error "Can't open server for %s" group)) |
| 12609 | (gnus-request-set-mark |
| 12610 | group (delq nil (list (if add (list add 'add '(read))) |
| 12611 | (if del (list del 'del '(read)))))) |
| 12612 | (setq setmarkundo |
| 12613 | `(gnus-request-set-mark |
| 12614 | ,group |
| 12615 | ',(delq nil (list |
| 12616 | (if del (list del 'add '(read))) |
| 12617 | (if add (list add 'del '(read)))))))))) |
| 12618 | (set-buffer gnus-group-buffer) |
| 12619 | (gnus-undo-register |
| 12620 | `(progn |
| 12621 | (gnus-info-set-marks ',info ',(gnus-info-marks info) t) |
| 12622 | (gnus-info-set-read ',info ',(gnus-info-read info)) |
| 12623 | (gnus-get-unread-articles-in-group ',info |
| 12624 | (gnus-active ,group)) |
| 12625 | (gnus-group-update-group ,group t) |
| 12626 | ,setmarkundo)))) |
| 12627 | ;; Enter this list into the group info. |
| 12628 | (gnus-info-set-read info read) |
| 12629 | ;; Set the number of unread articles in gnus-newsrc-hashtb. |
| 12630 | (gnus-get-unread-articles-in-group info (gnus-active group)) |
| 12631 | t)))) |
| 12632 | |
| 12633 | (defun gnus-offer-save-summaries () |
| 12634 | "Offer to save all active summary buffers." |
| 12635 | (let (buffers) |
| 12636 | ;; Go through all buffers and find all summaries. |
| 12637 | (dolist (buffer (buffer-list)) |
| 12638 | (when (and (setq buffer (buffer-name buffer)) |
| 12639 | (string-match "Summary" buffer) |
| 12640 | (with-current-buffer buffer |
| 12641 | ;; We check that this is, indeed, a summary buffer. |
| 12642 | (and (eq major-mode 'gnus-summary-mode) |
| 12643 | ;; Also make sure this isn't bogus. |
| 12644 | gnus-newsgroup-prepared |
| 12645 | ;; Also make sure that this isn't a |
| 12646 | ;; dead summary buffer. |
| 12647 | (not gnus-dead-summary-mode)))) |
| 12648 | (push buffer buffers))) |
| 12649 | ;; Go through all these summary buffers and offer to save them. |
| 12650 | (when buffers |
| 12651 | (save-excursion |
| 12652 | (if (eq gnus-interactive-exit 'quiet) |
| 12653 | (dolist (buffer buffers) |
| 12654 | (switch-to-buffer buffer) |
| 12655 | (gnus-summary-exit)) |
| 12656 | (map-y-or-n-p |
| 12657 | "Update summary buffer %s? " |
| 12658 | (lambda (buf) |
| 12659 | (switch-to-buffer buf) |
| 12660 | (gnus-summary-exit)) |
| 12661 | buffers)))))) |
| 12662 | |
| 12663 | (defun gnus-summary-setup-default-charset () |
| 12664 | "Setup newsgroup default charset." |
| 12665 | (if (member gnus-newsgroup-name '("nndraft:delayed" "nndraft:drafts")) |
| 12666 | (setq gnus-newsgroup-charset nil) |
| 12667 | (let* ((ignored-charsets |
| 12668 | (or gnus-newsgroup-ephemeral-ignored-charsets |
| 12669 | (append |
| 12670 | (and gnus-newsgroup-name |
| 12671 | (gnus-parameter-ignored-charsets gnus-newsgroup-name)) |
| 12672 | gnus-newsgroup-ignored-charsets)))) |
| 12673 | (setq gnus-newsgroup-charset |
| 12674 | (or gnus-newsgroup-ephemeral-charset |
| 12675 | (and gnus-newsgroup-name |
| 12676 | (gnus-parameter-charset gnus-newsgroup-name)) |
| 12677 | gnus-default-charset)) |
| 12678 | (set (make-local-variable 'gnus-newsgroup-ignored-charsets) |
| 12679 | ignored-charsets)))) |
| 12680 | |
| 12681 | ;;; |
| 12682 | ;;; Mime Commands |
| 12683 | ;;; |
| 12684 | |
| 12685 | (defun gnus-summary-display-buttonized (&optional show-all-parts) |
| 12686 | "Display the current article buffer fully MIME-buttonized. |
| 12687 | If SHOW-ALL-PARTS (the prefix) is non-nil, all multipart/* parts are |
| 12688 | treated as multipart/mixed." |
| 12689 | (interactive "P") |
| 12690 | (require 'gnus-art) |
| 12691 | (let ((gnus-unbuttonized-mime-types nil) |
| 12692 | (gnus-mime-display-multipart-as-mixed show-all-parts)) |
| 12693 | (gnus-summary-show-article))) |
| 12694 | |
| 12695 | (defun gnus-summary-repair-multipart (article) |
| 12696 | "Add a Content-Type header to a multipart article without one." |
| 12697 | (interactive (list (gnus-summary-article-number))) |
| 12698 | (gnus-with-article article |
| 12699 | (message-narrow-to-head) |
| 12700 | (message-remove-header "Mime-Version") |
| 12701 | (goto-char (point-max)) |
| 12702 | (insert "Mime-Version: 1.0\n") |
| 12703 | (widen) |
| 12704 | (when (search-forward "\n--" nil t) |
| 12705 | (let ((separator (buffer-substring (point) (point-at-eol)))) |
| 12706 | (message-narrow-to-head) |
| 12707 | (message-remove-header "Content-Type") |
| 12708 | (goto-char (point-max)) |
| 12709 | (insert (format "Content-Type: multipart/mixed; boundary=\"%s\"\n" |
| 12710 | separator)) |
| 12711 | (widen)))) |
| 12712 | (let (gnus-mark-article-hook) |
| 12713 | (gnus-summary-select-article t t nil article))) |
| 12714 | |
| 12715 | (defun gnus-summary-toggle-display-buttonized () |
| 12716 | "Toggle the buttonizing of the article buffer." |
| 12717 | (interactive) |
| 12718 | (require 'gnus-art) |
| 12719 | (if (setq gnus-inhibit-mime-unbuttonizing |
| 12720 | (not gnus-inhibit-mime-unbuttonizing)) |
| 12721 | (let ((gnus-unbuttonized-mime-types nil)) |
| 12722 | (gnus-summary-show-article)) |
| 12723 | (gnus-summary-show-article))) |
| 12724 | |
| 12725 | ;;; |
| 12726 | ;;; Generic summary marking commands |
| 12727 | ;;; |
| 12728 | |
| 12729 | (defvar gnus-summary-marking-alist |
| 12730 | '((read gnus-del-mark "d") |
| 12731 | (unread gnus-unread-mark "u") |
| 12732 | (ticked gnus-ticked-mark "!") |
| 12733 | (dormant gnus-dormant-mark "?") |
| 12734 | (expirable gnus-expirable-mark "e")) |
| 12735 | "An alist of names/marks/keystrokes.") |
| 12736 | |
| 12737 | (defvar gnus-summary-generic-mark-map (make-sparse-keymap)) |
| 12738 | (defvar gnus-summary-mark-map) |
| 12739 | |
| 12740 | (defun gnus-summary-make-all-marking-commands () |
| 12741 | (define-key gnus-summary-mark-map "M" gnus-summary-generic-mark-map) |
| 12742 | (dolist (elem gnus-summary-marking-alist) |
| 12743 | (apply 'gnus-summary-make-marking-command elem))) |
| 12744 | |
| 12745 | (defun gnus-summary-make-marking-command (name mark keystroke) |
| 12746 | (let ((map (make-sparse-keymap))) |
| 12747 | (define-key gnus-summary-generic-mark-map keystroke map) |
| 12748 | (dolist (lway `((next "next" next nil "n") |
| 12749 | (next-unread "next unread" next t "N") |
| 12750 | (prev "previous" prev nil "p") |
| 12751 | (prev-unread "previous unread" prev t "P") |
| 12752 | (nomove "" nil nil ,keystroke))) |
| 12753 | (let ((func (gnus-summary-make-marking-command-1 |
| 12754 | mark (car lway) lway name))) |
| 12755 | (setq func (eval func)) |
| 12756 | (define-key map (nth 4 lway) func))))) |
| 12757 | |
| 12758 | (defun gnus-summary-make-marking-command-1 (mark way lway name) |
| 12759 | `(defun ,(intern |
| 12760 | (format "gnus-summary-put-mark-as-%s%s" |
| 12761 | name (if (eq way 'nomove) |
| 12762 | "" |
| 12763 | (concat "-" (symbol-name way))))) |
| 12764 | (n) |
| 12765 | ,(format |
| 12766 | "Mark the current article as %s%s. |
| 12767 | If N, the prefix, then repeat N times. |
| 12768 | If N is negative, move in reverse order. |
| 12769 | The difference between N and the actual number of articles marked is |
| 12770 | returned." |
| 12771 | name (cadr lway)) |
| 12772 | (interactive "p") |
| 12773 | (gnus-summary-generic-mark n ,mark ',(nth 2 lway) ,(nth 3 lway)))) |
| 12774 | |
| 12775 | (defun gnus-summary-generic-mark (n mark move unread) |
| 12776 | "Mark N articles with MARK." |
| 12777 | (unless (eq major-mode 'gnus-summary-mode) |
| 12778 | (error "This command can only be used in the summary buffer")) |
| 12779 | (gnus-summary-show-thread) |
| 12780 | (let ((nummove |
| 12781 | (cond |
| 12782 | ((eq move 'next) 1) |
| 12783 | ((eq move 'prev) -1) |
| 12784 | (t 0)))) |
| 12785 | (if (zerop nummove) |
| 12786 | (setq n 1) |
| 12787 | (when (< n 0) |
| 12788 | (setq n (abs n) |
| 12789 | nummove (* -1 nummove)))) |
| 12790 | (while (and (> n 0) |
| 12791 | (gnus-summary-mark-article nil mark) |
| 12792 | (zerop (gnus-summary-next-subject nummove unread t))) |
| 12793 | (setq n (1- n))) |
| 12794 | (when (/= 0 n) |
| 12795 | (gnus-message 7 "No more %sarticles" (if mark "" "unread "))) |
| 12796 | (gnus-summary-recenter) |
| 12797 | (gnus-summary-position-point) |
| 12798 | (gnus-set-mode-line 'summary) |
| 12799 | n)) |
| 12800 | |
| 12801 | (defun gnus-summary-insert-articles (articles) |
| 12802 | (when (setq articles |
| 12803 | (gnus-sorted-difference articles |
| 12804 | (mapcar (lambda (h) |
| 12805 | (mail-header-number h)) |
| 12806 | gnus-newsgroup-headers))) |
| 12807 | (setq gnus-newsgroup-headers |
| 12808 | (gnus-merge 'list |
| 12809 | gnus-newsgroup-headers |
| 12810 | (gnus-fetch-headers articles nil t) |
| 12811 | 'gnus-article-sort-by-number)) |
| 12812 | (setq gnus-newsgroup-articles |
| 12813 | (gnus-sorted-nunion gnus-newsgroup-articles articles)) |
| 12814 | ;; Suppress duplicates? |
| 12815 | (when gnus-suppress-duplicates |
| 12816 | (gnus-dup-suppress-articles)) |
| 12817 | |
| 12818 | (if (and gnus-fetch-old-headers |
| 12819 | (eq gnus-headers-retrieved-by 'nov)) |
| 12820 | ;; We might want to build some more threads first. |
| 12821 | (if (eq gnus-fetch-old-headers 'invisible) |
| 12822 | (gnus-build-all-threads) |
| 12823 | (gnus-build-old-threads)) |
| 12824 | ;; Mark the inserted articles that are unread as unread. |
| 12825 | (setq gnus-newsgroup-unreads |
| 12826 | (gnus-sorted-nunion |
| 12827 | gnus-newsgroup-unreads |
| 12828 | (gnus-sorted-nintersection |
| 12829 | (gnus-list-of-unread-articles gnus-newsgroup-name) |
| 12830 | articles))) |
| 12831 | ;; Mark the inserted articles as selected so that the information |
| 12832 | ;; of the marks having been changed by a user may be updated when |
| 12833 | ;; exiting this group. See `gnus-summary-update-info'. |
| 12834 | (dolist (art articles) |
| 12835 | (setq gnus-newsgroup-unselected (delq art gnus-newsgroup-unselected)))) |
| 12836 | ;; Let the Gnus agent mark articles as read. |
| 12837 | (when gnus-agent |
| 12838 | (gnus-agent-get-undownloaded-list)) |
| 12839 | ;; Remove list identifiers from subject |
| 12840 | (gnus-summary-remove-list-identifiers) |
| 12841 | ;; First and last article in this newsgroup. |
| 12842 | (when gnus-newsgroup-headers |
| 12843 | (setq gnus-newsgroup-begin |
| 12844 | (mail-header-number (car gnus-newsgroup-headers)) |
| 12845 | gnus-newsgroup-end |
| 12846 | (mail-header-number |
| 12847 | (gnus-last-element gnus-newsgroup-headers)))) |
| 12848 | (when gnus-use-scoring |
| 12849 | (gnus-possibly-score-headers)))) |
| 12850 | |
| 12851 | (defun gnus-summary-insert-old-articles (&optional all) |
| 12852 | "Insert all old articles in this group. |
| 12853 | If ALL is non-nil, already read articles become readable. |
| 12854 | If ALL is a number, fetch this number of articles." |
| 12855 | (interactive "P") |
| 12856 | (prog1 |
| 12857 | (let ((old (sort (mapcar 'car gnus-newsgroup-data) '<)) |
| 12858 | older len) |
| 12859 | (setq older |
| 12860 | ;; Some nntp servers lie about their active range. When |
| 12861 | ;; this happens, the active range can be in the millions. |
| 12862 | ;; Use a compressed range to avoid creating a huge list. |
| 12863 | (gnus-range-difference |
| 12864 | (gnus-range-difference (list gnus-newsgroup-active) old) |
| 12865 | gnus-newsgroup-unexist)) |
| 12866 | (setq len (gnus-range-length older)) |
| 12867 | (cond |
| 12868 | ((null older) nil) |
| 12869 | ((numberp all) |
| 12870 | (if (< all len) |
| 12871 | (let ((older-range (nreverse older))) |
| 12872 | (setq older nil) |
| 12873 | |
| 12874 | (while (> all 0) |
| 12875 | (let* ((r (pop older-range)) |
| 12876 | (min (if (numberp r) r (car r))) |
| 12877 | (max (if (numberp r) r (cdr r)))) |
| 12878 | (while (and (<= min max) |
| 12879 | (> all 0)) |
| 12880 | (push max older) |
| 12881 | (setq all (1- all) |
| 12882 | max (1- max)))))) |
| 12883 | (setq older (gnus-uncompress-range older)))) |
| 12884 | (all |
| 12885 | (setq older (gnus-uncompress-range older))) |
| 12886 | (t |
| 12887 | (when (and (numberp gnus-large-newsgroup) |
| 12888 | (> len gnus-large-newsgroup)) |
| 12889 | (let* ((cursor-in-echo-area nil) |
| 12890 | (initial (gnus-parameter-large-newsgroup-initial |
| 12891 | gnus-newsgroup-name)) |
| 12892 | (input |
| 12893 | (read-string |
| 12894 | (format |
| 12895 | "How many articles from %s (%s %d): " |
| 12896 | (gnus-group-decoded-name gnus-newsgroup-name) |
| 12897 | (if initial "max" "default") |
| 12898 | len) |
| 12899 | nil nil |
| 12900 | (and initial |
| 12901 | (number-to-string initial))))) |
| 12902 | (unless (string-match "^[ \t]*$" input) |
| 12903 | (setq all (string-to-number input)) |
| 12904 | (if (< all len) |
| 12905 | (let ((older-range (nreverse older))) |
| 12906 | (setq older nil) |
| 12907 | |
| 12908 | (while (> all 0) |
| 12909 | (let* ((r (pop older-range)) |
| 12910 | (min (if (numberp r) r (car r))) |
| 12911 | (max (if (numberp r) r (cdr r)))) |
| 12912 | (while (and (<= min max) |
| 12913 | (> all 0)) |
| 12914 | (push max older) |
| 12915 | (setq all (1- all) |
| 12916 | max (1- max)))))))))) |
| 12917 | (setq older (gnus-uncompress-range older)))) |
| 12918 | (if (not older) |
| 12919 | (message "No old news.") |
| 12920 | (gnus-summary-insert-articles older) |
| 12921 | (gnus-summary-limit (gnus-sorted-nunion old older)))) |
| 12922 | (gnus-summary-position-point))) |
| 12923 | |
| 12924 | (defun gnus-summary-insert-new-articles () |
| 12925 | "Insert all new articles in this group." |
| 12926 | (interactive) |
| 12927 | (let ((old (sort (mapcar 'car gnus-newsgroup-data) '<)) |
| 12928 | (old-high gnus-newsgroup-highest) |
| 12929 | (nnmail-fetched-sources (list t)) |
| 12930 | (new-active (gnus-activate-group gnus-newsgroup-name 'scan)) |
| 12931 | i new) |
| 12932 | (unless new-active |
| 12933 | (error "Couldn't fetch new data")) |
| 12934 | (setq gnus-newsgroup-active (gnus-copy-sequence new-active)) |
| 12935 | (setq i (cdr gnus-newsgroup-active) |
| 12936 | gnus-newsgroup-highest i) |
| 12937 | (while (> i old-high) |
| 12938 | (push i new) |
| 12939 | (decf i)) |
| 12940 | (if (not new) |
| 12941 | (message "No gnus is bad news") |
| 12942 | (gnus-summary-insert-articles new) |
| 12943 | (setq gnus-newsgroup-unreads |
| 12944 | (gnus-sorted-nunion gnus-newsgroup-unreads new)) |
| 12945 | (gnus-summary-limit (gnus-sorted-nunion old new)))) |
| 12946 | (gnus-summary-position-point)) |
| 12947 | |
| 12948 | ;;; Bookmark support for Gnus. |
| 12949 | (declare-function gnus-article-show-summary "gnus-art" ()) |
| 12950 | (declare-function bookmark-make-record-default |
| 12951 | "bookmark" (&optional no-file no-context posn)) |
| 12952 | (declare-function bookmark-prop-get "bookmark" (bookmark prop)) |
| 12953 | (declare-function bookmark-default-handler "bookmark" (bmk)) |
| 12954 | (declare-function bookmark-get-bookmark-record "bookmark" (bmk)) |
| 12955 | (defvar bookmark-yank-point) |
| 12956 | (defvar bookmark-current-buffer) |
| 12957 | |
| 12958 | (defun gnus-summary-bookmark-make-record () |
| 12959 | "Make a bookmark entry for a Gnus summary buffer." |
| 12960 | (let (pos buf) |
| 12961 | (unless (and (derived-mode-p 'gnus-summary-mode) gnus-article-current) |
| 12962 | (save-restriction ; FIXME is it necessary to widen? |
| 12963 | (widen) (setq pos (point))) ; Set position in gnus-article buffer. |
| 12964 | (setq buf "art") ; We are recording bookmark from article buffer. |
| 12965 | (setq bookmark-yank-point (point)) |
| 12966 | (setq bookmark-current-buffer (current-buffer)) |
| 12967 | (gnus-article-show-summary)) ; Go back in summary buffer. |
| 12968 | ;; We are now recording bookmark from summary buffer. |
| 12969 | (unless buf (setq buf "sum")) |
| 12970 | (let* ((subject (elt (gnus-summary-article-header) 1)) |
| 12971 | (grp (car gnus-article-current)) |
| 12972 | (art (cdr gnus-article-current)) |
| 12973 | (head (gnus-summary-article-header art)) |
| 12974 | (id (mail-header-id head))) |
| 12975 | `(,subject |
| 12976 | ,@(condition-case nil |
| 12977 | (bookmark-make-record-default 'no-file 'no-context pos) |
| 12978 | (wrong-number-of-arguments |
| 12979 | (bookmark-make-record-default 'point-only))) |
| 12980 | (location . ,(format "Gnus-%s %s:%d:%s" buf grp art id)) |
| 12981 | (group . ,grp) (article . ,art) |
| 12982 | (message-id . ,id) (handler . gnus-summary-bookmark-jump))))) |
| 12983 | |
| 12984 | ;;;###autoload |
| 12985 | (defun gnus-summary-bookmark-jump (bookmark) |
| 12986 | "Handler function for record returned by `gnus-summary-bookmark-make-record'. |
| 12987 | BOOKMARK is a bookmark name or a bookmark record." |
| 12988 | (let ((group (bookmark-prop-get bookmark 'group)) |
| 12989 | (article (bookmark-prop-get bookmark 'article)) |
| 12990 | (id (bookmark-prop-get bookmark 'message-id)) |
| 12991 | (buf (car (split-string (bookmark-prop-get bookmark 'location))))) |
| 12992 | (gnus-fetch-group group (list article)) |
| 12993 | (gnus-summary-insert-cached-articles) |
| 12994 | (gnus-summary-goto-article id nil 'force) |
| 12995 | ;; FIXME we have to wait article buffer is ready (only large buffer) |
| 12996 | ;; Is there a better solution to know that? |
| 12997 | ;; If we don't wait `bookmark-default-handler' will have no chance |
| 12998 | ;; to set position. However there is no error, just wrong pos. |
| 12999 | (sit-for 1) |
| 13000 | (when (string= buf "Gnus-art") |
| 13001 | (other-window 1)) |
| 13002 | (bookmark-default-handler |
| 13003 | `("" |
| 13004 | (buffer . ,(current-buffer)) |
| 13005 | . ,(bookmark-get-bookmark-record bookmark))))) |
| 13006 | |
| 13007 | (gnus-summary-make-all-marking-commands) |
| 13008 | |
| 13009 | (gnus-ems-redefine) |
| 13010 | |
| 13011 | (provide 'gnus-sum) |
| 13012 | |
| 13013 | (run-hooks 'gnus-sum-load-hook) |
| 13014 | |
| 13015 | ;; Local Variables: |
| 13016 | ;; coding: utf-8 |
| 13017 | ;; End: |
| 13018 | |
| 13019 | ;;; gnus-sum.el ends here |